这篇文章简单讨论了一个自主实现的光线追踪渲染器。
光线追踪与光栅化的根本区别
光栅化的核心是几何到像素的映射。它把三角形投影到屏幕,然后对覆盖的像素着色。光照、阴影、反射等效果都需要额外技巧模拟,比如阴影贴图、屏幕空间反射。这些方法效率高,但本质上是近似,无法自然表达光在场景中的传播。
光线追踪则反过来:它从相机出发,对每个像素发射光线,追踪这条光线在场景中与物体的交互过程。是否被遮挡?是否反射?是否穿过透明介质?这些问题的答案直接决定像素颜色。这种方式天然支持精确阴影、镜面反射、折射和间接光照,因为它模拟的是光的传播逻辑,而不是视觉投影。
这种差异决定了两者的优化方向完全不同:光栅化优化的是几何处理和片段着色吞吐;光线追踪优化的是光线与场景的求交效率和采样策略。
第一步:用球体而非网格
不要一开始就加载 OBJ 或处理三角形网格。三角形求交虽然标准(比如 Möller–Trumbore 算法),但涉及深度细节,麻烦。
先用球体。ray-sphere 求交有解析解,快速实现。你可以快速验证:
光线是否正确命中最近物体?
表面法向方向是否正确?
阴影射线是否被正确遮挡?
当一个红球在白地板上投出清晰的阴影,并且被蓝光源照亮后地板呈现微弱的红色漫反射时,说明你的做的差不多是对的。这时再引入三角网格,才不会在调试时陷入麻烦,导致浪费一个上午和午饭的时间。
加速结构重要,重要!!!!!!
没有加速结构的光线追踪无法扩展到复杂场景。假设有11451400个三角形,每条光线都遍历全部求交,那别做了,毁灭吧。
BVH(Bounding Volume Hierarchy)是目前最实用的选择。它通过递归将物体分组,每组用一个包围盒(通常是 AABB)包裹。光线遍历时只需测试可能相交的子树,大幅减少求交次数,不会让你的那个垃圾电脑模拟喷气式发动机。
构建 BVH 的关键是分割策略。SAH(Surface Area Heuristic)是一种经验有效的启发式方法:它估算在某个位置分割后,后续光线平均需要测试多少图元,选择期望代价最低的分割方式。虽然 SAH 不是最优解,但推荐你用这个。
BVH 的遍历可以用栈实现,避免递归深度过大。同时,优先遍历距离相机更近的子树,有助于尽早找到最近交点,提升缓存局部性。
路径追踪也重要
Whitted 光线追踪只处理镜面反射和折射,无法表现漫反射表面之间的间接光照。例如,红墙照亮白地板的效果它无法生成。
路径追踪通过随机采样光线弹射方向,能正确求解渲染方程。虽然单次样本噪声大,但多样本平均后收敛到物理正确的结果。
要让路径追踪实用,需解决两个问题:
1. 直接光照收敛慢
解:每次弹射时,显式采样一个光源方向(即“连一条光线到光源”),单独计算这部分贡献。这称为 next event estimation,能显著提升直接光照的信噪比。
2. 漫反射采样效率低
解:对 Lambert 表面,使用 cosine-weighted 半球采样,而不是均匀采样。因为辐射亮度与入射角余弦成正比,这样采样更集中在高贡献区域。
这两项改进不需要复杂理论,效果值得你的时间投入。
材质抽象
不同材质的行为可以统一为一个接口:给定入射方向和表面信息,返回一个出射方向(或概率分布)以及对应的权重(attenuation)。
Lambertian 材质:采样一个 cosine-weighted 方向,权重为 albedo / π。
镜面材质:出射方向为反射方向,权重为反射率。
电介质(如玻璃):根据 Fresnel 项决定反射/折射比例,分别处理。
这样,主光线追踪循环无需关心具体材质类型,只需调用统一的函数。逻辑清晰,也便于扩展。
文章评论