摄像机实时渲染中的阴影投射阴影是如何产生的

摄像机实时渲染中的阴影投射阴影是如何产生的

为了让场景看起来更加真实并具有深度信息,我们通常希望光源能够将某些物体的阴影投射到其他物体上。

我们可以首先考虑现实生活中的阴影是如何产生的。 当光源发出的光线遇到未知物体时,该光线就无法再继续照亮其他物体。 因此,该对象将在其旁边的对象上投射阴影,而出现这些阴影区域是因为光线无法到达这些区域。

在实时渲染中,最常用的技术之一称为阴影贴图。 这种技术非常简单。 它首先会将相机位置与光源重合,那么场景中光源的阴影区域就是相机看不到的地方。 Unity 使用了这项技术。

在前向渲染(Base Pass[ForwardBase]和Additional Pass[ForwardAdd])路径中,如果场景中最重要的定向光打开阴影,Unity将计算该光源的阴影贴图纹理。 这个阴影映射纹理本质上是一个记录光源位置的深度图。 可见场景中与其最近的表面位置(深度信息)。

那么,在计算阴影贴图纹理时,我们如何确定最接近它的表面的位置呢? 一种方法是先将相机放置在光源的位置创作人,然后按照正常的渲染流程,即调用Base Pass和Additional Pass更新深度信息并获取阴影贴图纹理,但这种方法会导致某些性能问题。 浪费了,因为我们实际上只需要深度信息,而Base Pass和Additional Pass往往会涉及到大量复杂的光照模型计算。 因此,Unity选择使用额外的Pass来专门更新光源的阴影贴图纹理。 该Pass是LightMode标签设置为ShadowCaster的Pass。 这个Pass的渲染目标不是帧缓冲区,而是二十多个阴影映射纹理(或深度纹理)。 Unity首先将相机放置在光源的位置地图场景,然后调用Pass块unity 纹理投影映射,通过固定点的变换来获取光源空间中的位置。 并以此为基础,将深度信息输出到阴影映射纹理中。 因此,当光源的阴影效果开启时unity 纹理投影映射,底层渲染引擎会首先在当前渲染对象的unityshader中找到LightMode为shadowCaster的pass。 如果没有,就会继续在Fallback指定的unityshader中寻找。 如果仍然没有找到,则该对象无法将阴影投射到其他对象上(但它仍然可以接收其他对象的阴影)。 当找到LightMode为ShadowCaster的Pass时,unity会使用该Pass来更新光源的阴影贴图纹理。

传统的阴影贴图透视中,我们会在法线渲染Pass中将顶点位置变换到光源空间,以方便获取其在光源空间的三维信息。 然后,我们使用 xy 分量对阴影纹理进行采样并获取阴影贴图纹理中该位置的深度信息。 如果深度值小于顶点的深度值(通常由z分量获得),则该点处于阴影中。 但在unity5中,unity采用了与这种传统阴影采样技术不同的阴影采样技术,即屏幕空间阴影映射技术。 屏幕空间阴影映射最初是一种延迟渲染中生成的阴影的方法。 需要注意的是,并不是所有的平台统一都会使用这项技术,这是因为。 屏幕空间阴影贴图需要显卡支持MRT,而某些移动平台不支持。

在使用屏幕空间阴影映射技术时,Unity首先会通过调用LightMode的Pass为shadowCaster来获取可以投射阴影的阴影光源的阴影映射纹理和相机的深度纹理。 然后根据光源的阴影贴图纹理和相机的深度纹理获得屏幕空间的阴影贴图。 如果相机深度图中记录的表面深度大于转换为阴影贴图纹理的深度值,则表面可见。 但它处于那个光源的阴影中。 这样阴影贴图就包含了所有的阴影区域。 如果我们希望一个对象接受其他对象的阴影,我们只需要在着色器中对阴影贴图进行采样即可。 由于阴影贴图位于屏幕空间中,因此我们首先需要将模型中的表面坐标转换为屏幕空间,然后使用这些坐标对阴影贴图进行采样。

总结:

1.如果我想让这个对象接受来自其他对象的阴影,我必须在着色器中对阴影贴图纹理(包括屏幕空间的阴影贴图)进行采样。 将采样结果与最终的光照效果相乘,得到阴影效果。

2、如果我们想让一个物体给其他物体投射阴影,就必须将该物体加入到光源的阴影映射纹理的计算中,这样其他物体在对阴影映射纹理进行采样时就可以获取到该物体的相关信息。 在统一中,这个过程是通过将 LightMode 作为对象的 ShadowCaster 执行 Pass 块来实现的。 使用屏幕的投影映射技术后,Unity还会使用这个Pass来生成相机的深度图。

不透明物体的阴影

1.让物体投射阴影

在unity中,我们可以选择是否让对象投射或接受阴影。 这是通过在网格渲染组件中设置投射阴影和接收阴影属性来实现的。 投射阴影可以设置为打开或关闭。 如果打开Cast Shadow属性,Unity会将该对象添加到光源的阴影映射纹理的计算中,以便其他对象在对阴影映射纹理进行采样时能够获取该对象的相关信息。 这个过程是通过执行对象的ShadowCaster的Pass来实现的。 接收阴影允许您选择对象是否接受来自其他对象的阴影。 如果没有开启Receive Shadows,那么当我们调用Unity内置的宏和变量来计算阴影时,这些宏内部将不再通过判断该物体不具备接收阴​​影的功能来为我们计算阴影。

LightMode 是为 ShadowCaster 的 Pass 实现的。 这个自定义的Pass可以让我们更灵活地控制阴影的生成,但是由于这个Pass的功能通常可以跨多个unityshader使用,所以直接Fallback是更方便的用法。

要允许对象接收阴影,首先在 BasePass 中包含一个新的内置文件#include“AutoLight.cginc”。 用于计算阴影的宏在此文件中声明。 顶点着色器的输出结构体v2f中添加了内置宏SHADOW_COORDS。 该宏的作用是声明一个用于纹理采样的坐标。 需要注意的是,该宏的参数需要是下一个可用插值寄存器的索引值,在下面的代码中设置为4。 在定点着色器计算返回之前添加另一个内置宏 TRANSFR_SHADOW。 该宏用于定点着色器计算。 上一步中声明的阴影纹理坐标。 最后我们在片段着色器中计算阴影总和,使用内置宏SHADOW_ATTENUATION;

文章来源:https://blog.csdn.net/qq_41179365/article/details/80855665