Games104学习笔记(6)
GAMES104:游戏中地形、大气和云的渲染(上)
第 6 讲(上)|本段重点:大规模地形渲染与内容整合
视频时长:约 1 小时 16 分 36 秒
说明:本文根据本地视频的课件与讲解脉络整理,不是逐字稿。 这一节依旧基本听不懂。
一句话理解
大型游戏地形的难点不只是“画一张高度图”,而是要在超大尺度、近景细节、稳定的 LOD、材质多样性、流式资源与编辑能力之间做系统级取舍。课程给出的路线是:用自适应网格管理几何密度,用 GPU 管线提高细节生成效率,用虚拟纹理解决材质数据规模,并用相机相对渲染和装饰物/道路系统把地形接入完整世界。
本讲学习目标
- 区分高度场、三角网格、四叉树与体素地形的表示能力和代价。
- 理解地形 LOD 为什么会产生 T-Junction、裂缝和过渡问题。
- 了解 GPU Tessellation、Mesh Shader、Marching Cubes 在地形中的角色。
- 说明 Texture Splatting 为什么会在大世界中遭遇纹理数量与显存瓶颈。
- 掌握虚拟纹理的 virtual texture、page table、physical texture 三层思路。
- 知道相机相对渲染如何缓解超大世界的浮点精度问题。
- 建立“地形几何、材质、流式、装饰物和编辑工具必须协同设计”的认识。
1. 为什么自然场景是渲染系统的压力测试
自然场景同时具有几类互相冲突的需求:
- 尺度大:远山、地平线、道路网络与开放世界地表都要求很大覆盖范围。
- 近景细节多:岩石、泥土、草地、水沟和道路边缘需要足够的几何与材质细节。
- 视点变化剧烈:玩家高速移动、飞行或切换视角,LOD 不能明显跳变或出现裂缝。
- 内容要可编辑:关卡人员需要绘制地貌、道路、地表材质、植被和局部标记。
- 资源预算有限:地形可能覆盖数十到数百平方千米,但内存、显存和每帧带宽有限。
所以地形渲染更像一个“内容生产 + 资源调度 + 图形算法”的联合系统,而不是孤立的 Vertex Shader 技巧。
2. 高度场:简洁但能力受限的地形表示
2.1 Heightfield 的基本模型
高度场把地形写成二维位置上的单值高度:
$y = h(x,z)$
在规则网格上,每个格点保存一个高度值,再由相邻格点组成三角形。它天然适合山地、丘陵、平原等“从上方观察能投影到平面”的地形。
2.2 高度场的优点
- 数据布局规则,便于存储、采样和 GPU 并行处理。
- 高度图可视作纹理,编辑工具和生成算法容易接入。
- 碰撞、植被分布、法线计算、地表混合等功能较容易建立在规则坐标系上。
- 对远景而言,规则网格配合 LOD 的实现成本较低。
2.3 高度场做不到什么
由于每个 (x, z) 只有一个高度,普通高度场难以自然表达:
- 悬崖下方的掏空区域;
- 山洞、隧道、拱门;
- 同一水平位置的多层地表;
- 可被挖开的复杂体积地形。
因此,选择高度场意味着以表达力交换效率。开放世界中的大面积地表通常很适合它,但洞穴等局部结构常需要独立网格、体素数据或混合方案补足。
3. 地形 LOD:把三角形花在看得见的地方
3.1 视点相关误差是 LOD 的判断依据
课件提出视点相关的优化原则:LOD 不应只按世界距离粗暴切换,而应关注近似误差在屏幕上是否可见。影响因素包括:
- 相机与地形块的距离;
- 视场角与屏幕分辨率;
- 地形在当前投影下的屏幕尺寸;
- 地貌变化强度,例如平坦区域与尖锐山脊对网格密度的需求不同。
一个实用目标是:远处误差在屏幕上小到难以察觉,就不值得为它保留近景级别的三角形数量。
3.2 自适应网格细分
自适应细分会让近处或地形变化剧烈的区域使用更密集的三角形,远处或平坦区域使用更粗的网格。这样能显著降低总体三角形数,但也立刻引入邻接块不同细分层级的连接问题。
3.3 T-Junction 与裂缝
当一个细分较密的边与一个较粗的边相接时,细网格的顶点可能落在粗网格边的中间,而粗网格并没有对应顶点。这就是 T-Junction。
若不处理,两个块在光栅化和位移后可能出现细缝、漏光或不连续的轮廓。地形 LOD 的关键并非只会“切得更细”,而是要保证相邻区域的拓扑关系可衔接。
3.4 连续性策略
常见处理思路包括:
- 约束相邻块的 LOD 等级差,避免差距过大。
- 为边界准备 stitching(缝合)三角形或索引模式。
- 让细分模式满足可拼接的规则。
- 采用带过渡带的形变或几何混合,减少视觉突变。
这些策略都体现了一个事实:地形 LOD 是一张邻接关系图上的全局协调问题,不是每个块各自独立的决策。
4. 三角形、四叉树与不规则三角网
4.1 三角形细分
三角形是 GPU 最基本的图元。以三角形为单位细分,适合不规则地貌和局部误差控制,但邻接关系、边界缝合与索引管理更复杂。
4.2 QuadTree-Based Subdivision
四叉树把地表区域递归分成四个子块。它的主要优点是:
- 层级结构直观,容易构造与管理。
- 适合规则网格和块级流式加载。
- 容易按相机距离做粗粒度裁剪与 LOD 选择。
但四叉树块之间仍要处理不同 LOD 的边界;对于非常不规则的地貌,单纯规则划分的几何效率可能不如更自由的三角网。
4.3 TIN:Triangulated Irregular Network
不规则三角网(TIN)可在地形变化大的地方放更多顶点,在平坦区域放更少顶点。它的优势是几何密度更贴合误差分布;代价是构建、更新、邻接查询、流式和 GPU 数据组织都更复杂。
可以这样概括三者:
| 表示/划分 | 强项 | 主要代价 |
|---|---|---|
| 规则高度网格 | 实现简单、纹理化友好 | 容易在平坦区域浪费顶点,难表达洞穴 |
| 四叉树块 | LOD、裁剪、流式管理清晰 | 需要处理块边界与层级过渡 |
| TIN | 几何密度更贴合地貌 | 数据结构和邻接管理复杂 |
5. 让 GPU 参与生成地形细节
5.1 GPU Tessellation
硬件 Tessellation 管线通常包括 Hull Shader、Tessellator、Domain Shader 等阶段。它允许用较粗的控制网格作为输入,再根据距离、曲率或误差在 GPU 上生成更多三角形。
地形中常见的组合是:
- CPU 或前序阶段提供粗网格块。
- Tessellation 根据视点设定细分因子。
- Domain Shader 采样高度图或位移图,生成最终顶点位置。
这让“细节生成”更靠近 GPU,但不能消除细分过度、块边界不一致、位移后裂缝等问题。
5.2 Displacement Mapping 与细分
位移贴图真正改变顶点位置,能够产生轮廓变化;但若基础网格不够密,位移仍然只能看到粗糙台阶。因此它通常与 Tessellation 或足够高密度的几何配合。
不要把它与 Normal Mapping 混淆:法线贴图主要改变光照法线,不会改变物体轮廓,也无法让地形真的“凸起来”。
5.3 Mesh Shader Pipeline
课件还介绍了 Mesh Shader Pipeline:
- Amplification/Task 阶段可以决定如何组织工作组、筛选或放大任务。
- Mesh Shader 阶段直接输出一小簇顶点和图元。
相比传统固定的几何处理链,Mesh Shader 给引擎更多机会把小块几何生成、可见性筛选和数据布局放到 GPU 上协同完成。它适合现代 GPU 架构,但需要面向目标硬件设计回退路径与资源组织方式。
6. 当高度场不够用:洞穴与体积地形
6.1 挖洞为什么困难
高度场只能表示单层表面,因此“在地面挖洞”会破坏其单值函数假设。单独放置洞穴网格可以解决很多游戏需求,但地表与洞口的连接、材质过渡、碰撞和编辑工作流仍需额外设计。
6.2 体素表示
体素把三维空间离散成格子,每个格子可存密度、材质或符号距离等数据。它能够表示洞穴、悬空岩石和任意拓扑变化,因此适合可破坏地形或程序化地形。
相应代价是:
- 三维数据量远大于二维高度图。
- 需要从体数据提取可渲染表面。
- 编辑、存储、流式与碰撞都更昂贵。
6.3 Marching Cubes
Marching Cubes 通过检查每个体素单元角点的内外状态,选择相应的三角形配置来近似等值面。它是体素地形“从密度场变成三角网格”的经典步骤。
需要注意:Marching Cubes 解决的是表面提取,不自动解决大世界的分块边界、网格更新、LOD、纹理坐标和资源调度;这些仍是引擎系统问题。
7. 地形材质:从贴图混合到可扩展的层系统
7.1 Texture Splatting 的出发点
地形通常需要草地、泥土、碎石、岩石、积雪等多种材料。Texture Splatting 的基本做法是:
- 采样多个材质纹理;
- 用权重图/控制图决定每种材质的比例;
- 将各材质的颜色、法线、粗糙度等属性混合。
它非常直观,适合编辑器中的“刷地表材质”。
7.2 简单混合为什么容易显得不自然
如果仅用平滑权重做线性混合,岩石与草地会像被均匀揉在一起,缺少材料之间应有的覆盖关系和形态逻辑。课程随后给出更高级的做法:引入高度、坡度、噪声或材质优先级,形成带偏置的混合。
7.3 Height-Based Blending
高度混合利用各材质自身的高度信息或局部结构,让“更突出的颗粒/岩石”在过渡区保留形状,而不是被平滑平均掉。它通常更自然,但需要额外纹理数据与更复杂的采样、比较和归一化操作。
7.4 Material Texture Array
多个同规格材质纹理可放入 Texture Array,以层索引选择不同材料。它有助于统一采样接口、减少资源绑定复杂度,并适合材质层数量明确、纹理尺寸和格式统一的场景。
7.5 Parallax 与 Displacement 的取舍
对地表近景,课程对比了颜色贴图、凹凸/法线、Parallax Mapping 和 Displacement Mapping:
- Parallax Mapping 主要在像素阶段扰动纹理坐标,制造视差感,不改变真实几何轮廓。
- Displacement Mapping 改变几何位置,能提供真实轮廓,但需要网格细分和更多几何成本。
它们的选择取决于玩家距离、边缘轮廓是否可见、GPU 预算和内容复杂度。
8. 材质混合的规模瓶颈
8.1 多材质意味着多次纹理读取
每多一个参与混合的材质,通常就会增加 Base Color、Normal、Mask 等纹理读取。若一个地形像素需要混合很多层,带宽和像素着色成本会迅速上升。
8.2 巨型 Splat Map 的问题
把整个超大世界的材质权重全部放进一张或多张巨大控制图,看似直接,实际会带来:
- 视频内存中只需要场景的一小部分,但巨大贴图难以整体常驻。
- 高分辨率世界需要很多 mip 层与存储空间。
- 远处与近处对材质数据精度的需求不同,统一加载会浪费带宽。
- 编辑数据和运行时采样数据的理想格式未必相同。
这正是虚拟纹理要解决的问题。
9. 虚拟纹理:用地址空间管理巨大的地表材质
9.1 核心思想
虚拟纹理不要求把整张超大纹理完整放入显存。它把逻辑上巨大的纹理切成固定大小的 tile/page:
- Virtual Texture:逻辑地址空间,代表整个世界的材质结果。
- Page Table:将虚拟页映射到当前驻留的物理页。
- Physical Texture Cache:显存中真正存放已加载 tile 的有限缓存。
Shader 采样时先查询页表,再从物理缓存取对应 tile。不可见或低优先级区域可以不驻留,真正需要时再流入。
9.2 地形中的典型用法
课程列出的思路可以概括为:
- 在逻辑上为整个场景建立可索引的巨大地表材质空间。
- 按视点相关 LOD,只加载当前可见区域需要的 tile 数据。
- 将多层材质混合的结果预烘焙到 tile,并把结果放到物理纹理缓存中。
运行时由“每像素混合很多原始材质”,转为“多数情况下采样已生成的地表 tile”。这就是以离线/后台处理和流式调度,换取稳定的帧内着色成本。
9.3 虚拟纹理不是免费的
它引入了新的工程问题:
- 页缺失时如何反馈和调度。
- 快速移动时如何避免 tile pop-in。
- page table 更新与缓存替换如何保持正确。
- 不同 mip、边界过滤和 tile border 如何避免接缝。
- 编辑后哪些 tile 需要失效、重烘焙和重新上传。
因此,虚拟纹理的价值在于控制规模,而不是省去资源管理。
9.4 DirectStorage 与 DMA 的启发
课件还把虚拟纹理与 DirectStorage、DMA 放在同一脉络中:当存储设备、主存、显存和 GPU 的数据路径更高效时,地形 tile 的流式传输可以减少 CPU 参与和中间拷贝。对引擎设计而言,关键是把资源格式、IO 调度、解压位置和 GPU 上传路径当作一条完整管线来考虑。
10. 超大世界的精度:Camera-Relative Rendering
10.1 浮点精度为什么会出问题
单精度浮点数的可表示间隔会随数值绝对值增大而变粗。当相机和物体的世界坐标达到很大数量级时,细小的位置变化可能无法准确表示,表现为抖动、表面闪烁、物体边缘不稳定等。
10.2 以相机为局部原点
相机相对渲染的做法是:在送入图形变换前,用对象世界坐标减去相机位置,让相机附近的坐标回到接近零的范围。
这样,GPU 参与顶点变换与光栅化的坐标数值保持较小,能显著改善局部精度。世界逻辑仍可维护全局坐标体系;渲染层则使用局部坐标,二者通过变换矩阵衔接。
10.3 它影响的不只是 Vertex Position
采用相机相对坐标后,依赖世界坐标的系统也要保持一致,例如阴影、植被、粒子、Decal、物理/渲染同步、裁剪与材质中的世界空间效果。否则不同系统可能在不同原点下计算,造成视觉或逻辑错位。
11. 地形要与世界其他元素一起工作
11.1 Decorator Rendering
地形本身通常只是底面。岩石、树木、草、灌木、建筑碎片等 decorator 让场景获得尺度感和辨识度。它们同时带来大数量实例的裁剪、LOD、阴影、密度分布与风动等问题。
一个常见原则是:地形系统输出坡度、高度、材质类型、湿度或遮罩等字段;装饰物系统利用这些字段进行规则分布,而不是由每个系统独立猜测地表语义。
11.2 Road 与 Decal
道路往往由样条线编辑:样条既定义道路路径,也可驱动局部地形抬升、压平或雕刻。Decal 则适合叠加道路边缘、轮胎痕迹、湿地、污渍等局部表面信息。
课程展示了把道路和 Decal 结果写入/叠加到虚拟纹理空间的思路。这样可以避免给每条道路都增加大量独立几何或材质层,同时让道路与基础地形使用一致的流式与 LOD 机制。
11.3 程序化地形创建
程序化生成可以从噪声、规则、侵蚀近似或设计约束出发生成高度、材质分布和装饰物候选点。它的价值不只在“自动造山”,还在于为广阔区域建立可控的初始数据,并把重复性工作交给规则系统。
不过程序化结果仍需要与手工编辑、任务布局、道路、玩法空间和美术方向协作。规模化生成并不替代关卡设计。
12. 本讲的系统性结论
12.1 三个层次必须对齐
| 层次 | 关键问题 | 课程中的代表技术 |
|---|---|---|
| 几何层 | 哪些区域需要多少三角形,如何无缝连接 | LOD、四叉树、TIN、Tessellation、Mesh Shader |
| 表面层 | 大地表如何呈现多种材料又不压垮像素着色 | Splatting、Texture Array、Parallax、虚拟纹理 |
| 世界层 | 如何让大坐标、流式、道路、植被和编辑工具一致工作 | Camera-Relative Rendering、DirectStorage/DMA、Decorator、Decal、程序化生成 |
12.2 最重要的工程取舍
- 高度场高效,但不能原生表达任意拓扑。
- 更细网格能提升近景质量,却增加 LOD 连接和吞吐压力。
- 多材质混合更丰富,却放大纹理采样与显存成本。
- 虚拟纹理解决“规模”,但要求可靠的缓存、反馈与流式系统。
- 超大世界可以保留全局逻辑坐标,但渲染应尽量在相机附近的小坐标范围中进行。
13. 复习用速记
13.1 一条因果链
大世界覆盖范围大
→ 不能让所有区域使用同等几何密度
→ 需要视点相关 LOD 与无缝边界策略
→ 高细节地表需要多种材料混合
→ 原始材质层过多会造成带宽与显存压力
→ 虚拟纹理按可见性和 LOD 流式管理 tile
→ 大坐标场景还需要相机相对渲染保持数值稳定
→ 道路、植被、Decal 与编辑工具共享同一世界数据与资源管线。
13.2 容易混淆的概念
| 概念 | 主要解决什么 | 不是用来解决什么 |
|---|---|---|
| LOD | 按可见误差分配几何/纹理预算 | 任意拓扑表达 |
| Tessellation | 在 GPU 上细分控制网格 | 自动消除所有裂缝与过度细分 |
| Parallax Mapping | 增加像素级视差感 | 改变真实轮廓 |
| Displacement Mapping | 改变真实顶点位置 | 免费获得高密度几何 |
| Texture Splatting | 混合多个地表材料 | 自动解决大世界纹理常驻 |
| Virtual Texture | 按页流式管理巨大纹理 | 省去缓存与 IO 调度 |
| Camera-Relative Rendering | 缓解渲染坐标精度问题 | 取代世界逻辑坐标体系 |
14. 自测题
- 为什么普通高度场难以表达洞穴和悬空地形?
- 视点相关误差为什么比单纯“按距离切 LOD”更合理?
- 什么是 T-Junction?它会在地形渲染中造成什么问题?
- 四叉树和 TIN 各自更擅长解决什么问题?
- GPU Tessellation 与 Displacement Mapping 分别承担什么职责?
- Mesh Shader 相比传统几何管线带来了哪些可组织空间?
- Marching Cubes 在体素地形工作流中的输入和输出分别是什么?
- 简单 Texture Splatting 为什么容易出现不自然的平滑过渡?
- Height-Based Blending 如何改善材质过渡?
- 为什么多材质混合会显著提高像素着色和带宽压力?
- 虚拟纹理中的 virtual texture、page table、physical texture 分别是什么?
- 为什么虚拟纹理需要处理页缺失、缓存替换和 tile 边界?
- 相机相对渲染如何缓解浮点精度误差?
- 为什么道路、Decal、植被和地形不能完全作为彼此独立的系统?
15. 术语表
| 术语 | 含义 |
|---|---|
| Heightfield | 用二维位置映射单一高度值的地形表示 |
| LOD | Level of Detail,根据可见性/误差选择不同细节级别 |
| T-Junction | 一个网格边上的顶点未与相邻网格对应顶点连接的拓扑情况 |
| Stitching | 为不同 LOD 网格边界构造连接关系以避免裂缝 |
| QuadTree | 将平面区域递归划分为四个子区域的层级结构 |
| TIN | Triangulated Irregular Network,不规则三角网 |
| Tessellation | 在 GPU 上对控制网格进行细分的图形管线能力 |
| Mesh Shader | 可直接在 GPU 上生成/组织小簇顶点与图元的可编程阶段 |
| Voxel | 三维体素网格中的离散单元 |
| Marching Cubes | 从体数据的等值面提取三角网格的经典算法 |
| Texture Splatting | 用权重控制多个地表材质纹理混合的方法 |
| Texture Array | 在单一资源中存放多层同规格纹理的结构 |
| Virtual Texture | 以页表映射和物理缓存实现的超大逻辑纹理 |
| Page Table | 记录虚拟纹理页到物理缓存页映射的表 |
| Camera-Relative Rendering | 以相机附近为局部原点进行渲染,降低大坐标精度误差 |
| Decal | 贴附或投射到已有表面上的局部材质/细节效果 |
16. 参考资料
- B 站视频:06.游戏中地形大气和云的渲染(上)|GAMES104
- GAMES104 课程主页:GAMES104
- 课程讲义与视频索引:GAMES104 Slides