Screen Space Shadow Mapping
关于屏幕空间阴影具体的解释,实际上直接搜wikipedia是搜不到的。
实际上Screen Space Shadows可以基于Shadow Map,也可以像其他基于屏幕空间的技术如SSR一样用Ray Marching做。Unity中正是用了前者的方式,具体应该叫做Screen Space Shadow Mapping,后者暂不展开讨论。
那么Screen Space Shadow Mapping和传统的Shadow Mapping有什么区别呢?
我们知道传统的Shadow Mapping有两个步骤:第一步从光源生成Shadow Map,第二步拿Shadow Map和当前像素相机视角得到的深度值比较确定是否在阴影中。
而Screen Space Shadow Mapping不同,以Unity前向渲染模式为例:
1.首先和Shadow Mapping一样,从光源生成Shadow Map。
2.进行DepthPrepass,得到相机视角下的深度图_CameraDepthTexture。
3.进行ScreenSpaceShadowsPass,根据_CameraDepthTexture中的深度值还原出当前屏幕空间像素的世界坐标位置,再把世界坐标值转换到光源空间(阴影空间)坐标下,这样就和Shadow Map处于同一坐标系中,然后和Shadow Map中的值进行比较(Unity中利用 textureName.SampleCmpLevelZero(samplerName, uv, depth)比较),确定阴影,将输出的结果输出到_ScreenSpaceShadowMapTexture。
4.渲染(不透明)物体计算其阴影时直接采样_ScreenSpaceShadowMapTexture即可。
相比于传统的ShadowMapping这样做有什么好处呢?
1.软阴影:ScreenSpaceShadowMap不需要每个DrawCall都计算一遍PCF(只需要在ScreenSpaceShadowMapPass时计算一次PCF)。以URP12源码为例:
2.级联阴影:不需要多次访问级联阴影贴图,原因同上,只需要在ScreenSpaceShadowMapPass时访问一次即可。
3.综合1、2两点,实际上这是将阴影所有相关计算复杂度从世界空间降低到了屏幕空间。
不过,ScreenSpaceShadowMap并不是免费,必须要开启DepthPrepass(PreZ)(前向渲染,特殊的如URP中如果开了Decal会走Debuffer,实际上等价,说白了就是需要resolve)。对于带宽敏感的手机平台,尽量不要开启此功能。