概述
本文主要记录我在跟进 RayTracingInOneWeekend - The Book Series[1]代码过程中,产生的思考与遇到的问题的记录与回答。
RayTracingInOneWeekend
项目是怎么将场景渲染转为图片的?
PPM格式输出。PPM记录每一个像素的RGB信息,通过头部明文确认长宽,按行存储。对于输出部分,使用我们最熟悉的cout作为输出流:
std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";
for(){
for(){
std::cout << ir << ' ' << ig << ' ' << ib << '\n';
}
}
对于编译为.exe的文件,我们使用 .\RayTracing.exe > image.ppm,就可以将输出流记录到这个文件里。
ASCII格式
P3
300 168
255
220 235 255
220 235 255
220 235 255
...
另外其实还有二进制格式P6。
如果想要实现写入其他格式如JPG,可以尝试一些库,例如 stb_image_write.h 。一般而言不使用输出流,而是分配数组写入,经由库文件处理,执行.exe文件时自动生成图像。
RayTracingTheNextWeek
实现的快门动态模糊到底是什么原理?
- 快门时间决定时间范围,我们对发出光线随机一个时间点∈[快门时间],这条光线记录这个时间点的颜色信息。
- 一个像素发射多条光线,某些会捕捉到物体位于该像素的时刻返回强颜色,另一些则返回背景色,最终返回颜色的加权平均,从而形成半透明效果。
也就是说极端来讲,不管快门多长,存在一种情况所有光线都被随机到同一个时间点,这个时候就不存在动态模糊的物体了。
根据大数定律,极端情况概率趋近于零所有光线时间戳完全相同的概率为 \( \left( \frac{1}{\text{time1} - \text{time0}} \right)^N \)(\( N = \text{采样数} \)),实际渲染中采样数相当高,几乎不可能发生。
为什么BVH要选择最长轴作为分割轴?
最大程度减少子树包围盒重叠。
BVH构建的基本流程为:选择分割轴→沿轴排序物体→按某种规则将物体分为左右子树
随机选择分割轴,可能导致树结构不平衡:某些子树过深或浅、包围盒重叠低效:增加光线与多个子树求交概率。如图,这是沿X轴放置的两个重叠的方块:

如果沿X轴(最长轴)进行排序,就是先读到红色区域,再到紫色,再到蓝色,可以有效识别。
而如果是沿着Y/Z轴,则全程都接触到3个区域,就会造成BVH退化,最终使得光线需要与整个包围盒内物体求交。
// int axis = random_int(0,2);//随机轴可能导致BVH退化
int axis = bbox.longest_axis();
在应用了上面的算法后,对300x168、100采样、50深度、16线程的渲染,耗时可从15s降至11s。
不过这种简单的做法也有个例外:还是按上图的排布,但物体在Y/Z的长度要长于X轴排布长度,那这个方法就会改而选择Y/Z轴为分割轴,此时就失效了。实际工程中,会结合SAH(Surface Area Heuristic)等更复杂的策略来优化分割。
Comments NOTHING