Files
XCEngine/docs/plan/毕设/初稿/第三章.md

166 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第三章 体积渲染理论基础
第二章已经从渲染引擎角度说明了运行时系统、资源系统、场景组织和编辑器工作流等基础内容。本章进一步转入体积渲染本身的理论部分,重点讨论参与介质、光在介质中的衰减与散射、体绘制方程、光线步进以及稀疏体数据与空域跳过等关键概念。本章的目的不是对体积光传输进行过度抽象的数学展开,而是为后续基于 NanoVDB 的体积渲染模块设计与实现提供足够明确的理论基础。
## 3.1 参与介质与体积渲染基本概念
### 3.1.1 参与介质的定义
传统表面渲染主要关心光线在物体表面处发生的反射与折射而体积渲染关注的对象则是空间中的参与介质Participating Media。所谓参与介质是指光在传播过程中会与其内部粒子持续发生相互作用的介质例如云、雾、烟、火焰、水汽等。这类介质并不像三角形网格那样只在边界上发生光学变化而是会在体积内部对光线产生吸收、散射甚至发射作用因此它们的成像过程天然具有空间累积特征。
从工程角度看,参与介质通常可以分为均匀介质和非均匀介质两类。均匀介质在空间中的光学参数保持不变,便于分析和推导;非均匀介质的密度或光学参数随位置变化,更接近真实云层和烟雾的外观,也是实际应用中更常见的情况。在本文项目后续的体积渲染实现中,体数据本质上就是对这种空间变化进行离散表达,因此理解参与介质的基本性质是后续实现的前提。
### 3.1.2 吸收、散射与透射率
当光线穿过参与介质时,最基本的两类相互作用是吸收和散射。吸收表示光能被介质粒子消耗,导致沿原方向传播的光变弱;散射表示光线方向发生改变,其中一部分光离开原有传播方向,另一部分光可能从其他方向被重新散射到观察方向。通常用吸收系数 $\sigma_a$ 表示吸收强度,用散射系数 $\sigma_s$ 表示散射强度,两者之和称为消光系数或总衰减系数:
$$
\sigma_t = \sigma_a + \sigma_s
$$
在很多实时体积渲染实现中,密度场会被用来调制这些系数,使介质在空间上的不透明度和亮度分布发生变化。也就是说,密度并不是与吸收、散射并列的第三类光学现象,而更像是对局部光学参数的空间缩放。密度越大,局部吸收和散射通常也越强;密度越小,介质对光的影响则越弱。
透射率Transmittance描述的是光线在介质中传播一段距离后仍然保留下来的比例其取值范围在 0 到 1 之间。透射率越接近 1说明光几乎未被削弱透射率越接近 0说明光在介质中已被显著衰减。透射率是体积渲染中极为核心的量因为无论是背景光穿过体积后的结果还是光源传播到采样点的有效光照最终都要依赖透射率来表达。
### 3.1.3 体积颜色形成的基本原因
体积图像的形成并不是单纯“给体素上色”,而是光在介质中传播、衰减与散射共同作用的结果。从观察者方向看,体积颜色主要来自两个来源。其一是背景或后方物体发出的光在穿过介质时被衰减后剩余的部分;其二是来自光源的入射光在介质内部发生散射后,被重新导向观察方向所产生的内散射贡献。当前项目后续实现的重点主要也集中在这两部分,即背景透射与单次散射近似。
如果介质本身还会主动发光,例如火焰、高温气体等,则还需要考虑发射项。不过从本文当前项目的体积模块实现状态来看,重点仍然放在云、烟等主要受吸收和散射影响的体数据渲染上,因此本章后续分析将以吸收、散射和透射率累积为主。
## 3.2 比尔-朗伯定律与光线透射
### 3.2.1 比尔-朗伯定律的物理含义
比尔-朗伯定律Beer-Lambert Law描述了光在介质中传播时的指数衰减规律。对于均匀介质如果光线在介质中传播距离为 $d$,则透射率可写为:
$$
T(d) = e^{-\sigma_t d}
$$
该式说明,光的衰减并不是线性减少,而是随着传播距离和消光系数的增加呈指数下降。直观地说,当介质更“浓”或者光走得更远时,保留下来的光就会更少。这一定律在体积渲染中的意义非常直接,因为它给出了“光穿过介质后还能剩多少”的定量表达。
从算法视角看,比尔-朗伯定律还具有一个非常重要的性质,即分段可乘性。若一段路径可以分解为多个小区间,那么整条路径的透射率可以看成各区间透射率的乘积。这一性质使得连续体积的积分问题能够自然转化为离散光线步进中的逐步累积过程,也正是实时体积渲染中采用逐采样点更新透射率的理论依据。
### 3.2.2 消光系数、吸收系数与散射系数的关系
如前所述,吸收与外散射都会使沿当前方向传播的光减少,因此在透射率计算时通常统一用消光系数 $\sigma_t$ 表示总衰减。对于只考虑吸收的情况,可以仅使用 $\sigma_a$;而在更一般的参与介质渲染中,散射也会让光离开原传播方向,因此应把 $\sigma_s$ 一并纳入:
$$
\sigma_t = \sigma_a + \sigma_s
$$
需要注意的是,$\sigma_s$ 一方面参与透射率计算,因为散射会让光束损失能量;另一方面又会出现在内散射项中,因为正是散射作用把来自光源方向的入射光重新导向观察方向。也就是说,散射在体积渲染中同时承担“损失原方向能量”和“为观察方向提供贡献”两种角色,这也是体积渲染比单纯透明衰减更复杂的根本原因。
在实际工程实现中,常见做法是根据体数据提供的密度值对吸收系数和散射系数进行调制。例如,当某个采样点密度更大时,局部消光系数也更大,透射率会下降得更快;反之则衰减更弱。这种做法既便于统一控制体积外观,也便于将理论模型映射到离散体数据采样结果上。
### 3.2.3 透射率在体积渲染中的作用
透射率在体积渲染中至少有三类直接用途。第一,它用于描述背景光或后方物体颜色经过体积后的剩余比例,因此决定了体积区域是否会“遮住”背景。第二,它用于描述光源从入射方向传播到某个采样点时的衰减程度,因此直接影响采样点处的有效光照强度。第三,它用于前向积分时对已累积颜色进行权重控制,使离观察者更远处的散射贡献自动受到前方介质的遮挡。
对于均匀介质,透射率可以直接由闭式公式给出;但对于非均匀介质,消光系数会随空间变化,此时应采用积分形式:
$$
T(d)=\exp \left(-\int_0^d \sigma_t(x_t)\,dt \right)
$$
式中的积分项通常也记作光学厚度 $\tau$。这一定义说明非均匀体积的透射率本质上依赖整条路径上的密度分布而不是只依赖起点与终点之间的几何距离。后续第7章中光线步进和光照阴影估计实际上都是在用离散采样的方式近似这个积分。
## 3.3 体绘制方程与单次散射近似
### 3.3.1 体绘制方程的基本形式
为了完整描述光在参与介质中的传播理论上应从辐射传输方程出发。但对于工程实现而言更常使用的是其沿路径积分后的体绘制方程Volume Rendering EquationVRE。在忽略发射项并只关注背景透射和散射贡献的情况下体绘制方程可写成较常见的形式
$$
L_o = T(0,D)L_b + \int_0^D T(0,t)\,\sigma_s(x_t)\,L_i(x_t,\omega_i)\,p(\omega_i,\omega_o)\,dt
$$
其中,$L_o$ 表示观察方向上的出射辐射亮度,$L_b$ 表示体积后方背景或其他物体的辐射亮度,$T(0,D)$ 表示整段路径上的透射率,$L_i(x_t,\omega_i)$ 表示采样点从光源方向接收到的入射辐射亮度,$p(\omega_i,\omega_o)$ 为相位函数。这个方程的物理意义比较直观:最终看到的体积颜色,一部分来自背景光穿透体积后的结果,另一部分来自介质内部各个采样点把入射光散射到观察方向后的累积结果。
从工程实现角度看,这个积分方程往往没有简单解析解,特别是在介质非均匀、光源复杂或需要考虑阴影时更是如此,因此通常需要采用数值积分方法近似求解。也正因为如此,光线步进会成为体积渲染中最常见、也最直观的实现方式。
### 3.3.2 单次散射的含义
单次散射Single Scattering是指只考虑“光从光源出发经过一次介质散射后进入观察方向”的情况而忽略光在介质内部发生两次及以上散射的贡献。换句话说光子在进入观察者之前只被介质重新定向一次。这样做会忽略云层内部那种复杂的多次反弹与能量交换但可以显著降低计算复杂度。
在实时体积渲染中,单次散射近似非常常见。一方面,它能够较好描述烟雾、薄雾和部分低反照率介质的主要视觉特征;另一方面,它能够与光线步进、阴影步进和体数据采样自然结合,便于在图形管线或计算着色器中实现。当前项目后续的体积原型同样采用这一思路,因此本章理论分析将主要围绕单次散射展开。
### 3.3.3 相位函数的作用
即使在单次散射模型下也还需要回答一个问题某个采样点接收到的入射光中究竟有多少会被散射到观察方向这个角度分布由相位函数Phase Function描述。相位函数本质上是一个在方向球面上归一化的函数用于刻画散射光在不同方向上的分布情况。它通常依赖入射方向与观察方向之间的夹角 $\theta$。
最简单的相位函数是各向同性相位函数,它认为光会以相同概率向所有方向散射,此时:
$$
p(\theta)=\frac{1}{4\pi}
$$
但很多真实介质并不满足各向同性分布,云和雾等介质更常表现出明显的前向散射特征。工程中常用的近似模型是亨耶-格林斯坦Henyey-GreensteinHG相位函数
$$
p_{HG}(\cos\theta)=\frac{1-g^2}{4\pi(1+g^2-2g\cos\theta)^{3/2}}
$$
其中 $g \in [-1,1]$ 为非对称因子。当 $g=0$ 时退化为各向同性散射;当 $g>0$ 时表现为前向散射;当 $g<0$ 时表现为后向散射。后续第7章的工程实现虽然会采用简化形式但相位函数这一思想仍然是构成单次散射项的理论基础。
### 3.3.4 为什么在实时系统中常采用简化模型
如果严格考虑多次散射、复杂相位函数、多个动态光源和高精度体数据积分,那么体积渲染的计算量会迅速增大,很难满足实时应用对帧率的要求。因此,实时系统通常会在若干环节上做简化:例如只考虑单次散射、采用固定步长或分层步进、使用简化相位函数、对阴影进行近似积分,或者在必要时对空区域进行跳过。
这种简化并不意味着理论被放弃,而是意味着在已知完整物理模型的前提下,有选择地保留最影响画面结果的部分。对于工程设计类项目而言,这种从完整理论到可实时实现之间的取舍非常重要。后续章节的体积模块实现也正是在这一原则下,选择了适合当前项目阶段的实时方案。
## 3.4 光线步进算法原理
### 3.4.1 Ray Marching 的基本思想
由于体绘制方程通常难以直接求得解析解实际实现中往往采用数值积分近似而光线步进Ray Marching就是其中最典型的方法。其基本思想是先求出相机光线与体积包围区域的进入点和离开点再把这段区间划分为若干个小步长在每个采样点处估计局部密度、局部消光、局部散射贡献与透射率最后将这些局部结果累积起来近似连续积分。
若步长足够小,则每个小区间都可以视作局部均匀,这样就能用离散求和逼近连续积分。也正因为这一思想简单直接,光线步进非常适合作为体积渲染的工程实现起点。本文后续的 NanoVDB 体积原型同样以相机光线与体积边界求交为起点,然后在体积内部做离散采样累积。
### 3.4.2 正向步进与反向步进
按观察方向组织时光线步进通常可以分为正向步进和反向步进两种。正向步进一般指从靠近相机的一侧向远处推进也可理解为前向累积front-to-back反向步进则从远处向近处积分也可理解为后向累积back-to-front
从数值结果上看,两者都可以逼近同一个积分目标,但在工程实现上,正向步进往往更适合实时系统。原因在于,正向步进可以显式维护“当前剩余透射率”,当透射率已经很低时,后续更远处的采样贡献可以近似忽略,从而支持提前终止优化。反向步进虽然在某些推导上较直观,但不如正向步进方便做前端遮挡裁剪。当前项目后续的体积实现也更接近前向累积方式。
### 3.4.3 步长、最大步数与误差控制
步长是光线步进中的关键参数。步长越小,离散积分越接近连续积分,画面通常越平滑,细节也越稳定;但采样次数随之增加,运行开销也会显著变大。步长过大时,则容易出现条带感、细节丢失和阴影估计不稳定等问题。与步长相对应的另一个参数是最大步数,它决定了一条光线最多允许采样多少次,用于限制最坏情况下的开销。
因此,实时体积渲染本质上是在“精度”和“性能”之间寻找平衡。工程上通常会根据体素分辨率、包围盒大小、屏幕分辨率以及目标帧率来选择合适步长,并在必要时为主光线和阴影光线设置不同的采样密度。这种参数平衡在后续测试章节中也会体现出来。
### 3.4.4 提前终止、抖动等常见优化
在体积积分过程中并不是每一个采样点都同样重要因此常会配合若干优化策略。最常见的一类是提前终止Early Termination当累计透射率已经低于某个阈值时说明后续更远区域的贡献非常有限此时可以直接结束当前光线步进。对于较浓的烟雾或高密度区域这类优化能节省大量无效计算。
另一类常见方法是采样抖动Jitter。固定步长加固定采样起点容易带来规则性的条纹或分层感而在初始采样位置上引入轻微随机偏移可以打散这种结构化误差使图像在视觉上更平滑。除此之外还可以通过多分辨率步进、阴影光线使用更粗步长、分层包围盒裁剪等方式进一步优化。对于本文后续实现而言提前终止和空域跳过具有更直接的工程意义。
## 3.5 稀疏体数据与空域跳过思想
### 3.5.1 稠密体素网格的问题
如果直接使用规则三维网格存储体数据,那么无论某个区域是否真正包含有效密度值,都需要为其分配存储空间。当分辨率升高到 $N \times N \times N$ 时,存储量会按立方增长,显著增加显存和内存压力。更重要的是,若光线步进始终以固定步长穿过整个包围区域,那么大量采样都会落在密度接近 0 的空区域中,带来明显的计算浪费。
对于云、烟等典型体数据而言,真正有意义的部分往往只占整个包围空间的一小部分。也就是说,体数据天然具有“空间稀疏”的特点。如果仍然用稠密网格表示,就会在存储与计算两个层面同时承受不必要的代价。
### 3.5.2 稀疏体数据结构的意义
稀疏体数据结构的核心思想是只为真正包含有效信息的区域分配更细粒度的存储而对大量空区域或均匀区域采用更粗层级的表示。OpenVDB 及其面向 GPU 的 NanoVDB 就属于这一类思路。它们通过层级化节点结构组织体数据,使得体素值访问不再局限于简单的三维数组索引,而是能够根据当前区域是否活跃、当前层级分辨率以及节点类型进行更有选择性的访问。
对于实时渲染而言NanoVDB 的价值尤其明显。它在保留 VDB 层级稀疏表达思想的基础上,对数据进行了线性化组织,使 GPU 更容易访问。这样一来,体数据不但可以以更紧凑的形式存储在显存中,还能够在 Shader 中配合层级遍历或辅助访问器实现更高效的采样与判断。后续第7章的工程实现正是建立在这种 GPU 友好的稀疏表示之上。
### 3.5.3 空区域跳过对实时性的作用
空域跳过Empty Space Skipping的目标是尽量避免在无效区域上进行逐步长采样。其基本思想不是改变体绘制方程而是在数值积分过程中快速定位“哪些区域值得细采样哪些区域可以直接跳过”。如果能在光线进入空区域时一次跨过较长距离而不是继续做多个低价值采样那么体积渲染的实时性就会明显提升。
实现空域跳过的方式有很多,例如基于宏体素的占用标记、基于层级结构的活跃区判断,或者基于 DDA/HDDA 的层级步进方法。其共同点在于:当当前区域内不存在活跃密度,或者当前层级可以确认该区域为空时,光线可以直接前进到下一个可能有数据的位置。在 NanoVDB 场景下,这种思想与其层级节点结构天然契合,因此非常适合作为实时体积渲染的优化手段。
从后续工程实现角度看空域跳过并不是“附加优化”而是让稀疏体数据在实时环境中真正发挥价值的关键环节。只有把稀疏存储与跳空采样结合起来NanoVDB 的结构优势才能转化为实际帧时间上的收益。
## 3.6 本章小结
本章围绕体积渲染实现所需的核心理论进行了梳理,说明了参与介质的基本概念,以及吸收、散射、消光系数和透射率之间的关系;介绍了比尔-朗伯定律在均匀与非均匀介质中的表达形式,并说明了透射率在背景衰减、光照传播和颜色累积中的作用;进一步给出了体绘制方程的基本形式,解释了单次散射近似和相位函数的物理意义;最后分析了光线步进的数值积分思想,以及稀疏体数据与空域跳过对实时性的作用。
这些理论内容与后续工程实现之间是一一对应的:透射率累积将对应光线步进中的前向积分,单次散射与相位函数将对应体积光照近似,稀疏体数据与跳空思想将对应 NanoVDB 的 GPU 访问和 HDDA 优化。基于这些理论基础,下一章将开始转入渲染引擎总体架构设计。