游戏中地形大气和云的渲染

一、 讲座引言:渲染“自然之母”的挑战与乐趣

本讲座的核心是探讨如何在虚拟世界中,用代码(0和1)来重现我们所处的美丽、复杂的自然世界。这是一个极具挑战性但又充满乐趣的课题。

  • 核心目标: 在游戏引擎中,通过图形学技术渲染出逼真的自然环境,包括天空、云、植被、山川、河流等。
  • 面临的挑战:
    • 内容庞大: 自然界的元素极其丰富,技术细节繁多。
    • 数学密集: 背后涉及大量的数学公式和理论。
  • 开发者视角: 对于图形工程师(Graphics Engineer)而言,将策划和美术对自然美的构想转化为现实,是一项核心且富有成就感的工作。讲座的目标是深入原理,理解如何构建引擎来实现这些效果,而不仅仅是使用引擎

二、 成为“大地之神”:地形渲染入门

讲座将渲染自然的工作分为不同领域,首先从“大地”开始,探讨如何渲染地形(Terrain)。在游戏开发团队中,专门负责这部分工作的工程师常被爱称为 “大地之神”

1. 地形渲染 (Terrain Rendering) 的重要性

地形是构成虚拟世界的基础骨架,其表现力直接决定了世界的真实感和沉浸感。

  • 关键术语: 地形 (Terrain),在图形学和游戏引擎中,特指地表、山脉、峡谷等自然地貌的几何表示。
  • 业界标杆:
    • 写实风格: 《微软飞行模拟》(Microsoft Flight Simulator)通过与Bing地图数据流式传输结合,实现了对整个地球地貌的惊人还原。
    • 幻想/科幻风格: 《无人深空》(No Man's Sky)展示了地形渲染技术不仅能模拟地球,还能创造出千奇百怪的 外星地貌 (Alien Landscapes)

2. 基础方法:高程图 (Height Field)

这是实现地形渲染最经典、最基础,且至今仍在广泛使用的方法。

  • 核心观点: 高程图是一种用2D图像来描述3D地形高度信息的技术。它本质上是一张灰度图,其中每个像素的亮度值(或颜色通道值)直接对应于地形上某个(X, Z)坐标点的 高度(Y坐标)

    • 白色像素代表最高点。
    • 黑色像素代表最低点。
    • 灰色像素代表中间高度。
  • 关键术语: 高程图 (Height Field / Height Map)

  • 关联概念:

    • 等高线 (Contour Lines): 高程图是测绘学中等高线图在计算机图形学中的一种数字化表达。
    • 分形 (Fractals): 自然界的地形(如山脉、海岸线)在宏观和微观上都表现出自相似性,这与分形的数学特性高度吻合。这个特性对于程序化生成(PCG)地形至关重要。

3. 基于高程图的渲染流程

将一张高程图转换为3D模型的过程非常直观:

  1. 创建基础网格 (Grid): 在X-Z平面上创建一个均匀分布的、由大量顶点组成的平面网格(Grid)。网格的精细度决定了地形的细节程度。
  2. 顶点位移 (Vertex Displacement): 遍历网格上的每一个顶点,将其(X, Z)坐标映射到高程图的对应像素位置。
  3. 采样高度: 读取该像素的亮度值,并将其作为该顶点的 Y坐标
  4. 应用材质与光照: 在生成的地形模型上应用纹理、材质(如草地、岩石)和光照,最终渲染出逼真的效果。

专家点评: 尽管 Height Field 技术相对古老,但配合高质量的纹理、材质和现代光照技术,其表现力依然非常强大,是当今开放世界游戏地形渲染的主力方法之一。

4. 简单方法的局限性:可扩展性问题

上述基础渲染流程存在一个致命的弱点。

  • 核心问题: 可扩展性差 (Poor Scalability)
  • 问题分析:
    • 对于一个固定大小的均匀网格,如果要表达的世界范围非常大(例如,几千平方公里的开放世界),或者要求地形精度非常高,那么所需的顶点和三角形数量将会是天文数字。
    • 示例:
      • 一个 1km * 1km 的区域,若每米一个格子,会产生 1000 * 1000 * 2 = 200万 个三角形,这对于现代GPU是可接受的。
      • 但对于一个 100km * 100km 的世界,三角形数量将增长到 200亿,这远远超出了任何实时渲染硬件的处理能力。

这个可扩展性问题,引出了后续需要解决的核心技术难题:如何高效地渲染广袤无垠的开放世界地形?

大规模地形渲染——动态LOD与网格细分

一、大规模地形渲染的挑战:性能瓶颈

当我们从渲染独立的游戏对象(Game Object)转向渲染广阔的 开放世界(Open World) 地形时,传统的静态、高精度网格方法会遇到严重的性能瓶 ขวด。

  • 核心观点: 一个固定精度的巨大网格(例如,每米一个格子)会产生天文数字般的三角形数量,远超现代硬件的实时渲染能力。例如,一个几千平方公里的世界,其顶点数据将是无法承受的。
  • 解决方案: 观察现实世界,我们发现人眼对近处细节敏感,对远处细节不敏感。这为性能优化提供了思路,即引入 LOD (Level of Detail) 机制。

二、地形LOD的特殊性与核心思想

虽然LOD是图形学中的常见概念,但地形LOD有其特殊性,不能像独立物体那样简单地切换模型。

  • 核心观点: 地形是一个 连续的(Continuous) 表面。如果不同LOD级别的地块之间处理不当,就会在接缝处产生裂缝或孔洞。
  • 关键术语: T-Junction (T形接缝),这是指一个高精度地块的边上有顶点,而与之相邻的低精度地块的对应边上没有这个顶点,形成一个 "T" 字形的断裂,导致视觉上的裂缝。
  • 基本思想: 根据观察者(摄像机)的位置和朝向, 动态地(Dynamically)自适应地(Adaptively) 调整地形网格的精细程度。近处和视野中心的区域使用高密度三角形,而远处和视野边缘的区域使用稀疏的三角形。

三、决定LOD的关键动态因素

地形网格的细分程度(Tessellation Level)不应是静态的,而是要根据以下两个核心因素实时计算:

1. 距离 (Distance)

这是最直观的因素。

  • 原则: 摄像机 距离越近,网格越精细;距离越远,网格越稀疏。

2. 视场角 (Field of View - FOV)

这是一个经常被忽略但至关重要的因素。

  • 核心观点: FOV越窄,需要的细节越多。当FOV变窄时(例如,在游戏中开启高倍镜),屏幕上的同一片区域被放大,每个像素对应更小的地理范围,因此需要更高的网格密度来填充细节,否则会显得模糊或棱角分明。
  • 应用场景: 游戏中的 狙击镜/望远镜 效果。其本质就是 缩小摄像机的FOV,这要求渲染系统不仅要考虑物体距离,还必须将FOV纳入LOD的计算中,为被放大的区域提供更高精度的模型。
  • 数学解释: 在屏幕分辨率不变的情况下,FOV减小意味着每个三角形在屏幕上占据的像素数量会增加。为了维持视觉上的一致密度(例如,每N个像素一个三角形),就必须增加三维空间中的三角形数量。

四、网格简化的两大原则

为了实现高效且视觉无损的动态LOD,算法设计通常遵循以下两个原则:

  1. 视觉感知原则:

    • 根据距离FOV 综合判断。近处、窄FOV(视野中心)的区域需要高密度网格;远处、宽FOV(视野边缘)的区域使用低密度网格。
  2. 屏幕空间误差度量 (Screen-Space Error Metric):

    • 核心观点: 网格简化的目标是,简化前后地形表面在屏幕上的投影差异不超过一个可接受的阈值(如一个像素)。
    • 解释: 同样是10厘米的几何误差,在10米外可能清晰可见,但在1000米外则完全无法察觉。因此,我们不关心三维世界中的绝对误差(World-Space Error),而是关心它投影到屏幕上的 像素误差(Pixel Error)。只要这个误差小于一个像素,人眼就基本无法分辨。这是保证LOD切换平滑、不产生突兀“跳变”(Popping)的关键。

五、一种经典的实现方法:基于二叉树的三角形剖分 (Binary Triangle Tree)

这是一种历史悠久且易于理解的动态地形细分方法。

算法核心

该方法基于对 等腰直角三角形(Isosceles Right Triangle) 的递归剖分。

  1. 基础结构: 将地形初始网格看作由正方形(Quad)组成,每个正方形沿对角线切分成两个等腰直角三角形。
  2. 剖分规则: 当需要提高某个区域的精度时, 永远沿着当前等腰直角三角形的最长边(即斜边)的中点进行切分
  3. 结果: 每一次切分,都会将一个三角形变成两个更小的、形状相似的等腰直角三角形。
  4. 数据结构: 这种递归的剖分关系天然地形成了一个 二叉树(Binary Tree) 结构,便于管理和遍历。

关键问题与解决方案:T-Junction

  • 问题描述: 当相邻的两个三角形LOD级别不同时,一个可能被剖分了,而另一个没有。这导致在高精度三角形的边中点上存在一个顶点,但在低精度三角形的整条边上不存在这个顶点,从而形成 T-Junction,视觉上表现为地形裂缝。
  • 解决方案: 强制对齐。当检测到T-Junction时,强制低精度的三角形也进行一次剖分,使其边的顶点与高精度邻居对齐。这个过程通常需要递归检查,直到所有邻接边的细分级别匹配,从而消除裂缝。

总结: 这一部分内容从大规模地形渲染的根本挑战出发,引出了动态LOD的核心思想。重点讲解了决定LOD的两个关键因素——距离FOV,并提出了以屏幕空间误差作为优化目标的科学方法。最后,通过一个经典的二叉树三角形剖分算法,具体展示了如何实现网格的动态细分,并指出了该方法中必须解决的关键问题—— T-Junction。这些知识是构建现代大规模地形渲染系统的基础。


四叉树、LOD裂缝修复与网格优化

在探讨了基于三角形连续细分的地形渲染方法后,我们接下来将深入研究业界更为主流和实用的技术:基于 四叉树(Quadtree) 的方案,并探讨其如何优雅地解决LOD(Level of Detail)切换时产生的裂缝问题,以及另一种针对特定场景的网格优化思路。

一、 从二叉树到四叉树:更符合直觉的地形组织方式

前一部分提到的基于三角形二叉树的连续细分方法(如ROAM算法),虽然在渲染效率上可以做到很高(讲座中提到Unity Demo可渲染2500平方公里的地形),但在实际游戏项目开发中,它存在一个致命的缺点。

  • 核心观点: 基于三角形的细分方法在数据管理和资源组织上不直观,不符合游戏引擎的通用数据结构(如纹理、高度图等),而基于四叉树的“豆腐块”式管理法则更为高效和自然。
1. 三角形细分方法的局限性
  • 数据管理不直观: 其数据结构如同“七巧板”,由各种不规则的三角形拼凑而成。这对于地形数据的存储、流式加载和编辑都带来了巨大的复杂性。
  • 与资源格式不匹配: 游戏引擎中的资源,如 高度图(Height Field)纹理贴图(Texture) 等,天然就是以矩形(或正方形)网格存储的。如果使用三角形来管理,会导致近一半的存储和内存空间被浪费,并且处理逻辑也变得异常复杂。
2. 四叉树(Quadtree)方法的优势

四叉树方法将一块巨大的地形,通过不断地“四分”操作,递归地切分成更小的正方形地块。这种方法已成为现代游戏引擎地形系统的事实标准。

  • 核心思想: 将大地形递归地划分为正方形网格(“豆腐块”),形成层级结构,便于管理和进行LOD控制。

  • 层级结构示例:

    • 大地形 (World): 例如 128km x 128km。
    • 地块 (Block): 将大地形切分后的基本管理单元,例如 512m x 512m。这通常是 磁盘I/O和资源流式加载 的最小单位。一个Block会打包该区域所有相关数据(高度图、贴图、植被、建筑等)。
    • 补片 (Patch): 将Block进一步切分,例如 64m x 64m。这是 GPU进行绘制(Draw Call) 和LOD决策的基本单位。
  • 核心优势:

    • 直观性: “切豆腐块”的方式非常符合人类对地理区域划分的直觉。
    • 数据对齐: 正方形的地块与纹理、高度图等资源格式完美对齐,数据管理和访问效率极高。
    • 资源管理: 以 Block 为单位进行资源打包和流式加载,逻辑清晰,易于实现。当摄像机进入某个Block的范围时,引擎便加载对应的数据包。
    • 可扩展性: 这种结构天然支持 虚拟纹理(Virtual Texturing) 等高级技术,因为它们都依赖于类似的基于网格的页面管理思想。

二、 解决LOD边界裂缝:T-Junction问题与Stitching技术

无论是哪种LOD方案,当两个不同精细度的网格块拼接在一起时,都会在边界上出现顶点数量不匹配的问题,从而产生肉眼可见的裂缝。这个问题被称为 T-Junction

  • 核心观点: 相比于通过修改网格拓扑结构来修复T-Junction,四叉树方案采用一种更巧妙的“缝合”(Stitching)技术,通过生成“退化三角形”来无缝连接LOD边界,代码实现更简单。
1. 传统修复方式的弊端

传统方法(如在三角形二叉树方案中)通常需要动态地修改低精度网格的拓扑结构,即在低精度网格的边界上“再补几刀”,插入新的顶点以匹配高精度网格。这个过程非常复杂,容易出错,且计算开销较大。

2. Stitching (缝合/吸附) 技术

Stitching是一种极其聪明的顶点操作技巧,它不改变任何一边的网格拓扑结构。

  • 算法流程:

    1. 假设A地块(高LOD)和B地块(低LOD)相邻。A地块的共享边界上有5个顶点,而B地块只有3个。
    2. 在生成A地块的索引缓冲(Index Buffer)时,将A地块边界上多出来的“中间点”(例如第2和第4个顶点)在顶点着色器(Vertex Shader)中 “吸附” 到B地块边界上对应的顶点位置。
    3. 这样一来,原本用于连接这些中间点的三角形,其两个顶点会重合在一起,形成一个面积为零的三角形。
  • 关键术语: 退化三角形 (Degenerate Triangles)

    • 定义:指三角形的三个顶点共线,或者有两个(或三个)顶点重合,导致其面积为零
    • 作用:在图形渲染管线中,这种三角形在光栅化阶段通常不会产生任何像素片段,相当于被自动剔除了。
    • 优势:通过这种方式,我们用几乎零成本的方式“隐藏”了裂缝,实现了视觉上的水密(Watertight)网格,且整个过程无需复杂的CPU端拓扑运算,逻辑非常简洁。

三、 另一种思路:基于网格简化的不规则三角网 (TIN)

除了上述基于规整网格(Grid)的LOD方法,还存在一种适用于特定地貌的优化技术。

  • 核心观点: 对于包含大面积平坦区域的地形(如沙漠、平原),可以预先使用“网格简化”算法生成不规则三角网(TIN),用极少的三角形来表达平坦地表,同时保留关键的地貌特征。

  • 核心技术: 网格简化 (Mesh Simplification)

    • 流程: 从一个超高精度的原始地形网格开始,通过算法迭代地移除那些对整体形状贡献最小的顶点(通常位于平坦区域)。
    • 目标: 在尽可能减少顶点和三角面数量的同时,最大程度地保留地形的宏观特征(如山脊、沟壑的轮廓线)。简化后的顶点会巧妙地分布在这些特征线上。
  • 应用场景与案例:

    • 适用场景: 沙漠、戈壁、大平原等。在这些场景下,使用规整的四叉树网格会因为要维持网格结构而浪费大量三角形在平坦区域。
    • 业界案例: 《使命召唤:现代战争》(Call of Duty: Modern Warfare) 在其广阔的沙漠地图中就应用了此技术,实现了高效的地形渲染。

小结: 这一部分,我们从地形数据管理的角度出发,论证了四叉树结构相比于三角形二叉树的优越性。接着,深入剖析了四叉树方案中解决 T-Junction 问题的核心技术—— Stitching退化三角形,它是一种实现简单且高效的方案。最后,我们了解了针对特定地貌(如平原)的网格简化技术,作为对常规LOD方案的一种补充和优化。这些都是构建现代、高效、大规模地形渲染引擎的关键知识点。


地形渲染技术进阶——从静态优化到GPU驱动的实时细分

在探讨了基础的地形LOD方案后,本部分将深入两种截然不同的高级地形渲染思路:一种是针对特定场景进行极致优化的离线预处理方法,另一种是利用现代GPU强大能力的实时动态细分技术

一、 特定于内容的静态网格简化 (Content-Specific Static Mesh Simplification)

这种方法的核心思想是: 放弃通用的实时LOD算法,转而为特定地形在预处理阶段生成一个高度优化的静态网格

  • 核心观点: 与其在运行时动态计算LOD,不如在开发阶段就通过算法,为一块巨大的地形生成一个“最优”的、LOD已经固化了的静态网格。

  • 经典案例: 《使命召唤:现代战争》中的沙漠场景。

    • 问题: 在广阔、起伏的沙漠中,无论是基于四叉树还是三角形的常规LOD方法,都可能生成大量不必要的三角形。
    • 解决方案: 采用 网格简化 (Mesh Simplification) 算法进行预处理。
    • 效果:
      1. 顶点对齐山脊: 简化后的顶点被巧妙地放置在了地形的关键特征上,如山脊线,用最少的顶点勾勒出地形的核心轮廓。
      2. 三角形数量锐减: 在特定场景下,这种方法生成的三角形数量可以比实时LOD方案(如Quadtree LOD)少一个数量级,极大降低了渲染开销。
  • 优缺点分析:

    • 优点:
      • 极致的性能: 对于静态且特征明显的场景,能生成面数极少的优化网格。
      • 高效率的表达: 顶点分布在信息量最大的地方,符合信号处理中的高效采样原则,避免了在平坦区域的冗余采样。
    • 缺点:
      • 需要预处理 (Pre-processing): 增加了内容生产的复杂度和时间。
      • 缺乏运行时灵活性 (Runtime Flexibility): 网格是固定的,无法在运行时根据视点、光照等因素动态调整细节。
  • 适用场景: 这种技术并不普适,主要用于那些可以接受静态网格且地形特征非常适合做离线简化的特定游戏或关卡。


二、 现代地形渲染:GPU驱动的实时细分 (GPU-Driven Real-Time Tessellation)

随着GPU的发展,现代游戏引擎普遍采用 在GPU上实时生成和细分几何体 的方式来渲染地形,这提供了前所未有的细节和灵活性。

  • 核心观点: 将地形几何体的生成和LOD调整工作从CPU转移到GPU,利用GPU的并行处理能力和专用硬件单元,在运行时动态创建海量三角形,实现近处精细、远处粗糙的无缝过渡。
1. 传统方法:DirectX 11 的细分管线 (Tessellation Pipeline)

这是上一代GPU中实现动态细分的主流方式,由多个新的着色器阶段组成,虽然功能强大,但其设计在开发者社区中也因其复杂性而备受争议。

  • 关键阶段与术语:
    • 外壳着色器 (Hull Shader):
      • 作用: 接收一个基础面片(Patch)的控制点,并决定这个面片需要被细分成什么样。
      • 输出: 1. 定义细分后顶点位置的控制点数据;2. 告诉硬件细分器(Tessellator)每条边和内部需要被切割多少次的 细分因子 (Tessellation Factors)
    • 细分器 (Tessellator):
      • 作用: 这是一个固定功能硬件单元。它根据Hull Shader提供的细分因子,将输入的面片(Patch)暴力切割成大量更小的图元(如三角形)。
    • 域着色器 (Domain Shader):
      • 作用: 对细分器生成的所有新顶点进行操作。它的核心任务是计算每个新顶点的最终世界空间位置。
      • 典型应用: 在地形渲染中,Domain Shader会根据新顶点的UV坐标去采样一张 高度图 (Height Map),从而将一个平坦的细分网格“膨胀”成起伏的地形。
    • 几何着色器 (Geometry Shader):
      • 作用: (在某些管线中) 在Domain Shader之后,对整个图元(点、线、三角形)进行处理。可以用于计算每个顶点的其他属性,如法线、纹理坐标等。
      • 讲师观点: 讲师认为这个管线设计得非常抽象和反人类,尤其是Domain Shader和Geometry Shader的职责划分令人困惑。他更倾向于将Geometry Shader理解为 “后细分顶点着色器” (Post-Tessellation Vertex Shader)
2. 现代方法:DirectX 12 的网格着色器管线 (Mesh Shader Pipeline)

这是最新一代GPU架构带来的革命性变化,它用一个更统一、更灵活的编程模型取代了传统的细分管线。

  • 核心观点: Mesh Shader 将传统管线中从顶点处理到几何生成的多个阶段(Vertex, Hull, Domain, Geometry Shader)整合为一个高度可编程的统一阶段,给予开发者前所未有的控制力。

  • 关键术语与工作流程:

    • 网格着色器 (Mesh Shader):
      • 作用: 开发者可以在这个Shader中完全用代码逻辑来程序化地生成一小批顶点和索引。这批数据被称为一个 Meshlet
      • 掌控力: 开发者可以自己实现细分算法,不再依赖于硬件的固定功能细分器。从数据输入、几何体生成、到属性计算,所有逻辑都在一个Shader内完成。
    • 优势:
      1. 高度统一与灵活: 一个Shader干了过去多个Shader的活,逻辑更内聚。
      2. 编程模型更简洁: 摆脱了DX11管线令人困惑的多阶段交互。
      3. 性能潜力巨大: 允许开发者实现更高效的剔除和几何生成算法。

三、 引擎开发的现实考量:技术选型与硬件普及

作为引擎开发者,选择使用哪种技术不仅是技术问题,更是现实问题。

  • API与硬件依赖:

    • Mesh ShaderDirectX 12 的核心特性。
    • DirectX 12 需要 Windows 10 或更高版本的操作系统支持。
  • 开发者的困境:

    • 尽管Mesh Shader在技术上远比DX11的细分管线优秀,但如果仍有大量玩家使用不支持DX12的旧系统(如Windows 7),引擎就必须向下兼容,维护两套甚至多套渲染路径。
    • 因此,引擎团队会密切关注 Steam硬件调查 (Steam Hardware Survey) 等数据,以判断新技术的普及率,决定何时可以放弃对旧API的支持,全面拥抱下一代技术。
  • 结论: 引擎开发是一个在 前沿技术、硬件普及、玩家基数 之间不断权衡和演进的过程。地形渲染技术的变迁,完美地体现了图形API、硬件厂商和游戏开发者三者之间紧密协作、共同推动行业进步的关系。


动态变形与体素化方法

在这部分讲座中,我们从基于 GPU实时曲面细分 (GPU-driven Real-time Tessellation) 的地形系统出发,探讨了两种极为重要的进阶主题:一是如何实现动态可交互的地形,例如雪地脚印和爆炸弹坑;二是如何突破传统高度图的限制,表达 洞穴、悬崖 等复杂结构,并引出了 体素化 (Volumetric) 这一更根本的解决方案。

一、 GPU驱动的动态地形变形 (GPU-Driven Dynamic Terrain Deformation)

一旦地形的顶点是在运行时由GPU通过Tessellation动态生成的,我们就拥有了在Shader中实时修改地形形态的能力,这为动态交互效果打开了大门。

核心观点

实时曲面细分 (Real-time Tessellation) 是实现动态地形变形的技术基石。它允许我们在运行时通过Shader动态计算和调整顶点位置,而无需修改CPU端的原始网格数据,效率极高。

关键应用与实现

1. 实时地形变形 (Real-time Terrain Deformation)

这是一种让游戏世界更具沉浸感和真实感的强大技术。

  • 经典案例:

    • 拖拉机压出车辙: 模拟车辆驶过松软地面时,地面受压下沉并向两侧挤压的效果。其物理模型可以简化为地面下的弹簧系统。
    • 可交互的雪地效果 (Deformable Snow): 现代3A游戏中常见的惊艳效果,如角色在雪地中行走留下的深浅不一的脚印。
  • 实现思路 (以雪地为例):

    1. 维护一张“变形纹理” (Deformation Texture): 这张纹理通常以玩家为中心,跟随玩家移动。
    2. 记录交互信息: 游戏中的所有交互(如脚踩、爆炸、物体拖拽)都会被“绘制”到这张纹理上。纹理的像素值可以代表地形的高度偏移量
    3. 在Shader中应用偏移: 在Tessellation阶段生成地形顶点后,采样这张变形纹理,并将其值作为额外的高度偏移应用到顶点位置上。
    4. 配合其他效果: 为了达到最佳视觉效果,通常还会结合粒子系统(扬起的雪花)、材质变化(脚印边缘的新雪和压实后的旧雪材质不同)等。

2. 实践中的挑战:物理与视觉的同步

实现动态地形变形时,一个常见且棘手的问题是物理碰撞体的更新

  • 问题描述: 当炮弹在地上炸出一个大坑时,视觉上地形已经下陷,但物理碰撞网格(Collision Mesh)可能仍是平坦的。这会导致角色可以“悬空”走过弹坑,破坏了真实感。
  • 解决方案: 需要一套机制,能够根据地形的视觉变化,实时或异步地更新对应的物理碰撞数据。这是一个复杂的系统工程,也是衡量引擎能力的重要指标之一。

二、 超越高度图:处理复杂地形结构 (Beyond Heightfields)

传统的高度图(Heightfield)本质上是一个2.5D的表达,无法表示垂直悬崖、洞穴、天然石桥等具有 “上层”和“下层” 的复杂结构。

1. 传统方法:堆叠静态模型 (Placing Static Meshes)

最直接、最简单的方法。

  • 核心思想: 在标准的高度图地形上,由美术师手动放置预先制作好的静态模型,例如悬崖、岩石、隧道入口等,来“伪造”出复杂的视觉效果。
  • 优缺点: 实现简单,美术可控性高,但不够灵活,无法实现动态生成或破坏。

2. 巧妙的“挖洞”技巧:利用 NaN 剔除三角形

这是一个非常聪明的、利用GPU底层特性的编程技巧,可以在基于高度图的地形上“开洞”。

  • 核心思想: 在Vertex Shader中将特定顶点的位置输出为无效值 NaN (Not a Number),GPU在后续的光栅化阶段会自动 抛弃 (Cull) 任何包含该无效顶点的三角形。

  • 实现流程:

    1. 标记顶点: 美术师在工具中标记出需要“删除”的区域的顶点。
    2. Shader处理: 在Vertex Shader中,检查当前处理的顶点是否带有“删除”标记。
    3. 输出NaN: 如果是,则将其输出位置设置为 NaN
    4. GPU自动剔除: GPU硬件会自动丢弃由这些无效顶点组成的图元。
    5. 模型遮盖: 这样开出的洞口边缘是锯齿状的,通常需要美术师再放置一个精细的模型(如隧道口模型)来遮盖瑕疵,使其看起来更自然。

三、 未来的方向?体素化地形 (Volumetric Terrain)

这是一种从根本上解决复杂结构表达的方法,虽然目前在游戏引擎中用于大规模地形还不算主流,但其概念和相关算法非常重要。

核心概念:体素化表达 (Volumetric Representation)

  • 定义: 不再使用2D的高度图,而是将整个三维空间划分为一个巨大的 三维网格 (Voxel Grid)。网格中的每个单元被称为 体素 (Voxel)
  • 数据存储: 每个体素存储一个或多个值,例如 密度、物质类型 等。通过这些值,可以定义出任意复杂的实体形状。
  • 优势: 原生支持洞穴、悬崖、悬空岛屿等一切复杂三维结构,可以实现真正意义上的自由创造与破坏

关键算法:Marching Cubes

Marching Cubes 是一个里程碑式的算法,用于将离散的体素数据转换为可供渲染的连续三角网格。

  • 核心作用: 提取体素数据中由某个 阈值 (Threshold) 定义的 等值面 (Isosurface),并将其转换为三角网格。

  • 基本原理:

    1. 算法逐个处理一个由8个相邻体素组成的 2x2x2 立方体
    2. 根据这8个角点的值是大于还是小于预设的阈值(即判断点在物体内部还是外部),会形成一个8位的索引(2^8 = 256 种情况)。
    3. 通过一个 预计算的查找表 (Lookup Table),根据这个索引可以确定等值面如何穿过这个立方体,并生成1到5个三角形来近似这个表面。
    4. 将所有小立方体生成的三角形拼接起来,最终形成一个完整的、 水密的 (Watertight) 模型表面。
  • 广泛应用:

    • 医学成像 (Medical Imaging): 将CT、MRI扫描得到的切片数据重建为三维器官模型。
    • 科学可视化 (Scientific Visualization): 可视化流体模拟中的压力场、速度场等。
    • 数字人 (Digital Humans): 著名的“Visible Human Project”就是通过将人体逐层切片拍照,构建体素数据,再用Marching Cubes等方法重建出完整的数字人体模型。

高级地形技术——从几何生成到材质渲染

在本节中,我们将深入探讨现代游戏引擎中地形系统的两个核心方面:如何从抽象数据生成复杂的地形几何,以及如何为其渲染丰富、自然的材质。

一、 地形几何生成:从体素到网格 (Terrain Geometry: From Voxels to Mesh)

当地形不仅仅是一个简单的身高图(Heightmap)时,我们需要更强大的几何表达方式,例如体素(Voxel)。

1. Marching Cubes: 经典体素网格化算法

核心观点: Marching Cubes 是一种将体素数据(隐式表面)转换为显式多边形网格(Mesh)的经典算法。它可以从三维标量场(例如密度、距离函数等)中提取出等值面,从而生成平滑的表面。

  • 应用场景: 在讲座中,它被提及用于从一个“人体的体素化表达”中提取出从皮肤到内脏骨骼的完整模型。这个概念同样可以应用于生成复杂的地形,如洞穴、悬崖和拱门。

  • 关键术语:

    • 体素化 (Voxelization): 将空间划分为一个个微小的立方体(体素),并为每个体素赋予特定属性的过程。
    • Marching Cubes: 算法名称,通过查找预定义的包含15种基本拓扑结构的立方体表,来决定如何在一个体素单元内生成三角面片。
  • 在地形生成中的挑战:

    • LOD (Level of Detail): 直接应用 Marching Cubes 难以实现“近处精细,远处粗糙”的动态细节层次。
    • 水密性 (Watertightness): 在不同 LOD 级别之间拼接网格时,非常容易出现裂缝和孔洞,破坏模型的完整性。虽然已有解决方案(讲座中提到有上百种情况的查表法),但实现起来相当复杂。

2. 个人展望:体素化表达与全动态地形

核心观点: 讲者认为,基于体素的 全动态地形 (Fully Dynamic Terrain) 是一个非常酷且值得关注的未来方向,尽管目前技术尚不成熟。

  • 灵感来源: 以《我的世界》(Minecraft) 为例,其核心魅力在于极高的自由度和创造性,玩家可以任意改造世界。
  • 当前3A游戏的局限: 许多3A游戏虽然画面精美,但场景互动性较弱(例如,无法摧毁大部分墙体、无法挖掘战壕)。
  • 未来潜力: 实现真正的全动态地形将极大增强游戏的可玩性和沉浸感,例如在战场上挖地道进行战术突袭。这是一个需要硬件和算法共同进步才能实现的目标。

二、 地形材质渲染:从混合贴图到高级混合技术

当地形几何准备就绪后,下一步是为其赋予真实可信的材质外观。

1. 问题的规模:AAA游戏中的海量材质

核心观点: 为了营造一个丰富、可信的自然世界,AAA游戏需要混合数量庞大的地表材质。

  • 实例: 以《幽灵行动》(Ghost Recon) 为例,游戏中包含了十几种生物群落(Biomes)和近140种自然材质。真实世界的复杂度远超于此。

2. 基础方案:Splat Map (混合贴图)

核心观点: Splat Map 是最基础的地形材质混合技术,它使用一张控制贴图的各个颜色通道(R, G, B, A)来存储不同材质的权重。

  • 工作原理: 在 Shader 中采样 Splat Map,根据得到的权重值,将对应材质的属性(如颜色、法线、粗糙度等)进行线性混合。
  • 局限性: 简单的权重混合(类似 Alpha Blending)会导致材质过渡区域显得模糊、不自然,像是“一层沙子浮在石头上”,缺乏物理逻辑。

3. 进阶方案:基于高度的材质混合 (Height-Based Blending)

核心观点: 通过引入每种材质的 高度图 (Height Map),可以实现更加真实、符合物理逻辑的材质过渡效果。

  • 核心思想: 在混合两种材质时,不再仅仅依赖 Splat Map 的权重,而是将权重与材质自身的高度信息结合起来。 高度值更高的材质会“覆盖”高度值更低的材质

  • 效果: 这种方法可以模拟出沙子填满石头缝隙,而青草从岩石裂缝中生长出来的自然景象。沙子(高度较低)不会不自然地“浮”在岩石表面(高度较高)。

  • 算法逻辑 (概念):

    1. 为材质A和材质B分别计算加权高度:
      • effective_height_A = material_A_height * weight_A
      • effective_height_B = material_B_height * weight_B
    2. 比较两者大小,决定最终显示哪种材质:
      • if (effective_height_A > effective_height_B) use material_A else use material_B
  • 新的问题: 这种“非A即B”的硬切换(0/1切换)会导致过渡边缘非常锐利(Sharp)。在相机移动或远距离观察时,容易产生 闪烁和锯齿 (Shimmering & Aliasing)

4. 优化技巧:为平滑过渡添加 Bias

核心观点: 为了解决硬切换带来的问题,引入一个 Bias 值,在两种材质加权高度差很小的一个区间内进行平滑插值,而不是瞬时切换。

  • 这是一个经典的实时渲染Hack!
  • 工作原理:
    1. 计算两种材质的加权高度差 diff = effective_height_A - effective_height_B
    2. 定义一个 Bias(例如 0.2)。
    3. 如果 diff[-Bias, Bias] 这个区间内,则认为处于过渡带。
    4. 在这个过渡带内,根据 diff 的具体值计算一个平滑的混合因子(0到1之间),用这个因子来混合两种材质的最终颜色(Albedo)。
  • 效果: 既保留了基于高度的逻辑性,又让过渡区域变得柔和稳定,显著改善了视觉效果。

5. 处理海量材质:Texture Array (纹理数组)

核心观点: 当游戏中材质数量超过4种(Splat Map的RGBA通道数)时,需要使用 Texture Array 来高效地管理和采样海量纹理。

  • Texture Array 是什么: 它可以被看作是一个包含多张同尺寸、同格式的2D纹理的集合。在Shader中,可以通过一个额外的索引(通常是整数)来指定访问哪一张“切片”(slice)。

6. 关键概念辨析:Texture Array vs. 3D Texture

这两个概念极易混淆,但底层原理和用途完全不同。

  • Texture Array (纹理数组):

    • 结构: 一堆独立的2D纹理叠在一起。
    • 采样: 先用整数索引选择一张2D纹理切片,然后在这张切片上进行标准的2D双线性/三线性过滤。 它不会在不同的切片之间进行插值。
    • 用途: 高效管理大量离散的纹理资源,如地形材质库。
  • 3D Texture (三维纹理):

    • 结构: 一个真正的三维数据体,体素之间是连续的。
    • 采样: 进行三线性过滤 (Trilinear Filtering),即在采样点周围的8个相邻体素(上下两层各4个)之间进行插值,得到一个平滑的采样结果。
    • 用途: 存储连续的体积数据,如体积云、雾、烟雾或医学影像数据。

高级地形渲染技术:从多材质混合到虚拟纹理

在上一部分的基础上,我们继续探讨地形渲染中的高级主题。当地表需要表现数十甚至上百种不同材质时,如何高效地组织、混合并渲染它们,同时增加视觉细节,成为了一个核心挑战。

一、多材质混合:Texture Array 的优势

为了在地形上混合多种材质(如草地、泥土、岩石等),我们需要一种高效的方式来管理和采样这些材质的纹理。这里我们对比两种常见的数据结构:3D 纹理和纹理数组。

3D Texture (三维纹理)
  • 核心观点: 3D 纹理将多张 2D 贴图看作一个连续的三维数据体,采样时会在三个维度(X, Y, Z)上进行插值。
  • 关键术语: 三线性插值 (Trilinear Interpolation)
  • 工作方式: 当采样点 (u, v, w) 不精确落在某个纹素中心时,GPU 需要:
    1. w 所在的下层(slice)进行一次 2D 双线性插值(采样 4 个点)。
    2. w 所在的上层(slice)进行另一次 2D 双线性插值(采样 4 个点)。
    3. 最后,根据 w 的小数部分,对前两步的结果再进行一次线性插值。
  • 结论: 这个过程非常耗费性能(总共采样 8 个点,进行 7 次插值)。更重要的是,它隐含了“层与层之间是连续可插值的”这一假设,这不符合地表材质的物理特性——草地和岩石之间没有“半草半岩”的过渡材质。
Texture Array (纹理数组)
  • 核心观点: 纹理数组将多张 2D 贴图打包在一起,但各层之间完全独立,通过一个整数索引来访问特定层。
  • 关键术语: 索引访问 (Indexed Access)
  • 工作方式: 采样坐标变为 (u, v, index),其中 index 必须是整数,直接指定要采样的贴图层。GPU 不会进行层与层之间的插值。
  • 结论: 这种方式完美契合地形材质混合的需求。我们有几十种独立的材质,通过索引精确地选取其中几种进行混合,避免了无意义且昂贵的“层间插值”。
Splatting Map 的角色

在实践中,我们的 Splatting Map(或称权重图)会存储两类信息:

  1. 材质索引 (Material Indices): 对于每个像素,指出它受到哪几种材质的影响(例如,材质 #3、#13、#35)。
  2. 混合权重 (Blending Weights): 对应每种材质的混合权重(例如,材质 #3 权重 0.3,材质 #13 权重 0.7)。

Shader 的工作流程就是:读取索引,从 Texture Array 中采样对应的材质纹理,然后根据权重将它们混合起来。

二、增强地表细节与立体感

除了基础的颜色和法线,我们还可以使用更高级的技术来创造更逼真的凹凸感。

视差贴图 (Parallax Mapping)
  • 核心观点: 一种在片元着色器中模拟几何位移的视觉技巧,通过偏移纹理坐标 (UV) 来产生深度感和视差效果。
  • 工作方式:
    1. 假设地表下存在由 高度图 (Height Map) 定义的微观几何结构。
    2. 当视线以一定角度看向地表某点时,该点实际可见的表面会因为高度差而产生偏移。
    3. 通过 光线步进 (Ray Marching) 的方法,沿着视线方向在高度场内进行短距离步进,直到找到与表面的交点。
    4. 使用这个新交点的位置来采样纹理。
  • 效果: 相比传统的法线贴图,能产生更强烈的立体感,尤其适合表现碎石、砖块等凹凸不平的表面。
  • 缺点:
    1. 计算开销比普通采样更高(需要多次迭代)。
    2. 它只是一种视觉欺骗,无法改变模型的实际轮廓(剪影 (Silhouette) 依然是平的)。
位移贴图 (Displacement Mapping)
  • 核心观点: 利用现代 GPU 的 曲面细分 (Tessellation) 功能,根据高度图真实地移动顶点,从而改变模型的几何形状。
  • 工作方式:
    1. 在渲染管线的曲面细分阶段,将粗糙的低模网格动态地细分成非常精细的微观三角形。
    2. 在顶点着色器或域着色器中,采样高度图,并根据高度值沿顶点法线方向移动这些新生成的顶点。
  • 效果: 产生真实的几何细节,能够正确地改变模型轮廓和产生自遮挡,效果最逼真。
  • 趋势: 虽然目前在大型地形中全局使用尚不普遍,但随着硬件性能提升,这被认为是未来地形渲染的一个重要方向。

三、传统混合方法的性能瓶颈

尽管上述方法在逻辑上直观清晰,但在实际应用中,基于 Splatting Map 和 Texture Array 的传统方法存在严重的性能问题。

  • 核心问题: 纹理采样是昂贵的 (Texture Sampling is Expensive)
  • 瓶颈分析:
    1. 采样次数爆炸 (Sampling Count Explosion): 场景中每一个可见的地形像素,都需要执行完整的材质混合逻辑。如果一个像素混合了 4 种材质,每种材质又包含反照率、法线、粗糙度等多张贴图,那么单像素的纹理采样次数会急剧上升。
    2. 内存访问效率低下 (Inefficient Memory Access):
      • GPU 渲染一个区域的像素时,相邻像素可能需要访问截然不同的材质索引(一个像素可能用材质 #3 和 #75,旁边那个可能用 #64 和 #112)。
      • 这种对 Texture Array 的 随机、跳跃式访问 (Random Access) 严重破坏了 缓存局部性 (Cache Coherency)
      • 这好比让内存控制器在显存的各个地址间疯狂跳转,类似于机械硬盘磁头来回寻道,导致 GPU 的纹理单元(Texture Units)和缓存系统效率大打折扣,性能急剧下降。

四、解决方案:虚拟纹理 (Virtual Texture)

为了解决传统方法的性能瓶颈,业界提出了革命性的 虚拟纹理 (Virtual Texture) 技术。

  • 历史背景: 该技术的一个著名早期实现是 id Software 的 MegaTexture,由传奇程序员 约翰·卡马克 (John Carmack) 提出。

  • 核心思想: 按需加载,只将当前视野内需要的数据保留在显存中。

    • 绝妙类比: 你去旅行一天,没必要把家里所有衣物都装进一个巨大的行李箱背着走。你只需要带上当天要用的毛巾、牙刷和一套换洗衣物。虚拟纹理就是这个逻辑:硬盘上存着所有“行李”(完整的、超高分辨率的地形纹理),但显存这个“随身背包”里只装着当前马上要用的那一小部分。
  • 工作原理(高层概述):

    1. 离线预处理: 将地形上所有的材质混合、Splatting 等操作全部在离线阶段完成,烘焙成一张或多张分辨率极高(例如几十万 x 几十万像素)的巨型纹理,并为其生成完整的 Mipmap 链。这张巨型纹理存储在硬盘上。
    2. 分块管理 (Tiling/Paging): 将这张巨型纹理划分为固定大小的图块(Tiles 或 Pages),例如 512x512 像素。
    3. 运行时按需调度:
      • 在渲染每一帧时,引擎首先分析摄像机视野,确定哪些图块以及这些图块的哪个 Mipmap 等级是当前可见的。
      • 如果需要的图块不在显存中,就向系统发出请求,将其从硬盘异步加载到显存中的一个 物理纹理缓存 (Physical Texture Cache) 中。
      • 渲染时,Shader 不再直接访问海量的 Texture Array,而是通过一个 间接寻址贴图 (Indirection Table) 找到所需内容在物理缓存中的位置,然后进行一次简单的纹理采样。
  • 优势:

    • 极高的采样效率: 复杂的材质混合在离线完成。运行时每个像素最终只需要进行一次(或几次,取决于 PBR 工作流)高效的、缓存友好的纹理采样。
    • 突破显存限制: 能够管理远超显存容量的纹理数据,实现近乎无限的细节表现。

高级地形渲染技术:虚拟纹理与大规模世界挑战

这是系列讲座笔记的第 8 部分。本节将深入探讨现代游戏引擎中用于渲染广阔地形的主流技术—— 虚拟纹理(Virtual Texturing),并讨论其带来的数据流挑战以及大规模世界中的浮点数精度问题。


一、核心技术:虚拟纹理 (Virtual Texturing)

传统方法中,为广阔地形创建一张巨型、高分辨率的纹理会轻易耗尽显存。虚拟纹理是一种巧妙的“按需加载”和“数据虚拟化”技术,解决了这一难题。

核心观点

虚拟纹理的核心思想是,将一个逻辑上巨大无比的纹理(例如,几十万像素宽)分解成无数个固定大小的小块,我们称之为 瓦片(Tile)。渲染时,系统只将摄像机视锥内实际需要、且满足当前LOD(细节层次)的瓦片加载到显存中。

这套系统可以被生动地比喻成一个 “修车师傅与学徒” 模型:

  • 巨大的工具库 (硬盘/SSD): 存储着完整的、超高分辨率的地形数据(材质、几何体等)。
  • 修车师傅 (GPU渲染器): 正在进行渲染工作。
  • 小学徒 (内存/数据流管理器): 负责跑腿,按需取工具。
  • 手边的小工具包 (显存缓存): 显存中一块固定大小的区域,用于存放当前渲染任务必需的瓦片。

工作流程

  1. 分析需求: 渲染器(师傅)检查当前帧的画面,确定需要哪些区域(瓦片)的哪些细节层次(LOD)。
  2. 下达指令: 渲染器向数据流管理器(学徒)发送一个列表,请求所需的瓦片。
  3. 数据调度: 管理器(学徒)检查请求的瓦片:
    • 如果瓦片已在显存缓存(工具包)中,则直接使用。
    • 如果不在,就从硬盘(工具库)中加载,并放入显存缓存。如果缓存已满,则根据一定策略(如LRU)丢弃最久未使用的瓦片,腾出空间。
  4. 渲染执行: 渲染器通过一个 间接索引(Indirection Table) 来查找每个瓦片在显存缓存中的实际位置,并完成绘制。

这个过程是动态的,每一帧都在根据摄像机的移动和朝向进行瓦片的请求和替换。

二、虚拟纹理的巨大优势

这种架构之所以能成为业界主流,是因为它带来了两个革命性的好处。

1. 极大地降低了显存占用

  • 核心观点: 显存占用不再与整个世界的总数据量成正比,而是与屏幕分辨率和视口复杂性相关。

  • 数学原理: 无论你观察多远,更远的物体会使用更低LOD的瓦片。每一级LOD的瓦片数量都是上一级的 1/4。因此,所有LOD层级所需的瓦片总数是一个收敛的几何级数,其总和有一个固定的上限。

    这意味着,理论上显存占用有一个可预测的、不会无限增长的峰值。

2. 性能优化:预计算(Baking)

  • 核心观点: 复杂的计算(如多层材质混合)可以在瓦片从硬盘加载到显存时一次性完成,而不是在渲染时对每个像素实时计算。
  • 关键术语: 烘焙 (Bake)。当一个瓦片被请求时,CPU或GPU的计算单元可以预先将该瓦片对应的所有材质、贴图混合好,生成最终结果,然后存入显存缓存。
  • 优势: 在渲染循环中,着色器只需进行一次简单的纹理采样,极大地减轻了实时渲染的压力,提升了帧率。

结论: 虚拟纹理技术凭借其高效的内存管理和性能优势,已成为现代游戏引擎地形渲染的事实标准,远超传统的学院派方法。

三、数据流瓶颈与前沿硬件解决方案

虚拟纹理依赖于高效的数据调度,但传统PC架构中“硬盘 内存 显存”的数据链路存在瓶颈。

传统瓶颈

数据必须先由CPU控制从硬盘读入系统内存,再由CPU或驱动程序拷贝到显存。GPU作为最终的消费者,却无法直接获取数据,造成了延迟和不必要的CPU/内存带宽占用。

现代解决方案

为了解决这一瓶颈,业界发展出了新的硬件和API技术,值得下一代引擎开发者高度关注:

  1. DirectStorage (NVIDIA RTX IO / AMD SmartAccess Storage)

    • 核心思想: 数据在从硬盘传输到显存的过程中,在系统内存中保持压缩状态。解压缩的工作交由算力强大的GPU来完成。
    • 优势: 大幅减少了传输数据量,加快了加载速度,并解放了CPU资源。
  2. DMA (Direct Memory Access)

    • 核心思想: 允许GPU 直接从硬盘读取数据到显存,完全绕过CPU和系统内存。
    • 优势: 这是目前最高效的数据路径,延迟最低。
    • 典型案例: 索尼 PlayStation 5 的高速SSD架构就是这项技术的杰出代表。

未来趋势: 随着GPU在计算中扮演越来越重要的角色,PC架构正在演进。例如,GPU之间通过 NVLink 等技术直接互联,数据交换不再需要绕道系统内存。开发者在设计新引擎时,应充分利用这些现代硬件特性来最大化虚拟纹理系统的效率。

四、大规模世界的渲染挑战:浮点数精度

当游戏世界变得极其巨大时(例如,从地球延伸到太空),一个纯粹的数学问题会成为视觉上的噩梦。

问题根源:浮点数精度溢出

  • 核心观点: 计算机中用于表示坐标的32位浮点数(float)能表达的信息量是有限的。当一个数值变得非常大时(例如,距离世界原点几万米),其小数部分的精度会急剧下降。
  • 原理: 浮点数的存储结构决定了它在表示绝对值大的数字时,能分辨的最小步长也会变大。例如,在 100000.0 这个位置,可能已经无法精确表示 100000.01,下一个可以表示的数字可能是 100000.05

视觉表现

  • 抖动 (Jittering): 当摄像机和物体都处于远离世界原点的位置时,由于顶点坐标无法被精确表示,它们会在几个可表示的浮点数值之间来回跳动,导致模型在屏幕上不停地轻微抖动或闪烁。
  • Z-Fighting: 同样由于精度问题,两个靠得很近的表面可能在深度缓冲中无法被正确区分,导致它们交错闪烁。

实际影响

在几平方公里的大地图中,这个问题就已非常普遍。例如,一个距离原点几千米远的墙壁,其上一个仅凹进0.05米(5厘米)的窗台,就可能因为浮点数精度不足而与墙面产生抖动或穿模。

注意: 这个问题是所有基于大规模坐标系渲染引擎的共同挑战,需要通过特定的技术手段(如相机相对渲染、坐标系局部化等)来解决,这可能会在后续的讲座中讨论。


一、 大尺度世界渲染的挑战:浮点数精度问题

当游戏世界变得极其广阔时(例如,开放世界、太空游戏),一个核心的技术挑战浮出水面: 32位浮点数(float)的精度限制

1. 问题的根源:Z-Fighting

  • 核心观点: 浮点数的精度是相对的,而非绝对的。当一个浮点数的整数部分变得非常大时(例如,坐标值达到几千米外),其小数部分能够表示的精度就会急剧下降。

  • 具体场景:

    • 假设世界坐标原点在 (0,0,0)。一个物体在 2000 米外,其坐标可能是 2000.05
    • 如果在这个物体表面再贴一个厚度为 1 毫米(0.001米)的贴花,其坐标理论上是 2000.051
    • 由于浮点数精度限制,2000.052000.051 这两个值在GPU中可能被认为是同一个值,或者在一个极小的范围内抖动。
    • 结果: 在渲染时,GPU无法稳定地判断哪个面在前,哪个在后,导致两个表面交错闪烁,这种现象被称为 Z-Fighting(深度冲突)
  • 为什么不用Double: 虽然64位双精度浮点数(double)可以解决这个问题,但其内存占用和计算开销是 float 的两倍,对于需要处理海量顶点数据的GPU来说,这在性能和带宽上是难以接受的。

2. 解决方案

方案一:相机相对渲染 (Camera-Relative Rendering)

这是业界最主流和经典的解决方案。

  • 核心思想: 将渲染的坐标系中心从世界原点 (World Origin) 动态地移动到相机所在的位置
  • 工作原理:
    1. 在每一帧渲染开始前,获取相机在世界坐标系中的位置 P_camera

    2. 将所有需要渲染的物体的世界坐标 P_object_world 减去相机的位置,得到一个相对于相机的新坐标 P_object_relative

      P_object_relative = P_object_world - P_camera
      
    3. 将这个相对坐标 P_object_relative 和一个原点在 (0,0,0) 的视图矩阵传递给GPU进行后续计算。

  • 效果: 通过这种方式,相机永远处于坐标系的原点,其附近的物体坐标值会变得非常小。这使得浮点数的精度被最大限度地利用在小数部分,从而完美解决了近处物体的 Z-Fighting 问题。
  • 应用: 这是 Unity、Unreal Engine 等主流引擎的标准做法。

方案二:分块重置世界坐标 (Sub-Level Origin Rebasing)

这是在相机相对渲染基础上的进一步扩展,尤其适用于超大规模世界的流式加载。

  • 核心思想: 将巨大的世界地图切分成多个子关卡(Sub-Level)或区块(Chunk)。当玩家进入一个新的区块时,引擎会将整个世界坐标系的原点重置到该区块的中心。
  • 代表: Unreal Engine 5World Partition 系统就采用了类似的技术。这不仅解决了渲染精度问题,也为多人游戏中的坐标同步和物理模拟提供了便利。

3. 浮点数精度的深远影响

  • 物理模拟: 这个问题在物理模拟中更为致命。超大场景下的物理计算,如果因为精度问题导致数值不稳定,结果往往是物理效果的彻底崩溃(例如,物体被弹飞到无穷远)。一些研究甚至会探索 定点数物理模拟 来规避此问题。
  • 应用场景: 对于像《无人深空》(No Man's Sky) 这样的星际旅行游戏,玩家的坐标尺度从星球表面到星系级别,跨度极大,必须依赖此类技术才能实现。

二、 构建丰富的世界:地形之上的细节

当地形基础构建完毕后,需要添加各种细节元素来让世界显得生动真实。

1. 植被:树木的渲染

树木的渲染是一个独立的复杂课题,通常采用一套激进的 LOD (Level of Detail) 策略。

  • 核心观点: 根据物体与相机的距离,使用不同复杂度的模型来表达同一个物体。
  • 树木LOD链条:
    1. 近景: 使用完整的、高精度的 3D Mesh 模型。
    2. 中景: 逐渐过渡到由少量交叉面片组成的 Impostor(公告板的变种,也称“插片”)。这些面片上渲染了树木从不同角度看的图像。
    3. 远景: 简化为单个面向相机的 Billboard(公告板),甚至将成片的树林合并成一个 Billboard 渲染。
  • 关键技术/中间件: SpeedTree 是业界最成熟、最著名的植被渲染解决方案。

2. 装饰物 (Decorators)

  • 核心定义: Decorator 是指撒在场景中用于增加环境多样性和细节的小物件,例如 地上的草、小灌木、碎石 等。
  • 技术演进:
    • 早期做法: 使用简单的交叉面片或永远朝向摄像机的 Billboard。这种做法在玩家低头靠近观察时,会看到草片跟随着视角旋转的穿帮现象。
    • 现代做法: 3A 游戏会采用更复杂的 Mesh 和更高级的渲染技术,使得装饰物在近处也表现得非常自然。

3. 道路系统 (Road System) 与 贴花系统 (Decal System)

道路系统

  • 美术工具: 艺术家通常使用 样条曲线 (Splines) 来方便地创建蜿蜒或笔直的道路。他们只需拖拽几个控制点,就能生成平滑的路径。
  • 程序挑战:
    1. 纹理生成: 需要沿着样条曲线正确地生成和贴上道路纹理,并处理好道路交叉口的混合(Blending)问题。
    2. 地形修改: 道路不能直接“浮”在原始起伏的地形上。程序需要根据道路路径,对 高度场 (Heightfield) 进行修改,实现 挖山、填谷 的效果,使道路与地形自然地结合。

贴花系统 (Decals)

  • 核心定义: Decal 是一种将贴图(通常带 Alpha 通道)“投射”到场景中已有模型表面上的技术,用于增加局部细节。
  • 经典案例:
    • 射击游戏中,子弹打在墙上留下的 弹孔
    • 环境美术中,在墙上贴的 污渍、海报,或在地上画的 裂缝
  • 技术增强: Decal 可以结合 视差贴图 (Parallax Mapping) 等技术,来模拟凹凸感,让弹孔看起来真的像一个洞。

4. 统一的解决方案:虚拟纹理 (Virtual Texture) 的威力

讲座在这里将道路、贴花等后期细节与之前提到的 虚拟纹理 (Virtual Texture) 技术巧妙地联系了起来。

  • 核心观点: 将所有复杂的表面细节(地形材质混合、道路、贴花等)预先 烘焙 (Baking) 到虚拟纹理的对应图块(Tile)中。
  • 工作流程:
    1. 首先,地形材质根据混合规则被烘焙到虚拟纹理上。
    2. 然后,道路系统的纹理被叠加烘焙上去。
    3. 最后,艺术家放置的各种 Decals 也被一一烘焙到虚拟纹理上。
  • 巨大优势:
    • 简化运行时 (Runtime): 渲染时,GPU 不再需要处理复杂的多层材质混合、道路纹理叠加、Decal 投射等逻辑。它只需要对最终生成好的虚拟纹理进行一次采样即可。
    • 性能高效: 烘焙是一次性的离线过程。只要烘焙好的 Tile 还在缓存中,运行时的渲染开销就极低且固定。这极大地解放了运行时的渲染压力。

地形系统总结与程序化生成展望

我们将对之前讨论的地形渲染技术进行总结,并展望现代游戏引擎中更为前沿的地形内容生成方法—— 程序化生成(Procedural Content Generation, PCG)

一、 虚拟纹理(Virtual Texturing)的优势回顾

在深入内容创作之前,讲座首先重申了之前讨论的基于 Tile 的虚拟纹理系统的一个关键优势。

  • 核心观点:基于 Tile 的系统在处理和更新上极为高效。
  • 关键特性
    • 低更新成本:一个 Tile 的数据(如纹理、材质信息等)在生成一次后,只要该 Tile 依然存在于缓存或视野中,就无需重复更新
    • 持久性:这种“一次计算,多次使用”的特性,极大地降低了渲染和数据管理的开销,尤其适用于广阔的开放世界地形。

二、 地形内容的创建与编辑:从手动到自动

构建广袤的地形,不仅需要高效的渲染技术,更需要强大的内容创作工作流。讲座对比了两种主流的创建方式。

1. 传统的美术驱动工作流

这是一种基础且直观的方法,至今仍在广泛使用,其核心依赖美术师的手动编辑。

  • 核心观点:通过 高度场(Height Field) 定义地形基础形状,再由美术师手动绘制地表材质。
  • 关键步骤
    1. 生成高度场:首先创建一个灰度图或数据结构来表示地形的高低起伏。
    2. 手动绘制纹理:美术师使用笔刷等工具,在地形编辑器中直接“喷涂”或“绘制”不同的地表纹理,例如在某个区域绘制 砂石(gravel),在另一区域绘制 砖石(brick/stone)

2. 现代的程序化生成(PCG)工作流

随着游戏世界规模和复杂度的急剧增加,纯手动编辑变得不切实际。因此,基于规则和算法的程序化生成成为了现代游戏引擎的宠儿。

  • 核心观点:利用算法模拟自然规律,自动生成复杂、真实且多样化的自然环境,极大提升了开发效率和真实感。
  • 关键术语程序化内容生成 (Procedural Content Generation, PCG)
  • 应用实例展示
    • 地貌生成 (Geomorphology):通过模拟 流水对山体的侵蚀(water erosion),可以自动生成更加自然、逼真的山脉和河谷形态。
    • 道路系统 (Road Systems):算法可以根据地形的 坡度(slope)地表材质(surface material) (例如,在平缓的土地上生成道路,避开陡峭的岩石)来自动规划和生成路径。
    • 植被分布 (Vegetation Distribution):系统可以依据 海拔(altitude) 、湿度、坡度等环境因素,智能地放置植被。这不仅能生成植被,还能模拟真实的生态过渡,例如从低海拔的阔叶林到高海拔的针叶林的自然渐变。

三、 总结与展望:游戏引擎开发者的跨学科素养

讲座最后从技术延伸到了对游戏引擎开发者能力模型的思考,强调了跨学科知识的重要性。

  • 核心观点:优秀的游戏引擎开发工作,本质上是对现实世界规律的理解、抽象和模拟。它远不止是编程。
  • 关键洞察
    • 成为“博物学家”:为了创造一个可信的世界,引擎开发者需要学习和理解大量自然科学知识,包括地理学(如侵蚀作用)、生物学(如生态分布)、物理学等。
    • 跨学科的重要性:这种跨领域的知识储备是创造出沉浸式体验的基石。
    • 学习引擎的价值:无论未来是否直接编写引擎代码,学习游戏引擎的底层知识都能极大地拓宽技术视野,加深对游戏开发的理解。

掌握了这些地形相关的技术,可以说是迈出了成为“大地之神”的第一步。然而,技术的探索永无止境,这仅仅是一个开始。


游戏中大气与云的渲染

引言:天空渲染的重要性与挑战

天空和云是构建游戏世界氛围感和情绪表达的关键元素,其重要性不言而喻。无论是《地平线》的异星科幻感,还是《艾尔登法环》的史诗氛围,天空都扮演了至关重要的角色。

在技术实现上,我们需要明确区分两个核心概念:

  • 天空 (Sky/Atmosphere): 指的是整个大气层,可以理解为一个覆盖地表的巨大抽象球体(其上限通常定义为离地 100km 的卡门线)。它决定了天空的背景颜色、日出日落的色彩变幻等宏观现象。
  • 云 (Clouds): 是悬浮在大气层中的 体积实体 (Volumetric Entity)。它们离地表更近(通常在几百米到几千米高度),具有强烈的体积感和视差效应,是独立的渲染对象。

本篇笔记后半部分将专注于 天空(大气) 的渲染技术,从传统拟合方法过渡到现代基于物理的渲染方法。


一、传统的天空渲染方法:参数化拟合模型

这是一种早期或在性能受限平台(如移动端)上使用的简化方法,其核心思想类似于材质领域的 Blinn-Phong 模型—— 不关心底层物理原理,只求用一个简单的数学模型去拟合观察到的现象

  • 核心观点: 将天空颜色视为一个仅由几个关键角度决定的函数,通过一个解析解(或查找表)快速计算出结果。

  • 关键输入参数:

    1. 视角与天顶的夹角 (Angle to Zenith): 决定了你是在看头顶的天空还是地平线。
    2. 视角与太阳的夹角 (Angle to Sun): 决定了你是在朝向太阳看还是背向太阳看。
  • 优点:

    • 极其简单、快速,计算开销非常小。
    • 对于静态、简单的场景,能提供一个“看起来还行”的效果。
  • 缺点:

    • 仅限于地表观察 (Ground-level only): 无法实现从太空冲入大气的连续视觉效果。
    • 参数写死 (Hardcoded parameters): 模型中的参数都是固定的,无法动态模拟不同天气(如晴天、阴天、污染)下的大气状态。
    • 缺乏物理真实性: 无法满足现代 3A 游戏对动态、逼真环境的渲染需求。

二、现代物理方法:深入理解大气散射

为了达到 3A 游戏的高标准,我们必须转向基于物理的方法,从根本上模拟光线在大气中的传播和散射过程。

2.1 核心概念:参与介质 (Participating Media)

这是理解现代大气渲染的基石。

  • 核心观点: 大气并非真空,而是充满了微小粒子的 参与介质 (Participating Media)。光线在其中传播时会与这些粒子发生复杂的交互,从而产生了我们所见的各种光学现象(如蓝天、彩霞等)。

  • 大气中的两种主要粒子:

    1. 气体分子 (Gas Molecules): 如氮气、氧气等。它们的尺寸远小于可见光波长,主要导致 瑞利散射 (Rayleigh Scattering),对蓝光散射更强,是天空呈现蓝色的主因。
    2. 气溶胶 (Aerosols): 如灰尘、水滴、悬浮颗粒物等。它们的尺寸与可见光波长相当,主要导致 米氏散射 (Mie Scattering),散射没有明显的波长倾向,使天空呈现灰白色,是形成雾、霾和云底效果的主要原因。

2.2 光与介质的四种交互作用

当一束光在参与介质中传播时,其能量(辐射度)会发生变化,主要受以下四种物理过程影响:

  1. 吸收 (Absorption):

    • 描述: 光的能量被介质粒子吸收并转化为其他形式的能量(如热能),导致光线强度衰减。
    • 关键术语: 吸收系数 (Absorption Coefficient)
  2. 外散射 (Out-scattering):

    • 描述: 光线与粒子碰撞后,能量被散射到其他方向,导致其在原传播路径上的强度减弱。
    • 关键术语: 散射系数 (Scattering Coefficient)
  3. 自发光 (Emission):

    • 描述: 介质本身由于高温或化学反应而发光,为光路增加能量(如火焰、闪电)。
    • 关键点: 在绝大多数游戏的大气渲染中,这一项的贡献极小,通常被忽略
  4. 内散射 (In-scattering):

    • 描述: 这是最复杂但也是最关键的一项。来自其他所有方向的光线,在传播路径上的某一点被粒子散射后,恰好进入了我们的观察视线中。这是我们能看到“天空本身颜色”的根本原因。
    • 关键挑战: 计算内散射需要对一个点周围 整个球面 (4π steradians) 的入射光线进行积分,计算量巨大。

2.3 辐射传输方程 (RTE) - 统一的物理模型

将上述四种交互作用统一起来,就构成了描述光在参与介质中传播的物理学基础方程。

  • 核心观点: 辐射传输方程 (Radiative Transfer Equation, RTE) 是一个微分方程,它精确地描述了光线沿某条路径传播时,其辐射度(亮度)的变化率。

  • 概念性公式:

    光线亮度的变化 = 内散射 + 自发光 - 吸收 - 外散射

    在数学上,它通常表示为一个复杂的积分-微分方程。讲座中提到的是其简化的一维形式,但足以帮助我们理解其构成。

  • 重点: 对于引擎开发者而言, 理解 RTE 背后各项的物理意义,远比记忆复杂的公式本身更重要

2.4 实际应用:从观察者视角出发

在渲染中,我们关心的是从摄像机(P点)看向远处某一点(M点)时,最终进入我们眼睛的光是什么样的。这个过程可以分解为两个主要部分的贡献:

  1. 远处物体 M 本身的光。
  2. 从 P 到 M 路径上,由大气内散射产生的光。

为了计算第一部分,我们需要引入一个至关重要的概念:

  • 关键术语: 透射率 (Transmittance)
    • 定义: 描述了从 M 点发出的光,在沿着视线传播到 P 点的过程中, 有多少比例的光能够“幸存”下来,即没有被吸收或外散射掉。
    • 作用: 它决定了远处的物体因为大气的遮挡会变得多模糊、多暗。透射率越低,意味着大气越“厚”,能见度越低。

大气散射的物理与数学模型

本次学习笔记聚焦于真实感大气渲染的理论基础。我们将从宏观的 体渲染方程 (Volume Rendering Equation) 入手,并深入剖析构成我们所见天空色彩的两种核心物理散射模型: 瑞利散射 (Rayleigh Scattering)米氏散射 (Mie Scattering)


一、 体渲染方程 (Volume Rendering Equation - VRE) 核心思想

当我们观察的场景并非真空,而是充满了如空气、烟雾、云等 参与介质 (Participating Media) 时,光线在传播过程中会与介质发生复杂的相互作用。体渲染方程正是描述这一物理过程的数学工具。

对于工程师而言,无需死记硬背复杂的积分公式,关键在于理解其背后的两个核心概念:

  1. 透射率 (Transmittance)

    • 核心观点: 这个值描述了从场景中某一点 M 发出的光,在穿过一段充满介质的路径后,有多少比例能够最终到达观察点 P(例如我们的眼睛或摄像机)。
    • 通俗理解: 它衡量的是路径上光线的衰减程度。介质越浓厚,路径越长,透射率就越低,远处的物体看起来就越模糊、越暗。
  2. 内散射 (In-scattering)

    • 核心观点: 这个值描述了在从 MP 的路径上,来自其他方向的光源(如太阳、天空等)的光线,被路径上的介质粒子散射后,有多少能量 被“拐”进了 我们的视线方向,并最终到达观察点 P
    • 通俗理解: 它是路径上光线的增益部分。晴朗白天,即使你背对太阳,空气本身也会因为散射太阳光而“发光”,这就是内散射的直观体现。

总结: 我们最终在 P 点看到来自 M 方向的颜色,是 M 点自身颜色经过衰减(Transmittance)后,再叠加上路径中散射进来的光(In-scattering)的结果。

二、 大气散射的物理基础

要真实地模拟大气,我们需要了解其物理构成。大气散射现象主要由两个参与者决定:

  1. 光源:太阳 (Sun)

    • 关键特性: 太阳光是复合光,由大量不同波长的光谱组成。我们人眼经过长期进化,将这种光谱组合感知为“白色”。不同波长的光在与大气介质作用时,会表现出截然不同的行为。
  2. 介质:大气粒子 (Atmospheric Particles)

    • 大气中主要包含两类尺寸和性质迥异的粒子,它们分别主导了不同的散射类型:
      • 气体分子 (Gas Molecules): 如氧分子、氮分子等。它们的尺寸远小于可见光的波长。这是瑞利散射的主因。
      • 气溶胶 (Aerosols): 如灰尘、水滴、花粉、污染物等悬浮颗粒。它们的尺寸与可见光的波长相当或更大。这是米氏散射的主因。

三、 瑞利散射 (Rayleigh Scattering) - 天空之色

瑞利散射是解释“天空为什么是蓝色”的关键。

  • 核心观点: 当光线与尺寸远小于其波长的粒子(主要是气体分子)相互作用时发生。其最显著的特点是 对短波长的光(如蓝光、紫光)散射作用远强于长波长的光(如红光、橙光)

  • 散射特性:

    • 波长依赖性: 散射强度与波长的四次方成反比 ()。这意味着蓝光(波长较短)的散射效率比红光(波长较长)高得多。
    • 方向性: 散射在前进和后退方向上最强,在与光线垂直的侧面方向上最弱,整体分布形似一个“花生”或“哑铃”。
  • 相位函数 (Phase Function) 与公式解析: 相位函数用于描述光在被散射后,其能量在各个方向上的分布情况。瑞利散射的相位函数可以分解为两个部分:

    • 强度系数部分:
      • 波长 (): 1/λ⁴ 项是瑞利散射的核心,直接体现了其强烈的波长依赖性。
      • 海拔高度 (): 空气密度 ρ(h) 随海拔升高而指数级下降。因此,海拔越高,空气越稀薄,瑞利散射效应越弱。
    • 角分布部分:
      • 1 + cos²θ 项(其中 θ 是入射光方向与散射方向的夹角)在数学上完美地描述了“花生”形状的能量分布。
  • 现实应用:天空色彩的成因

    • 蓝天: 在白天,太阳光进入大气层。其中的蓝光成分被空气分子向四面八方剧烈地散射,使得整个天空都呈现出被散射的蓝色光。而红光等长波光则更多地沿直线传播。
    • 红日落: 在日出日落时,太阳光需要穿过更厚的大气层才能到达观察者。在这个过程中,绝大部分蓝光早已被散射到其他方向,只有穿透力更强的红光和橙光能够最终抵达我们的眼睛,因此天空呈现出暖色调。

四、 米氏散射 (Mie Scattering) - 云雾之白

米氏散射主要由大气中的气溶胶(尘埃、水滴等)引起。

  • 核心观点: 当光线与尺寸和其波长相当的粒子相互作用时发生。其特点是对所有波长的光散射程度相近,并且散射能量主要集中在前进方向

  • 散射特性:

    • 波长依赖性: 基本与波长无关。无论红光、绿光还是蓝光,都会被近似均匀地散射。这就是为什么云、雾、霾看起来是白色或灰色的原因。
    • 方向性: 具有强烈的前向散射特性。大部分光能会沿着原光线方向继续传播,形成一个明亮的光晕(例如在雾天看路灯)。其能量分布形状复杂,常被形容为“墨鱼”或“带鱼”状。
  • 相位函数 (Phase Function) 与公式解析: 米氏散射的相位函数通常使用 Henyey-Greenstein (HG) 模型 来近似拟合,这是一个在图形学中广泛应用的经验模型。

    • 不对称因子 (Asymmetry Parameter, g): 这是HG函数的核心参数,它控制着散射的方向性,取值范围为 [-1, 1]
      • g > 0: 前向散射g 越接近 1,散射能量越集中在前进方向,形状越尖锐。这是大气中气溶胶的典型情况。
      • g = 0: 各向同性散射 (Isotropic Scattering),能量在所有方向上均匀分布,函数退化为常数。
      • g < 0: 后向散射,能量更多地被反射回光源方向。
    • 实践应用: 在引擎中,参数 g 通常会开放给美术师,用于调整雾、霾或体积光等效果的视觉表现。

大气散射的物理模型与实时渲染技术

接下来的内容将是如何高效地求解或预计算这些由 RTE 导出的复杂积分,从而在游戏中实时地渲染出逼真的大气效果。

在这部分内容中,讲座深入探讨了大气散射的物理细节,并引出了解决这一复杂问题的两种核心实时渲染技术:光线步进(Ray Marching)和预计算(Pre-computation)。

一、 深入散射模型 (Scattering Models Deep Dive)

在前一部分的基础上,我们继续完善大气散射的物理模型,引入了 Mie 散射的相位函数以及光的吸收效应。

1. Mie 散射的相位函数 (Mie Scattering Phase Function)

Mie 散射描述的是光与大气中较大微粒(如水滴、尘埃等气溶胶)的相互作用。其散射方向性由一个关键的数学工具——相位函数 (Phase Function) 来定义。

  • 核心观点: Mie 散射具有强烈的方向性,通常是前向散射(光线偏转角度小),这与 Rayleigh 散射的均匀散射特性形成鲜明对比。

  • 关键术语: Henyey-Greenstein (HG) 相位函数

    • 这是一个广泛用于拟合 Mie 散射方向分布的经验公式,计算开销小且效果良好。
    • 公式如下:
    • 其中 是入射光方向与散射光方向的夹角。
  • 关键参数: g (不对称因子, Asymmetry Parameter)

    • g 控制着散射的方向性,取值范围为 [-1, 1]
    • g > 0: 前向散射 (Forward Scattering),光线倾向于沿着原始方向继续传播。这是大气中尘埃和水汽的主要特性。
    • g < 0: 后向散射 (Backward Scattering),光线倾向于被反射回光源方向。
    • g = 0: 各向同性散射 (Isotropic Scattering),向所有方向均匀散射。
    • 在实践中,g 通常是一个开放给美术师调整的参数,用以控制不同天气(如雾、霾)下光晕的效果。
  • 常见现象:

    • 雾 (Fog): 空气中的小水珠对所有波长的光进行无差别 Mie 散射,因此呈现白色。
    • 日晕/光晕 (Halo): 傍晚太阳周围或雾天路灯周围的光晕,是典型的 Mie 前向散射现象,将光线汇集到观察者眼中。

2. 光的吸收 (Light Absorption)

除了散射,大气中的某些分子还会吸收特定波长的光,将其能量转化为内能。

  • 核心观点: 光的吸收是决定大气颜色的另一个关键因素。它会“减去”光谱中的某些颜色。

  • 关键术语: 吸收 (Absorption)

    • 示例: 海王星呈现蓝色,是因为其大气富含 甲烷 (Methane),大量吸收了太阳光中的红光成分,最终只有蓝光被反射和散射出来。 臭氧 (Ozone) 在可见光波段也主要吸收长波光(红光)。
    • 渲染中的简化: 在构建大气渲染模型时,通常会做一个重要假设—— 将臭氧、甲烷等吸收性气体视为在整个大气层中均匀分布。这虽然与物理事实(如臭氧层集中在高空)不符,但极大地简化了计算模型,是在可接受的误差范围内实现实时计算的一种妥协。

二、 单次散射 vs. 多次散射 (Single vs. Multiple Scattering)

这是理解和实现高级大气渲染最核心、也最困难的概念。

1. 单次散射 (Single Scattering)

  • 核心观点: 只考虑光线从光源(太阳)出发,与大气粒子碰撞一次后,直接进入相机(眼睛)的过程。
  • 计算过程: 沿着视线方向进行积分,累加视线上每一个点接收到的太阳光,并经过一次散射后贡献到视点的能量总和。
  • 视觉效果与局限:
    • 可以产生基本的天空颜色、太阳在地平线时的暖色调过渡等效果。
    • 最大问题: 无法照亮阳光直射不到的区域。例如,在只有单次散射的模型中,山峦的背光面会是一片死黑,非常不真实。

2. 多次散射 (Multiple Scattering)

  • 核心观点: 光线在进入相机前,与大气粒子发生了多次碰撞和散射。
  • 物理过程: 整个天空可以被看作一个巨大的、连续的体积光源。不在你视线路径上的空气粒子被太阳照亮后,会向四面八方散射光线,这些光线又会照亮你视线路径上的其他粒子,最终这些“间接光”进入你的眼睛。
  • 视觉效果与重要性:
    • 这是实现逼真天空效果的钥匙
    • 它解释了为什么山峦的背光面依然有环境亮度,为什么天空本身看起来是“发光的”而不仅仅是一个背景板。
    • 多次散射是现代 3A 游戏中实现照片级天空和大气效果必须解决的难题。它本质上是一种 体积全局光照 (Volumetric Global Illumination)

三、 求解方法一:光线步进 (Ray Marching)

这是一种用于求解体积渲染问题的通用数值积分方法。

  • 核心观点: 将连续的积分问题离散化。沿着一条光线(如视线)进行步进,在每个步进点上采样和计算,最后将所有采样点的贡献累加起来,近似得到最终的积分结果。

  • 关键术语: Ray Marching (光线步进)

  • 算法流程 (以计算单次散射为例):

    1. 从相机位置出发,沿着视线方向设置一系列采样点。
    2. 在每个采样点 P
      1. 计算从 P 点到太阳的光线是否被遮挡,以及光线穿过大气的衰减(Transmittance)。
      2. 计算太阳光到达 P 点的能量。
      3. 应用散射模型(Rayleigh + Mie),计算 P 点的粒子将多少能量散射到了相机的方向。
    3. 将每个采样点的贡献,考虑其到相机之间的光线衰减后,累加起来。
    4. 最终得到的结果就是视线方向上所有单次散射光的总和。

四、 求解方法二:预计算大气散射 (Precomputed Atmospheric Scattering)

面对多次散射的巨大计算量,实时渲染通常采用“空间换时间”的策略,即预计算。

  • 核心思想: 将复杂的、与视角和光照相关的计算预先完成,并将结果存储在查找表 (Lookup Table, LUT) 中。 运行时,只需根据当前参数(如视角方向、太阳角度)进行查表和简单的插值,即可获得高质量的结果。

  • 关键术语: 预计算 (Pre-computation), 空间换时间 (Space-for-Time Tradeoff)

  • 经典模型: Bruneton 的预计算大气散射模型 (Precomputed Atmospheric Scattering)

    • 这是一个里程碑式的工作,奠定了现代实时大气渲染的基础。
    • 核心思路: 将大气光学现象分解为两个主要部分进行预计算:
      1. 透射率/通透度 (Transmittance): 从 A 点到 B 点,光线能量能剩余多少。
      2. 散射光 (Scattering): 在某个点,从某个方向看,能接收到多少散射光。
  • 预计算通透度 (Precomputing Transmittance)

    • 目标: 创建一个 LUT,能够快速查询从地表任意一点,朝任意方向看到大气层外的透射率。
    • LUT 的参数化 (输入):
      1. 观察点海拔高度 (Altitude)
      2. 视线与天顶方向的夹角 (View-Zenith Angle)
    • LUT 的内容 (输出):
      • 从该点(由海拔和视角定义)出发,沿着视线方向直到大气层顶端的总透射率
    • 直观理解: 这张表回答了这样一个问题:“我在某个高度,朝某个角度抬头看,能看到多少比例的星光?” 这为后续计算所有来自天空的光线衰减提供了基础数据。

基于预计算的大气散射 (Pre-computed Atmospheric Scattering)

在上一部分的基础上,我们继续深入探讨如何高效地计算和渲染真实的大气效果。这一部分的核心思想是 预计算 (Pre-computation),即将复杂的光学现象通过离线计算,存储在查找表 (Look-Up Table, LUT) 中,以在运行时实现高性能的实时渲染。这个方法尤其以 Bruneton 的经典模型 为代表。

1. 大气透射率 (Transmittance) 的巧妙计算

要计算散射,首先必须知道光在介质中传播时会衰减多少,这就是 透射率 (Transmittance)

  • 核心观点: 直接计算空间中任意两点 AB 之间的透射率是一个复杂的积分过程。讲座介绍了一种极为巧妙的方法,将这个计算大大简化。

  • 关键技巧: 预先计算一张二维查找表,这张表存储了从大气中 任意一点(由高度和视角天顶角定义) 沿着视线方向到大气层边界的总透射率。

  • 核心公式: 假设我们已经有了这张预计算表 T_to_edge(point),那么从中间某点 M 到观察点 V 的透射率 T(M -> V) 可以通过一次简单的除法得到:

    T(M -> V) = T_to_edge(M) / T_to_edge(V)
    
    • T_to_edge(M): 从 M 点沿视线方向到大气层边界的透射率。
    • T_to_edge(V): 从观察点 V 沿同一视线方向到大气层边界的透射率。
  • 重要意义: 这个方法将一个需要复杂积分的四维问题(两个三维点的组合)简化为两次二维查表和一次除法,极大地提升了效率。

2. 单次散射 (Single Scattering) 的参数化与预计算

大气散射最复杂的部分在于,对于空间中任意一点,其接收到的散射光来自各个方向,且与太阳位置、观察者位置和视角都相关。直接实时计算是不可行的。

  • 核心挑战: 如何为整个三维空间中,在任意视角任意太阳角度下的散射情况建立一个高效的表达?

  • 解决方案: 参数化 (Parameterization) 假设大气是球对称各项同性的,我们可以用少数几个关键参数来描述任意情况下的单次散射光照:

    1. 海拔高度 (Altitude): 观察点距离地面的高度。
    2. 视线天顶角 (View Zenith Angle): 视线方向与该点天顶方向(法线方向)的夹角。
    3. 太阳天顶角 (Sun Zenith Angle): 太阳光方向与该点天顶方向的夹角。
    4. 视线与太阳的夹角 (View Sun Angle): 视线方向与太阳光方向之间的夹角。
  • 预计算流程:

    1. 构建查找表 (LUT): 创建一个 四维查找表 (4D LUT),其四个维度就是上述四个参数。
    2. 填充数据: 使用 光线步进 (Ray Marching) 算法,遍历这个4D参数空间中的每一个点,计算出对应的单次散射积分结果(即内散射光 In-Scattering 的能量),并存入LUT中。
    3. 硬件实现: 在现代GPU上,这个4D LUT通常可以用一个 3D TextureTexture Array 来存储。例如,可以将三个角度参数“打包”到纹理的 U, V 坐标,将海拔高度映射到 W 坐标。由于大气散射是低频信号,使用硬件的线性插值可以获得平滑的查询结果。

3. 从单次散射到多次散射 (Multi-Scattering)

单次散射只考虑了太阳光被散射一次后进入人眼的情况,这会让天空的暗部(如背对太阳的区域)过暗。为了更真实的效果,需要计算多次散射。

  • 核心观点: 多次散射可以基于单次散射的结果,通过迭代的方式进行预计算。

  • 迭代过程:

    1. 二次散射: 将整个大气看作被单次散射照亮的光源。利用之前计算好的 透射率LUT单次散射LUT,再次进行积分,计算出二次散射对观察点的贡献,并存入一个新的 二次散射LUT
    2. 更高次散射: 重复上述过程,基于二次散射计算三次散射,以此类推。
    3. 最终结果: 将单次、二次、三次……(通常3-4次就足够)散射的LUT结果累加起来,得到最终的 多次散射查找表 (Multi-Scattering LUT)
  • 效果: 每增加一次散射,天空整体会变亮一些,颜色过渡也更柔和、更真实。这个最终的 Multi-Scattering LUT 的结构与 Single-Scattering LUT 完全相同,只是存储的数值不同。

4. 实时渲染中的应用与效果

当所有预计算完成后,在游戏运行时,渲染大气就变得非常高效。

  • 运行时 (Runtime): 对于屏幕上的每一个像素,我们只需要根据其对应的视角、高度、太阳位置等参数,到预计算好的LUT中进行几次纹理采样和简单的计算,就能得到最终的天空颜色。

  • 实现效果:

    • 动态日夜循环: 由于LUT已经参数化了所有太阳角度,因此可以非常逼真地模拟从日出到日落的完整光照和颜色变化。
    • 大气透视 (Aerial Perspective): 物体越远,颜色越淡,且越接近天空的颜色(通常是蓝灰色)。渲染远处的物体(如山脉)时,不仅要考虑雾效的颜色叠加,还要计算物体与观察者之间的空气柱所产生的散射光。这可以通过查询LUT计算“看到无穷远处的散射光”与“山脉处看向天空的散射光再乘以透射率”之间的差值来实现,从而让远山完美地融入天空的色调中,呈现出自然的蓝调或霞光色。
  • 业界应用: 像《FIFA》、《极品飞车》以及DICE工作室(如《战地》系列)的游戏都使用了这类技术,达到了非常前沿和逼真的大气渲染效果。

5. 经典预计算方法的局限性

尽管效果出色且运行时性能高,但这种经典的预计算方法也存在一些固有问题。

  • 预计算成本高昂:

    • 填充4D LUT需要大量的Ray Marching计算,非常耗时(从几毫秒到数秒不等)。通常只能在关卡加载时执行一次。
  • 缺乏动态实时性:

    • 预计算假设大气的物理属性(如密度、悬浮粒子成分)是静态的。
    • 如果需要实现动态天气变化(例如,从晴天平滑过渡到雾天或雨天),意味着大气参数在实时变化。这就要求每一帧都重新计算或插值LUT,而预计算的耗时使其难以实现平滑的实时过渡。
  • 美术迭代效率低:

    • 当美术师想要调整大气参数(如天空颜色、雾霾浓度)时,无法立即看到效果,必须等待漫长的预计算过程完成,这极大地影响了创作和调试的效率。

大气与云的实时渲染

一、 现代大气散射渲染:一种可扩展的实时方案

在之前的讨论中,我们了解了基于高维查找表(LUT)的预计算大气散射方法。然而,这种方法在应对动态天气变化(如起雾、天气晴转阴)时显得力不从心,因为它需要频繁重新计算整个昂贵的查找表,并且运行时的多次高维插值也极其消耗性能。

为了解决这些问题,业界(尤其以UE引擎为代表)提出了一种更具扩展性和生产力的实时渲染方案。

1. 核心思想:一个巧妙的物理近似

这个新方法的核心在于一个非常大胆的假设,它极大地简化了复杂的物理计算。

  • 关键假设:多重散射的各向同性 (Isotropic Multiple Scattering)

    • 传统方法中,最复杂的部分是计算光线在空气中经历无数次反弹的 多重散射 (Multiple Scattering)
    • 该方法大胆假设:对于空气中的一个点,来自四面八方被散射过来的光是 均匀的、低频的
    • 这个假设意味着,无论入射光如何,经过散射后,出射的光在所有方向上也是均匀的。
  • 简化推论:从路径积分到几何级数衰减

    • 在上述假设下,复杂的多重散射过程可以被简化为一个能量衰减问题
    • 我们只需要计算前一到两次的散射,就可以得出一个能量衰减的百分比
    • 后续的无数次散射,就可以看作是一个不断乘以该百分比的 几何级数 (Geometric Series)
    • 通过简单的高中数学(几何级数求和),我们就能以极低的成本近似计算出无限次散射的总贡献,从而避免了逐次进行复杂的路径积分。

2. 降维打击:简化查找表 (LUT)

传统4D查找表之所以复杂,是因为它试图预计算所有可能的情况(观察者高度、太阳角度、观察视角等)。新方法通过在每一帧固定变量来大幅降低LUT的维度。

  • 消除观察者高度 H 维度:在一帧内,观察者的海拔高度是固定的,无需将其作为一个动态查询维度。
  • 消除太阳角度维度:同样,在一帧内,太阳的位置也是固定的。
  • 最终结果:通过这种“降维打击”,原先复杂的4D或更高维的LUT被简化为一个极其高效的 2D查找表,通常只包含观察方向的 天顶角 (Zenith Angle)方位角 (Azimuth Angle)

此外,光线沿路径的衰减(透射率)则通过一个独立的 3D纹理 来进行路径积分,将不同的问题解耦处理。

3. 优势与局限性

这种方法虽然不是100%物理精确,但完美体现了迪士尼的“艺术可控性优于物理真实”的原则。

  • 优势:

    • 艺术家友好:参数调整实时反馈,能够轻松创造出各种奇特的、非地球的环境,例如拥有两个太阳的外星世界。
    • 完全支持动态效果:无论是天气变化、日夜交替还是雾气浓度变化,都能高效、平滑地实时呈现。
    • 硬件高效:简化的计算和更小的纹理查找,使得该方法在现代硬件上运行得非常快。
  • 局限性:

    • 在某些极端情况下,例如大气浓度极高(如浓雾或沙尘暴)的场景,各向同性的假设会与现实产生较大偏差,可能导致 色偏 (Color Shifting) 等视觉错误。

体渲染方程

体渲染方程(Volume Rendering Equation, VRE)是描述光线在参与介质(Participating Media,如烟雾、云、火焰、半透明液体)中传播时,光亮度(Radiance)如何随距离变化的数学模型。

它是辐射传输方程(Radiative Transfer Equation, RTE) 沿视线的积分形式。

1. 核心物理现象

在真空中,光沿直线传播亮度不变;但在介质中,光子会与粒子发生四种相互作用:

  1. 吸收 (Absorption): 光能转化为热能,亮度衰减。
  2. 外散射 (Out-scattering): 光子被弹射到其他方向,导致当前视线方向亮度衰减。
    • 注:吸收 + 外散射 = 消光 (Extinction)
  3. 自发光 (Emission): 介质本身发光(如火焰),增加亮度。
  4. 内散射 (In-scattering): 来自其他方向的光子被散射到当前视线方向,增加亮度。

2. 积分形式方程

这是最常见的用于实时渲染(如 Ray Marching)的形式。假设光线从点 出发,方向为 ,行进距离 到达观察点。

其中各项含义如下:

A. 透射率 (Transmittance, )

描述光线从 传播到 时,未被吸收或散射掉的剩余比例(即可见度)。根据比尔-朗伯定律(Beer-Lambert Law):

  • 处的消光系数 (Extinction Coefficient)(吸收系数 + 散射系数)。

B. 源项 (Source Term, )

描述在距离 处,单位长度增加的亮度。它包含自发光内散射

  • :介质的自发光(Emission)。
  • :散射系数(Scattering Coefficient)。
  • 相函数 (Phase Function),描述光线从 散射到 的概率分布(例如 Henyey-Greenstein 相函数)。
  • :入射到点 的所有方向的光照(通常是直接光照 + 间接光照)。

3. 离散化(用于 Ray Marching)

在游戏引擎的 Volumetric Fog 或 Cloud pass 中,我们无法计算解析解,通常使用黎曼和 (Riemann Sum) 进行步进近似:

而在实际 Shader 实现中,常用的积累混合模式(Alpha Blending)通常写作:

这其实是对上述积分方程的前向或后向差分近似。

总结

体渲染方程本质上就是在算:(背景光 总透射率)+ 沿途每一小段的(发光+散射入光) 该段到眼睛的透射率

Radiative Transfer Equation (RTE)

辐射传输方程 (Radiative Transfer Equation, RTE) 是描述光在介质中微观行为的核心控制方程。

VRE 是告诉你在屏幕上看到了什么(宏观结果),RTE是在描述光子在介质中每前进一步时到底发生了什么(微观机制)。从物理本质上讲,RTE 实际上是能量守恒定律在参与介质中的微分表达。

1. 标准形式

RTE 是一个积分-微分方程 (Integro-Differential Equation)。 对于空间中某一点 和某一方向 ,亮度 沿该方向的变化率定义为:

2. 逐项物理拆解 (The Terms)

这个方程左边是变化的梯度(Change),右边是导致变化的三个物理原因

A. 第一项:消光 (Loss Term)

这是唯一的负项,表示光能的损失。

  • 物理含义: 光子撞击到介质粒子后,不再沿原方向 传播。
  • (消光系数): 定义为
    • (吸收): 光能转为热能(彻底消失)。
    • (外散射): 光子没消失,但被弹到了别的方向(对于当前方向 来说,它算是“损失”了)。

B. 第二项:自发光 (Emission Term)

这是第一个正项

  • 物理含义: 介质本身就是光源。
  • 应用: 火焰、高温气体、或者霓虹灯管内部的等离子体。在绝大多数普通烟雾/雾气中,此项为 0。

C. 第三项:内散射 (In-scattering Term) —— 最复杂的项

这是第二个正项,也是实时渲染中最大的性能瓶颈。

  • 物理含义: 来自四面八方 () 的光子,撞击到当前位置 的粒子后,刚好被弹射到了我们需要计算的方向 上。
  • (散射系数): 决定了发生散射事件的概率。
  • (相函数 Phase Function): 决定了散射发生后,光子偏转角度的概率分布。
    • 各向同性 (Isotropic): 光向所有方向散射概率相同。
    • Mie 散射: 倾向于向前散射(Forward Scattering),如云和雾。
    • Rayleigh 散射: 对称分布,如大气散射(蓝天)。
  • : 必须把球面上所有入射光 都积分一遍。

3. 为什么 RTE 很难解?

请注意观察方程的结构:

(我们要解的未知量) 同时出现在方程的左边和右边的积分里。

这意味着:

  1. 递归依赖: 为了知道当前方向的亮度 ,你需要计算内散射;但计算内散射需要知道所有其他方向的亮度 ;而其他方向的亮度 又依赖于它们各自的内散射(其中包含 )。
  2. 多重散射 (Multiple Scattering): 这种递归关系在物理上对应着光子在介质中弹射一次、两次、无数次。
    • 如果忽略积分中的 (只取直接光),就是 Single Scattering
    • 如果考虑积分中的 ,就是 Multiple Scattering

总结

RTE 描述了光子在微观尺度上的生 (Source)死 (Sink)

  • 死: 撞到粒子被吸收或弹飞 ()。
  • 生: 粒子自己发光 () 或把别人的光弹过来 ()。

对于游戏引擎开发,我们通常不会直接解 RTE 微分方程,而是将其转化为积分形式 (VRE) 后,用 Ray Marching + 数值积分的方法来求解。


二、 云的渲染技术演进

云是构成真实天空的另一个关键元素。对云的渲染也经历了一个从简单到复杂、从静态到动态的演变过程。

1. 基础知识:云的分类

在图形学中模拟云,首先需要了解其基本物理形态。气象学上,云主要分为三大类,它们在不同高度的组合构成了我们看到的多彩云景:

  • 层云 (Stratus): 形态扁平,如薄纱一般,呈层状分布。
  • 积云 (Cumulus): 形态蓬松,如棉花堆,有明显的垂直发展。
  • 卷云 (Cirrus): 形态纤细,如羽毛或发丝,由冰晶构成。

2. 早期探索:过时的渲染方法

  • 方法一:网格建模 (Mesh Modeling)

    • 思路:由美术师放置基础的几何体(Mesh),再通过噪声函数或腐蚀算法在表面生成云的细节。
    • 评价:这种方法更偏向学术研究,在实际游戏开发中几乎不被使用,因为它过于静态且难以控制。
  • 方法二:公告板/插片 (Billboards/Impostors)

    • 思路:使用大量带有云纹理的 半透明面片 (插片),通过Alpha混合层层叠加,模拟出云的体积感。
    • 评价:这是过去十几年网游和老游戏的常用技术。其效果往往差强人意,看起来像是“巨大的棉花糖”,缺乏真实感和动态性,无法满足现代 3A级游戏 的视觉标准。

3. 现代主流方案:体积云 (Volumetric Clouds)

为了实现电影级的动态云海,现代游戏引擎普遍转向了基于体积的建模和渲染方法。

  • 核心概念:Volumetric Cloud Modeling

    • 这套方法论将云视为一个三维体,使用 体素 (Voxel) 来描述其内部的密度、形状和光照信息。这与之前提到的体积地形(Voxel Terrain)在思想上一脉相承。
  • 优势:

    • 完全动态:云的形态、密度、位置都可以在运行时通过算法(如噪声函数)实时生成和演变。
    • 形态逼真多变:能够模拟出云层复杂的内部结构、边缘细节以及飘动、消散等动态过程。
    • 光照真实:可以与大气散射系统结合,实现逼真的云层光照和阴影效果。
  • 挑战:

    • 算法复杂度高:实现一套完整的体积云系统涉及复杂的噪声生成、光线步进(Ray Marching)和光照计算。
    • 性能开销巨大 (Expensive):体积渲染需要大量的采样和计算,对GPU性能是巨大的考验,是渲染管线中的性能消耗大户。

程序化体积云的生成与渲染

本部分笔记将深入探讨现代3A游戏中用于创建逼真、动态云层的主流技术。该技术完全在运行时(Runtime)生成,核心在于程序化噪声光线步进(Ray Marching) 算法的结合。

一、 现代体积云渲染的核心思想

与传统的基于静态天空盒或简单面片(Impostor)的方法不同,现代体积云技术追求的是完全动态和程序化的解决方案。

  • 核心优势:

    • 全动态性: 云的形态、密度、位置和光照效果都可以实时变化,能够模拟云的飘动、形成与消散。
    • 表现力强: 能够创造出形态各异、层次丰富的云景,从稀疏的薄云到厚重的积雨云。
  • 核心挑战:

    • 算法复杂度高: 涉及多种噪声函数和复杂的渲染算法。
    • 性能开销大 (Expensive): 如果优化不当,会严重影响游戏帧率,是性能消耗大户。

二、 第一步:用噪声函数定义云的形态

云的形态并非由美术师手动建模,而是通过在GPU中程序化生成一个虚拟的3D体积来定义。这个过程的核心是 基础3D纹理 (Base 3D Texture)噪声函数 (Noise Functions)

1. 基础3D纹理 (Base 3D Texture)

这是一个关键的数据结构,用于描述云的基础分布和物理属性。

  • 空间分布: 纹理的坐标 (x, y, z) 对应于天空中的一个位置,定义了云可能存在的基础区域。
  • 云层厚度 (Cloud Thickness): 纹理的通道(例如R通道)存储一个 01 之间的值,代表该位置云的基础密度或厚度。通过调整这个值,可以控制云从薄雾到浓云的过渡。

通过对这个基础纹理进行平移,就可以实现云的飘动效果;对其进行 扰动 (Perturbation),则可以模拟云形态的动态变化。

2. 关键工具:噪声函数 (Noise Functions)

单纯的基础纹理生成的形状(如柱状)非常不自然。为了塑造出逼真的云朵形态,我们需要使用噪声函数对其进行“雕刻”和“腐蚀”。

  • 柏林噪声 (Perlin Noise)

    • 核心观点: 一种经典的梯度噪声,能够生成非常自然、连续、像“棉花絮”一样的柔和纹理。它是程序化生成自然现象(如地形、火焰、云)的基石。
    • 特点: 效果平滑,具有良好的伪随机性和多尺度特性。
  • 沃利噪声 (Worley Noise)

    • 核心观点: 一种基于特征点的元胞噪声,其数学基础是 沃罗诺伊图 (Voronoi Diagram)。它能生成类似“细胞结构”或“泡沫状”的纹理。
    • 特点: 能够创造出独特的絮状、团状或空洞结构,非常适合模拟积云的边缘和内部空洞。
3. 噪声的应用:从基础形状到复杂云体

通过组合不同频率和类型的噪声,我们可以为基础的云体增加丰富的细节,模拟出近似 分形 (Fractal) 的效果。

  1. 宏观塑形 (低频噪声): 使用低频(大尺度)的噪声对基础云体进行“侵蚀”,雕刻出云朵的大致轮廓和团块结构。
  2. 细节添加 (高频噪声): 使用高频(小尺度)的噪声在云的表面和边缘添加更精细的、棉絮般的细节。

这个过程就像用不同型号的刻刀雕刻一块木头,从粗犷的轮廓到精细的纹理。通过调整噪声函数的参数(即所谓的 Magic Number),开发者可以创造出千变万化的云朵形态。

三、 第二步:用光线步进(Ray Marching)渲染云的体积

定义了云的体积数据后,如何将其渲染到屏幕上是下一个关键问题。由于云没有清晰的几何表面,传统的 光栅化 (Rasterization) 方法不再适用。取而代之的是 光线步进 (Ray Marching) 算法。

1. 光线步进 (Ray Marching) 算法流程

核心观点: 从摄像机(眼睛)出发,向屏幕上的每一个像素发射一条光线,让其在场景中“步进”,沿途对云体积进行采样,并累积光照信息,最终计算出该像素的颜色。

其优化后的执行步骤如下:

  1. 光线投射: 从视点向天空方向发射光线。
  2. 快速逼近: 假设云层存在于一个特定的高度区间(例如海拔2km到8km之间)。光线首先以 大步长 (Large Step) (如50米、100米)快速前进,直到首次进入这个高度区间并“命中”云体积(即采样点的噪声值大于某个阈值)。
  3. 精细采样: 一旦光线进入云内部,步长切换为 小步长 (Small Step) (如10米、20米),以更高的精度在云中穿行。
  4. 采样与累积: 在内部的每一步,都对云的3D纹理进行采样,获取当前位置的密度,并计算光照效果,主要是 散射 (Scattering) 和透射。将每一步计算出的光照贡献累积起来。
  5. 结束步进: 当光线穿出云体积,或累积的光线能量已完全衰减时,步进结束。
2. 性能与优化

Ray Marching 的计算量巨大,必须进行大量优化才能实时运行。

  • 简化光照模型: 云内部的光照计算(尤其是多次散射 Multi-Scattering)极其复杂。在实时渲染中,会采用大量简化的近似模型。讲座中强调了一个重要的工程思想: “大胆假设,合理简化”。由于云的不透明度较高,许多复杂的物理现象可以被简化或忽略,从而在可接受的视觉效果和性能之间取得平衡。
  • 低分辨率渲染: 通常,体积云会以较低的分辨率(如1/4或1/8屏幕分辨率)进行渲染,然后通过时间性抗锯齿(TAA)或特定的升采样技术将其与全分辨率场景融合,以节省性能。

四、 效果与总结

通过上述“程序化噪声定义形态 + 光线步进渲染体积”的管线,可以实现令人惊叹的视觉效果。

  • 视觉表现:

    • 能够模拟复杂的云层结构和光影变化。
    • 能够实现太阳光穿透云层形成的 体积光 (God Rays) 和云层边缘的透光散射效果。
    • 能够表现云层从薄到厚、从聚到散的各种动态过渡。
  • 行业地位:

    • 这项技术是近十年来图形学领域的一项革命性变化
    • 如今,它已成为3A级游戏引擎的 标配 (Standard Feature)

最终结论: 我们在游戏中看到的逼真云朵,并非天空中漂浮的真实“面片”或模型,而是存在于 GPU中的一个虚拟3D纹理,我们通过 光线步进 (Ray Marching) 算法,在渲染时将其“解析”并绘制出来的结果。这是一个计算密集型但效果卓越的纯粹的“Shader魔法”。


Q&A

本环节针对学员提出的几个高质量问题进行了深入解答,揭示了许多理论知识在游戏引擎开发实践中的应用与权衡。

Q1: 四叉树管理的通用性

  • 问题: 本节课讲解的用于管理地形的 四叉树 (Quadtree),是否可以和之前课程中提到的 GameObjects 管理共用同一个数据结构?
  • 核心观点: 是,完全可以共用。这正是四叉树这类空间划分数据结构的强大之处。
  • 关键优势:
    • 统一管理框架: 四叉树能够将游戏世界中多种不同类型的资源(如地形、静态模型、动态物件等)纳入到同一个管理和调度框架中。
    • 统一的资源流式调度 (Streaming): 它可以实现从 磁盘 (Disk) 主内存 (Main Memory) 显存 (VRAM) 的统一、按需调度。
    • 解决资源瓶颈: 在开放世界等大规模场景中,任何一种资源都有可能耗尽内存或显存。统一的调度机制能够根据视点和优先级,智能地加载和卸载资源,避免系统崩溃。

Q2: 体积云的性能开销与优化

  • 问题: 体积云 (Volumetric Clouds) 的渲染是否非常耗时?
  • 核心观点: 是的,渲染开销巨大。其性能瓶颈主要在于渲染阶段,而非生成阶段。
  • 开销分解:
    • 生成 (Generation): 相对高效。使用 Noise 方程 在 3D 纹理中生成云的基本形态速度很快。
    • 渲染 (Rendering): 非常昂贵。屏幕上的每个像素都代表一条从摄像机射出的光线,每条光线都需要执行极其复杂的计算:
      1. 光线步进 (Raymarching): 在云的体积内进行多次采样。
      2. 散射计算 (Scattering): 计算光线在云内部的吸收和散射,即使是简化后的物理模型也相当复杂。
      3. 光照整合: 需要综合考虑太阳光、天光(大气散射)等多种光源对云颜色的影响。
  • 性能优化技巧 (Tricks of the Trade):
    • 降采样 (Downsampling): 以较低的分辨率(例如屏幕分辨率的 1/2 尺寸,即 1/4 像素数量)进行体积云的计算。
    • 升采样与模糊 (Upsampling & Blur): 将低分辨率的计算结果通过双线性插值等方式放大回原始分辨率,并应用模糊处理来隐藏块状瑕疵和闪烁。
    • 利用现代升采样技术: 诸如 NVIDIA DLSSAMD FSR 等时间或空间升采样技术,可以更智能、更高质量地重建图像,是优化体积云等高开销渲染的利器。

Q3: 课程难度

  • 核心观点: 渲染 (Rendering) 部分是整个课程中最难的环节
  • 原因:
    • 学科交叉: 现代实时渲染技术是光学物理数学计算机科学的高度结合体。
    • 技术演进: 经过三十多年的发展,许多简单的渲染方法已被淘汰,业界追求的是极致的视觉效果,这必然导致算法的复杂性增加。
  • 后续展望:
    • 后续其他主题(如动画、物理等)的难度预计会略有下降
    • 课程重点将放在讲解复杂算法背后的 核心思想、设计思路和关键瓶颈,帮助学员建立宏观的、体系化的理解,而非陷入无尽的数学细节。

Q4: 大气散射计算的终止条件

  • 问题: 在进行 大气散射 (Atmospheric Scattering) 计算时,多重散射 (Multiple Scattering) 的终止条件是什么?
  • 核心观点: 在游戏引擎的实时渲染实践中,通常不使用物理上精确的能量阈值,而是采用一个 硬性的阶数上限 (Hard Order Limit) 来强制终止计算。
  • 具体实践:
    • 为了在视觉效果和性能之间取得平衡,通常只会计算到 三阶或四阶多重散射 (3rd or 4th order multiple scattering)
    • 超过这个阶数后,对最终画面颜色的贡献会越来越小,但计算成本却会持续增加,因此在实时应用中会被直接截断。这是一种典型的 效果与性能的权衡 (Trade-off)