Jensen 光子映射 — 先撒光子再查密度的两 pass 全局光照
是什么
光子映射(Photon Mapping)是一种两步走的全局光照算法:
- 第一遍:撒光子——从光源往场景里发一大堆”光子”(每个光子代表一小份能量),它们在物体上反弹、最后落地,把”在哪里、从哪个方向来、带多少能量”存进一张表。
- 第二遍:查密度——正常做光线追踪,每碰到一个表面点 x,就去那张表里找”x 周围最近的 N 个光子”,按密度估出这里有多亮。
日常类比:第一遍像把一袋彩色沙子从天花板灯口撒下来,沙子滚来滚去最后停在地板和墙上;第二遍你拿放大镜走到任意一点,数一数这点周围 1 厘米里掉了多少沙子,密的地方就亮、稀的地方就暗。
存光子用的数据结构是 kd-tree(一种把三维空间反复二分的树),让”找最近 N 个光子”变成 O(log N) 而不是 O(N)。
为什么重要
在光子映射出现之前,全局光照里有两类硬骨头啃不动:
- 焦散(caustics)——阳光透过玻璃水杯在桌上聚出的亮纹,水池底部摇晃的网状光斑。本质是”光经过镜面/折射后聚到漫反射面上”。从眼睛反追的路径追踪要碰巧采到这条路径概率极低,结果是噪点满天飞。
- 次表面散射(SSS, Subsurface Scattering)——皮肤、牛奶、玉石那种”光钻进去走一段再出来”的半透明感。每个表面点其实是体积里一团光的出口,纯路径追踪要采体积积分,更慢。
光子映射的处理方式很工程:让光子从光源主动走过去,把难采的路径”预先存好”。第二遍渲染时这些路径是免费查表,不再依赖运气。
工业落地很快:
- Mental Ray(NVIDIA,曾经是电影业默认渲染器)原生支持
- V-Ray(Chaos Group,建筑可视化标准工具)至今还把光子映射作为焦散和 GI 的可选模式
- Corona Renderer(建筑可视化新贵)的 caustics solver 也是光子映射的变体
- Pixar RenderMan 在某些版本里把光子映射用作 GI 的辐照度缓存来源
到 2026 年,纯路径追踪 + 降噪网络(OptiX Denoiser、Intel Open Image Denoise)在很多场景里已经够用,但焦散和 SSS 这两类难题,光子映射仍然是稳定可控的工业答案。
核心要点
可以拆成 三个设计决策:
-
解耦光路 = 解耦数据结构 渲染方程的解需要”从光源出发的路径”和”从眼睛出发的路径”接起来。Veach 1995 的 BDPT 是在每条路径里同时做这件事,复杂且方差高。Jensen 的做法是把光源出发那一段独立成预处理:撒一次光子,得到一个全场景共享的能量分布快照。
-
密度估计 = 用近邻代替积分 要算 x 点的辐照度,理论上是对所有入射方向积分。光子映射把这个积分换成”x 周围半径 r 内有多少光子能量”——这是经典的 k 近邻密度估计(kNN density estimation)。代价是有偏(bias),优势是低方差(光子多了就稳定)。
-
kd-tree = 让密度估计便宜 场景里可能有几百万光子。每次查询都遍历会慢到离谱。kd-tree 把空间反复对半切,查询近邻是 O(log N)。论文 1995 / 1996 年的贡献之一就是把 kd-tree 这个老结构(Bentley 1975)专门为”光子查询”调好了——分裂面选最长轴、平衡构建、固定半径搜索剪枝。
三者合起来,工程上还分了三张图:
- 全局光子图(global photon map)— 所有间接光,粗采样
- 焦散光子图(caustics photon map)— 只存”镜面/折射后落到漫反射”的光子,密采样
- 体积光子图(volume photon map)— 用于 SSS 和参与介质(雾、烟)
分图的好处是查询时按场景类型挑合适的图,不会让焦散的细节被全局图的粗糙度淹没。
实践案例
案例 1:玻璃球桌面焦散
放一个透明玻璃球在桌面上,头顶一盏点光源。纯路径追踪要从眼睛出发追到桌面、随机采一个方向、希望碰巧穿过玻璃球折射后击中点光源——这条路径的概率约等于零,于是焦散位置全是噪点。
光子映射的版本:
- 第一遍从光源发 100 万光子,玻璃球折射后约 5 万落到桌面焦散圈里
- 第二遍渲染桌面那一圈像素,每像素查最近 100 个光子,能量平均 → 干净亮纹
V-Ray 里这就是 Caustics 复选框背后的算法。
案例 2:皮肤渲染
皮肤的真实感来自次表面散射:脸颊在强光下会呈现淡红,因为光钻进皮肤、在皮下散射、几毫米外再出来时已经被血色染过。
体积光子图把光子存进皮肤体积里。渲染时一个表面点不是直接查 BRDF,而是查”我半径 r 内的体积光子能量加权和”。Mental Ray 的 misss_fast_skin 着色器用的就是这套思路。
案例 3:博物馆建筑可视化
一个教堂内部,阳光从彩色玻璃透进来,地面上有彩色光斑,墙上有微弱的间接照明把整个空间染成暖调。
- 焦散光子图处理彩色光斑(高密度,需要细节)
- 全局光子图处理墙面的暖色间接光(低密度,需要平滑)
- 直接光照走标准光线追踪
- 三者叠加 = 一张工业级的建筑可视化效果图
这种”三张图分工”是 V-Ray / Corona 在建筑可视化领域的标准工作流。
踩过的坑
-
光子图是有偏估计:半径 r 里数光子等于把”那一小块面积上的能量平均”。r 选大了会糊掉细节(光斑边缘变模糊),选小了又会掉回噪声。自适应半径(Final Gathering 时按几何曲率调整 r)是工程修补。
-
内存压力大:百万级光子每个要存 (位置 12B + 方向 8B + 能量 12B + 标志 4B) ≈ 36B,3000 万光子要 1GB+。后来 SPPM(Stochastic Progressive Photon Mapping,Hachisuka 2009)用迭代精化让内存恒定不变。
-
焦散难调:玻璃球的焦散需要光子直接落到桌面才有效。如果场景里玻璃球被一层漫反射涂层覆盖,光子会在涂层处停下来——焦散没了。这要求建模师把”焦散物体”标记好,让光子穿过去。
-
光子图不是动画友好的:每帧场景变化都要重新撒一次光子,相机移动倒还好(光子图与视点无关,这是它的强项),但物体运动会让缓存失效。Adaptive Photon Tracing(2010 后的研究)尝试增量更新。
适用 vs 不适用场景
适用:
- 离线渲染中的焦散(玻璃、水、金属反射)
- 次表面散射(皮肤、牛奶、蜡、玉石、果汁)
- 参与介质(雾、烟、丁达尔效应)
- 静态场景的 GI 预计算(建筑可视化、产品渲染)
不适用:
- 实时渲染(光子图构建本身就要几秒到几分钟)
- 频繁动态的场景(光子图随物体动而失效)
- 极小内存预算(嵌入式、移动端)
- 已经有强力降噪网络的纯 path tracing 流程(OptiX 2018+ 让纯路径追踪在很多场景里追平)
历史小故事(可跳过)
- 1986 年:Kajiya 提出渲染方程和路径追踪,理论上能解一切,工程上焦散和 SSS 仍然算不动
- 1993 年:Lafortune & Willems 提出 BDPT,能处理大部分难路径但实现复杂
- 1995 年:Jensen 在 Eurographics Workshop 发表 photon mapping 早期版本
- 1996 年:Jensen 在 Rendering Techniques 给出完整两 pass 框架(本笔记主参考)
- 2001 年:Jensen 出书 Realistic Image Synthesis Using Photon Mapping,成为图形学经典教材
- 2008–2010 年:Hachisuka 系列工作(PPM、SPPM)解决内存问题
- 2018 年:NVIDIA RTX + OptiX 让实时光线追踪进入消费级,纯路径追踪在不少场景压过光子映射
- 至今:V-Ray / Corona / Mental Ray 在专业建筑可视化和影视特效里仍保留光子映射模式
学到什么
-
预处理换查询的经典权衡:第一遍贵(撒光子、建 kd-tree),第二遍便宜(O(log N) 查询)。同样思路在数据库索引、向量检索、空间数据库里反复出现。
-
有偏但低方差比无偏高方差更工程友好:纯路径追踪是无偏的(数学上正确),但方差大、收敛慢。光子映射主动接受少量偏差,换来稳定可控的画面。工程上”看起来对”比”数学上完全对”更重要。
-
分而治之的力量:全局图 / 焦散图 / 体积图三张分开存。每类光路用最合适的密度。这种”按问题特征分桶”的思路,在搜索引擎倒排索引、推荐系统多路召回里同样适用。
-
kd-tree 是空间索引的瑞士军刀:1975 年发明,1996 年用在光子查询,2010 年代用在点云配准(ICP),2020 年代用在 NeRF 加速结构。同一棵树服务三代图形学。
延伸阅读
- 论文 PDF:Jensen 1996 — Global Illumination using Photon Maps(22 页,含完整伪代码)
- 教科书:Henrik Wann Jensen, Realistic Image Synthesis Using Photon Mapping(A K Peters, 2001 / 2009 修订版)
- 后续工作:Hachisuka 2008 — Progressive Photon Mapping(解决内存问题)
- 现代变体:Knaus & Zwicker 2011 — Stochastic Progressive Photon Mapping
- 教程:PBRT 第 16 章 — Light Transport III: Bidirectional Methods(含 SPPM 实现)
关联
- kajiya-1986-rendering-equation —— 光子映射本质是用 Monte Carlo 解 Kajiya 渲染方程,只是把光路拆成”光源出发”和”眼睛出发”两段分别处理
- veach-1995-mis —— BDPT 用多重重要性采样接两段路径,光子映射用密度估计接两段路径,是同一问题的两种解法
- 3d-gaussian-splatting —— 都用空间分布的离散单元(光子 vs 椭球)替代连续场,思路一脉相承
反向链接
- 3d-gaussian-splatting —— 3D Gaussian Splatting — 用一堆 3D 模糊光斑重建场景
- kajiya-1986-rendering-equation —— Kajiya 渲染方程 — 把所有渲染算法统一成一个积分方程