在浏览器里造开放世界,第 5 部分:给漂亮东西做预算
作者:Oleg Sidorkin,Cinevva CTO 和联合创始人
刚看到?看系列导览。那里解释了 spike 是什么,并链接了所有部分。
这是视觉野心撞上算术的一章。
我们把渲染成本拆到不同的 spike 里去测,因为捆绑结果难诊断。一下全打开,你只学到一件事:帧很慢。你学不到是哪个功能吃了预算。
Spike 7 针对植被密度和动画成本。做法是从每个地形 chunk 的 32x32 密度图运行时撒点,喂给大块的 InstancedMesh。每根草叶和灌木丛都用一张滚动噪声纹理驱动顶点着色器风。我们盯的关键数字不是三角形数,而是中端 GPU 上的 draw call 开销和顶点吞吐量。我们发现,把实例 batch 进更少的 mesh,比降低每片叶子的多边形数更重要。
Spike 8 推了地形材质的复杂度。按坡度角和海拔加权的多层混合,悬崖面可选的 triplanar 投影,每层独立的法线图。着色器在做四层纹理的基于坡度的 splatting,每层都要采一次 diffuse 和一次 normal。这就是每个片元在加任何光照之前就有八次纹理采样。我们专门在集成 Intel GPU 上做了剖析,找下限。结论是:垂直表面上的 triplanar 投影值得这个代价,但加第五个 splat 层不值。
Spike 9 关注在真实地形和物体负载下的级联阴影图成本。CSM 三个级联是基线。我们专门用低角度的太阳位置测,因为那是级联压力最差的时候。最远的级联覆盖了一大块视锥切片,阴影图每 texel 的分辨率掉得很快。我们测了两级和四级之间的 GPU 时间差,又测了 1024 和 2048 阴影图分辨率之间的差。结果是三级 1024,给了我们相机附近可接受的接触阴影,同时没超出目标硬件 2ms 的 GPU 时间。
这个阶段最难的是产品纪律。有些效果看起来非常好,但相对于它的视觉效果,它吃掉的帧预算太多,仍然要被压住。
我们的规则变得简单:一个功能要往前推,必须能用测出来的帧时间数据解释它的成本。
听起来理所当然。但在大家对下一个视觉成果都很激动的快速原型周期里,这其实不常见。早期坚守这条规则,让后面围绕 clipmap 和体积区域的架构决策清爽很多,因为我们已经知道争夺同一份 16ms 的每个功能的单点成本了。
第 6 部分撞上第一次重要的地形架构转向:geometry clipmap。
本章涉及的技术
InstancedMesh 和 GPU 植被。 Three.js 的 InstancedMesh 用一次 draw call 渲染同一几何的 N 份副本。给植被用,每 chunk 一张 32x32 密度图驱动运行时撒点,把草叶和灌木丛撒进实例 buffer。风动画在顶点着色器里用滚动噪声纹理跑。规模一大,WebGPU 的 ComputeInstanceCulling 在光栅化前剔掉屏幕外和远处的实例,IndirectBatchedMesh 把多种植被打包进一个 buffer,用 multi-draw indirect 画出来。看我们的地形指南里关于 GPU 植被剔除的部分。
Triplanar mapping。 标准 UV 贴图在陡坡上会拉伸,因为 UV 坐标被压缩。Triplanar mapping 沿三个轴(X、Y、Z)各投一次纹理,再按表面法线做混合。悬崖面拿到 X 或 Z 投影(不拉伸),平地拿到 Y 投影。混合平滑且自动,不需要 UV 展开。给 PBR 地形用时,同一组混合权重同样用在 albedo、normal、roughness、AO 通道上。看 triplanar mapping 细节。
基于坡度和海拔的材质 splatting。 不用手画的 splat map,而是在片元着色器里基于地形属性程序化分配材质。低海拔的平地拿草,陡坡拿岩石,高海拔拿雪(只在足够平、能堆积的表面上),靠近海平面的拿沙。过渡用 smoothstep 做平滑混合。在我们的实现里,每个地形 chunk 评估四层纹理,每层一次 diffuse 一次 normal,所以每个片元在光照之前是八次纹理采样。看基于坡度和海拔的材质分配。
级联阴影图 (CSM)。 CSM 把相机视锥按距离切成 3-4 段(级联)。每段从太阳视角渲染一张阴影图,分辨率匹配它的距离。近的级联拿到高分辨率阴影(树下和建筑下的精细接触阴影),远的级联拿到较低分辨率(大块的山影)。地形着色器采样所有级联,每个片元选合适的那一段。性能成本:3-4 级 1024x1024 给阴影图渲染加约 0.5-1 ms,再加约 0.2-0.3 ms 采样。看地形阴影。
12 篇中的第 5 篇。 上一篇:第 4 部分 - 在花哨地形之前先做好流式加载 下一篇:第 6 部分 - Clipmap 改变了剧情 系列导览:/zh-CN/blog/2026-02-25-open-world-browser-series-guide