光线追踪(蒙特卡洛积分与路径追踪)
上一章我们把光照写成了渲染方程:
出射光 = 自发光 + 半球上所有入射光的反射贡献这个式子很优雅,但它也带来了一个现实问题:半球方向有无穷多个,间接光还会继续反弹。路径追踪的核心思路是把“全部算完”改成“随机抽样,长期平均”。这就是蒙特卡洛积分进入渲染的地方。
为什么需要蒙特卡洛积分
Section titled “为什么需要蒙特卡洛积分”如果只考虑一个表面点的直接漫反射,我们需要在法线半球上积分;如果再考虑一次反弹,积分空间会多出另一个表面点和另一个方向;反弹次数越多,问题的维度越高。
这类积分通常很难解析求解,因为:
- 场景几何可能很复杂,光线是否被遮挡只能通过追踪判断。
- 材质函数可能随方向、波长、纹理坐标变化。
- 间接光是递归的,一个方向上的入射光又来自别处的出射光。
- 小光源、镜面反射、焦散等现象会让贡献集中在很少的路径上。
蒙特卡洛方法不试图列举所有路径,而是随机选择一些路径,让估计值随着样本数增加逐渐靠近真实结果。
蒙特卡洛积分的基本形式
Section titled “蒙特卡洛积分的基本形式”假设我们要计算:
I = ∫ f(x) dx如果从概率密度函数 p(x) 中随机抽取 N 个样本 x₁, x₂, ..., xₙ,就可以用下面的估计式:
I ≈ (1 / N) Σ f(xₖ) / p(xₖ)这里最容易忽略的是分母 p(xₖ)。它表示“这个样本被抽到的概率密度”。如果某个区域被更频繁地采样,它在求平均时就必须被相应地除回来,否则结果会被采样策略本身扭曲。
蒙特卡洛估计有三个重要性质:
- 样本数越多,结果越稳定。 每个像素都可以看成很多随机路径贡献的平均值。
- 估计可以无偏,但噪声可能很大。 无偏表示长期平均会收敛到正确结果,不表示少量样本就好看。
- 收敛速度比较慢。 噪声大致按
1 / √N下降,所以样本数增加 4 倍,噪声通常只减半。
从渲染方程到随机估计
Section titled “从渲染方程到随机估计”渲染方程中的半球积分可以写成:
Lₒ(x, ωₒ) = Lₑ(x, ωₒ) + ∫Ω fᵣ(x, ωᵢ, ωₒ) Lᵢ(x, ωᵢ) cosθ dωᵢ用蒙特卡洛方法估计半球积分时,就变成:
Lₒ ≈ Lₑ + (1 / N) Σ fᵣ(x, ωₖ, ωₒ) Lᵢ(x, ωₖ) cosθₖ / p(ωₖ)每个随机方向 ωₖ 都对应一条新的光线。光线如果打到光源,就带回直接贡献;如果打到另一个普通表面,就继续递归估计那里的入射光。路径追踪就是把这种随机递归过程做成一条条光路。
交互辅助:样本、噪声与重要性采样
Section titled “交互辅助:样本、噪声与重要性采样”下面的小程序把“随机方向采样”和“像素估计收敛”放在一起观察。左侧半球表示从表面点发出的候选方向,右侧柱状图表示一个像素在不同样本下的估计值。
可以重点观察两件事:
- 把样本数调大时,右侧估计值会更稳定,但噪声不会线性下降。
- 切换到重要性采样后,样本会更多集中到高贡献方向;在相同样本数下,估计通常更平稳。
这个示意不是物理精确渲染器,而是用来帮助建立直觉:路径追踪的画面噪声不是“错误”,而是随机积分还没有充分收敛。
噪声、方差与收敛
Section titled “噪声、方差与收敛”路径追踪画面中的颗粒感通常来自方差。某些像素的大部分随机路径贡献很小,偶尔有一条路径打到强光源或高亮反射方向,就会带来很大的值。这种“多数很暗,少数很亮”的分布会产生明显噪声。
常见例子包括:
- 小面积光源照亮的区域,随机方向很难刚好命中光源。
- 光泽或镜面材质,能量集中在很窄的反射方向附近。
- 间接光路径很长,只有少数路径能经过有效的反弹组合。
- 焦散现象,例如玻璃杯把光聚到桌面上。
提高样本数可以降低噪声,但代价很高。实际渲染器通常会同时使用重要性采样、直接光采样、多重重要性采样、降噪等方法。
重要性采样的目标是让样本分布更接近积分函数的贡献分布。也就是说,哪里可能贡献大,就更常去哪里采样。
对于漫反射表面,贡献里有 cosθ 项,靠近法线方向的入射光通常权重更高。因此漫反射常用余弦加权半球采样,而不是在半球上完全均匀采样。
对于高光材质,样本应该更多靠近镜面反射方向;对于面积光源,样本可以直接在光源表面上抽取。重要性采样不会改变真实积分本身,它只是改变“怎么抽样”。只要估计式中正确除以对应的 p(x),结果仍然可以保持无偏。
一个常见错误是:采样策略变了,但权重没有跟着概率密度一起变。这样画面可能看起来更亮或更暗,却不是物理结果,而是估计器被写偏了。
路径追踪实现中通常会维护一个累积权重,常叫做路径吞吐量。它表示光线经过多次反弹后,当前路径还剩下多少贡献能力。
当路径在表面点采样一个新方向时,可以用类似下面的形式更新:
T_new = T_old × fᵣ(ωᵢ, ωₒ) cosθ / p(ωᵢ)其中:
T_old是此前路径累积下来的权重。fᵣ描述材质如何把入射方向的光反射到出射方向。cosθ来自表面投影关系。p(ωᵢ)是这次方向采样的概率密度。
如果路径后来打到光源,就把光源辐射乘上当前吞吐量累加到像素:
L += T × Lₑ这就是为什么路径越长,贡献通常越小,但仍然可能带来颜色渗透、柔和间接光和更真实的空间氛围。
路径追踪基本流程
Section titled “路径追踪基本流程”-
从相机生成主光线。
对每个像素发出一条或多条随机扰动后的相机光线。 -
寻找最近交点。
用加速结构找到光线首先击中的三角形、球体或其他几何体。 -
累加自发光。
如果交点是光源,或者材质带有自发光,就把它按当前路径吞吐量加入结果。 -
选择新的反弹方向。
根据材质分布、半球分布或光源采样策略随机生成下一条光线。 -
更新路径吞吐量。
用 BRDF、余弦项和采样概率密度更新路径权重。 -
继续追踪或终止路径。
达到最大反弹次数,路径离开场景,或者通过俄罗斯轮盘终止时停止。
概念上,它可以写成:
let radiance = 0;let throughput = 1;let ray = cameraRay(pixel);
for (let bounce = 0; bounce < maxBounces; bounce++) { const hit = trace(ray); if (!hit) break;
radiance += throughput * emittedLight(hit);
const sample = sampleMaterialDirection(hit); throughput *= sample.brdf * sample.cosine / sample.pdf; ray = spawnRay(hit.position, sample.direction);}真实渲染器会处理更多细节,例如法线偏移、纹理、介质、光源采样、多重重要性采样和数值稳定性。但上面这段流程已经抓住了路径追踪的主干。
直接光采样与下一事件估计
Section titled “直接光采样与下一事件估计”如果只按材质随机反弹,路径要“碰巧”打到小光源可能很困难。比如一个房间里只有一盏小灯,漫反射表面随机向半球发射方向,大部分方向都不会命中灯。
下一事件估计的做法是:在每次命中表面时,额外直接从光源上采样一个点,检查这个点是否可见。如果没有被遮挡,就把这条到光源的直接贡献加入当前像素。
这样一来,直接光不必完全依赖随机反弹碰运气。许多现代路径追踪器会同时使用:
- 材质采样。 根据 BRDF 选择下一条反弹方向,擅长处理高光和间接路径。
- 光源采样。 直接朝光源抽样,擅长减少小光源造成的噪声。
- 多重重要性采样。 把多种采样策略按权重合并,避免某一种策略在特定场景里表现很差。
俄罗斯轮盘终止
Section titled “俄罗斯轮盘终止”路径不能无限追踪下去。最简单的方法是设置最大反弹次数,例如超过 8 次就停止。但如果理论上无限次反弹仍有贡献,硬性截断会引入一定偏差。
俄罗斯轮盘是一种概率终止方法:路径在若干次反弹后,以某个概率继续,以另一个概率停止。如果路径幸运地继续,就把吞吐量除以继续概率作为补偿。
如果继续概率是 q:继续时 T = T / q停止时路径贡献为 0长期平均后,这种方法不会因为随机终止而系统性变暗。它的作用不是让单条路径更准确,而是在控制计算量的同时保持估计器的期望值正确。
常见渲染现象
Section titled “常见渲染现象”路径追踪把很多光照现象统一到随机路径上,因此一些画面特征可以从路径角度理解:
- 颜色渗透。 光线从红色墙面反弹到白色物体上,会把红色能量带过去。
- 柔和阴影。 面积光源上的不同采样点会产生不同遮挡边界,平均后形成柔和过渡。
- 高光噪声。 光泽材质只有少数方向贡献很大,低样本时容易出现亮点。
- 萤火点。 极少数路径带回异常高贡献,形成孤立的亮像素。
- 降噪痕迹。 后处理降噪可以减少颗粒,但过强时会抹掉纹理、细节或间接光变化。
理解这些现象后,调试渲染器时就不只是“画面不好看”,而是能判断问题更可能来自采样、材质、光源、终止策略还是后处理。
本章把渲染方程落到了路径追踪实现上:
- 蒙特卡洛积分用随机样本估计复杂积分,样本数越多结果越稳定。
- 路径追踪把半球积分和递归间接光转换成一条条随机光路。
- 采样概率密度必须进入权重计算,否则估计结果会产生偏差。
- 重要性采样通过更聪明地分配样本来降低方差。
- 路径吞吐量记录多次反弹后路径剩余的贡献权重。
- 直接光采样、下一事件估计和俄罗斯轮盘是路径追踪中非常基础的效率工具。
到这里,第五部分已经从“光线如何求交”推进到了“随机光路如何形成全局光照”。下一章开始,我们会转向材质与外观:同样一束光打到塑料、金属、玻璃和皮肤上,为什么会呈现完全不同的视觉结果。