在浏览器里造开放世界,第 4 部分:在花哨地形之前先做好流式加载
作者:Oleg Sidorkin,Cinevva CTO 和联合创始人
刚看到?看系列导览。那里解释了 spike 是什么,并链接了所有部分。
流式加载是"看起来不错"的项目通常崩盘的地方。
静帧里可以藏很多东西。穿越 chunk 边界时的 40 ms 卡顿,藏不住。
我们故意在做高级地形表示之前先测流式加载。这让加载和卸载行为的信号干净。
Spike 6 用简单的 chunk 内容验证了邻居切换的频繁切换情况。
然后我们在 Spike 11 转到真正的地形路径。高度 chunk 流式加载,worker 侧解码,从 17 到 33 到 65 采样网格的渐进式细化。
排序的事比我们想的还重要。如果一上来就直接压缩高度 chunk,每一次卡顿都会含糊不清。是解码问题、纹理上传问题,还是几何更新问题?Spike 6 在 Spike 11 加复杂度之前,先消除了一层不确定性。
这一章的一个实操教训贯穿了后面所有的 spike:上传 stall 必须直接测,不能从平均 FPS 推。平均 FPS 隐藏帧 spike,而帧 spike 才是用户真正感受到的东西。
第 5 部分进入视觉成本章节,植被、地形着色器和级联阴影在同一份帧预算里互相争夺。
本章涉及的技术
基于 chunk 的流式加载。 世界被切成一张独立 chunk 的网格(通常 64x64 米)。玩家移动时,后边的 chunk 卸载,前边的 chunk 加载进来。Skyrim 的 cell 系统就是这样:围着玩家加载一个 5x5 的 cell 网格,移动时换。浏览器版本在这个等式上加了网络延迟,所以基于玩家速度的预测式 prefetch 至关重要。看我们的流式架构指南。
渐进式 heightmap 细化。 地形先用低分辨率发出去,再细化。17x17 网格(64m chunk 用 4m 间距时的最小值)压缩后约 200 字节,立刻能渲染出一个可见的表面。然后流式发 33x33 细化(加细节),再发完整的 65x65 分辨率。每一级都在不替换前一级数据的情况下加入采样。这直接对应到几何 clipmap LOD 环——远处地形用低分辨率数据,近处用完整分辨率。看渐进式 chunk 加载。
差分编码和压缩。 Heightmap 数据压缩得很好,因为相邻格子值接近。差分编码存的是每格和它预测值(邻居平均)之差,把值挤到零附近。配上 zlib 或 brotli,一个 65x65 chunk 从 8.4 KB 原始数据降到 1-2 KB 压缩后。远处 chunk 用降低的精度(8-bit 而非 16-bit):0.5-1 KB。看地形数据压缩。
预测式 prefetch。 在玩家到达之前就加载 chunk。步行速度(5 km/h),prefetch 前方 2 个 chunk(128 米)。跑步速度(15 km/h),prefetch 4 个。加载环随速度方向偏移。一个优先队列按紧迫程度对挂起的请求排序,并取消玩家已经走远了的 chunk 的请求。看预测式 prefetch。
12 篇中的第 4 篇。 上一篇:第 3 部分 - 救了我们的那些不吸睛的 spike 下一篇:第 5 部分 - 给漂亮东西做预算 系列导览:/zh-CN/blog/2026-02-25-open-world-browser-series-guide