在开放世界或大场景渲染中,LOD(Level of Detail,多细节层次)是控制面数、保证帧率的核心手段。但当物体跨越LOD切换阈值的那一瞬间,高模被低模直接替换,顶点数量骤变,视觉上会产生一次明显的跳变,尤其在近处或角色面部等显眼位置时格外刺眼。这种突兀感的根源在于两个层级的几何形状不完全一致,解决思路有两个方向:一是让切换在一段距离区间内逐渐完成,二是让两个层级的几何在过渡区内进行混合。本文围绕这两个方向展开,给出可直接落地的实现方案。

LOD切换突兀的根源:几何形状不一致
LOD的本质是用多个不同精度的网格表示同一个物体,远处的物体用低模,近处的用高模。传统做法是设置一个或多个距离阈值,当相机到物体的距离跨越阈值时,直接换用另一个网格。问题在于,低模通常是高模的简化版本,两者虽然轮廓近似,但顶点位置不可能完全重合。切换发生在单帧之内,同一个像素的表面法线、光照结果会瞬间改变,人眼对这种突变极其敏感。
从数学角度看,切换突兀可以理解为屏幕空间误差的阶跃。高模与低模在同一个投影位置的法线差异越大、轮廓偏差越大,跳变就越明显。因此缓解突兀的第一步是评估两个层级之间的几何差异,常用的做法是计算屏幕空间误差(Screen Space Error),即几何体在特定距离下的世界空间误差投影到屏幕上的像素数。很多引擎在LOD选择时直接使用屏幕空间误差而非纯距离,这本身就为平滑过渡提供了量化基础。
除了几何差异,材质和着色的差异也会加剧突兀感。如果两个LOD使用了不同的法线贴图精度或不同的贴图mipmap层级,切换时纹理清晰度会突变。因此在做几何混合的同时,也要保证两个层级使用一致的贴图与着色参数,否则几何平滑了、纹理仍然跳变。
过渡区域:让切换发生在一段距离区间内
最直接的改进是不再使用单一阈值,而是为每个LOD定义一个过渡区间。以距离为例,假设LOD0切换到LOD1的阈值是50米,可以设置45米到55米之间为过渡区。在过渡区内,不再瞬间替换网格,而是让LOD0逐渐淡出、LOD1逐渐淡入。实现方式通常基于透明度混合:两个网格同时绘制,通过一个随距离变化的插值因子控制各自的alpha值。
这种方案的实现非常简单,以Unity的Shader为例,核心思路是在顶点着色器中根据物体到相机的距离计算过渡因子,并传递给片元着色器控制透明度:
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; float dist = distance(worldPos, _WorldSpaceCameraPos); // 过渡区起始距离与宽度 float lodFadeStart = 45.0; float lodFadeWidth = 10.0; // 计算淡出因子:过渡区内从1渐变到0 float fade = saturate((lodFadeStart + lodFadeWidth - dist) / lodFadeWidth); o.alpha = fade;
纯透明度混合的优点是实现成本极低,几乎不需要修改美术管线,适合植被、石头等复杂的背景物体。但它有两个明显缺陷:第一,透明混合需要两个网格同时渲染,draw call和填充率翻倍,密集场景下开销可观;第二,透明物体的渲染顺序问题会导致排序错误,尤其在物体自遮挡部分容易出现穿透感。因此过渡区宽度一般控制在切换距离的百分之十到二十之间,让翻倍渲染只发生在很短的区间内。
另一种不需要透明度的过渡技巧是抖动(Dither)淡入淡出。它在过渡区内根据屏幕空间噪声做alpha test,让两个网格的像素交错显示,远看近似半透明效果。由于不做真正的混合,物体保持不透明属性,不存在排序问题,且可以正常写入深度缓冲。原神的角色切换和许多UE项目中的植被LOD都采用这种方案。抖动混合在TAA(时域抗锯齿)配合下效果相当好,帧间的抖动图案会被抗锯齿算法自然抹平。
几何混合:让顶点真正动起来
透明或抖动混合本质上是在视觉上掩盖切换,几何形状的跳变仍然存在,只是被稀释了。更彻底的方案是几何混合(Geometry Morphing),即在过渡区内对低模的顶点位置做插值,使其逐渐贴合高模的形状。由于低模顶点是高模顶点的子集,可以让低模中的每个顶点记录一个目标位置,在过渡区内从简化位置插值到原始位置。
具体做法是为低模每个顶点额外存储两个属性:原始位置(即高模中对应顶点的位置)和当前简化位置。顶点着色器中根据过渡因子做线性插值:
struct appdata
{
float4 vertex : POSITION; // 简化后的顶点位置
float3 morphTarget : TEXCOORD1; // 对应的高模原始位置
float2 uv : TEXCOORD0;
};
v2f vert(appdata v)
{
v2f o;
// morph因子由外部根据相机距离计算,0表示完全简化,1表示完全贴合高模
float3 morphed = lerp(v.vertex.xyz, v.morphTarget, _MorphFactor);
o.pos = UnityWorldToClipPos(morphed);
return o;
}几何混合的好处是过渡真正连续,没有任何透明度开销,也没有排序问题,物体始终以不透明方式渲染。代价是顶点数据膨胀(多存一份位置)以及顶点着色器多一次插值计算。对面数已经很高的模型,额外存储可能不可接受,所以这种方案更适合中低面数的LOD,或者与曲面细分结合:基础网格保持低模,过渡区内通过 tessellation 逐渐增加细分级别,同时把顶点向高模位置偏移。
实现几何混合时有两个工程细节必须注意。一是法线处理:顶点位置插值后,法线也应当插值,否则光照会在过渡区出现异常。可以在两份顶点数据中同时存储法线并一起lerp,成本更低的做法是根据位置插值的比例对法线做同样的混合。二是morph target的生成:简化算法必须保证低模顶点集合是高模顶点的子集,像边折叠类简化算法天然满足这一点,折叠过程中记录每个保留顶点的原始位置即可;如果简化算法会重投影顶点位置,需要额外保存重投影前的位置作为morph目标。
工程实践建议与方案选型
三种方案各有适用场景。透明混合实现最快,适合快速验证和背景小物件;抖动方案在不透明管线下工作,兼容阴影和深度,是大型开放世界项目的常用选择;几何混合质量最高、无像素级开销,但需要工具链支持morph数据导出,前期投入最大。实际项目中经常组合使用:植被和岩石用抖动,主要角色和载具用几何混合。
过渡区宽度的选择需要平衡平滑度与性能。区间太窄,过渡速度太快,跳变只是被压缩但仍然可感;区间太宽,双份渲染或morph计算的范围扩大,开销上升。经验值是切换距离的百分之十到二十,同时过渡曲线建议使用平滑的缓动函数而非线性,例如smoothstep,让过渡开始和结束时速度渐变,观感更自然。
还有一个容易被忽略的点是切换距离本身的选取。阈值应该根据屏幕空间误差来定,保证在任何切换距离下,两个层级的屏幕空间差异都在一个像素以内,这样即使发生瞬间切换,用户也几乎无法察觉。过渡技术是对这个原则的补充,而不是替代——先把LOD层级的误差控制好,再叠加过渡混合,才能得到真正平滑的画面效果。此外,过渡期间两个LOD的阴影、贴图mipmap也应保持一致,必要时让阴影LOD独立于主渲染LOD切换,避免阴影层面的二次跳变。