US3S6L2——Unity中阴影的实现原理

Screen Space Shadow Mapping 技术

虽然上一篇提到了 Shadow Mapping 技术的原理,但是在 Unity 当中,使用的并不只是纯粹的 Shadow Mapping 技术
还会使用由微软研究院提出的 (首次提出是在2011年):Screen Space Shadow Mapping( SSSM ),翻译过来就是:屏幕空间阴影映射技术
它是基于 Shadow Mapping 技术的一种拓展和改进技术。

注意:并不是所有设备都支持 SSSM 技术!
在之后编写阴影相关 Shader 时,Unity 内部会帮助我们判断对应平台是否支持
不支持时会默认使用 Shadow Mapping 处理阴影

要理解SSSM(屏幕空间阴影映射技术)的原理,我们首先再来回顾一下 Shadow Mapping 技术:
即从光源的角度渲染场景,生成一张阴影映射纹理(深度图),记录场景中每个像素到光源的距离(深度)。
在真正渲染场景时,通过将顶点坐标转换到光源空间下,并与阴影映射纹理(深度图)中的深度值进行比较,判断像素是否在阴影中。

image

SSSM(屏幕空间阴影映射技术)基于 Shadow Mapping 技术的基础上,需要多生成一张深度图 —— 屏幕空间深度图
在屏幕空间阴影映射技术中,它会和 Shadow Mapping 一样,为每个光源生成对应的阴影映射纹理(从光源视角生成)。
并且还会生成一张屏幕空间深度图,这张屏幕空间深度图中,记录了从摄像机视角看到的每个像素的深度值(即每个像素点到相机的距离)

imageimage

更确切的说,是每个像素点对应的场景中的顶点离摄像机的深度值
(深度值 0~1 之间, 0 表示离摄像机近裁剪面最近的距离,1 表示摄像机远裁剪面 也就是最远的距离)

当有了 阴影映射纹理屏幕空间深度图 后,我们将利用他们携带的信息来决定最终的阴影效果。其中一件非常重要的事,就是坐标转换。
我们需要把 屏幕空间 的 像素位置 变换到 光源空间下,然后在 光源空间 下 比较每个像素的深度值 和 阴影映射纹理中的值,
如果当前像素的深度值大于光源深度图中的值,说明该像素在阴影中。

注意:当屏幕空间中的像素位置变换到光源空间下时,可能不在光源空间的可见范围内,这时我们无需进行比较判断,该像素不用进行阴影处理

SSSM(屏幕空间阴影映射技术)的原理:

  1. 基于光源位置生成的 阴影映射纹理
  2. 基于渲染游戏画面时得到的 屏幕空间深度图
  3. 将 屏幕像素位置 变换到 光源空间下
  4. 对 屏幕空间深度图 和 阴影映射纹理 进行采样 比较深度值,决定最后的阴影处理效果

Unity 中如何实现阴影

想要在Unity中实现阴影效果,必须注意以下三点:

  1. 对 光源 以及 物体进行相关设置:让光源开启阴影,让物体能够投射和接收阴影
  2. 想要向其它物体投射阴影的物体 Shader 中必须要有 LightMode​ 被设置为 ShadowCaster​ 的 Pass​,才能在阴影映射纹理中记录自己的信息
  3. 想要接收其他物体投射的阴影的物体 Shader 中,必须对阴影映射纹理进行采样,用于最后的光照计算

Unity 会调用 LightMode​(灯光模式)被设置为 ShadowCaster​(阴影投射器)的 Pass​(渲染通道)
来生成对应的阴影映射纹理(Shadow Mapping 技术),以便在后续的阴影计算中使用

阴影映射纹理的计算过程往往不需要我们手动处理,Unity 中提供了对应的阴影相关的宏帮助我们进行计算,我们直接调用它们即可

注意:如果 Shader 中没有 LightMode​ 为 ShadowCaster​ 的 Pass​,会在 Shader​ 中的 Fallback​ 指定的 Shader 中继续寻找,直到找到对应 Pass​。
如果没有找到,那么该物体就无法向其他物体投射阴影,因为在阴影映射纹理计算中无法计算该物体的信息。(但是该物体仍可以接收其他物体的投影)

而对于支持 SSSM(屏幕空间阴影映射技术)的设备来说,除了 阴影映射纹理外,还需要屏幕空间深度图。
屏幕空间深度图通常由摄像机在渲染过程中自动生成,并存储在摄像机的深度纹理中
我们只需要在计算时,从光源的阴影映射纹理 以及 屏幕空间深度图 中进行采样比较深度即可

同样这个过程往往不需要我们手动处理,Unity 中提供了对应的阴影相关的宏帮助我们进行计算,我们直接调用它们即可

需要注意的是,物体接收来自其他物体的阴影物体向其它物体投射阴影 是两个过程:

  1. 物体接收来自其他物体的阴影

    必须在 Shader 中对阴影映射纹理(SM或SSSM中的阴影图)进行采样,把采样结果和最后的光照结果相乘来产生阴影效果

  2. 物体向其它物体投射阴影

    必须将该物体加入到光源的阴影映射纹理的计算中,
    必须要有 LightMode​(灯光模式)被设置为 ShadowCaster​(阴影投射器)的 Pass​(渲染通道),
    这样才能让其他物体在对阴影映射纹理采样时,得到该物体的相关信息。

想要让 物体接收来自其他物体的阴影 和 物体向其它物体投射阴影,我们需要在 Unity 中对光源和物体进行一些设置:

  • 保证光源能够生成阴影映射纹理 —— 光源组件上设置 Shadow Type(阴影类型)

    image

  • 保证物体能接收其他物体的阴影 —— 网格渲染器组件上勾选 Receive Shadows(接收阴影)

    image

  • 保证物体向其它物体投射阴影 —— 网格渲染器组件上设置 Cast Shadows(投射阴影)

    image

    注意:对于一些单面对象(例如只渲染一面的平面),我们可以将 Cast Shadows 设置为 Two Sided(双面)投射双面阴影
    这意味着,即使光源在网格后面,平面或者四边形等单面对象也可以投射阴影

    Unity 的默认集合体 Plane 默认就无法从正面投射阴影,开启过后就可以让正面投射阴影

    imageimage