04_投影追加
投影追加
当前处理了兰伯特的明暗关系,现在进行投影的处理,使用屏幕空间投影
开启深度缓冲,调整深度贴图渲染模式
找到最开始创建的URP文件资源
调整参数到Force Prepass,原来默认是AfterOpaques
开启深度缓冲
参数介绍,来自DeepSeek

使用FrameDebug查看是否正常启用
这里发现开启了DepthTexture在FrameDebug中并没有调用,这是因为Shader中没有加入DepthOnly这个Pass进行处理 
追加DepthOnlyPass
在最上层的Pass下面追加DepthOnlyPass 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// DepthPass
pass
{
Name"DepthOnly"
Tags
{
"LightMode" = "DepthOnly"
}
ZWrite [_ZWrite]
ZTest LEqual
ColorMask 0
Cull [_Cull]
// HLSL程序段
HLSLPROGRAM
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON
#pragma vertex vert
#pragma fragment frag
struct Attributes
{
float4 positionOS : POSITION;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
};
Varyings vert(Attributes input)
{
Varyings output = (Varyings)0;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
return output;
}
float4 frag(Varyings input) : SV_TARGET
{
clip(1.0 - _AlphaClip);
return 0;
}
ENDHLSL
}
此时追加后在使用FrameDebug应该就正常了,DepthPrePass在绘制物体的Pass前面 
Shader参数追加

投影追加

分段解析
对于深度信息,实际上就是裁剪空间下的w分量,它已经是线性的了。如果要关联屏幕像素,就需要将屏幕空间宽度系数转换到深度空间,也就是进行透视除法,也就是需要除以W分量 这里先取得了W分量,然后计算了透视除法的因子,也就是1.0/linearEyeDepth
然后接着计算偏移的乘量,_ScreenSpaceShadowWidth是我们自定义的参数,这里乘上5.0是经验值为了方便调整参数,然后乘上透视因子perspective,最后除以100应该是进行百分制的换算,就计算完成
下面先对光向量进行处理,将光向量转换到视图空间,偏移值则是取X和Y分量,也就是仅在屏幕上上下或者左右偏移,最后乘上偏移的乘量,应该可以理解为灯光偏移百分之多少
计算屏幕UV采样坐标,原坐标就是裁剪空间的XY,Offset是NDC下的所以需要乘上屏幕的长宽,然后对坐标进行限制防止越界。然后就是采样,采样出来的是非线性的所以需要进行转换,最后就是进行线性转换
为了有效地利用深度缓冲区的有限精度,深度值通常会经过非线性映射。例如,常用的深度映射公式是: zndc = zclip/zclip + d 其中zclip是裁剪平面的位置 ,d是物体到摄像机的距离。这个公式导致了深度值在近处变化更快,而在远处变化较慢。 ** **这里进行最终投影计算,使用偏移过后的深度图减去原深度图,这里为了防止在距离非常近的情况下两个图相减效果不对所以需要对原深度图减去一个阈值这样可以保证两个图之间一定会有差值并且不会出现自阴影,最后进行x50这个经验值进行增强对比度在除以一个人为控制的衰减值,这里是除数所以必须大于0
完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//投影追加
float shadowAttenuation = 1.0;
#if _SCREEN_SPACE_SHADOW
{
//读取像素深度
float linearEyeDepth = input.positionCS.w;
float perspective = 1.0 / linearEyeDepth;
float offsetMul = _ScreenSpaceShadowWidth * 5.0 * perspective / 100.0;
float3 lightDirectionVS = TransformWorldToViewDir(lightDirectionWS);
float2 offset = lightDirectionVS.xy * offsetMul;
int2 coord = input.positionCS.xy + offset * _ScaledScreenParams.xy;
//钳制防止采样到边界
coord = min(max(0, coord), _ScaledScreenParams.xy - 1);
float offsetSceneDepth = LoadSceneDepth(coord);
float offsetSceneLinearEyeDepth = LinearEyeDepth(offsetSceneDepth, _ZBufferParams);
float fadeout = max(1e-5, _ScreenSpaceShadowFadeout);
shadowAttenuation = saturate((offsetSceneLinearEyeDepth - (linearEyeDepth - _ScreenSpaceShadowThreshold)) * 50 / fadeout);
}
#endif
投影效果

将投影追加到光照层级中
这里保证了能量守恒,将投影全部给了ShallowFade,Shadow部分保持不变。Shallowfade能量增加,Shallow能量减小了,这里计算特别复杂,需要画图分析 
投影效果

Enjoy Reading This Article?
Here are some more articles you might like to read next: