Chapter 5 – Shading Basics 着色基础

5.2 光源

在上一小节的Gooch着色模式中,光源的影响非常简单;其提供了着色模式中所需要的光线方向向量。当然,在真实世界中,光照是非常复杂的。可以存在多个光源,且它们的尺寸,形状,颜色,以及强度可以各不相同;间接光则增加了更多的变化。我们将在第九章中看到,pbr或者说写实的着色模式需要将我们上述所提到的参数都纳入计算公式。

与写实的着色模式相反,非写实的着色模式将会以不同的方式运用光照,具体则取决于游戏或者应用的需求。一些完全非写实的着色模型则完全不会考虑光照,或者类似于上一小节提到的Gooch着色模式,只是将光照作为一个方向向量。

光照复杂性的第二点在于着色模式如何对光照进行“反应”。也就是说使用该着色模型的表面,在没有或者缺乏光照时和接受光照时外表的差别。这又意味着两个部分:光源到物体表面的距离和阴影(我们将在第七章中学习),例如,物体表面是否面向光源等各种因素。

缺乏光照或者接受光照可以由一个简单的插值表示,也就是说,光照的强度是有一个范围的,例如,0到1,或者一个没有边界的值。一般来说,我们将着色模式分为光照部分与非光照部分,且我们会使用光照强度klight来对光照部分进行线性缩放:

我们也可以将光照强度转换为光线的颜色向量clight

并且将其变为多个光源,

而非光照部分funlit(n,v)则表示着色模型中“不被光照影响时的外观”。这一部分可以拥有各种形式,其取决于我们想要的外观表现和游戏的类型。例如funlit()=(0,0,0)意味着,如果没有光源,那么物体表面将会是完全的黑色。一般来说,我们将非光照部分来表示一些间接光或者一些并不来自于场景内摆放的光源的光线,例如,天空的光或者由周边物体反弹的光。而这一部分知识点,我们将在第十章和第十一章学习。

我们之前提到,如果光源对于物体表面某一点来说,其光线方向l与该点的法线向量n的夹角大于90°,那么这个光源并不会对物体表面的这一点造成影响。这可以认为是光线对于物体表面影响的一种特殊情况。这一理念不但适用于写实渲染也适用于非写实渲染。

光线对于物体表面的影响可以表现为一组射线,而撞击到物体表面的射线的浓度就是对物体表面进行着色时所需要的光线强度。如下图所示,每条撞击到物体表面的光线之间的空间向量l和向量n之间的夹角大小成正比。也就是说,撞击到物体表面的光线的浓度与向量l与向量n的余玄值成正比。这里我们也能看到,为什么我们将光线向量l的方向定义为光线发射方向的反向;这样我们在将其与法线向量进行点乘时就不需要取该向量的反向了。

更准确的说,只有当两个向量的点乘为正数时,点乘才与射线的浓度成正比。如果点乘的结果为负数,那么意味着光线来自于物体表面的背后,那么光线并不会对表面造成影响。所以,在将光照着色乘以光照点乘的结果之前,我们需要对点乘的结果进行clamp,如果其结果为负数,那么将其设为0。

即使对于非写实的着色模式,大多数情况我们也会使用上述这种支持多个光源的着色模式,因为其能够确保光照的一致性,尤其是对于那些处于阴影或者远离光源的表面。

对于函数flit来说最简单的选择是将其转换为一个恒定的颜色csurface,这样的话,上述着色模式中的光照部分就是Lambertian着色模式,其在1760年首次被提出!但是这个着色模式只适用于理想的漫反射表面,例如,完全没有光泽的表面。而我们在第九章中将会更详细的介绍Lambert着色模式,许多着色模式都以该表达式作为基础。

在本小节的各个公式中我们可以看到,光源通过两个参数来影响着色模式:向量l,其由物体表面一点指向光源以及光线的颜色clight。虽然有各种不同类型的光源,但是它们之间的主要区别就在于这两个参数。

我们将在之后的几个小节中为大家介绍几种常用的光源,它们都有一个共同点:对于表面上的一个点,每一个光源只会从一个方向l来影响(照亮)这个点。换句话说,游戏场景中的光源不同于现实世界中的光,其只是一个坐标点。在第七章和第十章中,我们将讨论另一种光源,其会以多个方向来影响表面上的点,例如,“区域光(area light)”。

5.2.1 方向光

方向光是一种最简单的光照模型。光线向量l和光线颜色clight都是恒定值,不过clight可能会由于阴影减弱。方向光并没有位置这一概念,如果光源距离物体非常远,那么我们会使用方向光。例如,场景中的太阳等等。

我们还可以进一步改变方向光的功能,保持其光线向量l不变,但是使clight不断变化。其常用于一些区域型的效果。例如,一块区被由两个立方体的空间所包裹,在立方体外部clight为(0,0,0),在内部则是另外一个值,那么在两个立方体之间的区域则是两个clight进行插值。

5.2.2 精准光(Punctual Light)

这里的punctual并不是准时的意思,而是指光源是拥有位置的(不同于方向光)。这一类光源指的是那些由一个坐标点射出光线的光源。所以点光源和聚集光是两种不同类型的精准光。光线向量l则取决于物体表面某一点p0基于光源plight的位置:

而基于上述等式求出的向量都是单位向量,这一操作也是常见运算,所以大多数着色器语言都内置了该函数。但对于精准光源来说,我们需要一个中间值r,其表示光源到表面上这一点的距离。我们需要使用这个值来计算光源基于距离衰减后的颜色clight

点光源/泛光灯

如果一个精准光源,其向所有的方向射出相同的光线,那么我们将其称为点光源/泛光灯。对于点光源来说,clight会基于距离r改变。下图展示了,距离变远之后光线变暗的原因,其类似于我们上一小节提到的光线向量与法线的夹角与光线强度的关系。

对于一个表面,点光源所射出的光线之间的空间与该表面到点光源的距离成正比。但不同于我们之前学习的余玄值与光线强度的关系,随着距离的增加,光线之间的空间会沿着表面的长与宽分别增大(假设表面是一个长方形),也就是说强度与r²成反比。所以,我们可以使用以下公式来计算光线的颜色clight,其中clight₀表示在距离为r0时预定义的光线强度:

而上述等式被称为inverse-square light attenuation。虽然理论上这个等式能够表现点光源光线的衰减,但是在运用到实际的着色模式中,其还是会造成一些问题。

首先,当物体表面一点与光源的距离非常近时,由于r趋近于0,这就会导致clight是一个无穷大的值。为了解决这个问题,我们会在等式的分母中加入一个变量ε:

而ε的值取决于各个游戏,例如,虚幻引擎使用ε=1cm。

此外,我们还能使用其他方法来解决r过小这一问题,例如,CryEngine和寒霜引擎,将r进行clamp:

不同于一个随机的ε值,rmin有着其物理意义,其将光源具体化为一个现实的物体,而rmin表示其半径。所以,如果r小于rmin,那么意味着物体表面位于光源的内部,而这显然是不可能的。

而第二个问题则会发生在物体表面距离光源太远,其不会影响着色的结果,但是会影响程序的性能。虽然光线的强度会随着距离增大不断减少,但强度永远不会变为0。出于性能的考虑,在距离达到某个数值时,我们应该将强度设置为0。为了避免在距离达到某种程度时光线强度突然变为0,我们会在上述等式前乘以一个窗口函数。而虚幻引擎和寒霜引擎都使用了下面这个函数:

其中+2并表示,如果求出的值为负数,那么先将其clamp至0,之后再对其平方。

不同的应用也会影响我们使用不同的窗口函数。例如,当我们以较低的频率(例如,在光照贴图或者顶点着色器)采样距离衰减函数,那么我们就必须保证在r=rmax时,其导数为0。CryEngine并不会使用光照贴图和顶点光照,所以它使用一个更简洁的窗口函数,其会在0.8rmaxrmax直接进行差值。

对于某些应用,其并不需要衰减函数满足我们之前的倒数平方,那么它们会使用其他的距离衰减函数:

其中fdist(r)是一个基于距离的函数。在某些游戏中,处于性能考虑,可能不会使用之前的倒数平方。例如,游戏正当防卫2中光照模型就极其简单,是一个十分便于计算的衰减函数,同时对于那些基于顶点的光照模型,其效果也足够平滑:

有时候我们也会根据游戏艺术表现的不同来改变这个衰减函数。例如,虚幻引擎作为一个商业引擎,同时用于写实类的游戏和非写实类的游戏,所以其也有两种光线衰减模式:一种是我们之前使用的倒数平方,而另一种是我们可以手动调节的衰减曲线。

聚集光(Spot Light)

与点光源不同,真实世界中几乎所有的光源都会射出不同的光线。而这个不同可以由方向衰减函数fdir(l)表示,这个函数与之前的距离衰减函数一同组成了空间中不同的光强度:

不同的fdir(l)能够产生不同的光照效果。而聚集光(spotlight)就是该类型的光源,其将光线投影到了一个圆形锥体中。而聚集光的方向衰减函数基于聚集光的方向向量s对称,因此我们可以构建一个基于向量s与光线向量的反向向量-l的夹角θs的函数。我们需要光线向量的反向向量,这是因为我们将l定义为由物体表面上一点指向光源的向量,而在方向衰减等式中我们需要的是由光源指向表面上一点的向量。

大多数聚集光的函数都由θs的余玄值组成。一般来说,聚集光有一个暗影角度(umbra angle)θu,其规定,如果θsθu,那么fdir(l)=0。这个角度的用处类似与上一小节中的rmax。同时,我们也会为聚集光定义一个penumbra角度θp,即使θs小于θp,光线的强度并不会继续增加。如下图所示。

聚集光会所使用的方向衰减函数基本相同。例如,寒霜引擎会使用fdirF(l),而three.js会使用fdirT(l):

函数smoothstep是一个常用的插值运算,其也是大多数着色语言中的内置函数。

其他精准光源

还有其他许多方法能使精准光源的clight改变。

函数fdir(l)并不仅限于见得聚集光衰减;其能够表示任何类型的方向上的衰减,包括真实世界中复杂的平板形式的灯光。而Illuminating Engineering Society定义了各种类型的灯光的标准格式,杀戮地带,虚幻引擎和寒霜引擎都使用了这一标准。

5.2.3 其他光源类型

方向光和精准光主要的不同在于光线向量l的计算方式。我们还可以使用不同的方式来计算光线向量以定义不同类型的光源。例如,古墓丽影使用胶囊状的光源,其是一条线段而不是一个点。对于每个着色fragment来说,fragment到线段上最近的点的线就是光线向量l

游戏中所使用的各种光源都是抽象意义上的光。现实世界中,光源有着其自身的尺寸,形状,而且它们会从各个方向照亮物体的表面。在渲染中,这一类型的光被称为区域光(area light),而且在实时渲染领域中对于这一类型光源的使用也在不断增加。区域光的渲染技术分为两个类别:模拟平滑的阴影边缘,模拟区域光对物体表面的着色。后者对于光滑的镜面表面最为明显,光源的形状和尺寸可以通过表面的反射进行辨识。虽然方向光和精准光源不在像之前那样无处不在,但这两类光源也不大会完全被舍弃。对于光线的区域性的研究也在不断发展,其运行效率也相对提高,相信在未来区域光会有更广泛的应用。

留下评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据