制作CloudShadow的方式之一是屏幕空间贴花(Screen Space Decal)。
在讲解CloudShadows之前先讲下目前贴花的实现方式。
Decal
以URP Decal System (URP12 +)为例:
1.DBuffer(为Decal单独设计的Buffer)
2.GBuffer(延迟管线)
3.Screen Space
对带宽紧张的移动端来说,Screen Space方案是相对友好的一种,但同时,如果大量使用贴花也很容易增加Overdraw。由于我们只拿到了颜色信息,所以很难处理贴花上复杂表面的光照效果。
Screen Space Decal(Depth Decal)
以URP12源码为例,其中绘制逻辑执行在DeaclDrawSystem.cs中,Unity贴花系统会根据对应的包围盒生成对应贴花位置的Mesh,整个贴花位于包围盒内,随后进行着色计算:
Shader实现的主要代码在ShaderPassDecal.hlsl中,根据ScreenSpace坐标计算Decal SurfaceData,然后写入光照效果到Color中:
一般来说,摄像机都在贴花Mesh Bounding Box外面。根据贴花Mesh Back(Cull Front)Depht和对应物体表面的深度信息进行比较,一种是Ztest Off(always)即总是更新颜色信息,随后计算出需要剔除的额外部分。
还有一种是通过Ztest Greater,把Depth Src中Depth小的部分过滤掉,同样还需要剔除不在bouding box中的部分。
URP中采用的是后者的策略。
演示一下更形象:
当然,如果相机处于bouding box内,则上述比较(比如深度的比较)需要反着来。
DBuffer/GBuffer Decal
DBuffer和GBuffer实现思路类似,这里讲下DBuffer思路,拿几张RT存储贴花中对应的Color、Normal等信息,后面计算光照时直接拿DBuffer中的信息计算,并与原Surface整合即可。
URP 14+中还支持了Decal Layers ,实际上就是在MRT多存了ID用来判断。
方法对比
Screen Space(屏幕空间):
- 优点:
- 相对于Decal DBuffer,Screen Space方法通常更简单高效,不需要额外的计算和内存开销。
- 缺点:
- Screen Space方法只能在屏幕空间内进行计算,无法处理在视锥体之外的物体或遮挡关系。
- Screen Space方法只能将贴花应用于可见表面,无法在物体内部或遮挡物体之后生成贴花效果。
- 由于依赖于屏幕空间的计算,Screen Space方法可能会出现一些伪像,如贴花在物体边缘的锯齿或闪烁。
Decal DBuffer(深度缓冲贴花):
- 优点:
- Decal DBuffer方法可以提供较高的可视质量,可以正确处理贴花的遮挡和混合效果。
- Decal DBuffer可以在全局范围内进行遮挡计算,不受屏幕空间的限制。
- 可以在多个物体表面正确地应用贴花,即使它们有不同的材质和几何形状。
- 缺点:
- Decal DBuffer方法需要额外的计算和内存开销,以存储和处理深度缓冲的信息。
CloudShadows
Decal本身实现可以用URP自带的Decal系统写SG,也可以自行写shader;我这里参照的是Colin佬的实现。
CloudShadow实现如下:
//...
//cloud shadows
//===================================================
float2 ShadowUV = TRANSFORM_TEX(decalSpaceUV.xy, _ShadowTex);
float2 NoiseUV = TRANSFORM_TEX(decalSpaceUV.xy, _DistortionTex);
//movement
//"_Time[1]" specifically represents the fractional part of the time value.
float time = _Time[1];
float4 noise = tex2D(_DistortionTex,NoiseUV - frac (time/_Frequency));
//displacement
float2 displacement = _DistortionStrength * 2;
float2 circle = PI * 5.0 / _Frequency;
displacement = displacement / 2.0 * (1.0 + (float2(time,-time) + noise.xy) * circle) - _DistortionStrength;
...
//...
当然,由于贴花最后经过Alpha blend,会和原SM产生的阴影不好融合, 所以对于云的阴影还有一些其他办法。
其他做法
在3A游戏中,一般使用以体积云为基础的多种技术来表现云影。
以个人觉得表现效果最好的大表哥2为例(来自SIGGRAPH2019 R星分享)。其中采取了体积云+ESM存储的技术,并且利用Sky Irradiance Probes(大世界中每256mX256m/per probe,烘焙对应区域的位置和遮挡关系,每个probe都会被编码成三阶的SH)来表现云的整体效果,效果非常惊艳。
引用&参考
1.https://www.lfzxb.top/screen-space-decal-in-urp-study/
2.https://zhuanlan.zhihu.com/p/432146964
3.https://zhuanlan.zhihu.com/p/635124759
4.https://github.com/ColinLeung-NiloCat/UnityURPUnlitScreenSpaceDecalShader/tree/master