Monte Carlo Integration
计算复杂的定积分时,通过随机采样的函数值的平均值 估计该函数的定积分
对于函数f(x)f(x)f(x)的积分∫abf(x) dx\int_a^b f(x) \, dx∫abf(x)dx,其蒙特卡洛积分表示为
1N∑i=1Nf(Xi)p(Xi) \frac{1}{N} \sum_{i=1}^N \frac{f(X_i)}{p(X_i)} N1i=1∑Np(Xi)f(Xi)
采样数量越多,误差越小
在x上采样,在x上积分
Path Tracing
前面介绍的Whitted-style ray tracing,只考虑了镜面反射和折射,并在遇到漫反射表面时停止反射光线
如左图,未考虑光线在遇到漫反射表面后的继续传播,天花板被渲染成黑色,且柱形的侧面没有与周围墙壁颜色相关的color bleeding
我们需要更符合物理规律的算法

下面围绕渲染方程进行展开
Lo(p,ωo)=Le(p,ωo)+∫Ω+Li(p,ωi)fr(p,ωi,ωo)(n⋅ωi) dωi L_o(p, \omega_o) = L_e(p, \omega_o) + \int_{\Omega^+} L_i(p, \omega_i) f_r(p, \omega_i, \omega_o) (n \cdot \omega_i) \, d\omega_i Lo(p,ωo)=Le(p,ωo)+∫Ω+Li(p,ωi)fr(p,ωi,ωo)(n⋅ωi)dωi
首先只考虑下面场景中一个像素接受到的直接照亮direct illumination的光 ,不考虑Le(p,ωo)L_e(p, \omega_o)Le(p,ωo)项

要计算定积分∫Ω+Li(p,ωi)fr(p,ωi,ωo)(n⋅ωi) dωi\int_{\Omega^+} L_i(p, \omega_i) f_r(p, \omega_i, \omega_o) (n \cdot \omega_i) \, d\omega_i∫Ω+Li(p,ωi)fr(p,ωi,ωo)(n⋅ωi)dωi
设置蒙特卡洛积分的概率密度函数pdf为p(ωi)=1/2πp(\omega_i) = 1/2\pip(ωi)=1/2π
得到定积分的计算结果
Lo(p,ωo)≈1N∑i=1NLi(p,ωi)fr(p,ωi,ωo)(n⋅ωi)p(ωi) L_o(p, \omega_o) \approx \frac{1}{N} \sum_{i=1}^N \frac{L_i(p, \omega_i) f_r(p, \omega_i, \omega_o) (n \cdot \omega_i)}{p(\omega_i)} Lo(p,ωo)≈N1i=1∑Np(ωi)Li(p,ωi)fr(p,ωi,ωo)(n⋅ωi)
初步得到path tracing的计算方式:
计算某一点反射的光时,随机采样n个入射方向,向这n个方向追踪光线
若光线到达光源,将结果加上上述公式计算出的结果
若光线到达物体,以该物体在该方向反射出的光作为上式中的Li(p,ωi)L_i(p, \omega_i)Li(p,ωi),计算公式结果加到最终结果中
这里可以把物体反射出的光看作另一种光源发出的光
伪代码如下:
shade(p, wo)
Randomly choose N directions wi~pdf
Lo = 0.0
For each wi
Trace a ray r(p, wi)
If ray r hit the light
Lo += (1 / N) * L_i * f_r * cosine / pdf(wi)
Else If ray r hit an object at q
Lo += (1 / N) * shade(q, -wi) * f_r * cosine / pdf(wi)
Return Lo
这种算法具有下列问题:
1.指数爆炸,需要计算的光线数量是采样数量的反弹次数次幂
因此我们假设采样次数N为1
这样只随机采样一个方向的光,没有for循环
另:采样次数不为1的算法称作分布式光线追踪Distributed Ray Tracing
而采样次数过少会带来很大噪声,我们对一个像素追踪多条路径并对radiance结果取平均值
ray_generation(camPos, pixel)
Uniformly choose N sample positions within the pixel // 对每个像素追踪多条路径
pixel_radiance = 0.0
For each sample in the pixel
Shoot a ray r(camPos, cam_to_sample) // 从摄像机到采样点追踪一条光线
If ray r hit the scene at p
pixel_radiance += 1 / N * shade(p, sample_to_cam) // 利用path tracing的函数shade
Return pixel_radiance

2.递归算法无限递归,计算机不能做无限次的光线弹射,减少弹射次数意味着截断power
我们采用俄罗斯轮盘赌Russian Roulette
从前我们采样时,永远会向表面上的采样点发射一个光线,返回radiance LoL_oLo
现在设置一个概率P,在发射光线后,有 P 的概率返回Lo/PL_o / PLo/P,有 1-P 的概率返回 0 ,这保证了返回radiance的数学期望仍然是LoL_oLo
现在解决path tracing的效率问题
若光源面积过小,采样发出的很多条光线因不会到达光源而被浪费

对于光源发出的光线,我们不在采样点上对立体角积分,而是在光源处采样,对光源面积A积分
设置光源的pdf = 1 / A
找到dωd\omegadω和dAdAdA的数量关系,立体角的定义是区域在单位球上投影的面积,我们将光源在垂直光线上的分量等比例投射到单位球上
下面θ′\theta'θ′表示光源法线与光线的夹角
dω=dA∥x′−x∥2cosθ′ d\omega = \frac{dA}{\|x' - x\|^2} \cos \theta' dω=∥x′−x∥2dAcosθ′
将dωd\omegadω代入到上面的公式,得到对光源积分的结果
Lo(x,ωo)=∫ALi(x,ωi)fr(x,ωi,ωo)cosθcosθ′∥x′−x∥2 dA L_o(x, \omega_o) = \int_A L_i(x, \omega_i) f_r(x, \omega_i, \omega_o) \frac{\cos \theta \cos \theta'}{\|x' - x\|^2} \, dA Lo(x,ωo)=∫ALi(x,ωi)fr(x,ωi,ωo)∥x′−x∥2cosθcosθ′dA

现在我们就把结果分为两部分:
1.光源直接照射(不考虑俄罗斯轮盘赌)
2.其他反射(indirect,考虑RR)
最后考虑光线是否被物体遮挡,得到最终path tracing算法:
shade(p, wo)
# Contribution from the light source.
Uniformly sample the light at x' (pdf_light = 1 / A)
Shoot a ray from p to x'
If the ray is not blocked in the middle
L_dir = L_i * f_r * cos θ * cos θ' / |x' - p|^2 / pdf_light
# Contribution from other reflectors.
L_indir = 0.0
Test Russian Roulette with probability P_RR
Uniformly sample the hemisphere toward wi (pdf_hemi = 1 / 2pi)
Trace a ray r(p, wi)
If ray r hit a non-emitting object at q
L_indir = shade(q, -wi) * f_r * cos θ / pdf_hemi / P_RR
Return L_dir + L_indir
Not covered
上面的讨论未涉及很多细节:
均匀采样半球 - 带权重的半球采样
蒙特卡洛的pdf选择 - 重要性采样Importance Sampling
随机数的选择 - 低差异序列Low Discrepancy Sequences
光源与半球采样的结合 - 组合采样Multiple Importance Sampling
像素的radiance为所有路径得到radiance的平均值? - 像素重建滤波器Pixel Reconstruction Filter
像素的radiance和color的关系 - 伽马矫正gamma correction
图形学博大精深