Intro
阅读之前最好对光线追踪, 光照方程, 辐照度量学等有一定的了解, 且本文只对光子映射做粗浅的介绍.
光子映射 Photon Mapping 和光线追踪一样是真实感渲染领域的一种渲染方法. 他能够很好的解决光线追踪类方法相对难以处理的 Caustics 等现象. 并且一般情况下能以一定的内存空间为代价, 获得更高的效率.
另外与使用 Monte Carlo 的各种光线追踪方法相比, 当算法出现 Artifact(瑕疵) 的时候, 光线追踪方法一般呈现高频信号, 很容易被人眼察觉. 光子映射产生的 Artifact 则多为低频信号, 相对不容易被人眼发现.(在图像上的高频信号意味着小范围内颜色上剧烈的变化)
算法核心
光子映射是一个 Two-pass 的方法, 第一个 pass 为光子追踪, 第二个 pass 进行渲染
- 光子追踪(Photon Tracing Pass): 从光源像场景发射携带能量的光子, 光子在场景中与物体表面进行交互(反射或折射), 并在非光泽表面物体相交时记录, 最后将这些光子存储在一个全局的光子图(Photon Map)中.
- 渲染(Rendering Pass): 使用传统的路径追踪技术从摄像机通过屏幕向场景中发射光线并在场景中传输, 当光线与物体表面相交时, 该顶点光照计算中的漫反射部分则使用统计的方法从前面产生的光子图中计算得出.
光子追踪pass与光线追踪的区别
与光线追踪不同的是光子映射的光子在与物体表面交互时, 要么被反射, 要么被折射, 要么吸收. 这是根据物体表面的材质参数按概率的方式(如俄罗斯轮盘, Russian Roulette)确定的. 而光线追踪方法在必要时, 一束光线可以根据表面材质, 一部分被反射, 一部分被折射, 从而分成多束光线.
光子存储
我们在光子中会存储: 光子的位置, Flux(功率), 入射方向和一个在 KD-Tree 中使用标志位.
struct Photon {
float x, y, z; // 位置
char p[4]; // RGBE编码的flux
char phi, theta; // 入射方向
short flag;
};
使用 FLux 的原因与我们需要使用统计学中的密度估计有关(密度估计也是在照片级真实感渲染领域的渲染方程中经常出现概率密度函数PDF的原因). 密度估计所涉及的数据一定是具有任意可加性的, 那么像 radiance 这种与方向有关的物理量则不适合使用. Flux 是单位时间内发出的能量, 与方向面积等都无关, 适合存储在光子中.
光子最终会被存储到 PhotonMap 中, 我们一般将 PhotonMap 处理为一颗平衡 KD-Tree. 因为算法后半在计算某点的 radiance 时会涉及很多空间查找操作, 用 KD-Tree 来存储光子可以加速光子查找.
Radiance 计算
根据 BRDF 理论, 某点 x 处沿着 w 方向的 radiance 是沿着角度 w’ 方向到达 x 点的 radiance 与 BRDF 系数 f 的乘积在该点半球空间上的积分(公式中的点乘项与立体角, Lambert’s Cosine Law等有关).
为了计算这个积分, 我们需要知道 p 点处入射的 radiance ( 公式中的 Li ). 光子图会为我们提供当前位置附近一定数量光子的 flux, 根据 Radiometry (辐照度量学) 可以进行 radiance 和 flux 之间的转换.
积分本质上可以理解为求和, 具体的公式推导我们不做分析, 我们只需要理解: 光子映射通常使用 kNN 最近邻估计, 通过选择 p 点一定范围内的 n 个光子来计算目标位置的 radiance.
参考文献
Jiff:Lightmass分析之 经典Photon Mapping算法介绍
Jiff:LightMass源码分析之光子追踪实现
Jiff:LightMass源码分析之光照评估
《Physically Based Rendering : From Theory to Implementation》Matt Pharr
《全局光照技术 从离线到实时渲染》秦春林
《A Practical Guide to Global Illumination using Photon Maps》Henrik Wann Jensen