面向浏览器开放世界的动态 LOD 与流式地形生成
使用 Perlin 噪声的高度图通常是程序化地形的默认起点。叠加几层 fBm 倍频,再应用颜色渐变,就能得到看起来有点像地形的东西。几乎所有教程都到此为止。但真实地貌并不像层层叠加的噪声。它们有被流水切割出的河谷、受应力断裂塑造的崖壁,以及经过数百万年侵蚀形成的洞穴和拱门。它们还有相互关联的纹理(平地上的草、陡坡上的岩石、林线以上的积雪),这些纹理与几何形态一样,都源自塑造地貌的物理过程。
本指南将介绍噪声之后的技术:基于物理的生成方法、能够处理洞穴和悬挑结构的体积表示、基于扩散模型的神经网络地形合成,以及在浏览器标签页中以 60fps 渲染这一切所需的 GPU 驱动 LOD 与流式管线。
这里的一切都针对我们的特定约束:一个运行在 WebGL 2 / WebGPU 中、通过网络流式传输、可由创作者编辑的多人开放世界。
为什么高度图还不够
高度图在每个网格点存储一个高度值。它是一个二维函数:给定 (x, z),返回 y。这种表示方式紧凑、适合 GPU 处理且渲染速度快。但对于创作者世界,它存在一些根本性限制。
无法表示洞穴或悬挑结构。 高度图无法表示同一点具有两个不同高度的地形。洞穴、拱门、悬崖悬挑、隧道和浮空岛都无法实现。Minecraft、No Man's Sky 和 Deep Rock Galactic 正因如此都需要体积地形。
无法表示垂直特征。 高度图中的垂直崖壁相当于接近无穷大的坡度,会造成严重的纹理拉伸和碰撞伪影。真实的悬崖具有平台、裂隙等水平特征,而高度图无法表示这些结构。
噪声看起来就是噪声。 即使使用 8 个倍频的 fBm 和域扭曲,地形仍会带有一种人工合成感。它缺少真实地质的方向性结构:山脊线、水系网络、沉积物堆积和构造褶皱。这些图案源自物理过程,而不是噪声函数。
创作者的编辑能力有限。 如果创作者只能修改高度,他们就无法挖掘隧道、创建洞穴或建造地下空间。对于一个真正能由创作者塑造的世界,地形表示不仅需要支持添加,还必须支持减去物质。
解决办法并不是彻底抛弃高度图。对于 90% 只是简单表面的地形,高度图仍然是最佳表示。真正的解决方案是混合方法:以高度图作为基础地形,在需要复杂几何体的区域叠加体积数据;使用物理正确的生成方法构造逼真的地貌;再通过神经网络合成获得噪声无法提供的多样性。
简短结论:我们实际上会构建什么
在深入讲解之前,先给出决策框架。本文其余部分将解释每个组成部分。
地形表示
使用高度图 + SDF 混合系统。高度图覆盖整个世界(廉价、紧凑且经过充分验证)。只有需要洞穴、悬挑结构或创作者雕刻特征的地方才存在 SDF 体积(可能占区块总数的 5-10%)。这样,世界中 90% 的区域都能维持高度图的低成本,同时在需要时支持任意几何结构。
生成管线
在服务器端以链式流程运行:
- Terrain Diffusion(或用于文本提示词的 MESA)根据种子生成基础高度图。它使用基于真实高程数据训练出的地质逼真地貌取代噪声。
- 解析侵蚀(水流功率定律)在几毫秒内细化高度图,生成水系网络和山脊线。
- TerraFusion 或 Geodiffussr 根据高度图生成相互关联的纹理(WebGL 2 降级方案则使用基于坡度/海拔的程序化规则)。
- 生态系统模拟生成植被密度图。
- Arenite 风格侵蚀在地形需要的区域为崖壁、拱门和洞穴生成 SDF 体积。
- 切分区块、压缩并上传至 CDN。 平均区块:2-8 KB。包含体积数据的复杂区块:20-100 KB。
创作者可以通过调整参数(“更湿润”“更多山地”“添加洞穴”)、勾画创作意图,或直接使用笔刷和 SDF 工具进行雕刻来与这条管线交互。
LOD 策略
| 浏览器能力 | 高度图 LOD | 体积 LOD | 植被 LOD |
|---|---|---|---|
| WebGPU | GPU 驱动的四叉树(CDLOD),配合计算着色器剔除与间接绘制 | 多分辨率 SDF,配合计算着色器 Marching Cubes 与 Transvoxel | ComputeInstanceCulling 配合间接绘制 |
| WebGL 2 | 几何裁剪图,使用 CPU 端环形更新 | 预生成 2-3 个 LOD 级别的网格并缓存 | CPU 视锥体剔除、InstancedMesh |
两条路径都在顶点着色器中使用几何渐变,以实现无突变的过渡。两者都为远处植被使用广告牌替身。WebGPU 路径更快(地形预算为 3.5ms),但 WebGL 2 路径同样可行(6.5ms)。
流式传输
渐进式加载:首先加载地形几何体(<100ms),其次加载纹理(<300ms),再次加载植被(<1s),最后加载体积数据(<3s)。根据玩家移动速度进行预取。内存预算:全部地形总计 256 MB。
编辑
使用高度图笔刷雕刻表面(抬高、降低、平滑、侵蚀)。使用 SDF 图元进行体积编辑(挖掘洞穴、添加拱门)。两者都会在本地即时生效,在 100-300ms 内同步到服务器,并通过增量更新广播给其他玩家。
应按什么顺序构建
第 1-2 个月:采用几何裁剪图的高度图地形。 仅支持 WebGL 2。从 CDN 流式传输。使用基于坡度/海拔的程序化材质。这样可在所有浏览器中显示地形。生成阶段使用噪声 + 解析侵蚀(Terrain Diffusion 可以稍后再做)。
第 3-4 个月:植被与大气。 根据密度图生成 GPU 实例化的草木。带昼夜循环的程序化天空。大气雾。级联阴影贴图。世界开始有了真实的场所感。
第 5-6 个月:创作者编辑。 高度图笔刷工具。多人地形编辑的增量同步。用于并发编辑的空间锁定。此时创作者便可以开始塑造世界。
第 7-9 个月:体积地形与 WebGPU 路径。 使用 SDF 叠加实现洞穴和悬挑结构。在 WebGPU 计算着色器中运行 Marching Cubes。使用 Transvoxel 处理 LOD 边界。提供 SDF 雕刻工具。由此解锁完整的创作工具集。
第 10-12 个月:神经网络生成与打磨。 使用 Terrain Diffusion 或 MESA 生成基础高度图。使用 TerraFusion/Geodiffussr 生成纹理。使用相量噪声生成微观细节。使用六边形平铺减少重复感。虚拟纹理。拉普拉斯混合。地形达到产品级品质。
按照这个顺序,2 个月后会有可玩的版本,4 个月后会有漂亮的版本,6 个月后会有可编辑的版本,12 个月后则会达到业界前沿水平。
本文其余内容将介绍每项决策背后的研究。
超越高度图的地形表示
有符号距离场(SDF)
有符号距离场在三维空间中的每个点存储到最近表面的距离。正值表示外部,负值表示内部,零交叉处就是表面本身。SDF 可以表示任意三维形状,包括洞穴、拱门和浮空几何体。
要将 SDF 渲染为网格,需要运行 Marching Cubes(或其变体),将零交叉面提取为三角形。网格分辨率取决于体素网格分辨率:一个 256x256x256 的 SDF 网格能以约 1 米的分辨率覆盖一个区块的地形。
浏览器实现: WebGPU Marching Cubes 通过计算着色器完全在 GPU 上运行。Will Usher 的 webgpu-marching-cubes 实现能够在浏览器中实时处理 256^3 网格,达到接近原生的性能。该算法具有极强的并行性(每个单元格都可独立处理),因此非常适合 GPU 计算。
@compute @workgroup_size(4, 4, 4)
fn marchingCubes(@builtin(global_invocation_id) id: vec3<u32>) {
let sdfValues = sampleSDF(id);
let caseIndex = classifyCell(sdfValues);
if (caseIndex == 0u || caseIndex == 255u) { return; }
let triangles = lookupTriangulation(caseIndex);
let vertices = interpolateEdges(sdfValues, triangles);
appendToMeshBuffer(vertices);
}存储成本: 一个采用 8 位量化精度的 256^3 SDF 未压缩时为 16 MB。但体积中的大部分区域都是空的(远离表面)。游程编码或稀疏八叉树存储通常能将其压缩到每个区块 100-500 KB,与高度图地形相当。
编辑: SDF 地形天然适合编辑。添加物质是距离场上的 min() 运算。移除物质(挖掘)则是与取反形状进行 max() 运算。形状之间的平滑混合使用 smoothMin()。这些运算可以在计算着色器中以交互式速率运行。
双重轮廓
Marching Cubes 将顶点放置在网格边上,这能生成平滑表面,却会丢失尖锐特征(悬崖边缘、岩石棱角)。双重轮廓会在每个单元格中放置一个最能表示单元格内部表面的位置顶点,从而保留锐利边缘和棱角。
该算法既需要每个网格点上的距离场值,也需要表面法线(SDF 的梯度)。它会为每个单元格求解一个小型最小二乘问题,以找到最佳顶点位置。由此生成的网格既能表现平滑地形,也能捕捉锐利的岩石特征。
神经双重轮廓(Chen 等,2022,arXiv:2202.01999)使用神经网络取代最小二乘求解器,以预测最佳顶点位置和边交点。它能实现更高的表面重建精度和特征保留能力,尤其适合复杂的自然岩石结构。
Transvoxel 算法
体积地形中最棘手的问题不是生成网格,而是 LOD 过渡。当高分辨率区块与低分辨率区块相邻时,两者的网格无法在边界处对齐,从而产生明显裂缝。
由 Eric Lengyel 设计的 Transvoxel 算法(transvoxel.org)通过在不同分辨率的边界之间插入特殊过渡单元来解决这一问题。这些单元使用额外三角形跨接分辨率差异,并精确匹配两侧网格。该算法将复杂的边界问题归纳为 73 个等价类(相比之下,暴力方法需要处理约 120 万种情况)。
Transvoxel 专为体素数据会动态变化的实时应用而设计,例如创作者编辑、侵蚀和采矿。它不受专利限制,并已用于正式发行的游戏(Space Engineers、Astroneer)。对于具有可编辑体积地形的浏览器世界,Transvoxel 就是合适的 LOD 解决方案。
混合方案:高度图基础 + 体积叠加
对于浏览器开放世界,实用的方案是一套分层系统:
第 1 层:高度图地形覆盖整个世界。这是一种用于丘陵、山谷和山脉的廉价、紧凑表示。它以每个区块的小型高度图片段进行流式传输(每个 2-4 KB)。渲染使用几何裁剪图,GPU 开销保持恒定。
第 2 层:体积叠加仅存在于需要复杂几何体的区块中。洞穴、崖壁、拱门、创作者挖掘的隧道以及地下空间都存储为稀疏 SDF 体积。只有包含体积数据的区块才需要承担 SDF 存储和 Marching Cubes 的成本。
第 3 层:创作者修改以 SDF 编辑的形式存储在基础层之上。创作者挖掘隧道时,会存储该隧道的 SDF 形状。渲染系统将高度图表面与体积减法和加法操作结合,生成最终网格。
这种混合方案对于平坦地形几乎没有额外成本(只需高度图),并且只在存在复杂结构的地方增加开销。在典型世界中,可能只有 5-10% 的区块需要体积数据。
物理正确的地形生成
噪声生成随机地形,物理过程生成逼真地形。两者的差异肉眼可见:噪声地形没有结构(各处都是随机起伏),而物理地形则具有由侵蚀和构造活动形成的水系网络、山脊线、冲积扇和崖壁岩层。
水力侵蚀:基础
水向低处流动,携带泥沙,并在流速减缓时将其沉积下来。只需模拟这一个过程并让其经历数百万年的虚拟时间,就能将毫无特色的噪声转变为具有可辨识地质特征的地形。 基于粒子的方法会在高度图上投放模拟雨滴。每个雨滴都沿坡面向下流动(顺着梯度),根据速度和坡度侵蚀物质,将沉积物作为溶解负载携带,并在流速降低或超过携沙能力时沉积泥沙。经过 200,000-500,000 个粒子后,地形会形成:
- 河谷,沿最大水流路径延伸
- 山脊线,分隔不同流域
- 冲积扇,形成于陡峭山谷通向平坦平原的位置
- 山地中的 V 形谷,以及冰川地形中的 U 形谷
**GPU 实现:**粒子模拟可以并行化。每个粒子彼此独立(这种近似忽略了粒子之间的相互作用,而对侵蚀模拟而言这没有问题)。WebGPU 计算着色器能以 60fps 每帧处理 10,000 个粒子,在约 3 秒的真实时间内完成 200,000 个粒子的模拟。
Sebastian Lague 的开源实现(GitHub)是标准起点。它使用 C# 在单线程上运行,能在几秒内处理一张 1024x1024 高度图。GPU 版本的速度快 50-100 倍。
热力侵蚀
水并不是唯一的侵蚀力。温度变化会导致岩石开裂和破碎(热力风化)。当两个地形点之间的坡度超过某种材料的休止角时,物质就会从高处落向低处。这会形成:
- 悬崖底部的崖积坡(坠落岩石形成的堆积)
- 随时间推移而变得平缓的山脊线
- 因材料而异的地形剖面(坚硬岩石能维持陡坡,松软土壤会坍塌成缓坡)
热力侵蚀比水力侵蚀简单。它是一种局部操作:对每个单元格,将其高度差与相邻单元格比较。如果坡度超过阈值,就将物质向下坡移动。每次迭代只需执行一次计算着色器通道,并在 50-100 次迭代后收敛。
结合水力侵蚀与热力侵蚀,可以生成比单独使用其中任何一种都自然得多的地形。水流刻蚀山谷;热力侵蚀则使山谷之间的山脊变得平缓,并用碎屑填充谷底。
水流功率定律:解析侵蚀
近期研究为基于粒子的模拟提供了另一种选择。水流功率定律(一种将侵蚀速率与汇水面积及坡度联系起来的地貌学方程)可以通过解析方式求解,而不必进行迭代模拟。
Cordonnier 等人(2024,HAL)将解析水流功率定律与滑坡和山坡扩散过程相结合。由此生成的地形具有物理依据,但其运行方式是数学函数,而不是随时间推进的模拟。输入基于噪声的高度图和参数(降雨率、岩石硬度、构造抬升速率),便可在数毫秒内得到侵蚀后的地形。
这种解析方法非常适合浏览器世界,因为它只需在地形生成期间于服务器端运行一次,无需反复迭代。创作者可以调整参数(“让这个区域更具山地特征”会调整抬升速率;“让这里更湿润”会增加降雨量并加深山谷)。
Arenite:多物理场侵蚀(SIGGRAPH 2025)
Arenite(项目页面)是一款基于物理的砂岩模拟器,可以从简单的初始条件生成天然拱门、岩凹、石林柱和孤峰。用户绘制可侵蚀性贴图(软岩层与硬岩层)和植被,然后系统模拟:
- 岩柱内部的应力分布
- 优先移除裸露软质材料的风蚀
- 水流造成的河流侵蚀
- 形成新地貌的粒子沉积
对于复杂地貌,该 GPU 实现在桌面级 GPU 上可于 5 分钟内完成。虽然这对浏览器实时运行来说太慢,但已足以用于服务器端生成。创作者可以定义具有软硬岩层的悬崖面,并在数分钟内得到逼真的天然拱门地貌。
其输出是一个 3D 体素场,可直接接入我们的 SDF/移动立方体管线。
跨表示形式的灵活侵蚀
IRIT-STORM 于 2024 年发表的一篇论文(“Flexible Terrain Erosion”,Springer)解决了一个实际问题:大多数侵蚀方法只能处理高度场。如果地形使用体素、SDF 或分层材料,就需要为每种表示形式分别编写侵蚀代码。
这种灵活侵蚀方法将侵蚀拆分为两个相互独立的过程:地形改变(从表面移除物质)和物质输运(通过遵循基本物理规律的粒子移动沉积物)。每个粒子的大小、密度、恢复系数和携沙能力均可配置。还可以使用可选的向量场控制粒子运动,从而实现逼真的流体动力学效果。
由于粒子通过统一的物质改变接口与地形交互,同一套模拟就能用于高度场、体素网格、隐式曲面和分层材料堆栈。对于我们的混合地形(高度图基础层 + SDF 叠加层),这意味着一个侵蚀系统即可处理两种表示形式。粒子模拟在 GPU 上并行运行。
河网与流域
侵蚀会刻蚀出河流,但要生成令人信服的河网,仅仅让水向下坡流动还不够。以下两种方法可以产生更好的结果:
排水优先生成(Amit Patel,Red Blob Games,项目)会先构建河网,再分配海拔。首先创建一个图(Voronoi 图或三角网格)。将边分类为山脊(无水流)、入口(水流流入)或出口(水流流出)。这样可以创建逼真的层级排水结构,让河流从小型支流汇聚为主要水道,并遵循针对不同河流类型的 Rosgen 分类系统(平坦地形中的辫状河道、山区中的狭窄峡谷)。
流量累积会追踪流经每个地形单元格的水量。均匀投放模拟降雨,让水向下坡流动,并统计每个单元格被经过的次数。累积量高的单元格会成为河道。累积量中等的单元格会成为季节性溪流。累积图还会决定侵蚀强度(水越多,侵蚀越强)和植被分布(河岸更湿润,能支持不同的植物物种)。
对于创作者世界,河网会在地形创建期间于服务器端生成,并按区块存储为一张 2D 流向图和一张水量累积图。浏览器客户端使用这些贴图渲染水面(在河道的正确高度放置平面),并控制植被分布(水边的植被更加茂盛)。
海岸与岸线生成
海岸线是地形与水体交汇的地方,它具有标准侵蚀无法生成的独特特征:海蚀崖、海滩沉积、潮滩、海蚀柱和海蚀平台。
NEWTS1.0(2024,MIT)使用两种侵蚀机制模拟岩石海岸线的演化:均匀后退(恒定侵蚀速率)和波浪驱动侵蚀(侵蚀速率取决于风区距离和入射波角度)。该模型会模拟数千年的演化,并生成与真实海岸地貌相符的岬角、海湾、海蚀柱和天然拱门。
对于浏览器世界,海岸特征会在世界创建期间预先生成。相关参数(主要波浪方向、沿岸岩石硬度变化)可让创作者控制海岸线的风貌。“挪威峡湾”设置会生成两侧陡峭的狭长海湾。“热带环礁”设置会生成低洼的沙质海岸和潟湖。
洞穴与地下空间生成
洞穴需要完全体积化的地形(SDF 或体素),因为高度图无法表示封闭空间。生成方法包括:
3D 噪声阈值化是最简单的方法。在每个体素位置采样 3D Perlin 或单纯形噪声。低于阈值的值表示实体,高于阈值的值表示空洞。调整阈值和噪声参数即可控制隧道直径、连通性和洞室大小。这种方法会生成让人联想到 Minecraft 的有机蠕虫状洞穴系统。
PLUME(Procedural Layer Underground Modeling Engine,程序化分层地下建模引擎)(2024,arXiv:2508.20926)使用分层程序化规则生成逼真的洞穴和熔岩管环境。它最初为太空探索研究而开发(用于模拟火星熔岩管),能够生成在地质上合理的地下结构,包括钟乳石、石柱和洞室系统。
结合元球雕刻的 L-System 隧道使用 L-System 文法让分支隧道路径在地形中生长,然后通过元球隐式曲面雕刻出实际的隧道几何体。元球可以产生平滑、圆润的洞壁。使用不同参数进行多轮处理,可创建主通道、侧室和狭窄的连接隧道。
对于创作者世界,洞穴生成会与 SDF 叠加系统结合。基础地形是高度图(没有洞穴)。当某个区块需要洞穴时(无论来自程序化生成还是创作者设计),系统都会生成一个 SDF 体,从基础地形中减去洞穴几何体。移动立方体管线负责渲染组合后的表面。
将植被视为物理过程
游戏中的植被通常通过“海拔 2000 米以下生成草地、1500 米以下生成树木、3000 米以上生成积雪”之类的规则进行程序化放置。这种方式速度很快,但会产生均匀且不真实的分布。
具有物理依据的植被模拟会将每株植物建模为对资源(光照、水和土壤养分)的竞争。模拟过程如下:
- 将种子散布到整个地形上
- 每株植物根据可用资源生长(水分来自水力侵蚀模拟中的水流,日照取决于坡度和坡向,土壤深度取决于侵蚀历史)
- 植物相互竞争:树木遮蔽草地,茂密树冠阻止新幼苗生长
- 经过一段模拟时间后,生物群系会自然形成:有水的山谷中生长森林,风吹强烈的山脊上植被稀疏,积水区域形成湿地
Deussen 等人的生态系统模拟(论文)可以生成符合现实世界生态模式的森林分布。该模拟在 2D 网格上运行(每个地形区块对应一个单元格),并生成密度图和物种分配结果,供渲染系统进行 GPU 实例化植被放置。
对于浏览器世界,植被模拟会在世界生成期间于服务器端运行一次。输出是每个区块的一组密度图:树木密度、草地密度、花卉密度和岩石碎屑密度。浏览器客户端在运行时结合 GPU 实例化使用这些贴图来散布植被。
基于扩散模型的地形合成
这是目前发展最快的领域。扩散模型(与图像领域 Stable Diffusion 背后的技术相同)正被应用于地形生成,其结果明显比基于噪声的方法更加真实。
Terrain Diffusion:Perlin 噪声的继任者
Terrain Diffusion(Goslin,2025,arXiv:2512.08309,项目页面)是自 1985 年 Perlin 噪声问世以来,程序化地形生成领域最重大的进展。此项研究后来被 SIGGRAPH 2026 接收,其规范的 arXiv 标题现为“InfiniteDiffusion: Bridging Learned Fidelity and Procedural Utility for Open-World Terrain Generation”。InfiniteDiffusion 算法与 Terrain Diffusion 框架是同一篇论文的两个组成部分。
其核心创新是 InfiniteDiffusion,一种为无界域重新构造扩散采样的算法。传统扩散模型只能生成固定尺寸的输出(例如 512x512 高度图)。InfiniteDiffusion 则可以生成无限延伸的地形,并具备:
- **种子一致性:**相同种子始终生成相同地形,就像 Perlin 噪声一样
- **常数时间随机访问:**无需先生成相邻区域,即可查询任意位置的高度
- **无边界瑕疵:**可无限生成,且没有可见接缝或重复图案
该系统使用由多个扩散模型构成的分层堆栈。顶层捕捉行星尺度的特征(大陆、山脉)。后续每一层都会添加更精细的细节(单个山峰、山谷和小尺度粗糙度)。紧凑的拉普拉斯编码可在从海平面到喜马拉雅山峰的巨大动态范围内稳定输出。
**性能:**生成速度可以跟上实时探索。论文报告称,即使在轨道速度这一理论极限(约 7,700 m/s)下,消费级 GPU 上的地形合成速度仍比移动速度快 9 倍。该项目包含一个 Minecraft 集成,展示了实时地形合成。 这对我们为何重要: Terrain Diffusion 基于真实世界的高程数据(地球的实际地形)训练并生成景观。输出中的河网、山脉、海岸特征和高原结构都源自训练数据,而不是手工调节的噪声参数。创作者可以说“生成类似苏格兰高地的地形”,扩散模型便会生成具有相应地质特征的结果。
模型在世界生成期间运行于服务器端。输出是标准高度图,会像其他地形数据一样流式传输到浏览器。客户端无需感知具体的生成方式。
TerraFusion:联合生成几何与纹理
TerraFusion(2025,arXiv:2505.04050)更进一步,可以联合生成高度图和地形纹理。其核心洞见是:地形几何与表面外观彼此相关(河床是沙质的,崖壁是岩石质的,平坦区域通常覆盖草地)。如果分开生成两者,就会产生不匹配。
TerraFusion 使用潜空间扩散模型,并为高度图和纹理分别配备 VAE,通过训练来建模二者的联合分布。该系统支持:
- 无条件生成: 随机生成合理且纹理匹配的地形
- 草图条件生成: 创作者绘制一张粗略地图(这里是山谷,那里是山脊,沿这条边缘分布悬崖),模型便会生成与草图相符的精细几何和纹理
这对创作者世界非常有用。创作者只需勾画地块的总体布局,系统便会补全地质上合理的地形及适当的表面材质。无需绘制高度图,无需涂抹混合纹理,也无需手动指定材质。
MESA:文本生成地形
MESA(2025,arXiv:2504.07210,CVPR 2025 研讨会)根据文本描述生成地形。它使用 Copernicus 计划提供的全球遥感数据训练,因此接触过各种类型的陆地景观。
输入“一个由陡峭花岗岩岩壁环绕、通往岩石海岸线的峡湾”这样的提示词,会生成具有相应地质结构的高度图。“起伏和缓的农田、低矮丘陵和宽阔河谷”则会生成截然不同的结果。
MESA 推出了 Major TOM Core-DEM 扩展数据集,将全球卫星影像与数字高程模型配对。这些训练数据使模型能够理解真实地形在各种尺度和气候带下的外观。
Geodiffussr:文本引导的地形纹理生成
Geodiffussr(2025,arXiv:2511.23029)接收现有高度图,在遵循高程数据的同时,根据文本描述生成纹理。“秋日森林”会在中等坡度区域生成橙色和金色植被,并在陡峭崖壁上保留裸露岩石。“热带海岸”会在低地生成棕榈植被,并在海平面位置生成珊瑚沙滩。
该系统使用多尺度内容聚合来确保纹理分配符合高程规律:积雪只会出现在物理上合理的海拔以上,水体位于洼地中,植被随海拔升高而变得稀疏。
对于创作者世界而言,这意味着无需改变几何形状,就能根据文本提示重新生成地形纹理。创作者先塑造所需地形,再描述氛围(如“阴暗的火山荒原”或“繁茂的温带森林”),系统便会生成适合的表面材质。
浏览器地形的动态 LOD
在浏览器中以完整分辨率渲染所有大规模地形是不可能的。一个 4 公里 x 4 公里、分辨率为 1 米的世界,仅地形就包含 1600 万个顶点。无论世界有多大,动态 LOD 都能将其缩减为恒定且可控的顶点数量。
几何剪裁图
几何剪裁图(Losasso 和 Hoppe,SIGGRAPH 2004,论文)仍是高度图地形 LOD 的黄金标准。其思路是:将地形渲染为一组以相机为中心的同心方形环。每个环的面积都是前一个环的两倍,但分辨率减半。
靠近相机的位置(最内层环):完整分辨率,网格间距 1 米。 向外一环:间距 2 米,覆盖 4 倍面积。 再下一环:间距 4 米,覆盖 16 倍面积。 依此类推,共设置 6–8 个层级,直到最外层环覆盖整个可视距离。
顶点总数保持恒定:大约为 N^2 * levels,其中 N 是以顶点数表示的环宽。N=256 且有 8 个层级时,总计约 50 万个顶点。无论世界宽度是 1 公里还是 100 公里,渲染规模都相同。
浏览器实现: 几何剪裁图可以在 WebGL 2 中工作,因为它只需要标准顶点缓冲区更新(无需计算着色器)。相机移动时,CPU 会以适当分辨率对地形采样,从而更新每个环的高度图数据。顶点着色器从纹理读取高度值,并使平面网格产生位移。
渐变过渡: 如果采用简单方式处理,LOD 层级之间的切换会产生明显的“跳变”。几何渐变(下文有专节详述)会在顶点着色器的过渡区域中混合不同层级的顶点位置,从而实现平滑、无跳变的过渡,并且不会增加额外的绘制调用开销。
CDLOD:四叉树自适应剪裁图
CDLOD(Strugar,2014,论文)使用四叉树取代固定同心环,对几何剪裁图进行了改进。四叉树会根据地形自适应调整:平坦区域使用粗粒度节点,而细节丰富的区域(悬崖、山脊)则进行更精细的细分。
这对创作者世界非常重要,因为不同区块的复杂度各不相同。平坦草地只需要最低分辨率。包含悬崖和洞穴的山地区域则需要最高细节。CDLOD 会把分辨率分配到真正需要的地方。
CPU 端的四叉树遍历非常轻量(只有几百个节点),用于确定以何种分辨率绘制哪些地形区块。GPU 将每个区块渲染为实例化网格,并使用各区块独立的 LOD uniform。
并发二叉树:行星级曲面细分
并发二叉树(CBT)是一种适合 GPU 的自适应地形曲面细分数据结构,由 Benyoub 和 Dupuy(Intel,HPG 2024,论文,GitHub)提出。
其核心思路是:将地形表示为一棵二叉树,其中每个节点都是一个三角形。自适应细分会拆分靠近相机的三角形,并合并远离相机的三角形。二叉树以一维数组(二叉堆)的形式完全驻留在 GPU 内存中,细分与合并操作则通过计算着色器执行。
2024 年的论文将 CBT 从方形高度图域扩展到了任意多边形网格。这意味着你可以细分球体(用于行星渲染)或任意基础网格(用于具有非矩形边界的游戏世界)。关键改进是:将 CBT 用作内存池管理器,而不是采用隐式编码,从而支持高得多的细分层级。
性能方面:在主机级硬件上,行星级地形曲面细分耗时低于 0.2 毫秒。该算法的性能会随处理器数量线性扩展。对于 WebGPU 而言,这为浏览器中的行星级地形渲染提供了一条可行路径,尽管实现复杂度很高。
使用 WebGPU 的 GPU 驱动 LOD
WebGPU 支持完全由 GPU 驱动的地形管线,无需 CPU 参与 LOD 决策:
计算阶段 1:视锥体与遮挡剔除。 计算着色器将每个地形区块的包围盒与视锥体及遮挡缓冲区(上一帧的深度缓冲区,经降采样处理)进行测试。不可见区块会被完全剔除。
计算阶段 2:LOD 选择。 对于可见区块,计算其屏幕空间尺寸并选择适当的 LOD 层级。将 LOD 层级和区块 ID 写入间接绘制缓冲区。
计算阶段 3:网格生成(用于体积地形)。 对包含 SDF 数据的区块运行移动立方体算法,以所选 LOD 分辨率生成网格。
间接绘制。 通过一次
drawIndexedIndirect()调用渲染所有地形区块。GPU 决定一切:绘制什么、使用何种分辨率以及采用什么顺序。
无论世界大小或复杂度如何,该管线的 CPU 开销都保持恒定(仅需调度计算着色器并发出间接绘制调用)。GPU 会处理所有逐区块决策。
@compute @workgroup_size(64)
fn lodSelection(@builtin(global_invocation_id) id: vec3<u32>) {
let patchIdx = id.x;
let bounds = patchBounds[patchIdx];
if (!frustumTest(bounds, viewProjection)) { return; }
if (occlusionTest(bounds, depthPyramid) == OCCLUDED) { return; }
let screenSize = projectedSize(bounds, viewProjection, screenDimensions);
let lod = clamp(u32(log2(maxScreenSize / screenSize)), 0u, MAX_LOD);
let drawIdx = atomicAdd(&drawCount, 1u);
drawArgs[drawIdx] = DrawArgs(patchIdx, lod, indexCount[lod], indexOffset[lod]);
}几何渐变:无跳变的 LOD 过渡
地形 LOD 中最明显的视觉瑕疵是跳变:当区块切换 LOD 层级时,顶点会突然跳到新位置。几何渐变通过在过渡区域内平滑插值不同 LOD 层级的顶点位置来消除这一问题。
该实现完全位于顶点着色器中。每个顶点同时存储当前 LOD 位置和下一级更粗糙 LOD 的位置。当相机距离跨过过渡阈值时,会使用一个渐变因子在两者之间进行混合:
float morphFactor = smoothstep(lodNear, lodFar, distanceToCamera);
float morphedHeight = mix(fineLodHeight, coarseLodHeight, morphFactor);
gl_Position = viewProjection * vec4(worldPos.x, morphedHeight, worldPos.z, 1.0);Hoppe 的几何剪裁图论文(GPU Gems 2,第 2 章)介绍了剪裁图环的完整实现。渐变区域位于每个环最外侧的 20%。在该区域内,顶点会平滑地收敛到下一环的分辨率。其视觉效果是:地形几何会在不同细节层级之间“融化”,而不是突然跳变。在常见的相机移动速度下,这种过渡几乎不可见。
图像空间混合(Scherzer 等,论文)是另一种方案,它会在屏幕空间中混合两个 LOD 层级的渲染图像。该方法能够处理差异更大的 LOD 切换(例如从网格切换到公告板),但需要为过渡区域增加一次额外的渲染阶段。
GPU 驱动的植被剔除
植被(树木、草、岩石)通常是开放世界中绘制调用的最大来源。简单粗暴的方式会在每一帧绘制所有植被实例。GPU 驱动的剔除现已可通过 Three.js 的 WebGPU 支持实现,它会在不可见实例到达光栅化器之前将其剔除。
Three.js 的 ComputeInstanceCulling(文档)为实例化网格提供视锥体和 LOD 剔除,在实例数量庞大时可获得 10–100 倍的性能提升。其管线如下:
- 计算着色器读取所有实例的包围球
- 将每个包围球与相机视锥体进行测试(六平面测试)
- 应用基于距离的 LOD:超过阈值的实例切换到较低细节层级,或被完全剔除
- 将保留下来的实例压缩写入缓冲区,并通过
drawIndirect绘制 CPU 不会执行任何逐实例工作。完成初始设置后,无论实例数量多少,每种植被的成本都只是一次计算调度加一次间接绘制调用。
对于更密集的植被,Three.js 的 IndirectBatchedMesh(文档)会将多种几何体类型(树木、灌木、岩石)打包到单个缓冲区中,并通过多重间接绘制来渲染。一个区块中的所有植被只需一次绘制调用。
结合我们植被系统中基于密度图的散布方式,这意味着:密度图在计算着色器中生成 50,000 个草叶位置,剔除阶段排除其中 70% 位于屏幕外或距离过远的草叶,然后一次间接绘制调用渲染剩余的 15,000 个草叶。CPU 总成本:几乎可以忽略不计。
体积地形的 LOD
体积地形(SDF + Marching Cubes)需要自己的 LOD 系统,因为网格是动态生成的,而非预先制作。具体方法如下:
多分辨率 SDF 存储。 以类似 mipmap 的层级结构,按多种分辨率存储 SDF。第 0 级为完整分辨率(1 米体素)。第 1 级为 2 米体素(数据量减少 8 倍)。第 2 级为 4 米体素。在每一级中,通过取最小绝对距离来对 SDF 进行下采样。
按 LOD 选择 Marching Cubes。 在与目标 LOD 匹配的 SDF 层级上运行 Marching Cubes。近距离区块使用第 0 级,远距离区块使用第 2 级或第 3 级。Transvoxel 算法负责处理不同层级之间的边界。
缓存。 生成的网格会一直缓存,直到 SDF 发生变化(创作者编辑)或 LOD 层级改变(摄像机发生显著移动)。对于静态地形,网格只生成一次,之后重复使用。
地形流式加载架构
区块数据格式
每个地形区块(64x64 米)都以紧凑的二进制包形式流式传输:
ChunkPacket {
header: {
chunkX: i16, chunkZ: i16,
version: u32,
flags: u8 // hasHeightmap | hasVolumetric | hasVegetation
}
heightmap: {
resolution: u8, // 65x65 for full, 33x33 for half, 17x17 for quarter
quantizedHeights: u16[resolution * resolution], // delta-encoded, zlib compressed
splatMap: u8[4 * resolution * resolution] // RGBA blend weights, LZ4 compressed
}
volumetric?: { // only present if flags.hasVolumetric
sdfResolution: u8, // typically 32 or 64
sparseOctree: bytes // run-length encoded sparse SDF
}
vegetation?: { // only present if flags.hasVegetation
treeDensityMap: u8[16 * 16], // 4m resolution density grid
grassDensityMap: u8[32 * 32], // 2m resolution density grid
rockDensityMap: u8[16 * 16]
}
creatorObjects: {
count: u16,
objects: PlacedObject[] // assetId + transform + properties, ~40 bytes each
}
}典型大小:
- 仅高度图的区块(平坦地形):压缩后 2-4 KB
- 高度图 + 植被:4-8 KB
- 高度图 + 体积数据 + 植被(复杂区块):20-100 KB
- 5x5 全细节邻域:总计 50-500 KB
渐进式区块加载
区块根据距离、移动方向和数据类型按优先级顺序加载:
优先级 1(立即,<100ms): 加载玩家即将进入的区块的高度图几何体。地形表面最先出现。即使采用最低分辨率(每个区块 17x17),地面也已经存在。
优先级 2(快速,<300ms): 加载混合贴图和地形纹理。地面开始呈现颜色。
优先级 3(流式加载,<1s): 升级到完整分辨率高度图。加载植被密度图。通过 GPU 实例化生成树木和草地。
优先级 4(后台,<3s): 加载包含洞穴或悬挑结构的区块的体积 SDF 数据。Marching Cubes 在 Web Worker 中生成网格,然后将缓冲区传输到主线程。
优先级 5(延迟加载,<10s): 加载创作者放置的对象、建筑结构的高分辨率纹理,以及花朵、小型岩石、碎屑等细节对象。
预测性预取
不要等玩家进入区块后才开始加载。应根据玩家的速度预测其前进方向,并提前加载:
- 步行速度(5 km/h): 提前预取前方 2 个区块(128m)。在常见的宽带延迟下,可以获得 200-400ms 的提前量。
- 奔跑/骑乘(15 km/h): 提前预取前方 4 个区块。加载环会随速度方向移动。
- 飞行/快速旅行: 在切换期间暂停渲染。以最高优先级流式加载目的地区块。当已有足够数据可渲染第一帧时,恢复渲染。
预取系统会跟踪哪些区块已在缓存中、哪些区块正在传输中(已请求但尚未到达),以及哪些区块是必需的。优先队列按紧急程度对待处理请求进行排序。玩家远离某些区块后,取消对应请求。
内存预算与淘汰
桌面端的浏览器标签页可以获得 2-4 GB 内存。地形系统只能使用其中一部分(其余内存还要留给渲染、物理、网络和 JavaScript 堆)。
目标预算:所有地形数据共计 256 MB。
按我们的典型区块大小计算:
- 缓存的全细节区块:约 100 个(10x10 邻域),每个 5-100 KB,原始数据共计 5-10 MB
- GPU 地形几何体:约 50 MB(可见地形的顶点缓冲区和索引缓冲区)
- 地形纹理:约 100 MB(KTX2 压缩并打包到图集)
- 植被实例缓冲区:约 50 MB(用于 GPU 实例化的位置、旋转和缩放数据)
- SDF 体积与缓存的 Marching Cubes 网格:约 50 MB
超出可见范围的区块会先从 GPU 内存中淘汰(纹理、顶点缓冲区),然后再从 CPU 缓存中淘汰。原始高度图数据最后才会被清除,因为它的保留成本最低,而且当玩家转身时,它也是最需要立即可用的数据。
生物群系过渡与消除平铺感
平滑的生物群系边界
真实景观中的生物群系之间不会存在生硬边界。森林不会在一条线上戛然而止,然后立刻变成沙漠。实际情况会有一个渐变过程:茂密森林逐渐变为零散树木,接着是灌丛地,最后是植被稀疏的沙漠。正确实现这种过渡,会让世界显得连贯,而不是由区块拼接而成。
AutoBiomes(Kötter 等人,论文)将程序化地形生成与简化的气候模拟相结合。温度、湿度和海拔共同决定每个位置的生物群系类型。在不同生物群系之间,材质权重和植被密度会在过渡区域内插值(通常宽 50-100 米)。过渡宽度随生物群系组合而变化:森林到草原的过渡宽阔而渐进,悬崖到水体的过渡则狭窄而陡然。
对于创作者世界,生物群系分配会在粗粒度网格上运行(每 16x16 米区域采样一次生物群系)。地形着色器读取当前片元及其相邻位置的生物群系值,在过渡区域中插值材质权重,并据此混合纹理。植被散布使用相同的插值密度值,因此树木密度会在森林边缘逐渐降低。
创作者控制:允许创作者在自己的地块上绘制生物群系覆盖层。系统会根据地形属性生成默认生物群系,但创作者可以覆盖这些结果。在低洼区域绘制“沼泽”,材质就会转变为浑浊水体、苔藓和枯树。生物群系绘制图是每个区块一张 16x16 的生物群系 ID 网格(256 字节),用于覆盖程序化分配结果。
六边形平铺:消除纹理重复
地形渲染中最常见的视觉瑕疵是纹理重复。一张 1 米大小的草地纹理平铺在 100 米宽的草甸上,会产生明显的网格图案。以下两种技术可以解决这个问题:
六边形平铺(Mikkelsen,演示)使用六边形网格取代方形平铺网格。每个六边形图块都以随机偏移和旋转对纹理进行采样,并混合六边形边界以隐藏接缝。最终得到的表面看起来均匀随机,而不是由纹理重复平铺而成。成本:每个片元大约额外进行 3 次纹理采样。这项技术已广泛应用于商业游戏,并可在任何片元着色器中运行。
随机纹理过滤(Pharr 等人,NVIDIA,2024,论文)不在着色前执行过滤,而是在着色后使用随机采样进行过滤。随机采样产生的误差很小,并且可以通过时空降噪有效处理。该方法能产生更准确的过滤结果,也适用于压缩纹理和稀疏纹理。对于地形,它既能消除平铺瑕疵,也能消除六边形平铺有时会在过渡处引入的过滤瑕疵。
对于浏览器世界,六边形平铺是更实际的选择(它可在任何着色器中运行)。随机过滤需要更多基础设施,但如果可以使用时序降噪(在采用 TAA 的 WebGPU 路径中可以实现),它会产生更好的效果。
地形材质系统
三平面映射
标准 UV 映射纹理在陡峭斜坡上会严重拉伸,因为 UV 坐标会被压缩。三平面映射沿三个轴(X、Y、Z)投射纹理,并根据表面法线进行混合:
vec3 blending = abs(normal);
blending = normalize(max(blending, 0.00001));
blending /= (blending.x + blending.y + blending.z);
vec4 xaxis = texture(material, worldPos.yz * scale);
vec4 yaxis = texture(material, worldPos.xz * scale);
vec4 zaxis = texture(material, worldPos.xy * scale);
vec4 color = xaxis * blending.x + yaxis * blending.y + zaxis * blending.z;悬崖表面采用 X 或 Z 轴投影(不会拉伸),平坦地面采用 Y 轴投影。混合过程平滑且自动完成,无需展开 UV。
Babylon.js 内置了三平面材质。Three.js 需要使用自定义着色器,但其实现只有大约 30 行 GLSL。
对于 PBR 地形,应对所有通道应用三平面映射:反照率、法线、粗糙度和环境光遮蔽。每个通道都使用相同的混合权重。
基于坡度和海拔的材质分配
与其手工绘制混合贴图,不如根据地形属性程序化分配材质:
float slope = acos(dot(normal, vec3(0, 1, 0)));
float altitude = worldPos.y;
float grassWeight = smoothstep(0.3, 0.0, slope) * smoothstep(2000.0, 1500.0, altitude);
float rockWeight = smoothstep(0.2, 0.5, slope);
float snowWeight = smoothstep(2500.0, 3000.0, altitude) * smoothstep(0.4, 0.1, slope);
float sandWeight = smoothstep(5.0, 0.0, altitude) * smoothstep(0.15, 0.0, slope);低海拔的平坦地面使用草地材质,陡峭斜坡使用岩石材质,高海拔地区使用积雪材质(但仅限足够平坦、能够积雪的表面),海平面附近则使用沙地材质。过渡平滑,并且符合物理规律。
对于创作者世界,可以将海拔阈值和混合区域作为每个区块都可绘制调整的参数开放给创作者。创作者可以升高或降低林线、扩大积雪覆盖范围,或者通过调整其地块的材质规则,将一座长满青草的山丘变成沙漠。
GPU 友好的拉普拉斯纹理混合
标准纹理混合(图层之间的线性插值)要么会产生明显接缝,要么会造成褪色、低对比度的效果。拉普拉斯金字塔混合可以解决这个问题,但传统方法需要成本高昂的预计算。
Wronski(NVIDIA,2025,JCGT)提出了一种 GPU 友好的变体,可以在实时着色器中运行,无需预计算,也不需要额外内存。该技术使用标准 mipmap 链近似拉普拉斯金字塔:同时在当前 mip 层级和更粗糙的层级上采样纹理,计算二者之差(即拉普拉斯分量),然后混合各图层的拉普拉斯贡献。
这种方法既能保留清晰的局部特征(单根草叶、岩石裂缝),又能在更大尺度上实现平滑混合。其成本是每个片元额外进行几次纹理采样。对于每像素需要混合 4 个以上材质图层的地形,其效果明显优于线性混合,尤其是在草地、岩石和沙地之间的过渡区域。
用于侵蚀细节的相量噪声
标准地形在近距离观察时通常显得平坦,因为侵蚀模拟是在高度图分辨率(1 米网格)下运行的。真实地形在厘米尺度上具有细微的侵蚀图案,如细沟、冲沟和风化裂缝。
Grenier 等人(2024,CGF)使用相量噪声实时添加地形微观细节。相量噪声通过定义一个输入周期函数的随机相位场来合成结构化图案。应用于地形时,它可以创建具有空间变化的侵蚀图案,并且:
- 让狭窄细沟跨尺度汇聚成更大的冲沟
- 自动与地形坡度对齐(侵蚀图案沿最大坡降线延伸)
- 实现最高 32 倍的细节放大(以高度图分辨率的 32 倍添加细节)
- 完全在片元着色器中运行,并保持交互式帧率
对于浏览器世界,相量噪声可作为细节层在地形着色器中运行。高度图负责提供大尺度形状,相量噪声则在片元着色器中添加逼真的微观侵蚀效果,而不会增加几何复杂度。其参数(图案频率、振幅、方向)可因生物群系而异:裸岩上的深沟、沙丘上的柔和波纹,以及干泥上类似粗糙树皮的纹理。
地形虚拟纹理
在大尺度场景中,地形纹理图集会变得难以管理。一个 4 km x 4 km 的世界,如果按每厘米 1 个纹素计算,就需要一张 400,000 x 400,000 像素的纹理。这显然不可行。
虚拟纹理(也称为 megatexture,源自 id Software 的 Rage)将地形纹理视为分页结构,从而解决这个问题。完整纹理在概念上始终存在,但只有屏幕上可见的图块才会被加载到 GPU 内存中。 流程如下:
- 反馈阶段: 使用着色器渲染地形,输出每个像素所需的纹理图块(图块 ID 和 mip 级别)。将结果读回 CPU(或使用计算着色器处理)。
- 图块加载: 从 CDN 加载请求的图块,或根据泼溅贴图数据以程序化方式生成。
- 间接寻址纹理: 使用一张小型纹理,将虚拟图块坐标映射到纹理图集中的物理图块坐标。
- 渲染阶段: 地形着色器查询间接寻址纹理以找到正确的物理图块,然后从图集中采样材质纹理。
WebGPU 的计算着色器可以完全在 GPU 上处理反馈分析和页表管理。CPU 只负责图块 I/O。
最终效果是:地形可以在任意分辨率下拥有独特纹理,同时不会导致纹理内存爆炸。远离相机的图块以低分辨率加载,近距离图块以高分辨率加载。总内存用量始终控制在固定预算内(纹理图集通常为 128-256 MB)。
动态地形效果
水洼与湿润效果
雨水不只是落下,还会积聚。它会在低洼处形成水洼,在表面产生湿润光泽,并从陡坡上流走。模拟这些现象,可以让天气与地形真正产生联系,而不只是叠加一层纯视觉效果。
基于着色器的方法并不模拟流体动力学,而是使用地形高度图来判断水会在哪里积聚:
float concavity = heightCenter * 4.0 - heightLeft - heightRight - heightUp - heightDown;
float puddleDepth = max(0.0, concavity * rainIntensity - evaporationRate * timeSinceRain);
float wetness = smoothstep(0.0, 0.02, puddleDepth);
vec3 wetColor = baseColor * 0.7;
float wetRoughness = baseRoughness * 0.3;
vec3 finalColor = mix(baseColor, wetColor, wetness);
float finalRoughness = mix(baseRoughness, wetRoughness, wetness);凹陷区域(高度图拉普拉斯算子的负值区域)会积水。凹陷越深,水洼越大。湿润表面会变暗,并且反射更强(粗糙度更低)。雨停后,该效果会随时间逐渐消退。
如需完整的水洼反射,可在水洼表面添加一个平面反射阶段。也可以使用成本更低的屏幕空间反射(SSR),Three.js 和 Babylon.js 的后处理栈都已提供该功能。
水洼表面的雨滴可通过反馈纹理中的简单二维波动方程实现(Saurel,2026,博客)。每滴雨都会产生向外传播并逐渐衰减的涟漪。波纹纹理会调制水洼的法线贴图,从而以 60fps 呈现逼真的涟漪图案。
脚印与地形形变
当玩家走过柔软地形(沙地、雪地、泥地)时,脚印可以增强身处实体世界的感觉。其实现方式是:为每个区块维护一张小型形变纹理(64x64 像素 = 4 KB),用于存储高度偏移。当角色踩到柔软地形时,将脚印形状压印到形变纹理中。
地形顶点着色器读取形变纹理,并从高度值中减去偏移。地形片元着色器则使脚印区域变暗(压实的土壤颜色更深)并提高粗糙度(受扰动的表面)。
脚印会随时间消退(积雪重新覆盖,雨水冲淡泥脚印),具体做法是逐渐将形变纹理归零。消退速度取决于天气:雨天较快,干燥环境下较慢。
在多人世界中,脚印数据是短暂且本地化的。每个客户端都会为可见玩家生成脚印。形变纹理无需在客户端之间同步(每个人看到的都是自己本地版本的临时脚印)。这样可以避免广播每一步动作所带来的网络开销。
程序化天空与昼夜循环
天空是任何开放世界中可见面积最大的表面,它决定了整个地形的氛围。
Three.js 提供了一个完整的天空系统,其中包含程序化太阳/月亮、昼夜循环、云层、星空和镜头光晕。Three.js 内置的 Sky 示例(也提供 WebGPU 版本)实现了 Preetham 的解析天空模型。
如需更符合物理规律的效果,webgpu-sky-atmosphere 将 Hillaire 的大气模型实现为 WebGPU 后处理。它支持多重散射相函数,并能基于物理原理生成正确的空气透视(远处地形显得更朦胧)、太阳与日落色彩,以及天空渐变。
TerrainView7 展示了使用预计算大气散射的 WebGPU 全尺度行星渲染,证明了符合物理规律的大气渲染可以在浏览器中运行。
对于创作者世界,天空参数(太阳位置、云层覆盖率、雾霾密度)会根据服务器的世界时钟在所有客户端之间同步。天空着色器在每个客户端本地运行,为所有玩家生成一致的光照效果。
地形光照:间接照明
直射阳光由阴影贴图处理,但阴影中地形的颜色和亮度(环境光/间接光)对于视觉质量同样重要。纯黑色阴影看起来并不自然。阴影应受到天空颜色的影响(晴天偏蓝,阴天偏灰)。
对于浏览器世界,实用方案如下:
使用可见性位掩码的屏幕空间间接照明(Jimenez 等,2023,论文)改进了标准 SSAO,它会为每个像素跟踪 32 个方向的可见性扇区。这样不仅能捕捉一个点被遮挡的程度,还能捕捉遮挡的方向性。薄表面可以正确地让光线从另一侧穿过。最终得到的间接照明能够对附近几何体作出反应,而无需构建全局照明基础设施。
其成本与 SSAO 相当(1-2ms),但视觉提升十分明显:峡谷中的地形会接收到峡谷壁的反弹光,悬垂结构的底部会被地面反射光照亮。该效果可与天空着色器的大气散射结合,生成符合物理原理的环境光照。
地形物理集成
Rapier 高度场碰撞体
Rapier 的 WASM 物理引擎提供了针对地形优化的原生高度场碰撞体。创建地形碰撞体非常直接:
const heights = new Float32Array(65 * 65);
// 填充高度图数据...
const groundCollider = RAPIER.ColliderDesc.heightfield(
64, 64, heights, new RAPIER.Vector3(64.0, 100.0, 64.0)
);
world.createCollider(groundCollider);高度场碰撞体使用结构化网格来高效执行宽阶段查询。针对高度场的射线检测复杂度为 O(log n),而三角形网格则为 O(n)。对于一个 65x65 的区块,碰撞查询可在数微秒内完成。
对于包含体积地形(SDF 叠加层)的区块,可以根据移动立方体算法的输出生成三角形网格,并使用三角网格碰撞体。这比高度场碰撞体成本更高,但可以处理任意几何形状。只需为最靠近玩家的 3-5 个区块生成三角网格碰撞体,远处区块不需要物理模拟。
性能: Rapier 的高度场碰撞体用于一个 65x65 区块时,每次物理步进中的角色控制器查询约增加 0.1ms。5 个带高度场碰撞体的活动区块:0.5ms。一个体积区块的三角网格碰撞体:0.2-0.5ms。地形物理总预算:低于 1ms,完全可以控制在整个世界 2-3ms 的物理预算内。
地形感知角色控制器
角色控制器需要根据地形属性作出响应:
- 坡度限制: 角色可以在最大 45 度的坡面上行走。更陡的坡面会导致角色滑落。该功能使用角色所在位置的地形法线(可通过高度图梯度低成本计算)。
- 表面材质响应: 在岩石上行走时,脚步声和移动速度应与沙地或泥地不同。地形的泼溅贴图可提供任意位置的表面材质。
- 台阶攀爬: 角色可以迈上最高 0.5 米的台阶。Rapier 的
KinematicCharacterController会根据可配置的台阶高度自动处理此功能。
浏览器中的交互式地形编辑
对于创作者世界,地形不只是自动生成的,还可以由玩家雕刻、修改和重塑。编辑工具必须响应迅速(即时视觉反馈),并且支持联网(其他玩家能在几秒内看到更改)。
WebGPU SDF 编辑器
Reinder Nijhoff 的 WebGPU SDF 编辑器证明了功能完整的 SDF 建模如今已经可以在浏览器中运行。该编辑器支持:
- 六种基本形状(球体、立方体、圆锥体、圆柱体、胶囊体、圆环体),并支持位置、旋转和缩放
- 布尔运算(并集、差集、交集),支持平滑混合和可配置的混合半径
- 层级场景图,支持组和嵌套运算
- 实时渲染,使用多个 GPU 计算着色器阶段、基于八叉树的空间划分(覆盖 16,384 个网格单元),并通过移动立方体或表面网格提取表面
- 通过阴影贴图实现时间抗锯齿和环境光遮蔽
每个基本体以 28 个浮点数(112 字节)的形式存储在单个 GPU 缓冲区中。得益于这种紧凑表示,一个复杂的地形编辑(由数十个 SDF 基本体定义的洞穴入口、拱门或雕刻后的悬崖表面)占用空间不到 5 KB,可以即时同步给其他玩家。
对于创作者世界,SDF 编辑工作流如下:
- 创作者选择一种雕刻工具(添加球体、减去立方体、平滑混合)
- 在世界中点击/拖动,以放置 SDF 基本体并调整其尺寸
- 客户端立即对修改后的 SDF 运行移动立方体算法,以更新本地网格(反馈时间 <16ms)
- 将 SDF 编辑(基本体类型 + 变换 + 混合模式,约 100 字节)发送到服务器
- 服务器验证编辑(位于创作者的地块内,且不与受保护区域相交),然后将其广播给附近玩家
- 其他玩家的客户端应用该 SDF 编辑,并重新生成本地网格
一项编辑对其他玩家可见所需的总往返时间为 100-300ms,具体取决于网络延迟。创作者本人会立即看到编辑结果,因为系统会在服务器确认前先在本地应用该编辑。
基于笔刷的高度图编辑
对于高度图层(即不需要体积特性的 90% 地形),可以采用更简单的编辑模型。创作者使用笔刷绘制高度修改:
- 抬高/降低: 在指定半径内按照衰减曲线增加或减少高度
- 平滑: 对指定半径内的高度取平均值,消除尖锐特征
- 压平: 将指定半径内的所有高度设为目标值
- 侵蚀笔刷: 在笔刷半径内局部执行数步水力侵蚀
高度图编辑以增量形式保存:即一小块叠加在基础地形上的高度变化。增量块非常小(由 16 位高度偏移组成的 32x32 网格 = 2 KB),可以通过单条消息同步给其他玩家。每个区块可以累积多个增量块,服务器会定期将它们合并到区块的持久化高度图中。
协作编辑约束
当多位创作者同时编辑同一区块时,系统需要制定规则:
- 空间锁定: 同一时间只能有一位创作者编辑某个 8x8 米的子区域。创作者开始一次编辑笔画时获取锁,抬起笔刷时释放锁。锁会在 10 秒无操作后超时。
- 非重叠编辑: 如果两位创作者编辑同一区块的不同部分,两项编辑均可无冲突地应用(因为它们修改的是不同的高度图单元或 SDF 区域)。
- 重叠编辑: 如果两位创作者编辑同一位置,服务器会按编辑抵达顺序依次执行。完成协调后,两个客户端会看到相同的最终结果。
这比放置对象所使用的完整 CRDT 方案更简单,因为地形编辑是对连续场(高度、SDF 距离)执行的增量操作,而不是离散的对象状态。
WebGPU 中的 Nanite 风格虚拟几何体
虚幻引擎 5 的 Nanite 会在构建时生成一个簇 DAG(有向无环图),并在运行时根据屏幕空间误差为每个簇选择正确的 LOD,从而渲染数十亿个三角形。整个流程都在 GPU 上运行。该方法已经被移植到 WebGPU。
Nanite WebGPU
Scthe 开发的 Nanite WebGPU(GitHub 星标数超过 1.1k)是 Nanite 核心架构的一套完整浏览器实现:
- 使用 meshoptimizer 的簇生成器离线构建的网格簇 LOD 层级
- 使用 WGSL 计算着色器实现的软件光栅化器(用于适应 WebGPU 中硬件光栅化无法高效执行逐簇绘制的限制)
- 使用视锥体和遮挡测试进行逐实例和逐网格簇剔除
- 用于极远距离对象的公告板替身
- 纹理和逐顶点法线支持 该管线会将网格拆分为由约 128 个三角形组成的簇。相邻簇会被分组,并在保留共享边界的同时简化每个组(使用 meshoptimizer)。此过程递归进行,直到整个网格折叠为单个簇。运行时,计算着色器遍历 DAG,并为每个组选择在当前屏幕分辨率下产生小于 1 像素误差的最粗粒度簇。
THREE-Nanite 是一个新兴的 Three.js 实现,即使要处理数十万个三角形,也能在集成显卡硬件上达到 20-40fps。它证明了即便是在低端浏览器硬件上,Nanite 风格的渲染也切实可行。
meshoptimizer:LOD 管线的基础
meshoptimizer(作者 Arseny Kapoulkine)是大多数浏览器兼容 LOD 管线背后的核心库。1.0 版(2025 年)提供:
- 带误差度量的网格简化(衡量形状发生了多大变化,用于 LOD 选择)
- 用于 Nanite 风格 meshlet 层级结构的簇生成
- 用于 GPU 友好三角形排序的顶点缓存优化
- 用于降低像素着色器开销的过度绘制优化
- 用于减小下载体积的顶点量化与压缩
meshoptimizer 1.0(发布于 2025 年 12 月)附带了一个新的单头文件 clusterlod.h,可直接实现 Nanite 风格的连续 LOD。它构建由簇组成的层级结构,对这些簇逐步分组并简化,既可以直接使用,也可以作为自定义管线的参考。这正是地形管线为 SDF/行进立方体网格 LOD 所需的簇 DAG 基元。
在我们的地形管线中,meshoptimizer 会将 SDF 地形的行进立方体输出处理成经过优化、完成分簇且带有 LOD 层级结构的网格。离线处理在服务器端运行。浏览器接收预先分簇的网格,并在运行时执行由 GPU 驱动的 LOD 选择。
使用 meshoptimizer 生成 LOD,再用 WebGPU 计算着色器在运行时进行选择,这一组合让浏览器地形拥有与 Nanite 相同的架构模式,同时针对 Web 环境的约束进行了调整。
地形感知的关卡设计
地形不仅是供玩家行走的表面。它的形态会引导玩家移动、吸引注意力,并塑造探索过程中的情绪节奏。最优秀的开放世界会将地形本身作为设计工具。
视线与地标
《The Level Design Book》的寻路章节介绍了地形高度如何控制玩家能看到什么,以及他们会前往何处。山脊会遮住另一侧的景象,从而激发好奇心。山谷会将移动路线汇聚到最低点。远处可见的高大地标(高塔、山峰、形态奇特的树木)会为玩家提供一个步行前往的目标。
对于创作者世界而言,这意味着地形生成应当产生自然的寻路特征。山脊线应当阻断视线,让玩家登上山顶并看到新区域时产生“豁然开朗的揭示时刻”。山谷应当汇聚到有趣的地点。还应当存在适合创作者放置远处可见地标的高地。
好奇心驱动的探索
普渡大学的研究(论文)总结了四种空间探索触发因素:
- 抵达极端地点(最高峰、最远边界、最深洞穴)。地形应当拥有清晰的极值点,让抵达这些地点的玩家获得回报。
- 探明视觉遮挡后的空间(那座悬崖后面有什么?那个洞穴里面有什么?)。阻挡视野的地形会促使玩家移动,以发现隐藏的内容。
- 格格不入的物体(荒野中的建筑、黑暗中的灯光)。创作者放置的物体与自然地形形成反差,从而吸引注意力。
- 理解空间连接关系(这条山谷如何连接到那片海岸?)。能够形成清晰易懂地理结构的地形,会鼓励玩家阅读地图和规划路线。
PlotMap:AI 辅助的兴趣点布局
PlotMap(arXiv:2309.15242)可以根据叙事需求自动规划兴趣点布局,例如某个任务需要一座河边村庄,另一个任务需要山顶遗迹,然后寻找满足这些空间约束的地形位置。对于创作者世界,可以用类似系统根据地形属性建议建筑位置:“这座山顶视野良好,适合建造瞭望塔”“这片受遮蔽的山谷适合建造村庄”。
流动的水与瀑布
河流和瀑布既具备视觉吸引力,又能带来环境音效和玩法功能(水可以作为障碍、资源或路径)。
河流渲染
开放世界中的河流通常会渲染为沿地形表面延伸的纹理条带。条带网格根据河流样条曲线(以控制点形式存储)生成,并投影到地形高度图上。河流着色器会应用:
- 沿水流方向排列的 UV,使其朝河流方向滚动,营造流水效果
- 河岸边缘的泡沫,出现在河流与河岸交界处(基于深度,类似海岸线泡沫)
- 基于河道宽度的流速变化(狭窄河段流速更快,宽阔河段流速更慢)
- 带有基于深度颜色的透明效果(浅水清澈,深水较暗)
对于浏览器世界,河流数据非常紧凑:一条样条曲线(每个河段 20-50 个控制点,约 400 字节),再加上宽度和流速参数。客户端会将样条曲线投影到自己的地形表面,从而在本地生成河流网格。
瀑布渲染
当河流从悬崖边缘跌落时,会使用瀑布粒子系统取代平坦的河流表面。实时水体模拟研究中的混合方法(EG)会将无法通过高度场表示的区域(瀑布、水花)转换为水雾、飞溅和泡沫粒子,这些粒子会与流体模拟交换质量和动量。
对于浏览器世界,瀑布可以更简单:检测河流样条曲线穿越地形高度突变的位置,在该点生成带有向下速度的粒子系统,并在底部添加泡沫飞溅效果。粒子是使用 GPU 实例化的四边形,带有滚动的 Alpha 纹理。每个瀑布使用 500 个粒子,只需一次绘制调用。流水声通过 Web Audio API 播放,并应用基于距离的衰减。
远处地形特征的替身渲染
树木、岩石、建筑及其他地形特征在远处会变得非常小。继续将它们渲染为完整的 3D 网格会浪费 GPU 周期。替身渲染会使用朝向摄像机的预渲染平面图像来替代远处物体。
八面体替身图集
八面体替身会从多个观察角度捕获 3D 物体的外观,并将其存储在纹理图集中。运行时,着色器会根据当前观察方向对图集进行采样,并在两个最接近的捕获角度之间插值。
半八面体图集只包含上半球视角,因为玩家很少从下方观察树木。在纹理尺寸相同的情况下,它能提供完整八面体图集两倍的角分辨率。Unity 的替身系统报告称,将 1,600 个树木实例从真实网格(每个含 14 万个三角形)切换为替身后,帧耗时从 111ms 降至 5.78ms。
对于浏览器世界,替身管线如下:
- 服务器端:从 16-32 个观察角度渲染每个资产,捕获颜色、法线和深度
- 打包为图集纹理(每个资产一张图集,约 256x256 像素,使用 KTX2 时小于 100 KB)
- 运行时:超过替身切换距离(通常为 100-200m)的实例,渲染为对图集进行采样的广告牌
- 在 20m 的过渡区域内,在网格与替身之间交叉淡化,以隐藏切换过程
广告牌泼溅(BBSplat)
广告牌泼溅(2024,arXiv:2411.08508)使用可学习的带纹理平面基元,将这种方法进一步推进。BBSplat 不使用预渲染视图,而是优化广告牌的位置和纹理,以便从任意角度都能尽可能准确地呈现 3D 物体。与 3D 高斯泼溅相比,它在保持视角相关外观的同时,最高可实现 17 倍的压缩率。对于浏览器世界中的远处地形特征,BBSplat 可以在改善角度覆盖范围的同时,减少每个资产的替身存储空间。
专业地形工具及其启示
在从零构建地形管线之前,值得先了解专业离线工具的工作方式。这些工具将数十年的地形生成研究浓缩成了可用于生产的工作流。
Gaea(QuadSpinner)使用 GPU 加速,修改后几乎可以即时反馈。它支持构建单边最高达 200 万像素的分块地形、自动导出 LOD 网格,以及基于节点的图结构,其中每个节点都代表一种物理过程(侵蚀、沉积、隆升、热力风化)。Gaea 的侵蚀节点之所以能生成仿佛手工雕刻的地形,是因为它们模拟的是具体物理过程,而不是通用噪声。关键在于:Gaea 并不使用单一的侵蚀算法。它分别提供河流侵蚀(河道切割)、热力侵蚀(悬崖崩解)、海岸侵蚀(波浪作用)和风蚀(沙丘形成)节点。在图结构中组合这些节点,可以生成具有特定气候地质特征的地形。
World Machine 采用了类似的图结构方法,但更专注于地形的宏观结构。它的“布局生成器”允许美术人员勾勒地形特征的大致形状(这里是山脉,那里是山谷,海岸线沿着这条边缘),然后由系统填充符合物理规律的细节。这正是我们想要的创作者工作流:勾勒意图,获得地质结构。
World Creator 的特色是在编辑过程中提供实时预览,并内置河流生成功能,可以分析地形,再根据排水分析自动计算水流路径。
我们从这些工具中获得的启示: 使用节点图组合物理过程,比任何单一算法都更强大。我们的服务器端生成管线应支持以下串联流程:噪声基底 > 构造隆升 > 水力侵蚀 > 热力风化 > 海岸侵蚀 > 植被。创作者可以控制每个阶段的参数。管线在服务器端运行数秒,即可生成高度图、混合贴图和植被密度图。
SoilMachine:开源地貌模拟
SoilMachine 是一款开源模块化地貌模拟器,它将多种侵蚀系统(水力、热力、风力)与沉积物运输和沉积过程结合起来。它使用 C++ 和 GPU 计算构建,为专业工具采用的多过程侵蚀方法提供了参考实现。
相关的 soillib(C++20,MIT 许可证)以可复用库的形式提供底层地貌模拟基元。hydro-gen 则在 OpenGL 计算着色器中实现了基于网格(浅水)和基于粒子(雨滴)的水力侵蚀,并支持实时调整参数。
这些开源工具可以改造后用于我们的服务器端生成管线。如果未来希望在浏览器中运行侵蚀模拟,为创作者提供实时反馈,其中的计算着色器实现可以直接迁移到 WebGPU。
多层地形材质
真实地形并不是单一表面,而是由多个层次组成:底部是基岩,上面是土壤,表面还会积累积雪或沙子。动态分层可以根据季节、天气和创作者操作改变地形的视觉特征。
分层高度场表示
不要只使用一张高度图,而是为每个网格单元使用多个高度层:
Cell {
bedrock_height: f16, // permanent rock surface
soil_height: f16, // accumulated soil/sediment above bedrock
snow_height: f16, // dynamic snow accumulation
water_height: f16 // standing water depth
}总计:每个单元 8 字节(单张高度图则为 2 字节)。对于 65x65 的区块,压缩前为 34 KB,依然十分紧凑。
可见表面为 bedrock + soil + snow。地形着色器会读取所有层,并相应地混合材质:土层较薄的地方会露出岩石;积雪处的表面呈白色;积水处则会形成水洼或湖泊。
动态积累
降雪期间,积雪会堆积在平坦、朝上的表面。堆积速率取决于表面法线(陡坡无法留住积雪)、温度(与海拔有关)和遮蔽情况(悬垂结构下方不会积雪)。每次天气周期(每隔几秒),计算着色器通道都会更新一次积雪层。
沙子的积累方式类似,由风驱动沉积。风从暴露的表面带走颗粒,并将其沉积在障碍物后方和受遮蔽的区域。
对于创作者世界,动态积累意味着不同天气下的地形外观也会有所不同。暴风雪期间,积雪会覆盖整个世界;天气放晴后,积雪则会融化。降雨会让洼地积水。这可以在无需创作者进行任何操作的情况下,让世界显得能够响应环境变化。
多层侵蚀
2024 年论文《使用多层高度图的 3D 实时水力侵蚀模拟》(EG)将侵蚀扩展到了多层结构。水对土壤的侵蚀速度比对基岩更快。沉积物会形成新的土层。模拟会维持各层的完整性(基岩始终位于土壤下方),同时支持悬垂结构等复杂特征(例如基岩悬在下方已被侵蚀的土壤之上)。 性能:在 RTX 3070 上以 2048x2048 分辨率运行时,每个模拟步骤约耗时 6ms。这对服务端生成来说足够快,但对浏览器逐帧模拟而言仍然太慢。分层表示适用于静态地形生成,而动态积雪和积水则通过成本更低的逐帧着色器运行。
草地、岩石与细节渲染
景观不仅需要地形几何体和纹理,还需要随风摇曳的草叶、散布在坡面上的岩石,以及花朵、卵石和枯枝等小型细节,让近距离视图显得自然。
GPU 实例化草地
基于浏览器的草地渲染已经在 Three.js 中得到充分验证,并可通过 GPU 实例化实现。al-ro 的草地演示采用的方法使用 InstancedBufferGeometry,通过一次绘制调用渲染 100,000 片草叶。
每片草叶都是一个简单的四边形(4-8 个三角形)。逐实例属性定义位置、高度、弯曲方向、颜色变化和风动相位。顶点着色器会:
- 读取逐实例变换
- 使用由世界位置和时间决定的正弦波应用风动动画
- 根据风力弯曲草叶(叶尖弯曲更多,叶根不弯曲)
- 应用颜色渐变(根部较暗、叶尖较亮,以表现次表面散射)
Codrops 的蓬松草地教程(2025,教程)演示了一种壳层纹理方法:以逐渐增加的偏移量多次渲染地面平面,每一层都对噪声纹理进行采样,从而营造出茂密草地体积的外观。对于极其密集的植被覆盖,这种方法比逐草叶实例化成本更低,但近距离观察时不够逼真。
对于创作者世界,草地密度来自每个区块的植被密度图。GPU 在渲染时根据密度图散布草叶位置。无需存储或流式传输逐草叶数据。每个区块的密度图是一个 32x32 网格(1 KB),GPU 可据此生成数千个草叶实例。
程序化岩石与悬崖细节
悬崖表面和岩石地形需要几何细节,而基础高度图或 SDF 无法在合理分辨率下提供这些细节。以下两种方法可以互为补充:
GPU 网格着色器表面重建(Raad 等,Eurographics 2025,论文)可在渲染时根据粗略控制网格生成程序化几何体。网格着色器读取基础地形表面,并添加位移、裂缝和凸起,而无需将细节几何体存储在内存中。这样既能降低显存占用,也能实现动态 LOD。
实例化岩石散布使用 GPU 实例化,将预制岩石网格放置在陡坡和悬崖边缘。计算着色器读取地形法线和坡度,并在坡度超过阈值的位置散布岩石实例。每个实例都是一个经过随机旋转和缩放的小型网格(200-500 个三角形)。借助实例化,散布 10,000 块岩石带来的渲染成本可以忽略不计。
道路与路径
创作者放置的道路、小径和路径需要贴合地形,并修改表面材质(用泥土或石材替换草地)。
所有主流游戏引擎采用的方法都是:将路径定义为一条样条曲线(一系列控制点)。将样条曲线投影到地形表面上。生成沿样条曲线延伸、略高于地形的条带网格。将道路纹理应用到该条带上。在地形着色器中,使用投影纹理或贴花,在样条曲线宽度范围内将地形材质混合为道路材质。
对于浏览器世界,创作者可以在地形上绘制路径。客户端生成控制点并将其发送到服务器(几十个 vec3 值)。服务器存储样条曲线。所有客户端通过将样条曲线投影到各自的地形网格上,在本地渲染道路条带。道路数据非常小(路径样条曲线点,可能只有 200 字节),但视觉影响很大:连接创作者建筑的路径会让世界显得有人居住。
地形阴影
地形阴影对于可读性(理解地形形状)和氛围(营造不同时段的情绪)都至关重要。在开放世界中,太阳会在整个可见地形上投射阴影。
级联阴影贴图(CSM)
CSM 将视锥体划分为 3-4 个距离范围(级联)。每个级联都会从太阳视角,以适合其距离的分辨率渲染一张阴影贴图。近距离级联:高分辨率(树木和建筑物下方的细致阴影)。远距离级联:低分辨率(大范围的山体阴影)。
Three.js 和 Babylon.js 都支持 CSM。地形的关键优化是:阴影贴图中仅渲染地形,而不渲染单独的草叶或小型细节。草地使用地形的阴影贴图实现自身阴影,而不是渲染自己的阴影。
**性能预算:**3-4 个 1024x1024 阴影级联。将地形渲染到阴影贴图中耗时 0.5-1ms(地形几何体已位于 GPU 内存中)。在地形着色器中采样 4 个级联会增加 0.2-0.3ms。
基于高度图的地形自阴影
对于 CSM 成本过高的超大地形,可以预先计算地平线图:为每个地形单元存储 8 个罗盘方向上的最大仰角。在渲染时,将太阳角度与地平线图进行比较,以判断某个点是否处于阴影中。Skyrim 正是通过这种方式处理远距离地形(超出 CSM 范围)的自阴影。
地平线图在服务端根据高度图计算(需要数秒处理时间),并以每区块一张 128x128 纹理(压缩后 16 KB)的形式进行流式传输。其视觉效果十分显著:即使在极远的视距下,山谷也能呈现真实的变暗效果。
用于流式传输的地形数据压缩
网络是浏览器世界的瓶颈。地形数据每减少一个字节,加载速度都会更快。
高度图压缩
原始 16 位高度图很容易压缩,因为相邻单元通常具有相近的值。处理流水线如下:
- **增量编码:**存储每个单元与其预测值(相邻单元平均值)之间的差值。增量值通常很小,并集中在零附近。
- **量化:**对于远距离区块,将精度从 16 位降低到 12 位或 8 位。在 500 米外,对于高度范围为 100 米的地形,8 位高度精度(0.4 米分辨率)与 16 位在视觉上没有区别。
- **熵编码:**对经过增量编码的数据流应用 zlib 或 brotli 压缩。典型压缩比:4-8 倍。
结果:一个 16 位的 65x65 区块从原始 8.4 KB 缩减到压缩后的 1-2 KB。降低至 8 位精度后:0.5-1 KB。
渐进式高度图流式传输
先发送低分辨率地形,然后逐步细化。一个 17x17 高度图(64 米区块采用 4 米单元间距时的最低规格)原始大小为 578 字节,压缩后不到 200 字节。地形可以立即显示。随后流式传输 33x33 的细化数据(添加奇数行/列采样点),再传输 65x65 的完整分辨率数据。每一级都会在不替换已有数据的情况下增加细节。
这与几何剪裁贴图的 LOD 环相对应:远距离地形使用低分辨率版本(17x17),中距离使用中等分辨率(33x33),近距离使用完整分辨率(65x65)。流式传输优先级与渲染 LOD 相匹配。
SDF 体数据压缩
稀疏 SDF 体数据可以大幅压缩,因为大多数体素都远离表面(为空白空间)。可选方案包括:
**游程编码:**对连续的相同值(空体素)进行编码。典型 SDF 体数据中 95% 以上为空,因此 RLE 可实现 10-50 倍压缩。
**稀疏八叉树:**仅存储包含表面穿越体素的八叉树节点。空白空间没有节点。一个包含单条洞穴隧道的 64^3 SDF 体可能只有 2,000-5,000 个占用节点(总计 262,144 个体素),每个节点存储为 1-2 字节。
熵驱动的渐进式压缩(2024,HAL)通过使用经过熵优化的平面和自适应量化递归划分空间,可应用于三维空间数据。它能生成针对率失真权衡优化的细化数据流,在网络流式传输的低码率条件下尤其有益。
整合所有环节:浏览器地形流水线
本文开头的战略概览给出了快速决策框架和分阶段构建计划。本节则完整介绍服务端生成流水线和浏览器渲染流水线的技术细节。
生成流水线(服务端)
生成流水线以物理过程的有向图形式运行,其设计灵感来自 Gaea 和 World Machine 的节点图方法。每个阶段接收上一阶段的输出并进一步细化。创作者可以控制每个阶段的参数。
| 阶段 | 输入 | 处理 | 输出 | 时间 |
|---|---|---|---|---|
| 1. 基础地形 | 种子或文本提示词 | Terrain Diffusion / MESA / 噪声 + fBm | 16 位高度图 | 1-5s |
| 2. 侵蚀 | 高度图 | 解析式水流功率 + 热力侵蚀 | 侵蚀后的高度图、流量累积图、沉积物图 | 0.5-2s |
| 3. 河流 | 侵蚀后的高度图、流量图 | 排水网络提取、河道雕刻 | 河流样条曲线、水位图 | 0.5s |
| 4. 海岸 | 海平面附近的高度图 | NEWTS 风格的波浪侵蚀 | 海岸线特征(悬崖、海滩、海蚀柱) | 1-3s |
| 5. 体积地形 | 高度图 + 创作者意图 | Arenite 侵蚀 / 洞穴生成 / SDF 雕刻 | 受影响区块的稀疏 SDF 体数据 | 1-60s |
| 6. 材质 | 高度图 + 侵蚀图 | TerraFusion / Geodiffussr / 程序化规则 | 混合贴图、地形纹理 | 1-5s |
| 7. 植被 | 高度图 + 流量图 + 材质 | 生态系统竞争模拟 | 每区块、每生物群系的密度图 | 1-3s |
| 8. 地平线图 | 最终高度图 | 8 方向最大仰角 | 每区块的自阴影纹理 | 2-5s |
| 9. 分块 | 所有输出 | 切片、增量编码、压缩、哈希 | CDN 上的区块包 | 5-10s |
| 总计 | 15-90s |
生成一个新的 4x4 公里世界需要 15-90 秒。创作者编辑(雕刻、参数调整)只会针对受影响的区块重新运行相关阶段,通常可在 5 秒内完成。
渲染流水线(浏览器)
| 步骤 | WebGPU 路径 | WebGL 2 回退路径 | 帧预算 |
|---|---|---|---|
| 1. 流式传输 | 优先级队列、预测式预取 | 相同 | 不适用(异步) |
| 2. 高度图地形 | GPU 驱动的 CDLOD 四叉树、计算着色器剔除、间接绘制 | 几何剪裁贴图、CPU 环带更新 | 0.5-1ms |
| 3. 体积网格 | 计算着色器 Marching Cubes + Transvoxel | 由 Web Worker 预生成网格,缓存 2-3 个 LOD | 0.5-2ms |
| 4. LOD 过渡 | 顶点着色器中的几何渐变 | 相同 | 已计入上项 |
| 5. 材质 | 三平面 PBR + 拉普拉斯混合 + 相量噪声细节 + 虚拟纹理 | 三平面 PBR + 线性混合 + 预烘焙混合贴图 | 1-1.5ms |
| 6. 植被 | ComputeInstanceCulling + IndirectBatchedMesh、六边形平铺地表植被 | CPU 视锥剔除 + InstancedMesh | 1-1.5ms |
| 7. 水体 | 沿水流方向的河流条带、基于深度的岸边泡沫 | 相同(反射更简单) | 0.5ms |
| 8. 阴影 | 3-4 级联 CSM + 地平线图自阴影 | 2 级联 CSM | 0.5-1ms |
| 9. 大气 | Hillaire 天空模型 + 体积雾 + 天气粒子 | Preetham 天空 + 距离雾 | 0.5ms |
| 10. 动态效果 | 水坑积聚、脚印变形、降雪/降雨 | 水坑积聚、雨水粒子 | 0.3ms |
| 地形总计 | 3.5-6.5ms |
在 60fps(每帧 16.6ms)下,地形系统在 WebGPU 上占用 21% 的帧预算,在 WebGL 2 上占用 39%。剩余预算可用于玩家化身、创作者对象、UI、网络和后处理。
为什么这套方案能在浏览器中运行
整套流水线围绕浏览器的三项限制进行设计:
**内存(最大 2-4 GB):**256 MB 的地形预算之所以足够,是因为高度图区块每个仅为 2-8 KB(经过增量编码),SDF 体数据采用稀疏存储(每个体积区块 100-500 KB),植被在运行时根据 1 KB 密度图生成,而纹理使用 KTX2 压缩(每张 1024x1024 纹理为 150 KB)。在任意时刻,浏览器中可见世界的总大小为 50-200 MB。
**无法访问磁盘:**所有内容都通过网络进行流式传输。渐进式加载让玩家能在 <100ms 内看到地形(低分辨率高度图),在 <300ms 内看到带纹理的地形,并在 <3s 内看到完整细节。根据移动速度进行预取,可以在正常探索过程中隐藏加载时间。
**GPU 性能差异极大:**WebGPU 路径面向高端台式机。WebGL 2 回退路径则支持包括移动设备在内的其他所有设备。两条路径使用相同的区块数据。区别在于渲染技术,而不是数据格式。运行 WebGL 2 的 Chromebook 与运行 WebGPU 的 RTX 4090 看到的是同一个世界,只是细节更少、视距更短。
研究论文
地形表示与网格生成
“Marching Cubes:一种高分辨率三维表面构建算法”——Lorensen 和 Cline(SIGGRAPH 1987)。DOI。这是从体数据中提取三角形网格的奠基性算法。38 年后,它仍然是应用最广泛的等值面提取方法。GPU 并行实现可在 WebGPU 计算着色器中实时运行。 《厄米特数据的对偶轮廓法》 —— Ju、Losasso、Schaefer、Warren(SIGGRAPH 2002)。DOI。生成能保留尖锐特征(悬崖边缘、岩石棱角)的网格,而这些特征在移动立方体算法中会被平滑。除距离值外,还需要表面法线。
《神经对偶轮廓法》 —— Chen 等(2022)。arXiv:2202.01999。使用学习型预测器替代对偶轮廓法中的最小二乘顶点定位。可为复杂自然特征提供更好的表面质量。
《Transvoxel 算法》 —— Lengyel(2009,2024 年更新)。transvoxel.org。实现体素地形的无缝 LOD 过渡。通过 73 种过渡单元类型消除分辨率边界处的裂缝。无专利限制,专为实时应用设计。
地形 LOD 与渲染
《几何裁剪图:使用嵌套规则网格进行地形渲染》 —— Losasso 和 Hoppe(SIGGRAPH 2004)。论文。使用同心 LOD 环,以恒定开销渲染地形。能够以交互帧率处理 40 GB 的地形数据。它是大多数浏览器地形渲染器的基础。
《CDLOD:地形渲染的混合 LOD》 —— Strugar(2014)。论文。对几何裁剪图进行四叉树自适应改进。不再仅根据距离,而是根据地形复杂度分配分辨率。
《GPU 驱动的渲染管线》 —— Ubisoft(SIGGRAPH 2015),Wihlidal 和 Hoppe。系统化定义了由计算着色器处理剔除、LOD 选择和绘制调用生成的 GPU 驱动方法。这是我们 WebGPU 地形管线采用的架构模式。
基于物理的地形生成
《用于快速地形生成的基于物理的解析侵蚀》 —— Cordonnier 等(2024)。HAL。采用解析形式的水流功率定律进行侵蚀,无需迭代模拟。可在几毫秒内生成物理上合理的地形。
《GPU 上的快速水力侵蚀模拟与可视化》 —— Mei、Decaudin、Hu(2007)。HAL。使用浅水模拟实现 GPU 并行水力侵蚀。它是大多数游戏引擎侵蚀实现的基础。
《Arenite:基于物理的砂岩模拟器》 —— SIGGRAPH 2025。项目。通过应力和侵蚀模拟,利用多物理场侵蚀生成拱门、石林和岩龛。在桌面 GPU 上运行时间不到 5 分钟。
《用于陡峭地形侵蚀的高效泥石流模拟》 —— Purdue CGVLAB(2024)。论文。通过 GPU 加速泥石流和陡坡侵蚀,生成逼真的山地地形特征。
《灵活的地形侵蚀》 —— IRIT-STORM(2024)。Springer。基于粒子的侵蚀系统,通过统一接口支持高度场、体素网格、隐式表面和分层材质。让混合地形表示可以使用同一套侵蚀系统。
地形细节与纹理
《适合 GPU 的拉普拉斯纹理混合》 —— Wronski(NVIDIA,2025)。JCGT。无需预计算即可实时进行地形材质的拉普拉斯金字塔混合。在消除接缝伪影的同时保留尖锐特征。每个片元只需增加少量纹理采样。
《使用可控程序化图案实时增强地形》 —— Grenier 等(2024)。CGF。使用相量噪声生成微侵蚀细节,分辨率最高可达高度图的 32 倍。沿坡度对齐的图案完全在片元着色器中运行。
自适应细分
《用于大型游戏组件的并发二叉树》 —— Benyoub 和 Dupuy(Intel,HPG 2024)。论文。一种适合 GPU 的二叉树数据结构,用于自适应地形细分。可在 0.2 毫秒内渲染行星级几何体。已从方形域扩展到任意多边形网格。
洞穴与地下空间生成
《PLUME:程序化分层地下建模引擎》 —— 2024。arXiv:2508.20926。一个开源框架,使用分层程序化规则生成逼真的洞穴和熔岩管环境。最初为太空探索机器人开发。
神经地形合成
《InfiniteDiffusion:为开放世界地形生成连接学习保真度与程序化实用性》 —— Goslin(2025,SIGGRAPH 2026)。arXiv:2512.08309。使用带拉普拉斯编码的分层扩散模型,生成无限且种子一致的地形。在消费级 GPU 上比基线方法快 9 倍。
《TerraFusion:地形几何与纹理的联合生成》 —— 2025。arXiv:2505.04050。使用草图条件控制的潜空间扩散,同时合成高度图和纹理。
《MESA:文本驱动的地形生成》 —— CVPR 2025 Workshop。arXiv:2504.07210。使用 Copernicus 遥感训练数据实现文本到地形的生成。
《Geodiffussr:保持高程保真度的生成式地形纹理化》 —— 2025。arXiv:2511.23029。使用流匹配生成遵循高程数据的文本引导地形纹理。
《Sketch2Terrain:AI 驱动的实时地形草图映射》 —— 2025。项目。在增强现实中将草图转换为地形。相比手动映射,效率提高 38%。
植被与生态系统模拟
《大型虚拟地形上的 GPU 实时程序化植被分布》 —— SBGames 2018。论文。根据生物和非生物因素,使用基于四叉树的方法在 GPU 上散布植被。
《森林的程序化生成与渲染》 —— 2022。论文。将 L 系统树木生成与生态系统竞争模拟相结合,实现逼真的森林分布。
《使用 GPU 工作图进行实时程序化生成》 —— AMD GPUOpen 2024。论文。使用 GPU 工作图在不到 4 毫秒内生成超过 7.9 万个植被实例。
浏览器 GPU 技术
《GSWT 渲染器》 —— SIGGRAPH Asia 2025。GitHub。基于 WebGPU + Rust/Wasm 的渲染器,使用高斯泼溅王氏砖生成带动态 LOD 和流式加载的无限 3D 地形。
《在浏览器中实现原生级速度的 GPU 计算:WebGPU 移动立方体》 —— Usher(2024)。博客。展示 WebGPU 计算在并行网格生成算法中可以达到原生级性能。
体素渲染与大型场景
《Aokana:面向开放世界游戏的 GPU 驱动体素渲染框架》 —— 2025。arXiv:2505.02017。使用带 LOD 和流式加载的稀疏体素 DAG,处理包含数百亿体素的场景。相比此前的顶尖方案,内存占用降低 9 倍,渲染速度提高 4.8 倍。专为集成到游戏引擎而设计。
程序化几何与表面重建
《使用 GPU 网格着色器进行实时程序化表面重建》 —— Raad 等(Eurographics 2025)。论文。在渲染时使用网格着色器从粗略控制网格生成细致的几何表面。无需在内存中存储高分辨率几何体即可实现动态 LOD。
地形阴影
《优化地形阴影》 —— AMD GPUOpen。博客。针对大规模地形的实用 CSM 优化。涵盖级联划分、高 GPU 效率的阴影贴图渲染,以及地形专用优化。
虚拟几何与网格优化
《Nanite WebGPU》 —— Scthe(2024)。GitHub,演示。在浏览器中完整实现 UE5 Nanite 架构:网格簇 LOD 层级、使用 WGSL 编写的软件光栅化器、逐网格簇剔除和广告牌替身。
《在几分钟内处理数十亿个三角形》 —— Kapoulkine(2025)。博客。Meshoptimizer v1.0,用于生成层次化聚类 LOD。能够高效地将大型网格处理为 Nanite 风格的簇 DAG。
《广告牌泼溅(BBSplat)》 —— 2024。arXiv:2411.08508。用于新视角合成的可学习纹理平面图元,相比 3D 高斯泼溅可实现 17 倍压缩。
地形光照与全局光照
《〈七日世界〉中的全局光照》 —— GDC 2025。演讲。用于 16 公里开放世界的混合 GI:神经网络压缩探针(69:1 压缩比)、基于机器学习的室内外漏光处理,以及动态探针响应。
《使用 AMD FidelityFX Brixelizer 实现 GI》 —— GDC 2024。论文。基于计算着色器的稀疏距离场级联与屏幕空间探针。无需硬件光线追踪。
《辐射度级联》 —— 2024。博客。使用级联辐射度结构实现无噪点的实时全局光照,无需时间累积。
关卡设计与探索
《PlotMap:游戏世界的自动化布局设计》 —— 2023。arXiv:2309.15242。在地形上辅助放置 POI,并满足叙事空间约束。
《空间探索触发器》 —— Purdue University(FDG 2022)。论文。驱动玩家探索的四种设计模式:极值点、视觉遮挡、不合环境的物体和空间连接。
数据压缩与流式传输
《熵驱动的 3D 点云渐进式压缩》 —— SGP 2024。论文。使用带自适应量化的递归空间划分,实现经过率失真优化的渐进式压缩。生成适合可变带宽网络流式传输的逐级细化数据流。
交互式编辑
《WebGPU SDF 编辑器》 —— Nijhoff(2026)。项目。功能完整的浏览器端 SDF 建模工具,支持实时移动立方体、布尔运算、平滑混合和八叉树空间划分。每个图元占用 112 字节。
河流与海岸生成
《程序化河流流域》 —— Patel(Red Blob Games)。项目。使用 Voronoi/三角形网格边分类,实现流域优先的河流生成。在分配地形高程之前先构建河流层级。
《NEWTS1.0:波浪与海侵陡坎造成海岸侵蚀的数值模型》 —— MIT(2024)。论文。使用均匀后退和波浪驱动侵蚀的简化海岸侵蚀模型。可生成符合真实地貌的海岬、海湾、海蚀柱和海蚀拱。
延伸阅读
- 浏览器 3D 开放世界技术介绍了浏览器多人创作世界的完整架构
- 2026 年 Web 游戏技术栈介绍 WebGL、WebGPU 和 WebAssembly 基础知识
- Three.js + USDC 技术报告介绍如何在浏览器中加载 3D 资源
- 前沿开源生成式 AI 模型介绍 AI 生成管线
- WebGPU 入门 —— 用于 GPU 驱动地形的计算着色器
- 使用 Web Worker 处理游戏逻辑 —— 将地形生成卸载到 Worker
- 游戏物理库 —— 用于地形交互和高度场碰撞体的物理系统
- 在哪里寻找免费游戏资源 —— 地形渲染所需的纹理和材质来源




