焦散是什么?如何正确地渲染焦散?

作者:Christopher Nichols 2019-12-06

焦散(Caustics)在现实生活中随处可见,但在渲染图中却很稀有。阅读本文以了解为什么省略焦散 – 以及如何以 Corona 轻松渲染焦散。

首先,让我们复习一下什么是焦散。对于任何做过 3D 渲染的人——尤其是光线追踪渲染器——可能对焦散有一些了解。大多数人认为焦散是来自穿过玻璃杯或液体的光线,产生焦散这种有趣的亮点。

大部分的 CG 艺术家都知道焦散需要花费大量的时间和精力渲染(没有时间可以使用云渲染农场),所以通常会省略焦散——而大家还是能在没有焦散的情况下渲染出写实的图像。

有些人则会做额外的操作,用作弊的方式产生焦散使图像更逼真。事实上,焦散对真实世界照明贡献很大。那为什么要忽略焦散呢?

在 Corona 4 中,程序开发人员决定将真正的焦散照明引入到渲染图中。有了焦散,我们可以创建更精确的图像光照。我们目标是让焦散更容易设置,更重要的是,使之对渲染时间有最小影响。

到处都有焦散——让我们来定义什么是焦散

我们先从定义焦散开始,观察为何在现实中,焦散占了图像照明的绝大部分。如前面提到的:大多数艺术家认为焦散是通过玻璃或液体聚焦的光。但计算机图形学的定义是:焦散是任何从光源到高光(反射或折射)、到漫反射表面、再到眼睛(或摄影机)的光照贡献。

本例中,射线从光源到高光反射>到高光反射>再到漫反射>到摄影机。

记住,即使是一片玻璃,比如窗户,也会折射光线。从技术上讲,所有透过窗户的光线都具有焦散性。

玻璃窗是一个焦散的例子。

基本上,如果光线以高光反射的方式从某表面反射,然后光线照亮到另一个漫射表面时,这样的过程就是焦散。这表示在真实世界,焦散占了场景光照很大部分。

由于光线穿过玻璃窗,因此这张照片中的所有光照都可能是焦散的。(100% 纯天然焦散)

你可能会想:如果我们已经忽略焦散这么久了,何以渲染图还能看起来真实?简而言之:焦散多年来一直是靠作弊的方式产生。但在讨论如何造假之前,让我们先解释一下为什么要造假。

焦散是很难计算的

事实上,多年来我们用来计算焦散的算法是非常复杂的。有一些暴力计算法又称蛮力法(brute-force method)能做出些效果,但是通常来讲它们都会花费过多的时间或者做了过多的省略,那最终结果就是:它只能给你提供实际焦散贡献中的很小的一部分。

许多渲染器使用一种叫做“光子映射(photon mapping)”的技术,但传统上,这些方法用起来非常复杂——如果没有正确地设置,渲染会非常慢或者产生很糟糕的结果(甚至又慢又糟糕!) 但之所以有人发明像光子图这样的技术,是因为从纯光线追踪计算焦散是很大的挑战。以下详细说明:

焦散是如何整合进光迹追踪流程中的?

探索光线追踪的最好方法是从观察自然开始。在现实生活中,光线是自光源发出的。光线从物体表面反射。这种反弹可以是通过反射或折射改变方向的镜面反射,也可以是朝四面八方扩散的漫反射。

假设我们根据这个模型来编写光线追踪渲染器——即从光源一直追踪到摄影机——我们将需要发射大量的光线来得到一张完整的图像。这是因为绝大多数来自光源的光线都没有投射到摄影机中。

因此,虽然这种方法最接近真实物理,最终会找到场景的所有照明贡献,但这个光照模型效率极低,甚至用超级计算机也需要几天才能得到简单场景的可用渲染。因此我们必须想出更有效的方法。

如果我们反向追踪光线,从摄影机开始追踪呢?

最好的解决办法是改变射线(rays)的走向。与其浪费大量的时间来计算无法到达摄影机的光线,不如从摄影机开始反向追踪直到找到光源?

这种方法被称为“反向路径跟踪(reverse path tracing)”——简称“路径跟踪(path tracing)”——今日,几乎所有光线追踪渲染器(ray tracers)都使用这种方法。事实证明,使用反向追踪,可以更有效地找到可用的光线 – 以更快的速度得到可用的渲染图。到底有多快呢,由原本需要几天的时间降到几秒钟就完成。

反向路径跟踪,简称“路径跟踪”——几乎所有射线跟踪器都使用这个技术。

问题1:大光源 vs 小光源

但在应用过程中,我们开始发现反向路径跟踪的局限性。当射线离开摄影机寻找光源时,如果这些光源很大,例如环境光或区域光源,就很容易找到光源。但许多光源很小,例如一盏灯泡。因此,许多从摄影机追踪到的光线可能会完全错过光源。而那些随机找到的光源,最终会在渲染中产生白色的小高光——称为“萤火虫效应(fireflies)”。

使用简易的反向路径跟踪,当光源小时,从摄影机追踪到的光线可能完全错过光源——这就导致了 “萤火虫”效应。

解决方案1:使用光源采样(light sampling)来寻找微小光源

但是有个解决方案可以帮助渲染器确保不会错过光源——即使是非常小的光源。这种方法叫做“光源采样”。作法是随机地在光源上的选取采样点,并将采样点连接到渲染路径。这种方法确保射线追踪到光源上。对于小且直接光源非常有效。

光源采样(light sampling)是确保渲染器不错过光源的解决方案。适用于所有光源尺寸。

问题2:间接光源,比如焦散,不能使用光源采样

请注意,上述解决方案谈的是 “小的直接光源“。问题是这种方法不适用于间接光源。其中一个原因是,虽然知道光源在哪里,但我们不知道间接光源的位置——说到间接光源,主要指的是焦散。一旦光线通过表面反射或折射,我们就不知道光源在哪里,直到追踪到光源。

这都是啥呀?不幸的是,光源采样的技术不能应用于间接光照,例如焦散。

焦散是很难计算的。光源采样能处理直接光源的“萤火虫”,但是随机光线仍然会产生萤火虫效应。

所以当光源采样解决掉了直接光源的萤火虫效应时,我们仍然会从焦散高亮点产生很多萤火虫效应。与增加光源采样技术之前遇到的问题是类似的,随机光线可能遇到焦散的高亮点,并产生萤火虫效应。那么我们该如何应对呢?

作弊的技巧1:箝制(Clamp)一切!

许多渲染器都使用这个解决方案——箝制所有东西——讲白了就是用漂亮的话术来宣告认输。如果偶然遇到一束来自焦散的非常明亮的光线,“那么就箝制掉”。(或者干脆忽略之。)

箝制一切!

到目前为止,穿过玻璃物体的光线没有产生焦散,我们只得到了影子。在有些情况下,箝制一切可以得到一张有点可信的渲染图。

但如果你回过头来考虑,即使是一片玻璃,技术上来说也是焦散性的,那么使用这种方法,我们会得到这样的图像:

对所有物体使用箝制的作弊技巧会在本该明亮的地方产生阴影。

作弊的技巧2:忽略玻璃

接下来讲另一个作弊方式。既然问题出在射线要么穿过高光表面,要么是在高光表面反弹,如果我们直接忽略玻璃,直接穿过它追踪射线呢?那我们就回到了光源采样那一步,回到了最简单的情形。

如果我们忽略玻璃,光线会直接穿过玻璃窗,到达室内漫反射表面,然后散射开来。

这个解决方案对建筑室内非常有效,例如:

在渲染时忽略玻璃,不会像上一个方法产生阴暗的影子,而是照亮建筑室内,观众可以自然地理解玻璃的存在。

但不幸的是,此时玻璃物体本身的效果就不太好了:

上图展示了忽略玻璃所造成的情况。

作弊的技巧3:混合玻璃/假玻璃/建筑玻璃

第三招,我们让一些光线穿过玻璃窗,但不是全部光线,这取决于玻璃的厚度、角度或颜色。这种方法可产生稍微可信的阴影形态:

这里我们用了介于中间的材质——通常称为“混合玻璃”、“假玻璃”或“建筑玻璃”。

这是大多数渲染解决方案在过去十年所用的技巧。有时被称为“混合玻璃”,“假玻璃”——还有个很时髦的名词:“建筑玻璃”。然而,以上这些作弊技巧没有一个是真正的焦散,因此没有一个是正确的。

如果我们不用作弊的技巧,那如果从双向追踪呢?

考虑到要得到正确渲染焦散图多么复杂且耗时,那么传统的作弊方法——比如“建筑玻璃”——可以算作是很好的折衷方案。直到 Corona Renderer 4 的出现。

如果我们告诉你,你可以很容易地渲染焦散,且只会对你的渲染时间产生很小的影响,会怎么样呢?这就是 Corona Renderer 4 的威力了——之所以达到这个效果,Corona 开发团队用的方法只是回归到基础罢了。

我们已经知道,如果追踪来自摄影机的光线(反向路径),在几乎所有的情况下,除了焦散以外,其他效果都非常好。而从光源开始追踪(正向路径),焦散效果会更好。但是,如果根据我们的需要,根据不同的情况,同时结合正向追踪和反向追踪两者的优点呢?注意这可并不是全新的概念——事实上,这项技术从 90 年代就已经存在了。1993年,Lafortune 和 Willems 共同发表了一篇关于双向路径跟踪的论文,里面提到了这个概念。

想法1:使用双向路径跟踪

第一种想法是所谓的“双向路径跟踪”(Bi-Directional Path Tracing ,简称BDPT)。这个解决方案是相当快的,但当涉及到焦散时,最终会丢失了太多的焦散射线,而最终解算的结果,在很多情况下,几乎看不到焦散。

使用双向路径跟踪(简称BDPT),很快得到的结果,但是焦散效果太少。

VCM 结合了太多的策略(而且速度很慢)

另一种作法是在 2012 年有人提出的,称为“顶点连接和合并(Vertex Connection and Merging)”,简称 VCM。该方法结合了光子映射和 BDPT。虽然这种方法更容易,可得到良好的结果,但会过度采样太多的射线,才能获得良好的结果,大幅增加渲染时间。

使用 VCM -结合光子映射和 BDPT -上图需要大量的过度采样和非常多的射线,才能产生好的结果。

Corona 使用反向路径追踪和光子映射

新版 Corona 渲染器的策略,是更有效地利用反向路径跟踪和光子映射。但是与其让用户费力地建立有效的光子映射,Corona 渲染器会替您找到有效的光子映射,为您最高效率地得到最正确的结果。

这意味着,现在,我们能够使用不过于少的光线(如 BDPT 技术)或者不过于多的光线(如 VCM 技术)来渲染焦散。Corona 使用了恰到好处的光线,并产生了正确的焦散效果,最小程度地增加渲染时间。

套句《格林童话 – 三只熊的故事》中那位金发姑娘的话来说: “Corona 渲染器不多不少刚刚好”。使用光子映射结合反向路径跟踪,能产生真实的焦散和有限的渲染时间增加。

这不代表焦散对 Corona 渲染器来说是个简单任务。撰写程序代码时,需要解决非常复杂的问题,比如:何时用正向射线,何时用反向射线?如何将这两种方法的结果结合起来?诸如此类。

但对用户来说,使用起来非常容易。产生焦散只是 Corona 4 中的一个勾取的选项方块。想渲染焦散吗?你将得到一个更接近现实的图像——焦散选项只会增加渲染时间约 50%。

“如果某张图具有焦散,那张可能是照片或是用 Corona 渲染出来的。”

——Vladimir Koylazov

在与 Vlado 的谈话中,他说他喜欢玩那种“猜猜看是CG或是照片”的小游戏。他总是会在图片中寻找焦散的痕迹——也就是说,如果图像中有焦散,那就应该是真的照片。但是现在 Corona 4 已经使得在渲染中添加焦散变得如此简单,那么 CG 或照片的测验将变得更加困难!

还有一件事……

值得注意的是,因为 Corona 现在能以非常精确的方式表现所有的光线——包括适当的焦散贡献——还有许多其他渲染软件包中看似微不足道的功能,包括:

正确的焦散动态模糊。举例,如果在街上看到一辆车的车轮发出的焦散,那辆车正在向前移动,而摄像机正以同样的速度跟踪那辆车。街道应该有相对于摄影机的运动模糊,但是焦散——街道上的焦散——不应该出现动态模糊。虽然理应如此,但您可能不会察觉到,其他的渲染引擎无法正确地做到这一点。

Corona 4 的一大知名功能是光线混合器(Light Mixer)。由于焦散可以从任何不同的光源产生,所以焦散也可独立出来,可在光混合器中正确地混合。

如果你正在用不同的渲染引擎做焦散的实验,请试着创建一座有涟漪的游泳池;游泳池旁放一面墙——墙上照理来说应该出现泳池的焦散效果。使用 Corona 渲染器,您将同时在泳池底部和墙上看到焦散。

尽管这合乎逻辑,但对于其他渲染解决方案不见得做得到。原因是:透过高光反射元素实际上很难看到焦散。而池底的焦散,是透过光线的折射才观察到焦散的。

本场景由 3Darcspace 工作室制作。以 Corona 渲染:渲染时间为 38 分 19 秒,共80个pass。

读到这里,你完全了解了焦散是多么重要,理解了为何长久以来一直被忽视,以及为什么众多渲染器要作弊这么多年。

你现在可以测试 CORONA 中的焦散特效,看看这么多年渲染图中漏掉了什么。