# 三 体积渲染方程 ## 从辐射传输方程到体绘制方程 在本章中,我们将学习支配体渲染的相关方程。 *** ## 光如何与参与介质相互作用并在体积中传播? 大多数书籍和论文在渲染(尤其是参与介质渲染)中采用相同的约定。因此,熟悉这些约定是很有必要的。体积通常被表示为细小的**微分圆柱体**。**观察者从圆柱体的一端俯视,我们从另一端用准直光束照射**,如下图所示。我们需要求解的是光束穿过体积后到达观察者眼睛的光强(即有多少光到达观察者)。 ![](images/2026/02/08/20260208_131632_832.webp) 这个光度量的专业术语是**辐射亮度(radiance)**,我们用字母 $L$ 表示。$L_i$ 是入射辐射亮度:照射到圆柱体上的光束强度。$L_o$ 是出射辐射亮度:从圆柱体另一端射出的光强。观察方向用 $\omega$(希腊字母欧米伽)表示。在代码中,这就是相机光线方向。 这是我们的基本设置。穿过圆柱体的窄(准直)光束与介质的相互作用有四种方式(假设这个微小圆柱体并非空的,而是充满了某些粒子): * **吸收(Absorption)**:部分光被吸收,光束强度降低,辐射亮度减小。 * **外散射(Out-scattering)**:组成窄光束的光子沿 $-\omega$ 方向传播(直接朝向眼睛),但在传播过程中可能被散射到其他(随机)方向。外散射的光子不再属于原光束,因此光束强度也会降低,辐射亮度减小。 * **内散射(In-scattering)**:散射也可能使照射到体积上的部分光被重新定向到原光束的传播路径上,光束强度因此增加,辐射亮度增大。 * **发射(Emission)**:气体在达到特定温度时会发光。此时电子获得能量,并以光子的形式释放。这些光子的传播方向是随机的,但最终会有部分光子沿原光束的路径传播。因此,发射会使光束强度增加。 需要注意的是,外散射和吸收都会导致光能损失,而内散射和发射会使沿 $\omega$ 方向朝向眼睛传播的窄准直光束强度增加。此外,**内散射和外散射属于同一现象:光子与构成介质的粒子 "碰撞"**。 ![](images/2026/02/08/20260208_131632_918.webp) 沿路径 $ds$ 的辐射亮度变化 $dL$ 等于沿方向 $\omega$ 上点 $x$ 处的入射辐射亮度 $L_i$ 与出射辐射亮度 $L_o$ 之差。这个辐射亮度变化也等于吸收、散射(内散射和外散射)和发射的净效应之和: $$ dL(x,\omega) = \text{emission} + \text{in-scattering} - \text{out-scattering} - \text{absorption} $$ 这并非一个 "严格意义上" 的方程,但在本章后续内容中,我们将看到它如何最终推导为辐射传输方程(RTE)和体绘制方程(VRE)。在此之前,我们需要先了解吸收系数、散射系数、比尔定律(Beer's Law)和相位函数。 *** ## 吸收系数、散射系数与消光 / 衰减系数 吸收系数(及散射系数)最好结合其应用的方程来介绍,但将方程与系数定义混合讲解可能会造成混淆,因此我们现在单独介绍这些系数。 ### 吸收系数 吸收系数(或吸收截面)$\sigma_a$ 表示光在介质中每传播单位距离被吸收的概率密度密度。吸收系数的单位是倒数距离(即 mm⁻¹、cm⁻¹ 或 m⁻¹)。 光被吸收的比例与入射光强无关。进入体积的光子数与射出体积的光子数之比(平均值)不随入射光子数变化。入射辐射亮度与吸收效应无关。总结一句话就是无记忆性。 此外吸收量与 $\sigma_a$、$ds$ 之间都存在线性关系:无论是将吸收系数加倍,还是将光在介质中的传播距离加倍,吸收量都会同等增加。 > **详细信息** > > 由于吸收系数的单位是倒数距离,因此其倒数为距离,称为平均自由程(mean free path)。平均自由程可理解为光子在与介质发生相互作用(散射或吸收)前,在体积中传播的平均距离。平均自由程是模拟多重散射的关键(这一主题我们将在单独的课程中讲解)。 > > $$ > \text{mean free path} = \frac{1}{\sigma} > $$ > > 请注意,平均自由程与吸收系数之间也存在线性关系:若吸收系数加倍,光子在与介质发生相互作用前的传播距离将减半。 ### 散射系数 散射系数 $\sigma_s$ 与吸收系数类似,但表示光子在介质中每传播单位距离被散射的概率密度。内散射和外散射对辐射亮度变化的影响不同,因此我们对它们进行区分:内散射会为沿 $\omega$ 方向朝向眼睛传播的光束增加光强,外散射则会导致光束在朝向眼睛传播时能量损失。然而,两者都属于同一散射现象。光子被内散射或外散射的概率相同,均由单一系数定义:散射系数 $\sigma_s$(希腊字母西格玛)。 > **详细信息** > > 在部分文献中,散射系数和吸收系数用希腊字母 "缪"($\mu$)表示,这在物理学及研究中子等粒子在物质中运动的领域是常见约定。而在计算机图形学中,西格玛($\sigma$)已成为普遍采用的约定。 ### 消光系数 如前所述,从外散射和吸收对辐射亮度变化的影响来看,两者是不可区分的 —— 它们都会导致沿 $-\omega$ 方向传播的光束辐射亮度降低。观察者所能感知到的只是光强的减弱:无论这种能量衰减是由光子吸收还是散射 / 反射引起的,都不会改变观察者的体验和观测结果。 > **详细信息** > > 当然,您可以通过设置探测器测量沿非 $-\omega$ 方向射出的光子,来区分吸收和外散射各自的贡献。这一点在后续介绍相位函数时会变得有用且有意义。 因此,在计算光穿过介质时的辐射亮度损失时,我们会将散射系数和吸收系数合并为一个系数,称为消光系数(extinction coefficient)或衰减系数(attenuation coefficient),其表达式为: $$ \sigma_t = \sigma_a + \sigma_s $$ 下标 $t$ 代表总衰减(total attenuation),也可写作 $\sigma_e$。 *** ## 比尔 - 朗伯定律的推导 我们在本课程的第一章就介绍了**比尔 - 朗伯定律(Beer-Lambert Law)**。之所以从该定律开始,是因为当您仅需计算光线透射率(而非辐射亮度)时,只需用到它。透射率与光穿过特定体积物体的比例有关,也可表述为 "物体的不透明度";而辐射亮度则定义了体积物体的亮度。我们将在后续更正式地介绍透射率的概念。 为了了解比尔 - 朗伯定律的来源,我们首先分析从点 $x$ 出发、沿方向 $\omega$ 传播距离 $s$ 的光束的辐射亮度导数。严格来说,方向应为 $-\omega$,但为简洁起见,我们使用 $\omega$(默认 $\omega$ 是观察者的观察方向,光束沿相反方向传播)。 辐射亮度的导数可表示为: $$ dL = -\sigma_a L(x, \omega) ds $$ 这个很好理解,首先由于吸收作用是能量损失,因此有 $-$ 号。而又由于 $\sigma_a$ 是 $\omega$ 这条路径上单个粒子被吸收的概率密度,因此需要乘以 $ds$ 才是单个粒子被吸收的概率。最后再乘以在 $ds$ 这个微元上的辐射度本身 $L(x, \omega)$,这也就是辐射亮度的导数。 前文提到,外散射和吸收都会导致辐射亮度损失,但为简化起见,我们首先假设辐射亮度损失仅由吸收引起。后续我们会扩展并推广这一推理(引入散射项)。 该方程表示了在点 $x$ 沿方向 $\omega$ 传播时,辐射亮度因吸收而损失的速率。您可以将其类比为河床的坡度:沿河流的每个点,地面的坡度可能不同 —— 坡度反映了海拔降低的速度。同样,导数 $dL$ 反映了光在传播过程中辐射亮度降低的速度。 ![](images/2026/02/08/20260208_131633_189.webp) > 这个方程告诉我们什么?它表明,辐射亮度沿光线路径的衰减速率与该点的辐射亮度本身成正比。比例常数 $\sigma_a(x)$ 是吸收系数,其值可能随位置变化。这意味着光的吸收速度取决于介质的局部特性。换一种直观的理解方式:想象一条流向大海的河流,任意点的地形坡度决定了该点水流海拔下降的速度。同样,吸收系数就像是辐射亮度的 "坡度"—— 某点的吸收系数越高,该位置的辐射亮度衰减就越快。正如坡度会沿河流路径变化一样,吸收系数也会沿光线路径变化,进而逐步影响辐射亮度的衰减。 有一个重要细节需要说明,以帮助理解背后的原理:$L(x,\omega)$ 表示沿方向向量 $\omega$ 上点 $x$ 处的光束辐射亮度。正因为如此,人们往往忽略 $L(x,\omega)$ 是一个函数这一事实 —— 这个函数正是比尔 - 朗伯定律本身,也是我们要求解的目标。如果绘制这个函数的图像,会发现随着光在介质中传播距离的增加($x$ 离光束进入介质的点越来越远),函数值逐渐减小。下图展示了该函数(对于给定的吸收系数)随 $x$ 的变化曲线,蓝色线条代表某一特定位置处函数 $L(x,\omega)$ 的变化率(即斜率): ![](images/2026/02/08/20260208_131633_700.webp) 我们的目标是利用方程 $dL=-\sigma_a L(x,\omega)ds$ 求解 $L(x,\omega)$。为解决这个问题,我们首先将方程改写为关于 $s$(而非 $x$)的函数,其中 $s$ 表示光束从入射点 $x$ 开始在介质中传播的距离。通过将 $x$ 替换为 $x_s = x + s \omega$,方程转化为关于 $s$ 的函数: $$ \frac{dL(s)}{ds} = -\sigma_a L(s) $$ 这里的导数(方程左侧)采用莱布尼茨符号表示。分母中的项至关重要:左侧应读作 "函数 $L(s)$ 对 $s$ 的导数",通俗地说,就是 "随着 $s$ 的变化,$L(s)$ 的变化速率是多少"。 求解 $L(s)$,即是解一个常微分方程(ordinary differential equations, ODE),此处为一阶常微分方程(因为仅涉及一阶导数)。 $$ \begin{array}{l} \frac{dL(s)}{ds} &=& -\sigma_a L(s)\\ \frac{dL(s)}{L(s)} &=& -\sigma_a ds\\ \frac{1}{L(s)} dL(s) &=& -\sigma_a ds\\ \int \frac{1}{L(s)} dL(s) &=& \int -\sigma_a ds\\ \int \frac{1}{L(s)} dL(s) &=& -\sigma_a \int ds\\ \ln(L(s)) &=& -\sigma_a s + C\\ e^{\ln(L(s))} &=& e^{-\sigma_a s}\\ L(s) &=& e^{-\sigma_a s} \end{array} $$ > 我们在计算中省略了常数 $C$,但下方体绘制方程(VRE)的完整推导会给出更全面的解。一阶齐次线性微分方程 $y'=-p(x)y$ 的通解为: > > $$ > \begin{array}{l} > \int \frac{1}{y} y' &=& \int -p(x) dx \\ > \ln|y|&=& P(x) + C\\ > |y|&=& e^{P(x)} e^{C}\\ > |y|&=& \pm e^C e^{P(x)}\\ > |y|&=& A e^{P(x)} > \end{array} > $$ > > 其中 $P(x)$ 是 $p(x)$ 的原函数。 这个结果就是比尔 - 朗伯定律的方程。该方程适用于均匀介质;对于非均匀介质,请参见下方的完整透射率方程。希望您能看出:$dL(s)$ 对应 $dy$,$ds$ 对应 $dx$,$L(s)$ 对应 $y$,$-\sigma_a$ 对应 $c$。如前所述,我们目前仅考虑了吸收的影响,但可以通过将 $\sigma_a$ 替换为消光系数 $\sigma_t$,将外散射引起的衰减纳入比尔定律: $$ L(s)=e^{-(\sigma_a+\sigma_s)s} $$ $$ L(s)=e^{-\sigma_ts} $$ 其中 $\sigma_t=\sigma_a+\sigma_s$。 下图展示了消光系数对体积不透明度的影响,以及随着系数值的增加,光被吸收的程度如何变化: ![](images/2026/02/08/20260208_131633_968.webp) *** ## 比尔定律、透射率与光学厚度 这引出了透射率(transmittance)的概念。透射率可理解为体积不透明度的度量,或者说,是光能够穿过体积的比例。更正式地说,透射率是穿过体积的光的比例: $$ T = \frac{L_o}{L_i} $$ 其中,如前所述,$L_i$ 是入射辐射亮度,$L_o$ 是出射辐射亮度。透射率也可以表示光在体积中两点之间传播的透过比例,可通过比尔定律计算: $$ T = e^{-\sigma_t s} $$ 其中 $s$ 是体积中两点之间的距离。您也经常会看到该方程写作: $$ T = e^{-\tau} $$ 其中 $\tau$(希腊字母陶)称为光学深度(optical depth)或光学厚度(optical thickness)。透射率有两种类型:仅考虑吸收的透射率称为内部透射率(internal transmittance),而考虑吸收、外散射等所有衰减因素的透射率称为总透射率(total transmittance)。对于消光系数(目前暂不考虑密度的概念)随空间变化的非均匀体积,我们需要沿光线对消光系数进行积分,表达式为: $$ \tau = \int_{s=0}^d \sigma_t(x_s)ds $$ 其中 $d$ 是光线穿过体积的距离。比尔定律的最终通用形式为: $$ T(d) = \exp \left(-\int_{s=0}^d \sigma_t(x_s) ds \right) $$ 如果您阅读了前面的章节,可能会记得在《3D 密度场的体绘制》(Volume Rendering of a 3D Density Field)一章中提到过"陶(tau)"这个术语——在该章节中,我们用它来累积光线穿过非均匀介质时的消光系数值。 *** ## 内散射与相位函数 最后,要构建一个描述光能在介质中传播的全局方程,我们还需要相位函数(phase function)这一关键部分。我们在《光线步进:精准实现!》(Ray Marching: Getting it Right!)一章中已经介绍过相位函数的概念。 ![](images/2026/02/08/20260208_131634_246.webp) *图 1:只有部分入射光会被散射到眼睛方向,具体比例取决于光线方向与观察方向之间的夹角。* 当光束中的光子与构成介质的粒子相互作用时,可能会被散射而非吸收,且散射方向是随机的——我们知道光子的入射方向,但无法预测其散射方向。当准直光束中的光子被散射时,光束会损失能量;然而,如果有其他光源从 $-\omega$ 方向照射圆柱体,那么该光源发出的、穿过圆柱体的部分光子可能会被散射到 $-\omega$ 方向,从而使沿 $-\omega$ 方向传播的光束能量增加——这就是内散射。问题在于,要知道内散射为光束增加了多少能量,我们需要确定:从某个倾斜角度穿过圆柱体的光束,有多少能量会被散射到 $-\omega$ 方向。这个比例由相位函数给出。 相位函数描述了沿方向 $\omega'$(注意此处的撇号)传播的入射光中,被散射到 $-\omega$ 方向的比例。请记住,这是一个三维过程,因此光会在整个方向球面上散射。散射光的分布当然取决于介质的特性,以及光线方向向量 $\omega'$ 与观察方向向量 $\omega$ 之间的夹角 $\theta$(希腊字母西塔)(这是文献中采用的约定)。 > **请注意** > > 相位函数的符号约定需要特别谨慎:$\omega$ 向量从点 $x$ 指向眼睛,$\omega'$ 向量从点 $x$ 指向光源(如图 1 所示)。经验法则:计算两个向量之间的夹角 $\theta$ 时,我们始终假设 $\omega$ 指向眼睛,$\omega'$ 指向光源。在代码中,当您使用光线方向向量和相机方向向量(可能与预期约定方向相反)计算夹角 $\theta$ 时,需要格外注意这一点。总而言之,相位函数描述了介质内任意点 $x$ 处光散射的角分布。 相位函数(记为 $f_p(x, \omega, \omega')$)具有以下特性: * **互易性(Reciprocal)**:$f_p(x, \omega', \omega) = f_p(x, \omega, \omega')$。交换两个向量,结果保持不变。因此,相位函数通常简记为 $f_p(x, \theta)$,其中 $\theta$ 是两个向量之间的夹角。 * **归一化到 1**:在方向球面(通常记为 $\mathbb{S}^2$)上的积分值为 1,否则会在散射事件中增加或减少辐射亮度: $$ \int_{\mathbb{S}^2} f_p(x, \omega, \omega') d\theta = 1 $$ 参与介质的散射行为分为两种类型: * **各向同性(Isotropic)**:方向球面上的所有方向被选择的概率相同。 * **各向异性(Anisotropic)**:方向球面上的某些方向被选择的概率更高,优先向后方或前方散射,如下图所示。例如,云表现出强烈的前向散射特性。 ![](images/2026/02/08/20260208_131634_531.webp) 各向同性介质的相位函数为: $$ f_p(x, \omega, \omega') = \frac{1}{4 \pi} $$ 在《光线步进:精准实现!》(Ray Marching: Getting it Right!)一章中,我们已经介绍过亨耶-格林斯坦(Henyey-Greenstein,HG)相位函数——这是最常用的各向异性相位函数之一。该函数仅依赖于夹角 $\theta$,定义为: $$ f_p(x, \theta) = \frac{1}{4 \pi} \frac{1 - g^2}{(1 + g^2 - 2g \cos \theta)^{\frac{3}{2}}} $$ > 关于该方程的归一化证明,请参见《光线步进:精准实现!》(Ray Marching: Getting it Right!)。 该函数最初用于模拟星系间尘埃的光散射(Henyey, L.C., and J.L. Greenstein. 1941. Diffuse radiation in the galaxy. Astrophysical Journal 93, 70-83),但由于其简洁性,也被广泛应用于模拟其他多种散射介质。在实际生产中,尽管该函数简单,但通常已足够(此外,模拟多重散射时需要对相位函数进行逆变换,而该方程的逆变换易于实现)。 其中 $-1 < g < 1$,$g$ 称为非对称参数(asymmetry parameter): * 当 $g < 0$ 时,光优先向后散射(后向散射); * 当 $g = 0$ 时,介质为各向同性(光向所有方向均匀散射); * 当 $g > 0$ 时,光优先向前散射(前向散射)。 上图展示了 $g = -0.2$ 和 $g = 0.2$ 时的示例:$g$ 的绝对值越大,光越倾向于向光源后方或相机/眼睛前方散射。云表现出强烈的前向散射效应,$g \approx 0.8$(J. E. Hansen. 1969. Exact and Approximate Solutions for Multiple Scattering by Cloudy and Hazy Planetary Atmospheres),这导致云在逆光时边缘会出现光晕效应。 ![](images/2026/02/08/20260208_131634_792.webp) 还可以使用其他相位函数,如施利克(Schlick)、米氏(Mie)或瑞利(Rayleigh)相位函数。请关注我们后续关于参与介质多重散射的课程,以了解更多这些模型的相关知识。 *** ## 辐射传输方程与体绘制方程 现在,我们已经掌握了构建最终方程所需的所有要素。第一个方程是辐射传输方程(Radiative Transfer Equation, RTE)。该方程的现代形式由苏布拉马尼扬·钱德拉塞卡(Subrahmanyan Chandrasekhar)在 1950 年出版的《辐射传输》(Radiative Transfer)一书中定义,该书此后成为该领域的标志性著作(至少是该主题的权威参考资料)。我们不会在此花费过多时间深入探讨,因为这本书确实……怎么说呢,相当复杂,您可以通过下面的快速浏览略知一二。 ![](images/2026/02/08/20260208_131635_282.webp) 我们可能会在本课程的后续修订版或单独的课程中深入讲解辐射传输方程(如果您感兴趣,可以查阅有限元方法、辐射度方法,以及论文《Modeling the Interaction of Light Between Diffuse Surfaces》和/或书籍《Radiosity and Realistic Image Synthesis》- Cohen, 1993)。目前,我们仅引用书中的一小段内容,认为它很好地总结(或介绍)了我们迄今为止所学的所有知识: > "在本章中,我们将定义辐射传输领域涉及的基本物理量,并推导支配辐射在吸收、发射和散射介质中传播的基本方程——传输方程。" 好了,言归正传。辐射传输方程考虑了我们之前列出的、导致能量沿方向 $\omega$ 传播时辐射亮度变化的所有因素:吸收、内散射、外散射,以及发射(本课程中我们将忽略发射项)。请记住,该方程描述的是辐射亮度(导数)沿方向 $\omega$ 的变化: $$ \frac{L(x+s \omega, \omega)}{ds} = \color{blue}{-\sigma_t L(x,\omega)} + \color{orange}{\sigma_s \int_{\mathbb{S}^2} f_p(x, \omega, \omega')L(x,\omega')d\omega'} $$ 标量 $s$ 表示沿方向 $\omega$ 的位置变化,这在本章开头已介绍过。 蓝色项代表吸收和外散射导致的损失;橙色项是内散射项,有时也称为源项(source term)。注意积分前的 $\sigma_s$ 项——这在概念上与我们之前介绍的、由吸收和外散射引起的能量损失方程类似: $$ \begin{array}{l} dL &= -\sigma_a L(x, \omega) \\ dL &= -\sigma_s L(x, \omega) \end{array} $$ 内散射的强度与光被散射的概率成正比,该概率由散射系数 $\sigma_s$ 给出。内散射项的其余部分已在前面描述过:对方向球面 $\mathbb{S}^2$ 的积分意味着,内散射项需要考虑来自所有方向($\omega'$)的光,并通过相位函数 $f_p(x, \omega, \omega')$ 进行加权。 为简洁起见,我们令: $$ L_s(x, \omega) = \int_{\mathbb{S}^2} f_p(x, \omega, \omega')L(x, \omega')d\omega' $$ 我们已经多次强调,辐射传输方程是一个积分-微分方程(integro-differential equation):它表达的是方向导数——点 $x$ 处沿方向 $\omega$ 的辐射亮度 $L$ 的导数。在文献中,您也可能看到该方程写作以下形式: $$ (\omega \cdot \nabla_x)L(x, \omega) = \color{blue}{-\sigma_t L(x, \omega)} + \color{orange}{\sigma_s L_s(x, \omega)} $$ 其中 $\nabla_x$(数学中称为德尔塔或纳布拉符号)可理解为函数的梯度(即导数的多维概念)。我们添加下标 $x$ 以表示对 3D 空间中点 $x$ 的三个坐标求梯度;若省略下标,$\nabla$ 也可能表示对方向 $\omega$ 的变化求梯度。当您沿方向 $\omega$ 移动时,辐射亮度会局部变化(增加或减少),其变化率与吸收项和散射项成正比。与我们接下来要介绍的体绘制方程不同,这个积分-微分方程告诉我们:当沿光传播方向"迈出一步"时,辐射亮度的变化速率是多少。 然而,这个微分方程对我们来说并不是特别实用——作为计算机图形学开发者,我们需要的是测量体积物体边界处的辐射亮度:该辐射亮度是光线(或观察方向)穿过物体后,经吸收和/或外散射衰减、并经内散射增强后的结果,如下图所示(该图现已为您所熟悉)。 ![](images/2026/02/08/20260208_131635_553.webp) 如前所述,辐射传输方程是一阶微分方程,其标准形式可表示为: $$ y' + p(x)y = \color{red}{q(x)} $$ 在数学中,这被称为一阶非齐次线性微分方程。我们需要求解的方程中同时包含函数 $y$ 及其导数 $y'$。现在,我们将辐射亮度函数重新定义为距离 $s$ 的函数,其中 $s$ 是沿方向向量 $\omega$ 的光束上任意点到某一参考点的距离: $$ \begin{array}{l} y \rightarrow L(s) \\ y'(x) \rightarrow \frac{dL(s)}{ds} \\ \end{array} $$ 并且: $$ \begin{array}{l} y' + \color{blue}{p(x)} y = \color{red}{q(x)} \\ L'(s) + \color{blue}{\sigma_t} L(s) = \color{red}{\sigma_s L_s(s)} \end{array} $$ 请记住,我们之前提到 $L_s$ 有时被称为源项——这是因为您可以将其理解为:光线沿途的某些位置会"出现"光,并被添加到光束的辐射亮度中,它是辐射亮度的"来源"。 这种标准形式的常微分方程有已知解(如果您感兴趣,可参见下方的推导): $$ y(x) = \int_{t=0}^x \color{red}{q(t)} e^{-\int_t^x \color{blue}{p} dt'} dt + C_1 e^{-\int_{t=0}^x \color{blue}{p} dt} $$ > **推导过程** > > 有多种方法可用于推导一阶非齐次线性微分方程的解,我们采用积分因子法(integrating factor method)。该方法的核心思想是:将常微分方程 $y' + p(x)y = q(x)$ 乘以一个函数 $I(x)$,得到: > > $\color{red}{I(x)y' + I(x)p(x)y} = I(x)q(x)$ > > 我们称这个方程为修正后的常微分方程。要理解其意义,您需要知道两个函数乘积的导数公式(乘积法则): > > $\frac{d}{dx} [f(x) g(x)] = f'(x) g(x) + f(x) g'(x)$ > > 现在,我们注意到:如果选择 $I'(x) = I(x)p(x)$,那么修正后的常微分方程左侧看起来就像是通过乘积法则计算的导数: > > $$ > \begin{array}{l} > \color{red}{I(x)q(x)=I(x)y' + I(x)p(x)y} \\ > \color{red}{=I(x)y' + I'(x)y}\\ > =\frac{d}{dx} [I(x)y] > \end{array} > $$ > > 两边积分得: > > $I(x)y = \int I(x)q(x)dx + C$ > > 注意到 $I'(x) = I(x)p(x)$ 本身是一个一阶齐次微分方程,我们在本章前面已经给出了这类方程的通解: > > $I(x) = e^{ \int p(x) dx}$ > > 现在,我们将积分因子 $I(x)$ 代入修正后的常微分方程: > > $e^{ \int p(x) dx}y = \int e^{ \int p(x) dx}q(x)dx + C$ > > 即: > > $y(x) = e^{-\int p(x) dx} \left( \int e^{\int p(x') dx'} q(x) dx + C \right)$ 要得到体绘制方程,只需将 $p$ 和 $q$ 分别替换为辐射传输方程中的 $\sigma_t$ 和 $\sigma_s L_s(x)$。 将辐射传输方程中的 $q$ 和 $p$ 替换为对应项(请阅读下方第二个注释),得到: $$ L(x, \omega) = \int_{s'=0}^s \exp\left(-\int_{s'}^{s} \textcolor{blue}{\sigma_t(x_{s''})} \, ds''\right) \left[\textcolor{red}{\sigma_s(x_{s'}) L_s(x_{s'},\omega)}\right] ds' + L(0) \exp\left(-\int_{s'=0}^{s} \textcolor{blue}{\sigma_t(x_{s'})} ds'\right) $$ 光线沿 3D 空间中的位置 $x$ 由标量 $s'$ 和 $s''$ 重新参数化,这两个标量表示从入射点 $x$ 开始沿光线的距离。形式上,我们将 $x$ 替换为 $x_s = x + s \omega$。 外积分(变量为 $s'$)从光线进入体积的入射点 $x$ 延伸到光线离开体积的出射点 $x_s$(即我们想要获取光传播到该点的辐射亮度值的位置),积分距离为 $s$,表示沿光线路径累积的、被散射到光线方向的光。 内积分(变量为 $s''$)从当前散射点 $x_{s'}$ 延伸到光线的终点 $x_s$,积分距离为 $s'$,表示从散射点 $x_{s'}$ 到终点 $x_s$ 的光衰减。 项 $L(0) \exp\left(-\int_{s'=0}^{s} \textcolor{blue}{\sigma_t(x_{s'})} ds'\right)$ 表示初始辐射亮度 $L(0)$ 在整个路径长度 $s$ 上被体积透射率衰减后的结果。 正如您所猜测的,这就是体绘制方程(Volume Rendering Equation, VRE)。 ![](images/2026/02/08/20260208_131635_887.webp) > 如果您疑惑:在展开推导过程中给出的方程(见上方方框)时,前置的指数项为何出现在方程的第二部分,而第一部分中却消失了——这是一个很好的问题。原因如下: > > $\int_0^s \sigma_t(x) \, dx = \int_0^t \sigma_t(x) \, dx + \int_t^s \sigma_t(x) \, dx.$ > > 换句话说,从 $0$ 到 $s$ 的积分可以拆分为从 $0$ 到 $t$ 的积分与从 $t$ 到 $s$ 的积分之和。因此: > > $\int_0^t \sigma_t(x) \, dx = \int_0^s \sigma_t(x) \, dx - \int_t^s \sigma_t(x) \, dx.$ > > 对两侧取指数: > > $e^{\int_0^t \sigma_t(x) \, dx} = e^{\int_0^s \sigma_t(x) \, dx - \int_t^s \sigma_t(x) \, dx}.$ > > 指数中的原始积分可拆分为两个积分,指数中的减法对应积分上下限的不同。将其应用于我们的方程: > > $L(x, \omega) = e^{-\int_0^s \sigma_t(x) \, dx} \left( \int_0^s e^{\int_0^s \sigma_t(x) \, dx - \int_t^s \sigma_t(x) \, dx} \sigma_s(t) L_s(t) \, dt + C \right).$ > > 化简后得到: > > $L(x, \omega) = e^{-\int_0^s \sigma_t(x) \, dx} \left( e^{\int_0^s \sigma_t(x) \, dx} \int_0^s e^{-\int_t^s \sigma_t(x) \, dx} \sigma_s(t) L_s(t) \, dt + C \right).$ > > 外层的指数项 $e^{-\int_0^s \sigma_t(x) , dx}$ 和 $e^{\int_0^s \sigma_t(x) , dx}$ 相互抵消: > > $L(x, \omega) = \int_0^s e^{-\int_t^s \sigma_t(x) \, dx} \sigma_s(t) L_s(t) \, dt + C e^{-\int_0^s \sigma_t(x) \, dx}.$ > > 其中 $C$ 是表示初始条件 $L(0)$ 的常数: > > $L(x, \omega) = \int_0^s e^{-\int_t^s \sigma_t(x) \, dx} \sigma_s(t) L_s(t) \, dt + L(0) e^{-\int_0^s \sigma_t(x) \, dx}.$ 最终,体绘制方程的形式为: $$ \begin{aligned} L(x, \omega) =& \int_{s'=0}^s \exp\left(-\int_{s'}^{s} \sigma_t(x_{s''}) \, ds''\right) \left[\sigma_s(x_{s'}) L_s(x_{s'})\right] ds' + \\ & L(0) \exp\left(-\int_{s'=0}^{s} \sigma_t(x_{s'}) \, ds'\right). \end{aligned} $$ > 需要说明的是,虽然我们不确定"体绘制方程"这一术语的首次提出者是谁,但该术语在 21 世纪初才开始被广泛使用。它出现在皮克斯研究院(Pixar Research)2017 年发布的《生产级体绘制》(Volume Rendering for Production)文档中,但在此之前已有人使用。如果您有相关信息,欢迎告知我们。 右侧的 $L_0$ 项对应于从观察者视角来看可能位于体积物体后方的物体发出的辐射亮度。如果体积物体后方有一个实体物体,那么该物体沿 $\omega$ 向量"反射"的辐射亮度 $L_0$ 将在穿过整个体积距离 $s$ 后,被体积的透射率衰减。 此外,如果考虑**发射项**,我们需要在内散射项旁添加发射项 $L_e$(注意发射源旁的 $\sigma_a$ 项)。 由此可得体积渲染方程的最终形式: $$ \begin{aligned} L(x, \omega) &= \int_{s'=0}^s \exp\left(-\int_{s'}^{s} \textcolor{blue}{\sigma_t(x_{s''})} , ds''\right) \left[\textcolor{red}{\sigma_s(x_{s'})} L_s(x_{s'},\omega) + \textcolor{red}{\sigma_a(x_{s'})} L_e(x_{s'},\omega)\right] ds' + \\ &\quad L(0) \exp\left(-\int_{s'=0}^{s} \textcolor{blue}{\sigma_t(x_{s'})} ds'\right) \end{aligned} $$ 另外别忘了: $$ L_s(x, \omega) = \int_{\mathbb{S}^2} f_p(x, \omega, \omega')L(x, \omega')d\omega' $$ > 体绘制方程对我们这些专注于计算机图形学的人来说更实用,因为它将辐射传输方程转化为一个积分——尽管该积分没有解析解,但至少可以通过黎曼和(Riemann sum)等技术求解(这正是我们在前几章中实际采用的方法)。 用 $T(s)$ 表示透射率项,令: $$ T(s) = \exp\left(-\int_{s'=0}^{s}\sigma_t(x_{s'}) ds'\right) $$ 如您现在所知,这表示光在介质中传播距离 $s$ 后的透射率,因此体渲染方程可写作: $$ L(x, \omega) = \int_{s'=0}^s T(s')\left[\sigma_s(x_{s'}) L_s(x_{s'}, \omega)+\sigma_a(x_{s'}) L_e(x_{s'}, \omega)\right] ds' + T(s)L(0) $$ * 外积分(变量为 $s'$)从体积的入射点延伸到出射点 $x_{s'} = x_s$,表示沿光线路径累积的、被散射到光线方向的光。 * 内积分 $T(s)$ 表示从散射点到出射点的光衰减。 * $s$ 是光穿过体积的总距离。 如果您能读到这里,恭喜您!您已经掌握了计算机图形学文献中最复杂的方程之一。 > **一段历史** > > 如果要为体绘制的介绍推荐一篇论文,那一定是詹姆斯·T·卡吉亚(James T. Kajiya)1984 年发表的《光线追踪体积密度》(Ray-Tracing Volume Density)。这篇论文表明,体积物体渲染绝非新兴技术——但当时的硬件性能甚至不足以将光线追踪应用于实体表面,更不用说光线步进体积物体了。直到 20 世纪 90 年代末至 21 世纪初,随着《超时空接触》(Contact)等电影的出现,体绘制才开始在生产中应用(因为大预算电影的制作成本终于可以承受这种技术)。下图是该论文的截图,展示了卡吉亚首次通过光线步进实现的体积物体渲染结果。 这篇论文可能是整个计算机图形学研究史上最重要的 10 篇论文之一。如果您有不同意见,欢迎告知我们。 现在的核心问题是:我们如何计算这个积分(答案当然不是 42)?我们在本课程中已经展示了一种解决方案——光线步进法,但还存在其他方法。光线步进法曾经是主流,但现在已被认为相当过时(不过我们仍然认为它是学习体绘制的良好起点)。如今,主流方法是使用跟踪算法和随机采样方法。我们将在本课程的最后一章简要介绍这一主题。 *** ## 从方程到代码 我们理解这些方程可能令人望而生畏,部分读者可能只关心它们如何转化为代码。本课程的前四章将带您完成这一过程,因此我们不会在此重复。如果您还没有阅读前几章,建议您先阅读。但这里提供一些线索,帮助您将方程的不同部分与各章节对应起来: * $L(0)T(s)$ 项对应我们在本课程第一章学到的内容。$L(0)$ 简单表示实体物体(如下方图片中的红墙)反射的光,这些光穿过体积。该光(物体的颜色)仅被 $T(s)$ 衰减,其中 $s$ 是光穿过体积的距离,$T$ 就是比尔定律。如果物体是均匀的,则 $T(s) = \exp(-s * \sigma_t)$(见第一章);如果体积是非均匀的,则需要计算体积的光学厚度(见第三章),方程为 $T(s) = \exp(- \int_{t=0}^s \sigma_t(x_t) dt)$。如果仅考虑该项,体积球体将保持黑色——该项仅负责处理来自背景、穿过体积的光。 * 方程右侧的第一项 $\int_{t=0}^s T(t)\left[\sigma_s(x_t) L_s(x_t, \omega)\right] dt$ 对应单次散射项。要了解其如何转化为代码,请阅读第一章至第三章——该项负责球体的照明。 *** ## 我们接下来将学习什么 本节课的大部分内容致力于讲解光线步进算法,但你需要了解的是:尽管该算法直到最近(至少在 21 世纪 10 年代中期之前)几乎是体渲染的唯一选择,但现代渲染引擎如今在处理体渲染时,通常会采用基于蒙特卡洛的随机方法。既然它已被视为过时技术,我们为何还要花费大量时间学习?一方面是出于历史原因;另一方面,对于 CGI 编程新手(尤其是几乎没有数学基础的人)而言,通过光线步进算法入门体渲染(及体渲染方程),远比通过复杂度高得多的随机方法更容易。 ### 光线步进算法为何会被淘汰?主要有两个原因: * 它无法准确模拟光线在真实世界中与体积介质相互作用的行为(我们稍后会详细说明); * 随机方法对真实物理过程的模拟效果要好得多。 我们本可以从一开始就使用随机方法(该方法自 20 世纪 60 年代起就已为人熟知),但问题在于:这种方法的计算量是光线步进算法的无数倍,用它生成一张图像需要等待极长的时间(至少感觉上是这样)。光线步进算法虽然计算密集,但远不及随机方法 —— 这也是它直到最近仍作为体渲染首选解决方案的原因(即便如此,我们也是等到 20 世纪 90 年代末才开始在实际生产中应用光线步进算法,直到 21 世纪 00 年代末才开始普及)。幸运的是,随着计算能力的持续提升,我们现在使用随机方法能够在合理时间内得到结果;而且由于其生成的效果更优,光线步进算法已逐步被基于随机方法的方案取代。证毕(Quod erat demonstrandum)。 ### 接下来我们看看光线步进算法的局限性所在 要回答这个问题,我们需要先理解光线在介质中的传播方式。光子进入体积介质后会发生这样的过程:它沿直线传播一段距离,最终与介质(例如构成体积的粒子)发生相互作用。正如我们所知,光子随后可能被散射(改变传播方向)或被吸收。如果发生散射,它会继续在介质中传播,但方向是随机的 —— 至少与撞击介质粒子前的传播方向大概率不同。这种 "传播 - 相互作用" 的循环会一直持续,直到光子被吸收或最终脱离介质。下图展示了这一过程:三个光子从顶部进入一个立方体体积介质后的不同命运。 ![](images/2026/02/08/20260208_131636_111.webp) 其中两个光子(红色)最终被吸收,只有一个光子(绿色)脱离介质(脱离方向与进入立方体时的传播方向不同)。 光子的运动轨迹可以描述为一种 "随机游走"(random walk)—— 这也正是它的名称由来。我们还能观察到,光子在被吸收或脱离介质前,会与介质发生多次相互作用,即多次散射。而这正是光线步进算法的核心短板:它仅考虑了光子与体积介质之间的**单次相互作用**。 ### 单次散射 vs 多次散射:低反照率 vs 高反照率体积物体 这种仅考虑单次相互作用的模式被称为 **"单次散射"(single scattering)**—— 我们只计算经过一次介质相互作用后,被重新导向观察者的光线。虽然有些体积介质的单次散射效应显著(例如蒸汽火车或火山喷发产生的黑烟),但许多其他类型的体积介质(尤其是云)表现出强烈的 **"多次散射"(multiple scattering)特性:光子在脱离(或被吸收)前,会与物体发生无数次相互作用。这也是云呈现明亮白色的原因,而蒸汽火车或火山产生的烟则是黑色的。我们称白色的云具有** "高反照率"(high albedo),而黑色的烟柱具有 **"低反照率"(low albedo)**。下图展示了低反照率和高反照率体积介质的差异:左侧的烟含有大量粒子,而云由水滴构成 —— 这也是两者视觉差异的主要原因。当然,黑烟之所以呈现黑色,也因为它会吸收大量光线。 ![](images/2026/02/08/20260208_131636_379.webp) 总而言之,光线步进算法对于低反照率物体(如烟雾)的模拟效果尚可接受 —— 这类物体的外观主要由单次散射效应主导(如下图中橙色光线所示);但对于高反照率物体的模拟效果则很差 —— 这类物体的外观主要由多次散射效应主导(大多数脱离介质的光子都经过了多次相互作用,而非单次散射所假设的仅一次作用)。 ![](images/2026/02/08/20260208_131636_688.webp) 顺便一提,在对比烟雾和云时还需注意:烟雾通常是**各向同性**(isotropic)的,而云则表现出强烈的(前向)散射特性。某种程度上,我们可以将光线步进算法比作 "直接光照"(direct lighting):直接光照总比没有光照好(这是显而易见的),但显然不如同时包含直接光照和间接光照(indirect lighting)的场景渲染效果。使用光线步进算法时,我们完全忽略了间接光照部分 —— 如下例所示,间接光照对于生成照片级真实感图像至关重要。因此,光线步进算法无法捕捉这一效应是一个严重的问题。 ![](images/2026/02/08/20260208_131637_233.webp) 这带来了一个实际应用层面的问题:例如,要模拟云的外观,你必须向体积介质中注入更多光线(即在场景中添加额外光源)—— 这本质上是一种 "作弊" 手段,而非让计算机进行物理上准确的 "正确计算"。那么问题来了:替代方案是什么?如何才能实现 "正确计算"? ### 基于随机的追踪方法 "正确计算" 的核心是让计算机模拟光子与介质的真实相互作用过程 —— 换句话说,模拟光子的随机游走行为。这类方法旨在追踪光子在体积介质中的传播路径,因此被称为 "追踪方法"(tracking methods)。这并非一种 "新" 方法:它于 20 世纪 60 年代被开发出来,用于模拟中子等粒子穿过板材的辐射过程。尽管该方法用途广泛、功能强大,但计算量也极大。如果你想自行深入了解相关主题,可以在互联网上搜索 "蒙特卡洛粒子输运"(Monte Carlo particle transport, MCPT)或 "蒙特卡洛光线 / 光子输运"(Monte Carlo light/photon transport)。我们在此不深入探讨该技术的细节:首先,我们已在本页面(蒙特卡洛模拟)提供了该方法的实际实现案例;其次,我们计划尽快(2022 年)撰写相关课程 —— 请关注 "高级 3D 渲染" 板块的更新(课程名称暂定为《体素路径追踪》)。目前,我们只需了解其核心思想:模拟光子在体积介质中的传播路径。其目标仍是求解体渲染方程: $$ L(s) = \int_{s'=0}^s T(s')\big[\sigma_s L_s(s') \big]ds' + T(s)L(0) $$ (关于蒙特卡洛方法的更多内容,请参阅《蒙特卡洛方法的数学基础》和《蒙特卡洛方法实践》)。与光线追踪类似,我们不会采用 "正向模拟"(即追踪光子从光源到观察者 / 传感器的传播路径),而是采用 "反向追踪"—— 从观察者到光源。粒子在介质中的传播路径可由一系列 "步长" 构成,每一步都包含 "距离" 和 "方向" 两个参数。我们将利用对介质本身的认知(尤其是其散射系数、吸收系数和相位函数),通过随机采样确定光子的步长和方向,从而模拟这一行为。如前所述,基于随机的蒙特卡洛模拟或积分方法计算量极大。你可能听说过 "delta 追踪"(delta tracking)等技术,它们可用于优化这一过程(但会增加代码复杂度)。delta 追踪也将在《体素路径追踪》课程中详细讲解。