【Unity Shader入门精要学习】复杂的光照(三)

Unity的阴影

阴影是如何实现的

当一个光源发射的一条光线遇到一个不透明物体时,这条光线就不可以再继续照亮其他物体(不考虑光的折射),那么这个物体就会向旁边的物体投射阴影,阴影区域的产生是因为光线无法到达这些区域。也就是会在正常渲染的Pass中把顶点位置变换到光源空间下,得到光源空间的三维位置信息,然后使用XY值在阴影映射纹理上采样,得到该点在ShadowMap中的深度值,和计算得到的Z值进行比较,如果ShadowMap中的深度值比计算得到的Z值小,那说明该点在阴影中。

1、ShadowMap

在实时渲染中,最常用的一种技术:ShadowMap。这种技术理解起来非常简单,它会首先把摄像机放置在和光源重合的位置上,那么摄像机看不见的地方就是就是该光源产生的阴影区域。
在前向渲染路径中,如果场景中最重要的平行光开启了阴影,Unity就会为该光源计算它的阴影映射纹理(shadowMap)。这张纹理本质上也是一张深度图,它记录了从该光源位置出发,能看到的场景中距离它最近的表面位置信息(深度信息)。

2、ShadowCaster

如何判定距离光源最近的表面位置呢?一种方法,先把摄像机放置在光源位置上,然后按照正常的渲染流程,即调用BasePass,AddPass,来更新深度信息,得到阴影映射纹理。但这种方法会对性能造成浪费,因为仅仅需要深度信息,而这两个Pass中往往涉及更多复杂的光照计算。所以Unity提供了第二种方法,ShadowCaster,用一个额外的Pass来专门更新光源的阴影映射纹理:

    Pass {
        Name "ShadowCaster"
        Tags { "LightMode" = "ShadowCaster" }
          }

这个Pass的渲染目标不是帧缓冲,而是阴影映射纹理(或是深度纹理)。Unity会把摄像机放置到光源的位置上,然后调用该Pass,通过对顶点变换后得到光源空间下的位置,并据此来输出深度信息到阴影到阴影映射纹理中。因此,当开启了光源的阴影效果后,引擎会在当前要渲染的物体的Shader中找到LightMode为ShadowCaster的Pass如果在当前Shader中没有找到,那么会在FallBack中继续寻找(递归寻找),直到找到,如果没有找到那么这个物体将无法向其他物体投射阴影。如果找到了Unity就会使用该Pass来更新光源的阴影映射纹理。

3、屏幕空间的阴影映射技术(Screenspace Shadow Map)

屏幕空间的阴影映射技术原本是延迟渲染中产生阴影的方法,但是需要显卡支持MRT(多渲染目标)。
Unity首先会通过调用LightModeShadowCaster的Pass得到阴影映射纹理以及摄像机的深度纹理,然后根据阴影映射纹理和摄像机纹理得到屏幕空间的阴影图。如果摄像机的深度图记录的表面深度大于转换到阴影映射纹理中的深度值,则说明虽然这点可见,但是处于该光源的阴影中。通过这样的方式,阴影图就包含了屏幕空间中所有阴影的区域。如果想要一个物体接收来自其他物体的阴影,只要在shader中对阴影图采样。由于阴影图是在屏幕空间下的,因此需要把物体表面从模型空间转换到屏幕空间下,然后使用这个坐标对阴影图进行采样。

4、物体投射阴影和接收阴影是两个过程

一、接收阴影,需要在shader中对阴影映射纹理(包括屏幕空间的阴影图)进行采样,把采样的结果和最后的光照结果相乘来产生阴影效果。
二、投射阴影,需要把该物体加入到光源的阴影映射纹理的计算中去,从而让其他物体在对阴影映射纹理进行采样时可以得到该物体的相关信息。在Unity中这个过程就是执行该物体LightModelShadowCaster的Pass来实现的。如果使用了屏幕空间的阴影映射技术,该Pass还会产生一张摄像机的深度纹理。

你可能感兴趣的:(【Unity Shader入门精要学习】复杂的光照(三))