到目前为止我们实际上没有对之前的实现有什么实质性的更改,只是换了一种实现方式,所以得到的效果自然也是差不多的。但是改变实现方式是为了能够实现重要性采样,这一节我们将直接对光源进行采样。
1 对光源采样的 PDF
对光源直接采样就是把渲染方程中的对 $\omega$ 的积分改为对光源面积 $A$ 的积分,因此要做一个积分变量的替换,图形学中我们用立体角公式已经推导过 $d\omega$ 和 $dA$ 的关系:
$$
d\omega = \frac{dA·cos\theta’}{||x - p||^2}
$$
回顾我们之前改写的蒙特卡洛计算渲染方程:
$$
color_{out} = \frac{albedo·s(direction)·color_{in}}{p(direction)}
$$
我们只需要解出对光源采样的 $p(direction)$ 即可。因为无论对 $\omega$ 采样还是对光源 $A$ 采样,得到的方向的概率应该都是一样的,所以:
$$
p(direction)·d\omega = \frac{1}{A}·dA
$$
其中 $\frac{1}{A}$ 是对光源面积 $A$ 均匀采样的概率密度,把上面 $d\omega$ 和 $dA$ 的关系式带入即可得到:
$$
p(direction) = \frac{||x-p||^2}{cos\theta’·A}
$$
也就是对光源采样的概率密度函数。这实际上和我们图形学中推导的,通过积分变量替换改写渲染方程,再用对光源采样的 $pdf=\frac{1}{A}$ 进行蒙特卡洛积分计算是完全一样的,只是这里我们把整个积分替换的系数和对光源采样的 pdf 统一写成了对方向 $\omega$ 在光源方向上采样的 pdf。
2 实现
有了上面的公式我们可以改写 ray_color
函数:
1 | // 得到光线颜色 |
然后修改主函数:
1 | ... |
每个像素只采样 10 根光线,得到的效果:
比之前每个像素采样 100 根光线的噪声还要小很多。
3 单向光源
可以看到上面的结果中,噪声主要集中在光源附近,这是因为光源是双面的,光源和天花板之间有一个很小的缝隙,为了解决这个问题我们可以让光源只向下发光,修改光源材质的 emitted
函数:
1 | // 自发光材质,用作光源 |
然后实现一个翻转类,使得我们能够翻转光源法线,使它的法线全部指向 -y 方向:
1 | // 翻转光源法线,使其只有正面发光 |
然后在场景中调用翻转:
1 | // Cornell Box场景 |
同时修改 ray_color
函数:
1 | // 得到光线颜色 |
得到的效果: