Loading... # Lec 16 蒙特卡洛路径追踪 ## 1. 蒙特卡洛积分 **蒙特卡洛积分**(Monte Carlo Integration) 是一种积分的方法。 常规的求定积分的方法:$\int_{a}^{b}f(x)\ dx = F(b)-F(a)$ ,$F(x)$ 是 $f(x)$的解析式。 如果$F(x)$并不方便求,那么可以用蒙特卡洛积分来近似求解定积分: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/image-20211108151555651.png?x-oss-process=image/resize,p_40) - 定义积分函数:$\int_{a}^{b}f(x)dx$ - 依照一种概率密度函数 $p(x)$,随机采样位置 $X_i$ - 近似求解定积分即为: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/image-20211108183909399.png?x-oss-process=image/resize,p_50) 这其实是对期望的一种近似,N值越大,采样越多,结果就越准确: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/image-20211108185953219.png?x-oss-process=image/resize,p_65) ## 2. 路径追踪 简单的Whitted-style的光线追踪方法有很多地方不符合物理规律,比如不能很好地解决粗糙表面漫反射表面(全方向反射)的问题,而且用的是Blinn-Phong光照模型。接下来我们就着手解决这些问题。 回想一下渲染方程: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211108220039.png) 解决这个方程的难点在于:1. 积分难解、2. 递归形式 所以我们想到用蒙特卡洛积分来解决。 先暂且不考虑自发光 $L_e$,则利用蒙特卡洛积分,对入射方向$\omega_{i}$进行采样,后半部分的积分结果可以转化为: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211108221420.png?x-oss-process=image/resize,p_55) 至于概率密度函数 $p(\omega_{i})$ ,我们选择较为简单的**均匀采样**,即对着整个半球的每一个立体角都均匀采样,所以可得:$p(\omega_{i}) = 1/2\pi$ 暂且**不考虑间接光照**,伪代码如下: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211108222650.png?x-oss-process=image/resize,p_45) ### 2.1 全局光照 ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211108223149.png?x-oss-process=image/resize,p_55) 考虑到从其他物体来的间接光照,比如上图中对于P点,需要考虑有一条从Q点反射过来的光线。即 $L_i(p, \omega_i)=L_o(q,\omega_o)$ 而且 $\omega_o = -\omega_i$ 这就是之前所说的 **递归特性**。那如果将上面的伪代码改成如下递归形式,能解决问题吗? ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211108231111.png?x-oss-process=image/resize,p_45) 答案是 **不能** 每一条出射光线都需要采样N个入射光线来计算,那么每一条从物体来的入射光线同样需要N个入射光线,这将会是平方级别的增长。即使平均每次只有100条光线是从物体来的,那经过3次弹射之后就需要1000000条入射光线的计算,这样的计算开销十分庞大。 ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211108231822.png?x-oss-process=image/resize,p_45) 显然,只有N=1的时候,计算量才不会爆炸级增长。但N=1又是另一个极端,会使得结果欠采样,非常的 "noisy"。 有一种曲线救国解决方案是:每次求间接光照只会采样一条,但是会**重复多次寻找到多条路径**,最后取平均来模拟采样。如下图所示,在同一个像素内采样多个点来生成光线,进行路径追踪 ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211109003336.png?x-oss-process=image/resize,p_60) 参考伪代码如下: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211109003601.png?x-oss-process=image/resize,p_55) ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211109003629.png?x-oss-process=image/resize,p_55) ### 2.2 递归出口:俄罗斯轮盘赌 Russian Roulette 仍然要注意一个问题,这个递归没有一个停止的出口。这里不采用之前Whitted-style光线追踪中提到过的限制弹射次数的方法,而是选择**俄罗斯轮盘赌(Russian Roulette)**算法。 ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211109004037.png?x-oss-process=image/resize,p_55) - 预先定义一个概率 $P$。在每次进行一个点的着色计算之前, 生成一个$0-1$的随机数。 - 如果随机数是在 $ 0 - P$ 之间,则进行计算,将得到的 $L_o$ 除以 $P$,即返回 $L_o/P$ - 如果是 $P - 1$ 之间,则停止反射,返回结果 0 其原理也是期望相关,不多做证明。 伪代码修正: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211109180837.png?x-oss-process=image/resize,p_55) ### 2.3 改进直接光照效率:对光源采样 这样便是一个完整的正确的算法了。但是仍然效率比较低 原因在此: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211109181255.png?x-oss-process=image/resize,p_52) 在每次计算直接光照的时候,如果光源很小,采用均匀采样的话,只有很少的光线才会触及光源,尤其当光源较小的时候,这种现象越明显,大量采样的光线都被浪费了。 因此我们可以将直接光照的计算部分独立出来,同时在计算直接光照的时候改进为**直接对光源进行采样**,这样所有采样的光线都一定会击中光源(如果中间没有别的物体),没有光线再会被浪费了。 ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211110005459.png?x-oss-process=image/resize,p_65) 假设光源的面积为 $A$,那么对光源进行**均匀采样**的概率密度函数 $p(\omega_{o}) = 1/A$ (因为$\int \operatorname{p(\omega_{o})} dA=1$) 但原始的渲染方程是对入射光线 $d\omega_{i}$ 进行积分的,我们需要对其进行修改,改成**对光源面积** $dA$ **的积分** 这一修改需要用到 $d\omega_{i}$ 和 $dA$ 的关系,这一关系可以通过立体角的定义(在单位球面上的投影,见[笔记6的0.2节](https://www.irimsky.top/archives/270/))得到: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211110005823.png?x-oss-process=image/resize,p_45) (注意是$\theta'$,在上图是光线与光源法线的夹角) 重写渲染方程得到: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211110010036.png?x-oss-process=image/resize,p_55) 就可以利用蒙特卡洛的方法对光源进行采样从而计算直接光照的积分值了,对于间接光照,依然采用先前的方法进行光线方向的均匀采样。最终伪代码如下,分直接光照(无需RR算法)和间接光照两部分计算: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211110011955.png?x-oss-process=image/resize,p_50) 不过需要注意一个问题:如果光源与观测点之间有物体阻挡,光源就不会有贡献。那么如何得知有没有阻挡呢? 解决方法是回归原始的原理:如果某一点能看见光源,那么它就能被光源照亮。所以只需要先从观测点向着光源发射一条光线,如果光线被阻挡,则不再考虑来自直接光源的部分 伪代码修改如下: ![](https://irimskyblog.oss-cn-beijing.aliyuncs.com/content/20211110012956.png?x-oss-process=image/resize,p_50) 最后修改:2021 年 11 月 10 日 02 : 24 AM © 允许规范转载