Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

阴影的基本原理

一、现实中阴影产生的规则

现实中阴影的产生规则 现实中的阴影产生规则是在不考虑光线反射的前提下 当一个光源发射的一条光线遇到一个不透明物体 A 时,这条光线就不能够再继续照亮其他物体了(物体 B 的一部分) 相当于光线被更靠近光源的 A 物体挡住了,一些这时物体 A 就会向旁边的物体 B 投射阴影 也就是说,阴影区域的产生就是因为光线无法到达

二、Shadow Mapping 技术

基于 阴影区域的产生就是因为光线无法到达 的这个规则 前辈 Lance Williams(兰斯·威廉姆斯)在 1978 年时最早提出了 Shadow Mapping(阴影贴图)技术。这是计算机图形学中第一个提出的通用阴影算法。 Shadow Mapping 的基本原理: 将摄像机的位置放在和光源重合的位置上 那么场景中关于这个光源的阴影区域就是摄像机看不到的位置 注意:一般情况下,点光源用透视投影,平行光用正交投影 2024-10-02 100808.png

Shadow Mapping 在 Unity 中的本质 其实就是生成一张深度图(阴影映射纹理),一般存于显存中 这张深度图记录了从该光源位置出发,能看到的场景中距离它最近表面的位置 (一般记录其深度信息,值转换为 0~1 之间,0 最近,1 最远) 2024-10-02 101207.png

三、Unity 中如何应用 Shadow Mapping 技术

阴影映射纹理的生成是由光源完成的。 在每帧渲染的早期阶段,Unity 会对每个能够投射阴影的光源创建一个对应的摄像机视角,这个视角用于捕获从光源位置看到的场景。平行光设置为正交摄像机,点光源可能会设置多个视角,捕获多个方向的立方体阴影贴图。Unity 会渲染场景的深度信息,这些深度信息表示从光源到场景中每个物体的距离不考虑颜色信息,最终存储在显存中。

实时阴影映射纹理需要每帧更新,但对于静态光源和静态场景,可以使用预烘焙的阴影贴图,减少实时计算的开销。 有了 阴影映射纹理 后,我们只需要在 Pass 中将顶点位置变换到光源空间下,得到顶点在光源空间下的三维位置信息。 然后使用 X 和 Y 分量对阴影映射纹理进行采样,得到阴影纹理中该位置的深度信息。 如果阴影纹理中取出的深度值小于该顶点的深度值,那么说明该顶点位于阴影中。(深度图中值为 0~1 之间,0 最近,1 最远)

四、Unity 中实现阴影的原理

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

1. Screen Space Shadow Mapping 技术

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

基本原理

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

SSSM(屏幕空间阴影映射技术)基于 Shadow Mapping 技术的基础上 需要多生成一张深度图 —— 屏幕空间深度图。 在屏幕空间阴影映射技术中 会和 Shadow Mapping 一样 为每个光源生成对应的阴影映射纹理(从光源视角生成)。 并且还会生成一张屏幕空间深度图 这张屏幕空间深度图中 记录了从摄像机视角看到的每个像素的深度值(即每个像素点到相机的距离) 更确切的说是每个像素点对应的场景中的顶点离摄像机的深度值 (深度值 0~1 之间, 0 表示离摄像机近裁剪面最近的距离,1 表示摄像机远裁剪面 也就是最远的距离) 2024-10-02 103405.png 当有了 阴影映射纹理 和 屏幕空间深度图后 我们将利用他们携带的信息来决定最终的阴影效果。 其中一件非常重要的事,就是坐标转换。 我们需要把 屏幕空间的像素位置 变换到 光源空间下 然后在 光源空间 下 比较每个像素的深度值 和 阴影映射纹理中的值, 如果当前像素的深度值大于光源深度图中的值,说明该像素在阴影中

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

2. Unity 中如何实现阴影

  • Unity 会调用 LightMode(灯光模式) 被设置为 ShadowCaster(阴影投射器)的 Pass (渲染通道) 来生成对应的阴影映射纹理(Shadow Mapping 技术),以便在后续的阴影计算中使用 阴影映射纹理的计算过程往往不需要我们手动处理 Unity 中提供了对应的阴影相关的宏帮助我们进行计算 我们直接调用它们即可 注意: 如果 Shader 中没有 LightMode 为 ShadowCaster 的 Pass,会在 Shader 中的 Fallback 指定的 Shader 中继续寻找,直到找到对应 Pass。如果没有找到,那么该物体就无法向其他物体投射阴影, 因为阴影映射纹理计算中就不会计算该物体的信息。(但是该物体仍可以接收其他物体的投影)
  • 对于 支持 SSSM(屏幕空间阴影映射技术)的设备来说 除了 阴影映射纹理外,还需要屏幕空间深度图。 而屏幕空间深度图通常由摄像机在渲染过程中自动生成,并存储在摄像机的深度纹理中 我们只需要在计算时 从光源的阴影映射纹理 以及 屏幕空间深度图 中进行采样比较深度即可

注意点:

  1. 物体接收来自其他物体的阴影 和 物体向其它物体投射阴影 是两个过程:

    1. 物体接收来自其他物体的阴影 必须在 Shader 中对阴影映射纹理(SM 或 SSSM 中的阴影图)进行采样 把采样结果和最后的光照结果相乘来产生阴影效果
    2. 物体向其它物体投射阴影 必须将该物体加入到光源的阴影映射纹理的计算中,必须要有 LightMode(灯光模式)被设置为 ShadowCaster(阴影投射器)的 Pass (渲染通道),这样才能让其他物体在对阴影映射纹理采样时,得到该物体的相关信息。
  2. 我们需要在 Unity 中对光源和物体进行一些设置

    1. 保证光源能够生成阴影映射纹理 —— 光源组件上设置 Shadow Type(阴影类型) 2024-10-02 104250.png
    2. 保证物体能接收其他物体的阴影 —— 网格渲染器组件上勾选 Receive Shadows(接收阴影) 2024-10-02 104305.png
    3. 保证物体向其它物体投射阴影 —— 网格渲染器组件上设置 Cast Shadows(投射阴影) 2024-10-02 104256.png

评论