先聊个现象:把一束阳光透过装满水的玻璃杯投到桌面上,墙上会出现一道弯弯的亮纹,水里还会泛起一缕缕打转的光丝。又比如游泳池底那张不断晃动的光网,或者一枚玻璃戒指在纸上投出的一圈圈高亮光斑。这些亮得出奇、边界清晰又带着明显聚集效果的光影,就是焦散(Caustics)。
做渲染的朋友对这个词应该不陌生,尤其当你第一次用路径追踪渲染玻璃器皿,兴致勃勃地把采样数拉到5000,结果桌面上的高光还是布满彩噪,怎么调都压不下去——那一刻你才算真正认识了焦散。它不是什么边缘特效,也不是后处理能糊弄过去的装饰元素,它是光的真实输运结果。正因如此,它成了离线渲染器里最考验算法功底的一类效果,也成了实时光栅化渲染几乎绕不开的难题。
这篇文章我会从物理本质、主流算法、实际案例到排查心得,把这几年我在项目里和焦散“搏斗”的经验完整摊开。适合刚上手Blender、Mitsuba等离线渲染工具的新手,也适合准备用实时方案模拟焦散效果的技术美术。你不需要一上来就懂辐射度量学,但我会尽量把原理讲得足够清楚,让你踩坑之后不是瞎调参数,而是能推导出问题出在哪一环。
1. 焦散到底是什么:一个常见又难搞的光学现象
1.1 焦散的物理本质
先别急着开渲染器,我们把“焦散”这个词拆开。它对应的英文Caustics来源于希腊语“καυστός”,意思是“燃烧的”。这个命名不是没来由的——早在古希腊,阿里斯托芬的剧作里就提到用装水的玻璃球聚光生火。你用手电筒照一个装满水的球形烧瓶,就能看到光线被“拧”成一束,落在纸上形成极亮的点,温度足够让纸冒烟。这就是聚焦。
焦散的物理本质,其实就是光的能量在传播过程中被折射或镜面反射重新分配,形成局部能量密度剧烈升高的区域。直白点说,光本来是均匀地往前跑的,结果穿过一个透明球体后,不同位置的光线被弯折到几乎同一个方向,在这个方向上能量密度骤然拉升,视觉上就出现一条细亮的线或一片亮斑。
这跟普通的反射镜面高光不一样。镜面高光是光线直接从光源打到光滑表面、以等于入射角的角度弹进眼睛形成的,它在观察者位置不变时通常是一个清晰的小点。而焦散是光线经过至少一次折射或反射之后再打到某个漫反射面上,形成的光斑位置和形状取决于透明体的曲率、折射率和光源形状。更关键的是,焦散的能量密度可以比入射光高出好几个量级,这也解释了为什么同样一盏灯照在普通白墙上只有灰白一片,一旦中间隔了一个玻璃球,桌面就可能出现刺眼的亮纹。
如果把这个问题用渲染术语表达:焦散是辐射度(Radiance)在空间上的聚焦现象。它天然带有高频特征——光斑的亮度分布非常锐利,尺度很小、对比度极高。这种高频特征正是蒙特卡洛渲染器最害怕的东西。
1.2 为什么说焦散是渲染器的“硬骨头”
要理解焦散为什么难渲染,得先知道路径追踪是怎么工作的。
路径追踪的原理是从摄像机出发,向每个像素发射大量光线,光线在场景里弹射,每撞到一个表面就根据该表面的BSDF采样一个新的方向,直到命中光源。这个过程等价于在“光传输路径空间”里做蒙特卡洛采样,把所有样本的亮度加权平均得到像素颜色。
问题在于,焦散路径的结构很特殊:光源 → 镜面/透明体折射面 → 漫反射面 → 摄像机。在这种路径里,光必须在玻璃表面发生一次“定向折射”,然后恰好打到摄像机对着的那片漫反射区域。如果你从摄像机出发随机采样方向,想精确命中那条“折射后恰好指向光源”的方向,概率极低。
打一个比方:你站在操场上,闭着眼睛朝四面八方扔飞镖,要扔中远处看台上某个固定座位,这概率基本是零。传统的路径追踪就是在干这件事——它在每块玻璃表面随机选折射或反射方向,要恰好连到光源上的概率太低,于是焦散区域全是黑色噪点,只有把采样数拉到几万次,噪点才勉强收敛。
正是因为这种“概率性盲选”的低效性,焦散成为了经典路径追踪的短板。当然,现代渲染器已经用各种手段缓解这个问题,比如双向路径追踪、光子映射、MLT,后面我们会逐一展开。但要记住一个总原则:焦散的难度不在“光多不多”,而在“找到那条正确的光路难不难”。
1.3 焦散的分类:反射焦散与折射焦散
在动手渲染之前,先建立两个基本分类,后面排查问题会非常有用。
第一类是反射焦散(Reflective Caustics)。光线经过镜
