UE5批渲染技术解析:高效绘制抗锯齿粗线条的底层实现与优化
1. 项目概述UE5批渲染绘制粗线条的底层逻辑在UE5的渲染管线中批渲染Batch Renditing是提升绘制效率、减少Draw Call的核心技术。当我们谈论“绘制粗线条”时这通常不是一个简单的几何体绘制问题而是涉及如何在保持高性能的同时将非标准几何图形如可变宽度的线条高效地集成到批处理流程中。很多开发者在使用Slate UI或自定义绘制时会遇到需要绘制抗锯齿粗线条的需求比如游戏内的雷达图、技能指示器、自定义编辑器视图的辅助线等。如果直接使用多个细三角形拼接会严重破坏批处理导致性能骤降。这次源码阅读的目标是深入UE5渲染线程的146号位置这通常指代某个特定的源码文件或函数如RenderBatch.cpp中的相关逻辑剖析引擎如何将“绘制一条参数化的粗线条”这个高级指令分解、转换为GPU能够高效批处理的图元数据。这不仅仅是调用一个DrawLine函数那么简单它背后是顶点缓冲区的动态构建、实例化数据的组织、着色器参数的传递以及渲染状态的精确切换等一系列复杂操作的精密协作。理解这个过程对于优化自定义UI渲染、开发高性能的矢量图形工具乃至深入理解现代游戏引擎的渲染架构都至关重要。2. 核心需求与方案选型解析2.1 为什么需要专门的批渲染方案来绘制粗线条在实时渲染中线条本质上是由三角形构成的。一条最简单的、无宽度的屏幕空间线段可以通过两个三角形拼成的四边形或一个单独的Line Strip来绘制。但当线条需要具备以下特性时事情就变得复杂了可变宽度线条的粗细可能随长度变化或在两端有不同的半径如圆头、方头。高质量抗锯齿为了消除锯齿感边缘需要平滑过渡这通常需要在片段着色器中进行计算。大量绘制一帧内可能需要绘制成百上千条这样的线条如复杂的网格、图表。动态数据线条的位置、颜色、粗细可能每帧都在变化。如果为每一条线条单独提交一个Draw CallGPU驱动层的开销将是灾难性的。因此必须采用批渲染将多条线条的数据顶点、索引、实例参数打包到同一个或少数几个顶点/索引缓冲区中通过一次或少数几次Draw Call完成绘制。UE5的Slate渲染器和许多内部工具链都重度依赖此技术。2.2 UE5中的粗线条绘制方案从CPU数据到GPU图元UE5处理此问题的典型模式不是使用几何着色器动态生成三角形因为移动端支持度和性能开销问题而是采用在CPU端预先计算好三角形网格Tessellation然后通过实例化Instancing或合并顶点缓冲区的方式进行批量提交。具体到“146”可能指向的上下文例如在Slate渲染的FSlateElementBatch构建过程中其流程可以拆解为数据层应用层如Slate Widget提供线条的起点、终点、粗细、颜色等参数。几何生成层在渲染线程或RHI命令录制时根据这些参数将一条“逻辑线条”展开为一个由多个三角形构成的“条带”Ribbon。例如一条圆头线条会被展开为两个半圆由多个三角形扇形构成和一个矩形主体。批处理层将多条线条生成的顶点数据位置、UV、颜色填充到动态顶点缓冲区中。关键在于相同渲染状态着色器、混合模式、纹理等的线条会被合并到同一个批次Batch中。渲染层提交这个批次顶点着色器根据顶点数据放置三角形片段着色器根据UV信息计算线条内外区域和抗锯齿。这个方案的优势在于最大化批次合并只要渲染状态一致无论多少线条都能合并渲染。顶点数据高效仅存储必要的顶点属性数据紧凑。着色器逻辑清晰抗锯齿等效果在片段着色器中用数学公式精确计算不依赖多重采样。注意这里提到的“在CPU端生成几何”是UE5 Slate渲染的常见策略适用于UI等动态2D元素。对于3D世界中的粗线条如Debug绘制引擎可能采用其他路径如使用自定义的Mesh Pass。3. 源码核心流程深度拆解假设我们聚焦于Slate渲染器中的一个典型流程相关代码可能在SlateRendering/RenderBatch.cpp或SlateRHIRenderer.cpp中。我们以伪代码和逻辑描述的形式还原其核心步骤。3.1 批次Batch的创建与键值Key生成批处理的核心是“合并”。UE5使用一个FSlateElementBatch来代表一个可绘制的批次。决定两条绘制指令能否合并的关键在于它们的**渲染键Render Key**是否相同。// 伪代码示意批次键值的构成 struct FSlateBatchKey { FShaderResource* ShaderResource; // 使用的着色器 FTexture* Texture; // 基础纹理对于线条可能是空或1x1白色纹理 ESlateDrawEffect DrawEffects; // 绘制效果如忽略Alpha、无Gamma校正等 ESlateBatchDrawFlag DrawFlags; // 绘制标志 // ... 其他渲染状态如混合模式、采样器状态等 };当请求绘制一条粗线条时系统会根据当前设置的着色器、纹理通常是GWhiteTexture和混合模式如SE_BLEND_AlphaComposite生成一个Key。如果接下来要绘制的另一条粗线条具有完全相同的Key它们就可以被合并到同一个FSlateElementBatch中。3.2 几何数据的生成与填充这是最核心的一步。一个名为GenerateLineGeometry的函数或类似功能函数会被调用。它的输入是线条的起点P0、终点P1、粗细Thickness输出是一系列顶点。几何生成原理以圆头线条为例计算方向与法线首先计算线条方向向量Dir normalize(P1 - P0)。其法线向量为Normal (-Dir.y, Dir.x)。计算四个角点对于方头线条线段的两个端点各自有两个角点。端点A的左侧点P0 - Normal * Thickness/2端点A的右侧点P0 Normal * Thickness/2端点B同理。这四个点构成一个四边形。圆头处理圆头需要将端点处的半圆离散化成多个三角形扇形。通常会以一个额外的参数控制圆头的分段数如8段。这样一个端点就会生成一个由中心点和多个边缘点构成的扇形。构建三角形列表将四边形或扇形分解为三角形并确定每个三角形的三个顶点索引。设置顶点属性为每个顶点填充数据。位置Position计算好的屏幕空间或局部空间坐标。UVTexCoord对于线条内部填充UV通常用于在片段着色器中计算“到中心线的距离”。一种常见技巧是将UV的U方向设为沿线条方向0到1V方向设为沿法线方向-1到1。这样在着色器中abs(V)就可以表示该点到中心线的归一化距离。颜色Color线条的颜色通常每个顶点都相同或支持端点渐变。// 伪代码示意为一条方头线条生成4个顶点构成两个三角形 void AddThickLineVertices(FVertexBufferData VBuffer, FVector2D P0, FVector2D P1, float Thickness, FColor Color) { FVector2D Dir (P1 - P0).GetSafeNormal(); FVector2D Normal(-Dir.Y, Dir.X); // 2D垂直向量 float HalfThick Thickness * 0.5f; FVector2D Verts[4]; Verts[0] P0 - Normal * HalfThick; // 起点左 Verts[1] P0 Normal * HalfThick; // 起点右 Verts[2] P1 - Normal * HalfThick; // 终点左 Verts[3] P1 Normal * HalfThick; // 终点右 // 定义两个三角形 (0,1,2) 和 (2,1,3) uint16 Indices[6] {0, 1, 2, 2, 1, 3}; for(int i 0; i 4; i) { FSlateVertex Vertex VBuffer.AddVertex(); Vertex.Position Verts[i]; // 巧妙设置UVU为沿线条方向V为沿法线方向归一化到[-1,1] Vertex.TexCoord FVector2D((i0||i1)?0.0f:1.0f, (i0||i2)?-1.0f:1.0f); Vertex.Color Color; } // 将索引添加到索引缓冲区 VBuffer.AppendIndices(Indices, 6); }3.3 动态顶点/索引缓冲区的管理UE5不会为每一帧都创建新的GPU缓冲区那会带来巨大的分配开销和内存碎片。相反它使用一个或多个大的、环形的Ring Buffer动态顶点缓冲区Dynamic Vertex Buffer。缓冲区申请当开始构建一个帧的渲染数据时渲染器会向RHI申请一大块显存作为动态顶点缓冲区。数据填充在遍历所有需要绘制的元素包括我们的粗线条时系统将计算好的顶点和索引数据依次追加Append到这块缓冲区的当前偏移位置。偏移管理每次填充后当前偏移指针向后移动。如果剩余空间不足以容纳下一批数据可能会刷新当前批次并提交绘制然后从缓冲区头部重新开始环形使用或申请新的缓冲区。GPU提交最终一个FSlateElementBatch包含了指向动态缓冲区中某一段数据的指针偏移量和大小以及渲染状态Key。在RHI线程这些批次被排序按状态Key以减少切换开销后转换为具体的RHI命令如RHIDrawIndexedPrimitive。实操心得理解动态缓冲区的环形管理机制是优化自定义渲染的关键。如果你在自己的插件或游戏线程中大量生成动态几何体模仿此模式能有效避免每帧CreateVertexBuffer的昂贵开销。正确的做法是预先创建一个足够大的FRHIVertexBuffer每帧用RHIUpdateBuffer或RHIUnlockBuffer来更新数据。4. 着色器端抗锯齿与像素完美渲染CPU生成了带特定UV的几何体真正的“粗线条”视觉效果是在GPU着色器中完成的。UE5的Slate着色器通常是SlateVertexShader.usf和SlatePixelShader.usf或其变体会处理这个过程。4.1 顶点着色器简单的传递顶点着色器的主要工作是将顶点位置从局部空间转换到屏幕空间对于Slate可能已经是经过投影的坐标并将UV和颜色等属性传递给片段着色器。这部分通常很简单。4.2 片段着色器实现抗锯齿粗线条核心逻辑在片段着色器。我们利用顶点着色器传递过来的UV假设V分量范围是[-1, 1]来计算片段到线条中心线的距离。// 伪HLSL代码示意片段着色器逻辑 float4 MainPS(FSlateInterpolants Interpolants) : SV_Target { // 从插值数据中获取UV和颜色 float2 UV Interpolants.TexCoord; float4 Color Interpolants.Color; // 计算到中心线的归一化距离。UV.v在中心线为0在边缘为/-1。 float DistanceToCenter abs(UV.v); // 定义线条的“边界”。例如我们希望线条在距离0.9时开始透明过渡。 float SoftEdge 0.1; // 抗锯齿软化区域宽度 float Alpha 1.0 - smoothstep(1.0 - SoftEdge, 1.0, DistanceToCenter); // 如果完全在线条外部可以提前丢弃片段以提升性能但Alpha混合时可能不需要。 // if (DistanceToCenter 1.0) discard; // 应用计算出的Alpha值 Color.a * Alpha; // 返回最终颜色引擎会根据Batch的混合模式进行混合。 return Color; }原理解释UV.v在顶点中已被精心设置在线条中心为0在几何边缘为1或-1。smoothstep函数是抗锯齿的关键。它在指定区间内进行平滑的Hermite插值。当DistanceToCenter从0.9变化到1.0时Alpha从1.0平滑过渡到0.0从而在视觉上产生边缘柔化的抗锯齿效果。SoftEdge参数控制抗锯齿区域的宽度。这个值可以与DPI缩放SlateScale联动在高分辨率屏幕上使用更小的软化区域以保持线条锐利。4.3 渲染状态与混合粗线条批次通常会启用Alpha混合。常见的混合模式是SRC_ALPHA和ONE_MINUS_SRC_ALPHA以实现透明的叠加效果。这也是为什么批次合并要求混合模式一致的原因之一。5. 性能优化与高级技巧5.1 批次合并的极限与拆分虽然合并是目标但动态顶点缓冲区有大小限制如256KB或1MB。当单帧内需要绘制的线条总几何数据量超过一个缓冲区的容量时渲染器会自动进行批次拆分。此外渲染状态的变化是批次打断的主要原因。例如在绘制一堆红色粗线条的过程中突然要绘制一条带纹理的线条。线条的混合模式从AlphaComposite切换到Additive。着色器参数发生变化如启用不同的像素着色器。理解这些打断点有助于在应用层组织绘制命令尽可能将相同状态的绘制调用集中在一起这是最有效的CPU端渲染优化。5.2 几何复杂度的权衡圆头线条比方头线条需要更多的顶点分段数决定。在需要绘制极大量线条如成千上万条的场景下可以考虑降低圆头分段数在较小的缩放级别下4段或6段可能就足够了。使用方头如果美术风格允许方头线条的几何复杂度最低。LOD细节层次根据线条在屏幕上的像素长度动态调整其几何复杂度。很远的线条甚至可以用一个四边形代替圆头。5.3 避免每帧重复生成静态几何对于位置、粗细、颜色不变的线条例如UI的静态边框最佳实践不是在每帧都重新计算其顶点数据并填充到动态缓冲区。UE5的Slate对于静态元素有缓存机制。我们可以借鉴这个思想缓存顶点数据如果一组线条是静态的在初始化时生成其顶点/索引数据并保存在一个持久的顶点缓冲区中。使用实例化如果多条线条形状相同如都是同一粗细的圆头短线但位置、颜色不同可以考虑使用实例化渲染。将线条的“模型”顶点数据放在一个缓冲区将位置、颜色等每实例数据放在另一个缓冲区通过一次Draw Call绘制所有实例。这比合并顶点数据到同一个缓冲区有时更高效尤其是当实例数据频繁变化时。6. 常见问题与调试技巧实录6.1 线条绘制不出来或闪烁检查视口和裁剪确保线条的坐标在当前的渲染视口Viewport和裁剪矩形内。Slate渲染有严格的裁剪体系超出范围的几何体会被剔除。检查深度测试如果是3D场景中的Debug绘制确认深度测试状态是否正确。可能需要禁用深度写入DepthWriteMask::Zero并设置合适的深度比较函数如LESS_EQUAL。检查缓冲区溢出如果你在自定义路径中手动填充动态缓冲区务必确保没有写入超出申请的内存范围这会导致未定义行为通常是渲染错误或崩溃。验证着色器使用图形调试工具如RenderDoc捕获一帧检查你认为是“粗线条”的Draw Call。查看其输入的顶点数据是否正确位置、UV以及像素着色器输出是否合理。6.2 线条边缘锯齿严重确认抗锯齿逻辑在片段着色器中检查计算DistanceToCenter和smoothstep的代码是否正确。确保SoftEdge参数不为零。检查UV传递确认从顶点着色器到片段着色器的UV插值是否正确。在RenderDoc中检查顶点着色器的输出。分辨率与DPI缩放抗锯齿的软化宽度SoftEdge应该是基于物理像素的。如果你的坐标系统是“点Point”或与DPI缩放相关需要将SoftEdge也进行相应的缩放。通常SoftEdge可以设置为1.0 / (Thickness * Scale)的数量级。6.3 性能瓶颈定位Profile GPU使用Unreal Insights或第三方GPU性能分析工具查看绘制粗线条的像素着色器是否成了瓶颈过度复杂或过度绘制。Profile CPU查看游戏线程或渲染线程中生成线条几何数据的函数如你自定义的GenerateLineGeometry是否耗时过高。对于大量线条应考虑算法优化或缓存。检查Draw Call数量在控制台输入stat slate或stat rhi查看Draw Call计数。如果粗线条的绘制导致Draw Call数量异常增高说明批次合并可能不理想需要检查绘制命令的提交顺序和状态切换频率。6.4 与UE5内置Debug绘制对比UE5提供了DrawDebugLine等函数用于在3D世界中绘制调试线。这些函数通常走的是不同的渲染路径如DebugRenderSceneProxy它们可能使用更简单但批次效率较低的方式。在性能要求高的场景如每帧绘制大量Debug线理解并模仿Slate的批渲染机制实现自己的高性能Debug绘制器会带来显著的性能提升。我个人在实现一个复杂的节点编辑器视图时就曾需要绘制海量的连接线。最初使用简单的每线一个四边形的方式在节点数量超过500时帧率就开始下降。后来参考Slate的批处理思路将所有连接线的几何数据在每帧收集、合并然后通过一个自定义的Slate元素进行一次性绘制Draw Call从上千次降到了个位数性能提升了数十倍。关键点在于要精心设计顶点数据格式确保UV能携带足够的信息供着色器区分线条的内外边缘这是实现高质量抗锯齿批渲染的精髓所在。

相关新闻

3分钟解锁Microsoft 365完整功能:Ohook开源方案终极指南

3分钟解锁Microsoft 365完整功能:Ohook开源方案终极指南

3分钟解锁Microsoft 365完整功能:Ohook开源方案终极指南 【免费下载链接】ohook An universal Office "activation" hook with main focus of enabling full functionality of subscription editions 项目地址: https://gitcode.com/gh_mirrors/oh/ohoo…

2026/10/12 2:05:56 阅读更多 →
重磅审核!许继电气专家组莅临芯瑞科技,共探电力光模块深度合作

重磅审核!许继电气专家组莅临芯瑞科技,共探电力光模块深度合作

近日,国内电力装备龙头企业许继电气资深审核专家任老师、张老师一行莅临成都芯瑞科技股份有限公司,开展全流程、高标准专项审厂工作。围绕特高压、智能电网、新能源、充换电、轨道交通及工业智能化五大核心业务,许继电气对配套光通信产品的宽…

2026/10/12 1:40:49 阅读更多 →
AI辅助JS逆向实战:高效定位与破解猿人学token加密参数

AI辅助JS逆向实战:高效定位与破解猿人学token加密参数

1. 项目概述:当JS逆向遇上AI辅助分析最近在爬虫圈子里,猿人学的题目一直是检验JS逆向功力的“试金石”。第二届第三题,那个绕来绕去的token加密参数,相信让不少朋友挠头。传统的逆向方法,比如扣代码、补环境、下断点&a…

2026/10/10 8:53:08 阅读更多 →

最新新闻

物理与动画系统架构深度解析:从帧循环到Transform协同的引擎设计要点

物理与动画系统架构深度解析:从帧循环到Transform协同的引擎设计要点

做引擎这几年,最常被问到的一个问题就是:物理和动画这两个模块放在一起讲,是不是有点强行组CP?其实不是,这俩在帧循环里的位置紧挨着,数据耦合又深,渲染那边等着同一份Transform结果。你拆开看会…

2026/10/12 2:06:09 阅读更多 →
28岁没房没车别焦虑:转行前先搞懂副业与财富自由的底层逻辑

28岁没房没车别焦虑:转行前先搞懂副业与财富自由的底层逻辑

28岁,无车无房,收入一眼望到头,拿着几千块的工资,晚上躺床上刷手机,看到别人晒新房晒婚礼,再想想自己连恋爱都不敢谈,一种说不出的恐慌直接顶到嗓子眼儿。这种日子我太熟悉了,因为我…

2026/10/12 2:06:09 阅读更多 →
给Claude Code接入MCP搜索:告别过期答案,实时联网查资料

给Claude Code接入MCP搜索:告别过期答案,实时联网查资料

我印象最深的一次,是在某次项目重构里要用到一个库的最新接口。Claude Code 三两下就把代码写完了,看起来头头是道,结果一编译直接报错。后来我自己上官网翻文档才发现,这个库在两三周前刚改过一次函数签名,而 Claude …

2026/10/12 2:06:09 阅读更多 →
Ant Design Landing 设计资源页解析:Sketch 源文件、数据模型与前端实现

Ant Design Landing 设计资源页解析:Sketch 源文件、数据模型与前端实现

前端文档 【免费下载链接】ant-design-landing :mountain_bicyclist: Landing Pages of Ant Design System 项目地址: https://gitcode.com/gh_mirrors/antd/ant-design-landing 点击查看 免费下载 Ant Design Landing(ant-design-landing)是…

2026/10/12 2:06:09 阅读更多 →
Apache Beam Python 的 Mean 聚合变换:Globally 与 PerKey 用法及底层实现

Apache Beam Python 的 Mean 聚合变换:Globally 与 PerKey 用法及底层实现

【免费下载链接】beam Apache Beam is a unified programming model for Batch and Streaming data processing. 项目地址: https://gitcode.com/gh_mirrors/beam18/beam 点击查看 免费下载 本文围绕 Apache Beam Python SDK 中计算算术平均值的 Mean 聚合变换展开…

2026/10/12 2:06:09 阅读更多 →
Jenkins 2.346.1 内网离线安装插件:依赖解析与版本匹配实战

Jenkins 2.346.1 内网离线安装插件:依赖解析与版本匹配实战

简介:本资源面向在内网、隔离网等无外网环境中部署Jenkins的运维与DevOps工程师,针对Jenkins 2.346.1无法在线拉取插件的问题,提供一套完整的离线插件安装方案。压缩包共约2000个文件,整体314.4MB,涵盖90个jpi与30个hp…

2026/10/12 2:05:08 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →