Scratchapixel(7)光线运动
光线传播
想象一条光线从光源发射, 到达漫射表面被反射,然后到达镜面,然后再到达漫射表面,最后到达眼睛。如果我们把光标记为L
,把漫射表面标记为D
,把镜面标记为S
,把眼睛标记为E
。那么我们可以将该光线的路径标记为LDSDE
。
最短的光线路径是LE
,即光直接从光源到达眼睛。如果光线在表面弹射一次,那么路径可能是LSE
或LDE
,而这就是直接光照的例子。比如你看向湖面上的阳光。而如果你看着湖面上的倒影,那么路径应该是LDSE
,这是一个间接光照的例子。
Paul Heckbert 提出标记路径的概念在他1990年发布的一篇论文上,Adaptive Radiosity Textures for Bidirectional Ray Tracing。我们也可以用正则表达式来表示光路。如L(D|S)*E
你会想,这很不错,但是和渲染有什么关系呢?我们追踪从眼部出发的光线(眼部射线,初级射线或者摄像机射线),并检查这些射线是否与场景中的物体接触。我们把交点设为P,接下来要做两件事:1. 计算有多少光从光源到达P(直接光照)。2. 计算多少光间接从其他表面反射到达P(间接光照)
- 要计算P点光照的直接光照,我们从P点追踪一条到达光源的射线,如果射线和其他物体接触,那么P点处于该光源的阴影之中。(这也是为什么我们叫这些射线为阴影射线--shadow rays)
- 间接光照的计算通过生成新的射线,称为二次射线,将其从P点射向场景中。下面将详细解释。
二次射线
如果这些二次射线与场景中的物体相交,那么我们可以假设光线沿着射线从它们相交的表面到达P点。我们知道被表面反射的光线数量取决于到达表面的光线数量。因此我们要知道有多少光线顺着二次射线反射到P点需要:
- 计算到达二次射线和表面交点的光线数量
- 测量表面反射了多少光线到P点,使用二次射线的方向作为我们的观察方向
记住镜面反射是视角相关的:镜面表面反射的光线量取决于你从哪个角度观察反射。漫反射是视角无关的:漫射表面反射的光线量不随角度改变。因此除非是漫射的,一个表面在不同方向反射的光线数量不相同。
计算有多少光到达二次射线和表面的交点,和计算有多少光到达P点没什么区别。这取决于表面的属性,并且由着色器(shader)来完成。我们在下个章节讨论着色器。
场景中的其他表面也可能反射光线到P点。显然我们不知道是哪一个表面,光线可以来自P点表面上方的所有有可能的方向(光也可以来自表面下方,如果这个物体是透明或半透明的--但是我们暂时忽略这个情况)。但是我们不能测试每个可能的方向,我们只测试一部分方向。这就类似于采样测量求平均值一样。如蒙特卡洛算法(Monte Carlo ray tracing)。射出一些射线来近似计算到达点的光线量。缺点在于这只是一个近似值,优点在于它至少解决了这个困难的问题。
计算间接光照是一个递归(recursive)过程。二次射线从P点产生,而这些二次射线产生了新的交点,从这些新的交点又产生了新的二次射线,如此往复。我们计算光线弹射的次数,这可以是无限的。所以我们为了避免这种情况,我们一般在一定次数的弹射后就停止生成二次射线。因此P点可能看上去比实际上更暗,因为剩余的弹射被忽略了。
然而幸运的是,每次光线弹射的时候都会损失一小部分能量,这意味着随着弹射次数的增加,弹射对该点间接光照的贡献递减。所以你可能会认为,再计算更多的弹射对图像的影响太小,配不上模拟它所花费的时间。
举个例子,如果我们决定每与表面接触就生成32条射线,来计算间接光照的量。那么在第一次弹射后我们有32条二次射线。每条二次射线又与表面接触生成新的32条二次射线,我们就有了1024条射线。在三次弹射后我们就有了32768条射线!
上面一大串解释是为了告诉你,无论是计算P点的间接还是直接光量,规则都是很简单的。唯一牺牲的物理精度就是限制了我们计算弹射的最大次数,这是为了确保模拟不会永无尽头地进行下去。在CG中我们称这个算法为单向路径追踪(unidirectional path tracing),它属于光线运动算法中的路径追踪的类别。这是最简单也是最基础的基于光线追踪的光运动模型(light transport models),也被叫做Whitted光线追踪。它被称为单向(unidirectional)因为它只从眼部向光源追踪。
经典的光线追踪通过追踪从眼部到场景中的光线来生成图像,递归地探寻镜面反射和发射的方向,并追踪指向点光源的射线来模拟阴影(Paul S. Heckbert - 1990 in "Adaptive Radiosity Textures for Bidirectional Ray Tracing" )
这个方法最初由Appel在1986年提出–(“Some Techniques for Shading Machine Rendering of Solids”),后来被Whitted进行了进一步的改进--(An improved illumination model for shaded display -1979)。
首次提出该算法时,Appel和Whitted只考虑了镜面和透明物体。这只是因为为这些材质计算二次射线相比漫射表面需要更少的射线。为了计算一个镜面的间接反射,你只需要生成一条反射射线到场景中。如果物体是透明的,你需要为反射和折射分别生成一条射线。然而如果表面是漫射的,为了近似计算P点间0接光照的值,你需要在朝向入射点法线的半球内生成更多射线。这远比仅仅计算反射和折射花费更多,所以他们二人在刚开始只发展了镜面和透明物体的概念。但是将他们的算法拓展到间接漫反射是很直接的
但是事情没有那么简单。拿单向路径追踪来举例,一些光线路径相比其他的更复杂,更难以高效计算。尤其是对于那些涉及到镜面间接照明漫射表面的光线路径。举个例子:
从图中看出,光线由画面上方的光源发射,经一个(透明)玻璃球折射,然后被聚集到下方屏幕的一个点上。这被称为焦散。注意:实际上光线没有直接到达P点(P处于球体的“阴影”下)。所有的光都间接来自于球体,通过折射和传递的方式。这种情况下更自然的方法是从光源追踪光线到达眼睛。但是如果我们换另一种方式呢?
计算间接到达P的光线,假设P点表面是漫射的,在随机方向生成一些射线来检查哪个表面将光线反射回P。但是这样做我们会失败,因为所受的光都来自于球体的底部表面。显然我们或许可以通过从P点向球面发射射线来解决这个问题,但由于我们的方法限定了我们不能有先验知识来知道光如何从光源传播到场景中的任意一点。(我们没有先验知识来知道光处于球体上方,而且无法假定这个光就是通过折射和传递对P点起作用的光)。唯一的办法就是随机方向生成射线,其中某条可能击中球体并回溯到光源(这并不能保证)。所以非常不幸地我们将无法成功计算有多少光间接到达P点。
使用Heckbert的光路命名规则,我们可以说这种LS+DE
的光路在CG中很难通过基本的眼部追踪手段去模拟。即如果在被漫射表面反射到眼睛之前,如果光线还经过了一个或多个镜面的反射,那么我们很难使用单向路径追踪去模拟。
光线传播的艺术登场
显然,简单地从眼部追踪在某些例子中不太有效。当场景中只有漫射表面的时候它很好用,但是如果场景中混杂着镜面和漫射面时,就会出现问题。
这意味着我们需要发展策略(算法)来适应各种可能的场景。我们需要一种策略来像模拟LD+E
路径一样高效地模拟LS+DE
路径。这促成了发展新的光线传播算法(light transport algorithms)来解决该问题。
光线传播算法不是很多,但是也有一些。在任何时候提出的最佳光传输算法的规则中没有规定必须使用光线跟踪来解决问题 。实际上,有许多方法使用了所谓的混合方法。光子映射(Photon Mapping)是一个例子。它需要预先计算存储在特殊数据结构中的光照信息(如光子映射表或点云),再进行图片渲染。通过利用结构中的信息,更高效地解决了这些困难的光路。在前面我们说我们没有先验知识知道光在球体上方。然而光子映射某种意义上在渲染前观察场景并试图获取先验知识,来了解光子去了哪里。
但是多路渲染很难去管理,它需要额外的工作,额外的磁盘空间,而且直到所有的预计算完成才能开始渲染。(所以你需要等一会才能看到实际效果)。在很长一段时间内这些算法很流行,因为它们实现了渲染例如焦散这样的很难用光线追踪实现的效果。所以能实现总比什么都没有强。当然,一个统一的方法是更好的,它不需要多路,也可以流畅地整合进现有的框架。
一些算法以光线追踪为基础,延伸了单向路径追踪的概念。我们可以使用另一种算法,双向路径追踪(bi-directional path tracing)。它的原理很简单,对于每一条你生成的从眼部到场景中的射线,你同样可以生成一条射线从光源生成到场景中,并试图连接各自的路径。有许多重要的光线传播算法,如单向路径追踪,双向路径追踪,梅特罗波利斯算法(metropolis light transport ),(instant radiosity),光子映射。
总结
可能CG中最常见的神话之一就是,光线追踪是解决全局照明的终极和唯一的方法。但是它也有限制,它不是唯一的方法。我们可以把光线传播算法分为两种:
- 不使用光线追踪的算法,如光子映射,辐射度算法。
- 使用,并且只使用光线追踪的算法。
只要该算法有效地捕获难以用传统的单向路径跟踪算法捕获的光路,现代的实现中确实倾向于仅仅基于光线跟踪的光传输方法,仅仅因为光线跟踪是一种更自然的思考方式,并提供统一的计算全局照明的方法 (意味着你不需要辅助结构来存储光线信息)。但是这只是对于离线渲染来说,对于实时渲染,还是基于不使用光线追踪的算法,如阴影映射等。