Unity进阶修炼之路(2)——光照:上篇
Unity关于光照这部分的说明在知识点顺序上有点乱,也许并没有考虑的不同水准的读者,所以我打算写这样一篇博文,将其知识点重新整理下,希望能够帮大家了解和掌握相关的光照知识。文中我会以我的理解来解释一些概念,并不会非常严谨的遵循一些专业术语,或是度娘的解释,我觉得专业解释网上够多了,我的本意是希望大家能理解和搞懂这些概念,而非咬文嚼字。同样,本文不涉及具体的对这些光照技术在unity中应用操作,具体的使用方法,unity的文档看下就明白。我觉得要真正学会使用,必须先理解相关的知识,这才是本文的目的
首先放一张知识体系图,便于大家参考
基础知识
光照与阴影
上帝说我要光,便有了光,从字面上理解,光照即是光。在计算机图形学中,光照的概念不单单只是模拟光的照射结果,同时,担负显示物体颜色的作用。其实,在现实世界中,所有的颜色都来源于光照,没有光的照射和光的吸收,反射,我们是无法观察到颜色的。因此在计算机图形学中,光照计算还有着色的作用。
有光照,则自然会产生阴影,阴影的产生就等同与我们一般理解,只有当光线照射物体,物体遮挡了光线后,差生阴影。阴影是全黑的,那么我们在现实中其实看到很多阴影并不是纯黑,这就牵涉到另一个概念,全局光照
全局光照
我们知道,光照射到物体表面,或多或少都会产生反射,反射的光会进一步着亮其他的物体。这就是全局光照,当在一个场景中,我们不但会有主要的照射光(比如太阳),还会有众多的反光,这些反光在一起就构成了全局光照。
Shadow Distance阴影距离
这是unity中的一个概念,用来控制在这个距离内和距离外产生阴影的方式
Shadow map阴影贴图
经常可以听到做3D的人嘴上挂着这个名词,感觉非常高大上,那么阴影贴图是什么呢?阴影贴图更类似于一种在3D渲染过程中计算并生成阴影的计数。当然,实际上也真的在这个过程中存在这样一张贴图,但这张贴图往往是存在GPU中有3D引擎生成的,而非美术导出给我们。如果美术给我们所谓的阴影贴图,那实际上应该是指光照贴图。阴影贴图是一张存储了物体每一个顶点在光照空间(以某个光源为原点的坐标系空间中)的远近信息(允许我不用那么专业的深度信息来解释)。有了远近的信息,我们就能知道哪个顶点更靠近光,哪个光源更远离光,自然更近光的会遮挡住光,产生阴影。因此,每个光源,如果是采用实时光照计算的时候,都会产生一张阴影贴图。
光照贴图
相对的,我们在现实场景中的阴影,实际上是可以通过光照贴图来模拟的,不要觉得只有亮的色彩才能用光照贴图,我们前面就讲过了,任何颜色都来源于光照,即使阴影的颜色也是来源于光,只不过阴影部分的光是被完全吸收的,所以呈现黑色。而实际上,场景中,存在多种反射(全局光),所以我们看到的大部分阴影,其实是光照。
Cookie
我在百度上查了好久,都没有找到一个比较合适的对Cookie这个术语在阴影渲染中合适的翻译,所以我就把它称为剪影。当我们在光源上,遮盖上一个有形状的物体,那么这个物体的阴影就会以该物体的轮廓形状被投射到其他物体表面。在实际的3D引擎或是电影手法中,我们有时要模拟这个物体,比如我想投射一个人形的阴影,那是不是我一定要在光源前摆放一个人物模型,即是对于我们的游戏,这个模型完全没有作用,玩家也看不到这个模型。
我们可以用一个简单的方法来模拟这个效果,那就是用一张贴图而不是一个模型来遮挡这个光源,这个贴图就是Cookie。当然这张贴图有其自身的要求,它必须是带有alpha通道的,而且,它也不需要你真的把这张贴图放到一个mesh面片上,遮挡在光源前,只要提供这样一张有alpha通道的贴图,渲染引擎自然会实现对应的效果,这样是不是简单方便很多了。
Shadow mask阴影遮罩
阴影遮罩和Cookie很类似,它也是一张贴图,因为一张贴图最多可以保存4个通道的信息,所以,阴影遮罩可以保存一个贴图元关于4个光源的遮挡信息,也就是阴影信息。简单的理解,阴影遮罩也可以像cookie那样产生一个阴影的轮廓。不同的是,阴影遮罩是作用于光照贴图的,其轮廓直接对应光照贴图,更接近最终的结果。而cookie是作用于光源,其产生的阴影可能还需要进一步的坐标转换。另外,shadow mask可以保存4个光源的阴影信息,而cookie只能作用于一个光源
光路
光路是指光,从被照射的表面达到光源所经过的一系列的反射,折射,路径。直接发散到物体表面的我们称为发散光,直接照射到物体表面,并反射到观察对象(比如我们的眼睛)的称为直接光,直接照射到物体表面,但是经过两次以上反射才进入观察者的称为间接光。这三者的区别很明显了,发散光不能被我们直接观察到,间接光则是要反射两次以上,其余的都是直接光。通常对光路的计算,是从最后反射到观察者的表面开始直到光源的。
光照探针
光照探针是unity采用的一种渲染技术,和烘焙光照贴图类似,保存光照的信息。不同是,光照贴图保存的是光如何照射表面的,光照探针则是保存光如何穿过空间的。通俗点说,我们在空间中设置了一个探测范围,来探测光线如何穿过这一区域。有的这个信息,我们就可以进一步计算光的反射,用来计算直接光,间接光。
全局光照GI
在了解了相关的基础知识后,我们来详细学习Unity中的全局光照。
在Unity中全局光分成了两种,实时全局光Realtime GI,和烘焙全局光Baked GI。实时全局光是对光路进行实时的计算,来得到对各个表面的光照着色结果,它消耗的计算资源自然是非常多的。还有一种就是我们预先计算这些反射光的光照着色结果,并存储于光照贴图中,在运行时加载贴图即可。这相对就会比较节省资源。
在Unity中,即使对于Realtime GI,也并不是完全实时计算所有的光路的,而是采用了一种叫Precomputed Realtime GI的计数来对Realtime GI中的一分部进行了预先计算。当然,能够进行预计算的必须是静态物体。相对的光源类型必须是动态的,如果光源是静态的,那么光只会对Baked GI起作用。不难理解,对于运动的发光物体,我们无法采用Realtime GI,因为unity里使用了Precomputed Realtime GI,对于不停在运动的物体,是无法预计算的。而对于运动的被照射物体,我们也是无法预计算的。
光照
讲了那么多,终于讲到真正的主题,光照了。在Unity中光照分成三种,实时光,烘焙光,混合光。为此,我列了一张表,来帮助区分和理解这三种光照方式
实时光Realtime lighting
实时光会产生直接的光照,也会产生全局光(一般来讲,这个全局光是指间接光,但我理解,发散光也应算在其中)。从表上可以看到,对间接光,使用Realtime GI来计算,而我们前面已经说了,Realtime GI中使用了Precomputed Realtime GI。预计算实时全局光,不计算光路最后一段,也就是光源到第一个反射表面的信息,所以我们可以看到实际计算的光路是这样的。
而实时光产生的直接光照,则会通过阴影贴图来生成阴影,或是通过光照着色,显示为物体的颜色。
烘焙光Baked lighting
对于烘焙光,unity是预计算整个光路的,看下图
混合光Mixed lighting
混合光又分成4中不同的方式,这些方式的区别在于什么时候,直接光使用实时计算,烘焙光采用预计算的方式不同
Baked Indirect mode
在这种模式下,unity只对间接光进行预计算,在阴影距离内的阴影完全是实时计算产生的
可以看到光路计算图,蓝色的部分代表间接光。对于超出阴影距离的阴影,实际是不做计算处理的。而对于直接光,则是使用实时计算。
Shadowmask mode
在这种模式下,unity预计算静态物体投射到静态物体的阴影,并保存在shadow mask中
可以看到,间接光依然是预计算,不同是阴影被预计算了,使用的shadowmask方式
Distance Shadowmask
该方式和Shadowmask mode非常类似,不同的只在于对于在Shadowdistance内的阴影采用Shadow map的实时计算法方式
Subtractive mode
该模式是唯一将直接光进行预计算,烘焙进光照贴图的方式
对比前两张图,我们看到,从光源发出的直接光进入了预计算。该模式中,静态物体不能接受动态物体的投射阴影,除非是从主光源投射下来的。动态物体则只能通过光照探针来接受从静态物体投射下的阴影。