在浏览器里造开放世界,第 1 部分:我们从尝试把它弄崩开始
作者:Oleg Sidorkin,Cinevva CTO 和联合创始人
刚看到?看系列导览。那里解释了 spike 是什么,并链接了所有部分。
我们在做一个完全跑在浏览器里的多人开放世界。不安装、不上应用商店,只要一个 URL。一开始最大的风险显而易见:浏览器到底能不能在保留游戏玩法、物理和网络余量的同时,把一个持久 3D 世界渲染到可玩的帧率?
大多数开放世界项目以一种可预测的顺序失败。先是有了一个不错的概念。然后做出了一个漂亮的场景。然后你发现你的帧预算在玩法存在之前就已经被用光了。
我们想在把任何东西投入进去之前,先把渲染预算这个问题回答了。所以 Spike 1 跳过了漂亮的预告片,直接去测量。
设置故意做得简单。一块 512 米的地形 mesh,用分层 sine 噪声加岛屿衰减生成程序化高度,一个水面平面,大气雾,还有 500 个实例化物体。我们用的是普通 WebGL 加 Three.js,ACES 色调映射,没开阴影。
我们不在乎它好不好看。我们在乎的是,相机在场景里移动的时候,场景能不能稳住。
这个 spike 出了两个东西,塑造了整个项目。
第一,我们确认了:只要第一遍做得克制,桌面端还有真正的余量。这给了我们之后去尝试更难的地形架构的信心。
第二,我们立了一个基线契约。之后每一个 spike 都必须解释自己的成本相对这个场景是多少。如果一个新功能看上去不错但代价太大,就不让它升级。
那个基线纪律后来在我们撞上接缝瑕疵、混合 LOD 过渡、计算驱动的 meshing 时变得关键。没有一个稳定的参照,每个 bug 看上去都比实际大。
第 2 部分我们从渲染转到输入手感。Worker 物理在架构文档里听起来不错。它只在你按键时角色还感觉立即响应的情况下才有意义。
本章涉及的技术
Heightmap 地形。 一个 2D 网格,每格存一个高度值。GPU 在顶点着色器里把一块平面 mesh 顶起来,形成地形表面。Heightmap 紧凑(一个 65x65 的 chunk 在 16-bit 下大约 8 KB),对 GPU 友好,渲染快。局限是不能表达洞穴、悬岩或者任何会折回到自己上面的表面。关于 heightmap 的限制以及之后用什么,看我们的地形生成指南。
Three.js。 我们整个项目用的渲染库。Three.js 把 WebGL 2(后来还有 WebGPU)抽象成带相机、灯光、材质和几何对象的场景图。它提供 InstancedMesh 用一个 draw call 渲染同一几何的多个副本,还有视锥剔除、PBR 材质和后处理。看 Three.js GitHub。关于 Three.js 在浏览器开放世界栈中的位置,看我们的浏览器 3D 技术指南。
InstancedMesh。 Three.js 的一个特性,用一个 draw call 渲染同一几何的 N 份副本,每份位置/旋转/缩放不同。每实例变换存在一个矩阵属性 buffer 里。这就是我们在 Spike 1 里用 500 个物体不用 500 次 draw call 的方式。要大规模做植被,GPU 驱动的实例化剔除能进一步推。看我们的地形指南里关于 GPU 植被剔除的部分。
帧预算。 在 60 fps 下,每一帧给所有东西的时间是 16.67 ms:JavaScript 逻辑、物理、渲染、合成。一个"预算检查" spike 测的是一个基线场景吃掉多少时间,剩下多少是已知给后面加功能用的。这个做法来自 3A 开放世界开发,GTA V、Skyrim、Elden Ring 都用激进的 LOD 和流式加载来留在固定的帧预算内。
12 篇中的第 1 篇。 下一篇:第 2 部分 - Worker 物理和输入延迟的恐惧 系列导览:/zh-CN/blog/2026-02-25-open-world-browser-series-guide