3D数学-基础光照

3D数学-基础光照

好记性不如烂笔头啊,还是记录一下!


概述

我们要模拟真实的光照环境来生成一张图像,需要考虑三种物理现象:

  • 首先,光线从光源(light source)中发射出来。
  • 然后,光线和场景中的一些物体相交: 一些光线被物体吸收了,而另一些光线被散射到其他方向。
  • 最后,摄像机吸收了一些光,产生了一张图像。

下面,我们将对每个部分进行更加详细的解释。


光源

在实时渲染中,我们通常把光源当成一个没有体积的点,用 l l 来表示他的方向。那么,我们如何测量一个光源发射出了多少光呢?也就是说,我们如何量化光呢?在光学里,我们通常用辐照度(irradiance)来量化光。对于平行光来说,它的辐照度可通过计算在垂直于 l l 的单位面积上单位时间内穿过的能量来得到。在计算光照模型时,我们需要知道一个物体表面的辐照度,而物体表面往往时和 l l 不垂直的,那么如何计算这样的表面的辐照度呢?我们可以使用光源方向 l l 和表面法线 n n 之间的夹角的余弦值来得到。需要注意的是,这里默认方向矢量的模都为1。

![avatar][image1]

由上图可知辐照度是和照射到物体表面时光线之间的距离 d / cos θ d/\cos\theta 成反比的,因此辐照度就和 cos θ \cos\theta 成正比。 cos θ \cos\theta 可以用光源方向 l l 和表面法线 n n 点积来得到。这就是使用点积来计算辐照度的由来。

环境光源

环境光源也称为环境光,是一种低强度光源,由环境中所有相邻表面的反射光产生。在真实世界中,物体可以被间接光照(indirect light)所照亮。光线通常会在多个物体间反射,最后进入摄像机,在进入摄像机前,可能经过了不止一次的物体反射。利用环境光可以近似表示场景中某一区域的大概亮度,而不用对场景中所有对象件的间接反射光进行复杂的运算。环境光的计算非常简单,它通常是一个全局变量。

平行光源

平行光源,也成为无穷远光源,是从无穷远处发出的某一方向的光的光源,平行光源特别谁和模拟太阳一类的光,该光源发出的光线是平行的。该光源在有限的空中没有具体的位置,照射范围也是无限的,其强度不会随着距离的增加而减弱。

点光源

点光源是从空间中一点向各个方向发出的等强度的光的光源。点光源发出的光的强度与传播距离成反比自然衰减。

聚光灯光源

聚光灯光源除了有一个首选方向之外,其他特点与光源类似,聚光灯光源发出的光的强度与传播距离成反比自然衰减。


吸收和散射

光线由光源发射出来后,就会与一些物体洗青椒。通常,相交的结果有两个:散射(scattering)吸收(absorption)

散射只改变光线方向,但不改变光线的密度和颜色。而吸收只改变光线的密度和颜色,但不改变光线的方向。光线在物体表面经过散射后,有两种方向:一种将会散射到物体内部,这种现象被称为折射(refraction)透射(transmission);另一种将会散射到外部,这种现象称为折射(refraction)。对于不透明物体,折射进入物体内部的光线还会继续与内部的颗粒进行相交,其中一些光线最后会重新发射出物体表面,而另一些则被物体吸收。那些从物体表面重新发射出的光线将具有和入射光线不同的方向分布和颜色,如图:

![avatar][image2]

为了区分不同的散射方向,我们再光照模型中使用了不同的部分来计算它们:高光反射(specular)部分表示物体表面是如何反射光线的,而漫反射漫反射(diffuse)部分则表示有多少光线会被折射、吸收和散射出表面。根据入射光线的数量和方向,我们通常使用出射度(exitance)来描述它。辐照度和出射度之间是满足线性关系的,二它们之间的比值就是材质的漫反射和高光反射属性。


着色

着色(shading)指的是,根据材质属性(如漫反射属性等)、光源信息(如光源方向、辐照度等),使用一个公式去计算沿某个观察方向的出射度的过程。我们也把这个公式称之为光照模型(Lighting Model)不同的光照模型有不同的目的。例如,一些用于描述粗糙的物体表面,一些用于描述金属表面等。


光照模型

BRDF光照模型

我们已经了解了光线在和物体表面相交时会发生那些现象,当已知光源位置和方向、视角方向,我们就需要知道一个表面是如何和光照进行交互的。例如,光线从某个方向照射到一个表面时,有多少光线被发射?反射的方向有哪些?而BRDF(Bidirectional Reflection Distribution Function)就是用来回答这些问题的。当给定模型表面上的一点时,BRDF包含了对该点外观的完整的描述。在图形学中,BRDF大多数使用一个数学公式来表示,并且提供了一些参数来调整材质属性。通俗来讲,当给定入射光线的方向和辐照度后,BRDF可以给出在某个出射方向上的光照能量分布。

标准光照模型

标准光照模型是对真实场景进行理想化和简化后的模型,也就是说它们并不能真实地反映无敌和光线之前的交互,是一种经验模型。标准光照模型只关心直接光照(direct light),也就是那些直接从光源发射出来照射到物体表面后,经过物体一表面的一次反射直接进入摄像机的光线。

它的基本方法是,把进入到摄像机内的光线分为4个部分,每个部分使用一种方法计算它的贡献度。这4个部分是:

1.环境光(ambient)

这里我们使用 c a m b i e n t c_{ambient} 来表示,这个部分用于描述该表面的所有间接光照。由于环境光通常是一个全局变量:

c a m b i e n t = g a m b i e n t c_{ambient}=g_{ambient}

2.自发光(emissive)

这里我们使用 c e m i s s i v e c_{emissive} 来表示,这个部分用于描述给定一个方向时,一个表面本身会想该方向发射多少辐射量。这里我们需要计算直接由发光体表面进入摄像机的光线。它的计算也很简单,就直接使用了该材质的自发光颜色:

c e m i s s i v e = m e m i s s i v e c_{emissive}=m_{emissive}

3.漫反射(diffuse)

这里我们使用 c d i f f u s e c_{diffuse} 来表示,这个部分用于描述从光源照射到模型表面时,该表面会向每个方向散射多少辐射量。由于是向各个方向散射的辐射度,所以在漫反射中,视角的位置是不重要的,应为反射是完全随机的,因此可以认为在任何反射方向上的分布都是一样的。但是,入射光线的角度很重要。

兰伯特光照模型

漫反射符合兰伯特定律(Lambert's law):反射光线的强度与表面法线和光源方向之间夹角的余弦值成正比。因此漫反射部分的计算如下:

c d i f f u s e = ( c l i g h t m d i f f u s e ) m a x ( 0 , n l ) c_{diffuse}=(c_{light} \cdot m_{diffuse}) \cdot max(0, n \cdot l)

其中 n n 是表面法线, l l 是指向光源的单位矢量, m d i f f u s e m_{diffuse} 是材质的漫反射颜色, c l i g h t c_{light} 是光源颜色和强度。需要注意的是,我们需要防止法线和光源方向点乘的结果为负值,这样可以防止物体被从后面来的光源照亮。

半兰伯特光照模型

兰伯特光照模型有一个问题是,在光照无法到达的区域,模型的外光通常是黑的,没有任何的明暗变化,这会使模型的背光区域看起来就像一个平面一样,失去了模型细节的表现。为此,有一种改善技术被提出来,这就是半兰伯特(Half Lambert)光照模型。Valve公司在开发游戏《半条命》时提出了改善方案,在原兰伯特光照模型上进行了一个简单的修改,广义的半兰伯特光照模型的公式如下:

c d i f f u s e = ( c l i g h t m d i f f u s e ) ( α ( n l ) + β ) c_{diffuse}=(c_{light} \cdot m_{diffuse})( \alpha (n \cdot l) + \beta )

绝大多数情况下 α \alpha β \beta 的值都是0.5,即公式为:

c d i f f u s e = ( c l i g h t m d i f f u s e ) ( 0.5 ( n l ) + 0.5 ) c_{diffuse}=(c_{light} \cdot m_{diffuse})(0.5*(n \cdot l)+0.5)

通过这样的方式,我们可以把 n l n \cdot l 的结果范围从 [ 1 , 1 ] \left [ -1,1 \right ] 映射到 [ 0 , 1 ] \left [ 0,1 \right ] ,因此模型的背光区域也可以有敏感变化,不同的点积结果会映射到不同的值上。实际上半兰伯特光照模型没有任何的物理依据,它仅仅是一个视觉加强技术。

4.高光反射(specular)

Phong光照模型

这里我们使用 c s p e c u l a r c_{specular} 来表示,这个部分用于描述从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量。这也是一种经验模型,并不符合真是世界中的高光反射现象。它可用于计算那些沿着完全镜面反射方向被反射的光线,这可以让物体看起来是有光泽的,例如金属材质。

计算高光反射需要知道的信息比较多,如表面法线、视角方向、光源方向、反射方向等。在本节中,我们假这些矢量都是单位矢量。这四个矢量中,我们只要知道其中前三个矢量即可,而第四个矢量——反射方向可以通过计算获得:

![avatar][image3]

N N 是表面法线, L L 是指向光源的矢量, R R 为反射方向,根据图中可知:

L R = X L - R = X

L L N N 上的投影为 N L N \cdot L , 则投影的矢量为 ( N L ) N (N \cdot L)N ,则有以下关系式:

X = 2 ( L ( N L ) N ) X = 2(L-(N \cdot L)N)

X X 代入之前的式子可得:

R = L X = L 2 ( L ( N L ) N ) = 2 ( N L ) N L R = L - X = L - 2(L-(N \cdot L)N) = 2(N \cdot L)N - L

解决了反射方向的计算,我们就可以利用Phong模型来计算高光反射了,如图:

![avatar][image4]

公式为:

c s p e c u l a r = ( c l i g h t m s p e c u l a r ) m a x ( 0 , v r ) m g l o s s c_{specular}=(c_{light} \cdot m_{specular}) \cdot max(0, v \cdot r)^{m_{gloss}}
其中 m g l o s s m_{gloss} 是材质的光泽度(gloss),也被称为反光度(shininess)。它用于控制高光区域的“亮点”有多宽, m g l o s s m_{gloss} 约大,亮点就越小。 m s p e c u l a r m_{specular} 是材质的高光反射颜色,它用于控制改材质对于高光反射的强度和颜色。 c l i g h t c_{light} 是光源颜色和强度

Blinn-Phong光照模型

Phong光照模型能够很好的表现高光效果,不过Phong光照模型的缺点就是计算量较大。针对这个问题,Blinn提出了一个简单的修改方法来得到类似的效果。它的基本思想是,避免计算反射方向 r r ,为此,Blinn-Phong光照模型引入了一个新的矢量 h h ,它是通过对 v v l l 的取平均后在归一化得到的。即:

h = v + l v + l h = \frac{v+l}{\vert v+l \vert}

然后使用 n n h h 之间的夹角进行计算,而非 v v r r 之间的夹角,如图:

![avatar][image5]

公式为:

c s p e c u l a r = ( c l i g h t m s p e c u l a r ) m a x ( 0 , n h ) m g l o s s c_{specular}=(c_{light} \cdot m_{specular}) \cdot max(0, n \cdot h)^{m_{gloss}}

在一般情况下,Blinn-Phong光照模型效率会更高。


着色模型

有了上面的基本光照模型的计算公式,那么我们在哪里计算这些光照模型呢?通常来讲我们有以下两种:

逐顶点光照(per-vertex lighting)

逐顶点光照(per-vertex lighting)也被称为高洛德着色(Gouraud shading)。在逐顶点光照(per-vertex lighting)中,我们在每个顶点上计算光照,然后会在渲染图元内部进行线性插值,最后输出成像素颜色。由于顶点数目往往远远小于像素数目,因此计算量往往要小于逐像素光照(per-pixel lighting)。但是,由于逐顶点光照(per-vertex lighting)依赖于线性插值来得到像素光照,因此,当光照模型中有非线性的计算(例如计算高光反射(specular))时,逐顶点光照就会出问题,由于逐顶点光照(per-vertex lighting)会在渲染图元内部对顶点颜色进行插值,这会倒是渲染图元内部的颜色总是暗于顶点处的最高颜色值,这在某些情况下会产生明显的棱角现象。

逐像素光照(per-pixel lighting)

逐像素光照(per-pixel lighting)也被称为Phong着色(Phong shading),在逐像素光照(per-pixel lighting)中,会以每个像素为基础得到它的法线(可以是对顶点法线插值得到的,也可以是从法线纹理中采样得到的),然后进行光照模型的计算。与之前提到的Phong光照模型不同,这种在面片之间对顶点法线插值的技术称为逐像素光照(per-pixel lighting)或者Phong着色(Phong shading)


本节教程就到此结束,希望大家继续阅读我之后的教程。

谢谢大家,再见!


饮水思源

参考文献:

《3D游戏与图形学中的数学方法》

《Unity Shader 入门精要》