Skip to content

在浏览器里造开放世界,第 10 部分:接缝混乱和拐角 boss 战

作者:Oleg Sidorkin,Cinevva CTO 和联合创始人

刚看到?看系列导览。那里解释了 spike 是什么,并链接了所有部分。

如果说之前几部分感觉是按部就班,这一章感觉是格斗。

Spike 17 到 22 是我们的"corner case 时代"。双 LOD marching cubes、heightmap 到 MC 的边界接缝、三或四个 LOD 级别交汇的混合分辨率拐角 chunk、GPU 接缝生成、fallback 模式行为。每个 spike 处理一个我们撞上或者预见到的具体失败场景。

Spike 17 测的是同时启用两个 LOD 级别的双 marching cubes。挑战是一个 chunk 的邻居在不同面上可能在不同分辨率。Spike 16 来的过渡 cell 逻辑一次只对一个面有效,但当一个 chunk 在多个面上都需要过渡 cell 时,顶点 buffer 管理就复杂了。每个面的过渡 cell 必须在不覆盖其他面的情况下生成并追加。

在新标签页里打开 Spike 19 ↗ · 看源码

反复出现的第一个反派是绕序。好几次我们以为是拓扑问题,最后发现是朝向问题。背面剔除在吃合法的接缝三角形,因为绕序相对主 mesh 是反的。根因相同,视觉症状因相机角度而异。修复方法是在过渡 cell 发射代码里强制一致的绕序约定,并用双面材质切换来验证。

第二个反派是来自部分正确的虚假信心。一段接缝可以从某个相机角度看完美,等高低分辨率 chunk 角色交换时就坏掉。过渡 cell 是非对称的。它从高分辨率侧和低分辨率侧的采样方式不同。如果你在某种配置下把"哪边是高分辨率"的逻辑搞反了,你只在相机移到特定位置时才看得到 bug。

然后是我们最喜欢的恢复之一。我们在追 heightmap tile 上的一个接缝切断瑕疵,怪罪过渡逻辑。烧了两天。真凶是过期的 overdraw。前一帧的高分辨率几何在 chunk 降到低 LOD 之后还住在 buffer 尾部。Draw 范围还设着旧的、更大的顶点数。一旦我们把 draw 范围裁到 compute shader 的 atomic counter 报出来的活跃顶点数,那个"神秘接缝问题"消失了。

那是一个绝佳提醒:渲染 bug 经常伪装成 meshing bug。几何一直都是对的。是 draw call 读到了合法数据末尾之外。

到 Spike 22 我们在测混合 fallback——chunk 可以在特定条件下从 marching cubes 切到 heightmap 模式,比如 chunk 不含体素编辑、且离相机够远时。这给了我们比全有全无策略更实用的路径。近场被编辑的 chunk 用 MC 拿体素自由度。远场未编辑的 chunk 用 heightmap 拿效率。

在新标签页里打开 Spike 22 ↗ · 看源码

这一章是过山车的陡降。让人抓狂同时也高产。很多单点修复都很小,有时就是一行改个比较操作符或者一个偏移。但它们产出的对"LOD 过渡、buffer 管理、draw 范围之间如何交互"的理解,一点也不小。

第 11 部分讲混乱之后的稳定层:基于策略的 chunk 模式,以及从反应式 bug 修复到显式系统规则的过渡。

本章涉及的技术

双 LOD marching cubes。 同时在两个分辨率级别跑 marching cubes,用过渡 cell 缝合边界。挑战是单个 chunk 的邻居在不同面上可能是不同分辨率,所以每个面要独立生成过渡 cell。每个面的过渡 cell 在不覆盖其他面的情况下追加到顶点 buffer。Atomic counter 跨所有面跟踪总活跃顶点数。

绕序(winding order)。 三角形里顶点的顺序决定哪一面是"正"面。一致的绕序(通常从外面看是逆时针)是背面剔除所要求的。过渡 cell 发射三角形时,绕序必须和主 mesh 的约定一致。搞反了背面剔除会吃掉合法的接缝三角形,看上去就是从某些相机角度缺面。常用 debug 手段是把材质切到 side: THREE.DoubleSide,确认瑕疵是绕序问题还是真的拓扑缺口。

Heightmap-to-MC fallback。 一种混合 chunk 模式,远处或者未编辑的 chunk 用 heightmap 地形(便宜、平坦表面),近处或者编辑过的 chunk 用 marching cubes(体素、支持洞穴)。Fallback 决策依赖于和相机的距离、以及 chunk 里有没有 SDF 编辑。heightmap chunk 和 MC chunk 之间的接缝需要自己的过渡几何,类似 Transvoxel,但桥接的是两种不同的表示而不是两个 LOD 级别。看heightmap + 体素覆盖混合

Draw 范围和 atomic counter。 GPU 驱动 mesh 生成里,compute shader 把顶点写进 buffer,递增一个 atomic counter 来跟踪发射了多少顶点。Draw call 必须用这个 counter 作为顶点数,而不是 buffer 容量。如果 draw 范围没裁到活跃计数,前一帧的过期顶点(还住在 buffer 尾部)会产生鬼影几何:薄针和闪烁三角形,看上去像拓扑错误,其实是读过合法数据之外产生的渲染瑕疵。


12 篇中的第 10 篇。 上一篇:第 9 部分 - Transvoxel 从脚手架开始 下一篇:第 11 部分 - 策略模式,不是硬编码模式 系列导览:/zh-CN/blog/2026-02-25-open-world-browser-series-guide