到此为止关于漫反射材质的全部内容就都完成了,现在只剩最后一步——用我们新的实现方式修改金属和电介质材质,使得新的光线追踪器支持镜面反射和折射。
1 统一管理散射光线
对于镜面反射和折射,如果用新的渲染方程会出现 pdf 值为 0 的情况,因此我们使用之前隐式的渲染方程,也就是采样 pdf 和光线散射 pdf 一致。为此我们首先需要新增一个结构体来统一管理散射光线,然后根据散射光线的类型选择对应的渲染方程:
1 | // 统一管理散射光线 |
然后修改材质抽象类:
1 | class material { |
接下来用新的结构体和方法改写之前实现的 Lambertian 材质:
1 | // lambertian材质类,在单位球面上采样得到散射方向 |
接下来修改主函数:
1 | /*******创建场景*******/ |
2 金属和电介质
有了统一管理散射光线的方法,我们可以修改之前的金属材质:
1 | // 金属材质类 |
同时修改电介质材质类:
1 | // 电介质材质类 |
然后修改 ray_color
函数:
1 |
|
然后修改场景:
1 | // Cornell Box场景 |
3 对球体和物体列表采样
上面的主函数中我们对透明玻璃球也进行了额外采样,因此类似于之前光源所在的 zx 平面,现在我们需要实现球体和物体列表的 pdf_value
函数和 random
函数,对球体采样的具体推导过程可以查看《RayTracingTheRestOfYourLife》第 12.3 节,这里直接给出代码,首先修改球体类:
1 | class sphere : public hittable { |
工具函数新增:
1 | // 在球体外对球体随机采样 |
然后是物体列表 hittable_list
:
1 | class hittable_list : public hittable { |
4 最后一步
最后一步我们来处理掉之前图片中有时会出现的黑点或者异常像素,这是因为一些不好的采样计算出了很大的或者 NaN 的颜色,使得整个像素受损,因此我们可以在写颜色的时候处理这种情况:
1 | // 向数组中写入一个颜色,用到了指针的引用传递 |
最后看一下效果: