细节层次(LOD)
一块一百单位外的石头和一块两千单位外的石头,现在用同样多的三角形绘制;而在两千单位外,
它们几乎全部落进同一个像素里。LODGroup 让一个物体在小到没人分辨得出来时,交给
更便宜的网格去画 —— 这是道具密集的场景里最大的一笔节省。
在 Estella 自带的第三人称示例里,一片 35 块石头的场地就用上了它;在一万个道具的规模上, 三角形从 191 万降到 4.2 万,GPU 时间从 3.5 ms 降到 0.27 ms(数字在下面)。
LODGroup 组件
Section titled “LODGroup 组件”LODGroup 挂在和 MeshRenderer 同一个实体上。渲染器说这个物体是什么 ——
那个网格就是 level 0,即最精细的一级;组件说的是它变小之后谁可以顶替它:
world.insert(rock, MeshRenderer, { mesh: 'assets/models/rock-lod0.esmesh', lit: true });world.insert(rock, LODGroup, { lod1: 'assets/models/rock-lod1.esmesh', lod2: 'assets/models/rock-lod2.esmesh', lod1Size: 0.25, // 小于屏幕高度的四分之一 → 换 lod1 lod2Size: 0.11, // 小于八分之一 → 换 lod2 cullSize: 0.02, // 小于五十分之一 → 干脆不画 hysteresis: 0.1,});把这个组件去掉,最精细的网格就回来了 —— 渲染器的其他部分一点没变。
这些尺寸是屏幕占比,不是距离
Section titled “这些尺寸是屏幕占比,不是距离”lod1Size 和它的兄弟们,是物体包围体覆盖视口高度的比例。1.0 表示物体正好一屏高,
0.02 表示五十分之一屏高。
它刻意不是距离,因为距离在不同尺度、不同镜头下给的是同一个答案:
- 一百米外的高塔和一颗石子,在屏幕上不是一个大小;
- 同一个位置,30° 视场看到的物体是 60° 的两倍大;
- 正交相机的答案根本不随距离改变。
屏幕占比同时覆盖这三件事,而且与分辨率无关 —— 同一个数字在手机上和桌面上都对。
迟滞 —— 为什么级别不会闪
Section titled “迟滞 —— 为什么级别不会闪”一条只用一个数字划的边界,会因为相机的轻微呼吸而来回翻转;而每帧都在换网格,远比换本身
更显眼。hysteresis 让每条边界变得不对称:物体在阈值处往下交给更粗的网格,但要长回
到超出阈值那么一个比例之后,才会换回精细的一级。
默认 0.1 时,lod1Size = 0.20 的含义是:
- 缩小穿过 0.20 → level 1 接手;
- 重新长过 0.22 → level 0 才回来。
把它设成 0 就是一条裸阈值,会闪。它同样作用于 cullSize,所以在可见边缘的物体不会眨眼。
这个选择是什么、不是什么
Section titled “这个选择是什么、不是什么”它属于相机,不属于实体。 同一帧里的两个相机 —— 主视角和小地图,或者分屏 ——
看到的是同一个物体的两个尺寸,可以画成两个不同的级别。实体上没有 currentLOD
可读、也没有可写。
它是在整组的包围体上量的。 用来做判断的包围体是所有级别的并集,所以它不随当前 画的是哪一级而改变。若按当前网格取包围体,就会反过来影响挑出它的那个判断,进而振荡。
剔除先跑。 视锥已经拒绝掉的物体根本不会被测量,所以这份开销只花在可能被看见的东西上。
实例化不受影响。 选级只改变一次 draw 引用哪份几何,别的都不变,所以同一级的道具照旧 合并成一次 instanced draw。三个级别的一片场地到达 GPU 时大约是三次 draw,而不是每个道具一次。
- 槽位按顺序填 —— 先
lod1,再lod2,再lod3。留空的槽位就是梯子的终点,它后面的 一律到不了(引擎会告警)。 - 尺寸必须递减。
lod2Size小于lod1Size,依此类推。 cullSize: 0完全关掉剔除,于是这一组永远不会消失。- 蒙皮的一组必须共用一副骨架。 蒙皮网格的每一级都要有相同的关节数;对不上的那一级会 画成绑定姿势,引擎会说出来。
这笔交易换来了什么
Section titled “这笔交易换来了什么”在一片纵深排布的道具场地上实测,512×512,WebGL2:
| 道具数 | 三角形 | GPU |
|---|---|---|
| 1 000,始终最高精度 | 195 776 | 0.505 ms |
1 000,带 LODGroup |
4 248 | 0.116 ms |
| 10 000,始终最高精度 | 1 912 960 | 3.595 ms |
10 000,带 LODGroup |
42 392 | 0.27 ms |
每一万个道具的选级大约花掉 0.2–0.3 ms 的 CPU,这就是那笔交易:花 CPU 去挑,省 GPU 去画。 数量越大,汇率越划算 —— 这正是重点,它决定了一万个道具的场地到底能不能发出去。
LODGroup 讲的是同一个物体在不同尺寸下的事。它不是流式加载:每一级的网格都常驻,
不会加载也不会卸载;小到 cullSize 以下的物体只是不画,并没有从世界里移走。
把远处的物体合并成一个代理(HLOD),以及把世界分块加载,都是另外的题目。