首页 理论教育虚拟现实应用开发实践:全局照明方法解决光线追踪问题

虚拟现实应用开发实践:全局照明方法解决光线追踪问题

【摘要】:全局照明方法试图解决由光线追踪所带来的一些问题。一个光线追踪器往往模拟光线在遇到漫反射表面时只折射一次,而全局照明渲染器模拟光线在场景中的多次反射。由全局照明方法产生的图片看起来真正让人信服。表4-2全局照明的优缺点用直接照明照亮一个简单的场景如下。图4-1用全局照明照亮这个简单的场景图4-2用Terragen渲染场景原本灰色地墙面,再也不是原始的灰色,在它们上面有了些暖意。

全局照明方法试图解决由光线追踪所带来的一些问题。一个光线追踪器往往模拟光线在遇到漫反射表面时只折射一次,而全局照明渲染器模拟光线在场景中的多次反射。在光线追踪算法里,场景中的每个物体都必须被某个光源照亮才可见,而在全局照明中,这个物体可能只是简单的被它周围的物体所照亮。很快就会解释为什么这一点很重要。

全局照明的优缺点如表4-2所示。由全局照明方法产生的图片看起来真正让人信服。这些方法独自成为一个联盟,让那些老式渲染器艰苦地渲染一些悲哀的卡通。但是,而且是一个巨大的“但是”,但是它们更加地慢。正像你可能离开你的光线追踪渲染器一整天,然后回来看着它产生的图像激动到发抖,在这儿也一样。

表4-2 全局照明的优缺点

用直接照明照亮一个简单的场景如下。用3D Studio对这个简单的场景进行了建模,想让这个房间看起来就像被窗外的太阳照亮一样。因此,设置了一个聚光灯照射进来。当渲染它时,整个房间都几乎是黑色的,除了那一小部分能够被光射到的地方。打开环境光只是让场景看起来呈现一种统一的灰色,除了地面被照射到的地方呈现统一的红色。在场景中间加入点光源来展现更多细节,但场景并没有你想象中的被太阳照亮的房间那样的亮斑。最后,把背景颜色设为白色,来展现一个明亮的天空(见图4-1)。

用Terragen渲染了一个天空盒来作为光源,并把它放置于窗户之外。除此之外没有使用任何其他光源。无需任何其他工作,这个房间看起来被真实的照亮了。注意以下几点有趣的地方:

(1)整个房间都被照亮并且可见,甚至那些背对者太阳的表面。

(2)软阴影。

(3)墙面上的亮度微妙地过度。

图4-1 用全局照明照亮这个简单的场景

图4-2 用Terragen渲染场景

原本灰色地墙面,再也不是原始的灰色,在它们上面有了些暖意。天花板甚至可以说是呈现了亮度变化(见图4-2)。

辐射度渲染器的工作原理

清空你脑子里任何你所知道的正常的光照渲染方法。你之前的经验可能会完全地转移你的注意力。

当你想询问一个在阴影方面的专家,他会向你解释所有他们所知道的关于这个学科的东西。你的专家是在我面前的一小片墙上的油漆。

你:“为什么你在阴影当中,而你身边的那一片跟你很相像的油漆却在光亮之中?”

油漆:“你什么意思?”

你:“你是怎么知道你什么时候应该在阴影之中,什么时候不在?你知道哪些阴影投射算法?你只是一些油漆而已啊。”

油漆:“听着,伙计。我不知道你在说什么。我的任务很简单:任何击中我的光线,我把它分散开去。”

You:“任何光线?”

油漆:“是的。任何光线。我没有任何偏好。”

因此你应该知道了。这就是辐射度算法的基本前提。任何击中一个表面的光都被反射回场景之中。是任何光线。不仅仅是直接从光源来的光线,是任何光线。这就是真实世界中的油漆是怎么想的,这就是辐射度算法的工作机制。

接下来将详细讲解怎样制作你自己的会说话的油漆。

这样,辐射度渲染器背后的基本原则就是移除对物体和光源的划分。现在,你可以认为所有的东西都是一个潜在的光源。任何可见的东西不是辐射光线,就是反射光线。总之,它是一个光的来源,一个光源。一切周围你能看到的东西都是光源。这样,当我们考虑场景中的某一部分要接受多少光强时,我们必须注意把所有的可见物体发出的光线加起来。

(1)基本前提:

①光源和普通物体之间没有区别。

②场景中的一个表面被它周围的所有可见的表面所照亮。

现在你掌握这个重要的思想。我将带你经历一次为场景计算辐射度光照的全过程。

(2)一个简单的场景。我们以这个简单的场景开始:一个有三扇窗户的房间。这里有一些柱子和凹槽,可以提供有趣的阴影。

它会被窗外的景物所照亮,我假设窗外的景物只有一个很小、很亮的太阳,除此之外一片漆黑(见图4-3)。

图4-3 窗外很小、很亮的太阳光,窗内场景

现在,我们来任意选择一个表面。然后考察它上面的光照(见图4-4)。

图4-4 表面光照

图4-5 面片一“小油漆”

由于一些图形学中难以解决的问题,我们将把它分割成许多小片(如油漆)(见图4-5),然后试着从它们的角度来观察这个世界。

从这里开始,我将使用面片来指代“一小片油漆”。(www.chuimin.cn)

选取他们之中的一个面片。然后想象你就是那个面片。从这个角度,这个世界看起来应该是什么样子呢(见图4-6)?

(3)一个面片的视角。将我的眼睛贴紧在这个面片之上,然后看出去,就能看见这个面片所看见的东西。这个房间非常黑,因为还没有光线进入。但是我把这些边缘画了出来以方便你辨认。

通过将它所看见的所有光强加在一起,我们能够计算出从场景中发出的所有能够击中这个面片的光强。我们把它称为总入射光强。

这个面片只能看见房间以及窗外漆黑的风景。把所有的入射光强加起来,我们可以看出没有光线射到这里(见图4-7)。这个面片应该是一片黑暗。

图4-6 面片

图4-7 累加所有入射光效果

(4)一个较低处的面片的视角。选择柱子上的一个稍低一些的面片。这个面片能够看到窗外明亮的太阳。这一次,所有的入射光强相加的结果表明有很多的光线到达这里(尽管太阳很小,但是它很亮)。这个面片被照亮了。

(5)墙柱上的光照。为墙柱上的每个面片重复这个过程,每次都为面片计算总入射光强之后,我们可以回头看看现在的柱子是什么样子。

在柱子顶部的面片,由于看不见太阳,处在阴影当中。那些能看见太阳的被照得很亮。而那些只能看见太阳的一部分的面片被部分地照亮了(见图4-8)。

如此一来,辐射度算法对于场景中的每个其他的面片都用几乎一样的方式重复。正如同你所看到的,阴影逐渐地在那些不能看见光源的地方显现了。

图4-8 墙柱光照

图4-9 面片变成光源

(6)整个房间的光照:第一次遍历。为每个面片重复这个过程,给我们呈现这样的场景:除了那些能够从太阳直接接受光线的表面之外,所有的东西都是黑的。

因此,这看起来并不像是被很好地照亮了的场景。忽略那些光照看起来似乎是一块一块的效果。我们可以通过将场景分割为更多的面片来解决这个问题。更值得注意的是除了被太阳直接照射的地方都是全黑的。在这个时候,辐射度渲染器并没有体现出它与其他普通渲染器的不同。然而,我们没有就此而止。既然场景中的某些面片被照得十分明亮,它们自己也变成了光源,并且也能够向场景中的其他部分投射光线(见图4-9)。

(7)在第一次遍历之后面片的视角。

那些在上次遍历时不能看见太阳而没有接收到光线的面片,现在可以看到其他面片在发光了(见图4-10)。因此在下次遍历之后,这些面片将变得明亮一些。

图4-10 其他面片发光

图4-11 太阳光照射到表面后一次反射效果

(8)整个房间的光照:第二次遍历。这一次,当你为每个面片计算完入射光强之后,上次全黑的面片现在正被照亮(见图4-11)。这个房间开始变得有些真实了。

现在所发生的是太阳光照射到表面之后反射一次时,场景的效果。

(9)整个房间的光照:第三次遍历。

第三次遍历产生了光线折射两次的效果。所有的东西看起来大致相同,只是轻微的亮了一些。

下一次遍历也仅仅是让场景更加明亮,甚至第16次遍历也并没有带来很大的不同。在那之后已经没有必要做更多的遍历了。

辐射度过程集中在一个光照解决方案上缓慢地进展。每一次遍历都给场景带来一些轻微的变化,直到产生的变化趋于稳定。根据场景复杂度的不同,以及表面的光照特性,可能需要几次或几千次遍历不等。这取决于你什么时候停止遍历,告诉它已经完成了(见图4-12)。

(10)更加详细的算法描述:面片。面片的属性主要包括:

①辐射光强(emmision)。尽管我曾说过我们应该认为光源和普通物体是一样的,但场景中显然要有光发出的源头。在真实世界中,一些物体会辐射出光线,但有些不会。并且所有的物体会吸收某些波段的光。我们必须有某种方法区分出场景中那些能够辐射光线的物体。我们在辐射度算法中通过辐射光强来表述这一点。我们认为,所有的面片都会辐射出光强,然而大多数面片辐射出的光强为0。这个面片的属性称为辐射光强。

反射率(reflectance)。当光线击中表面时,一些光线被吸收并且转化为热能(可以忽略这一点),剩下的则被反射开去。我们称反射出去的光强比例为反射率。

③入射和出射光强(incident and excident lights)。在每一次遍历的过程中,记录另外两个东西是有必要的:有多少光强抵达一个面片,有多少光强因反射而离开面片。我们把它们称为入射光强和出射光强。出射光强是面片对外表现的属性。当我们观看某个面片时,其实是面片的出射光被我们看见了。

图4-12 场景亮度变化

对于面片的数据结构,了解了一个面片的所有必要属性,就要定义面片的数据结构。结构定义如下所示。