深度图也是会粉的。

这次笔记没什么美术含量,算是一个代码解析,如果有错误的地方欢迎勘误。

UnityURPshader光照部分学习分析(下)-ShadowCaster和Depthnormals

Lit.shader中的Subshader结构

之前一直忘记提到shader中的结构了,由于默认的Lit.shader是一个单pass实现的效果,所以Lit中把大部分的光照计算都放在了”ForwardLit”这个pass中,这个pass里依靠properties变量来决定Blend、ZWrite、Cull的命令,并且在下面首先有一行图形API版本需求的声明,然后是大量的keywords,这些keyword是否被定义决定了在光照时的计算方式(在前两篇的代码解释中已经提及),比如我们常用的multi_compile_fragment系列。

然后就是直接使用顶点和片元着色器进行光照。

而下面的几个pass”ShadowCaster”、”GBuffer”、”DepthOnly”、”DepthNormals”、”Meta”、”Universal2D”等,则是其它产生作用的pass,我们通常会用到ShaderCaster和DepthNormals两个pass,它们的作用分别是向shadowmap中写入阴影和在深度法线图中写入深度和法线。

代码
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295

Pass
{
// Lightmode matches the ShaderPassName set in UniversalRenderPipeline.cs. SRPDefaultUnlit and passes with
// no LightMode tag are also rendered by Universal Render Pipeline
Name "ForwardLit"
Tags{"LightMode" = "UniversalForward"}

Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
Cull[_Cull]

HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5

// -------------------------------------
// Material Keywords
#pragma shader_feature_local _NORMALMAP
#pragma shader_feature_local _PARALLAXMAP
#pragma shader_feature_local _RECEIVE_SHADOWS_OFF
#pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED
#pragma shader_feature_local_fragment _SURFACE_TYPE_TRANSPARENT
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON
#pragma shader_feature_local_fragment _EMISSION
#pragma shader_feature_local_fragment _METALLICSPECGLOSSMAP
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature_local_fragment _OCCLUSIONMAP
#pragma shader_feature_local_fragment _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature_local_fragment _ENVIRONMENTREFLECTIONS_OFF
#pragma shader_feature_local_fragment _SPECULAR_SETUP

// -------------------------------------
// Universal Pipeline keywords
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION
#pragma multi_compile_fragment _ _SHADOWS_SOFT
#pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION
#pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3
#pragma multi_compile_fragment _ _LIGHT_LAYERS
#pragma multi_compile_fragment _ _LIGHT_COOKIES
#pragma multi_compile _ _CLUSTERED_RENDERING

// -------------------------------------
// Unity defined keywords
#pragma multi_compile _ LIGHTMAP_SHADOW_MIXING
#pragma multi_compile _ SHADOWS_SHADOWMASK
#pragma multi_compile _ DIRLIGHTMAP_COMBINED
#pragma multi_compile _ LIGHTMAP_ON
#pragma multi_compile _ DYNAMICLIGHTMAP_ON
#pragma multi_compile_fog
#pragma multi_compile_fragment _ DEBUG_DISPLAY

//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma multi_compile _ DOTS_INSTANCING_ON

#pragma vertex LitPassVertex
#pragma fragment LitPassFragment

#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl"
ENDHLSL
}

Pass
{
Name "ShadowCaster"
Tags{"LightMode" = "ShadowCaster"}

ZWrite On
ZTest LEqual
ColorMask 0
Cull[_Cull]

HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5

// -------------------------------------
// Material Keywords
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON

// -------------------------------------
// Universal Pipeline keywords

// This is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
#pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW

#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment

#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
ENDHLSL
}

Pass
{
// Lightmode matches the ShaderPassName set in UniversalRenderPipeline.cs. SRPDefaultUnlit and passes with
// no LightMode tag are also rendered by Universal Render Pipeline
Name "GBuffer"
Tags{"LightMode" = "UniversalGBuffer"}

ZWrite[_ZWrite]
ZTest LEqual
Cull[_Cull]

HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5

// -------------------------------------
// Material Keywords
#pragma shader_feature_local _NORMALMAP
#pragma shader_feature_local_fragment _ALPHATEST_ON
//#pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON
#pragma shader_feature_local_fragment _EMISSION
#pragma shader_feature_local_fragment _METALLICSPECGLOSSMAP
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature_local_fragment _OCCLUSIONMAP
#pragma shader_feature_local _PARALLAXMAP
#pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED

#pragma shader_feature_local_fragment _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature_local_fragment _ENVIRONMENTREFLECTIONS_OFF
#pragma shader_feature_local_fragment _SPECULAR_SETUP
#pragma shader_feature_local _RECEIVE_SHADOWS_OFF

// -------------------------------------
// Universal Pipeline keywords
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
//#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
//#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION
#pragma multi_compile_fragment _ _SHADOWS_SOFT
#pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3
#pragma multi_compile_fragment _ _LIGHT_LAYERS
#pragma multi_compile_fragment _ _RENDER_PASS_ENABLED

// -------------------------------------
// Unity defined keywords
#pragma multi_compile _ LIGHTMAP_SHADOW_MIXING
#pragma multi_compile _ SHADOWS_SHADOWMASK
#pragma multi_compile _ DIRLIGHTMAP_COMBINED
#pragma multi_compile _ LIGHTMAP_ON
#pragma multi_compile _ DYNAMICLIGHTMAP_ON
#pragma multi_compile_fragment _ _GBUFFER_NORMALS_OCT

//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma multi_compile _ DOTS_INSTANCING_ON

#pragma vertex LitGBufferPassVertex
#pragma fragment LitGBufferPassFragment

#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitGBufferPass.hlsl"
ENDHLSL
}

Pass
{
Name "DepthOnly"
Tags{"LightMode" = "DepthOnly"}

ZWrite On
ColorMask 0
Cull[_Cull]

HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5

#pragma vertex DepthOnlyVertex
#pragma fragment DepthOnlyFragment

// -------------------------------------
// Material Keywords
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON

#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
ENDHLSL
}

// This pass is used when drawing to a _CameraNormalsTexture texture
Pass
{
Name "DepthNormals"
Tags{"LightMode" = "DepthNormals"}

ZWrite On
Cull[_Cull]

HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5

#pragma vertex DepthNormalsVertex
#pragma fragment DepthNormalsFragment

// -------------------------------------
// Material Keywords
#pragma shader_feature_local _NORMALMAP
#pragma shader_feature_local _PARALLAXMAP
#pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON

#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitDepthNormalsPass.hlsl"
ENDHLSL
}

// This pass it not used during regular rendering, only for lightmap baking.
Pass
{
Name "Meta"
Tags{"LightMode" = "Meta"}

Cull Off

HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5

#pragma vertex UniversalVertexMeta
#pragma fragment UniversalFragmentMetaLit

#pragma shader_feature EDITOR_VISUALIZATION
#pragma shader_feature_local_fragment _SPECULAR_SETUP
#pragma shader_feature_local_fragment _EMISSION
#pragma shader_feature_local_fragment _METALLICSPECGLOSSMAP
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED

#pragma shader_feature_local_fragment _SPECGLOSSMAP

#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitMetaPass.hlsl"

ENDHLSL
}

Pass
{
Name "Universal2D"
Tags{ "LightMode" = "Universal2D" }

Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
Cull[_Cull]

HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5

#pragma vertex vert
#pragma fragment frag
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON

#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/Utils/Universal2D.hlsl"
ENDHLSL
}

ShadowCaster

shadowcaster pass的结构非常简单,它写入深度、并且进行深度测试,但不写入颜色,使用了一个宏来进行直接光和点光之间的转换,然后就是两个函数,这两个函数在ShadowCaster.hlsl中。

代码
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

#ifndef UNIVERSAL_SHADOW_CASTER_PASS_INCLUDED
#define UNIVERSAL_SHADOW_CASTER_PASS_INCLUDED

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"

// Shadow Casting Light geometric parameters. These variables are used when applying the shadow Normal Bias and are set by UnityEngine.Rendering.Universal.ShadowUtils.SetupShadowCasterConstantBuffer in com.unity.render-pipelines.universal/Runtime/ShadowUtils.cs
// For Directional lights, _LightDirection is used when applying shadow Normal Bias.
// For Spot lights and Point lights, _LightPosition is used to compute the actual light direction because it is different at each shadow caster geometry vertex.
float3 _LightDirection;
float3 _LightPosition;

struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct Varyings
{
float2 uv : TEXCOORD0;
float4 positionCS : SV_POSITION;
};

float4 GetShadowPositionHClip(Attributes input)
{
float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
float3 normalWS = TransformObjectToWorldNormal(input.normalOS);

#if _CASTING_PUNCTUAL_LIGHT_SHADOW
float3 lightDirectionWS = normalize(_LightPosition - positionWS);
#else
float3 lightDirectionWS = _LightDirection;
#endif

float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirectionWS));

#if UNITY_REVERSED_Z
positionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#else
positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#endif

return positionCS;
}

Varyings ShadowPassVertex(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);

output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
output.positionCS = GetShadowPositionHClip(input);
return output;
}

half4 ShadowPassFragment(Varyings input) : SV_TARGET
{
Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
return 0;
}

#endif

这个函数写的就是一个普通的unlit函数做的事情,但是在vertex中用GetShadowPositionHClip()来替换了原本的TransformXXXToHClip(),而这个函数它调用了一个叫AppyShadowBias()的函数来计算裁剪空间坐标,这个函数在Shadows.hlsl中。

代码
1
2
3
4
5
6
7
8
9
10
11
12

float3 ApplyShadowBias(float3 positionWS, float3 normalWS, float3 lightDirection)
{
float invNdotL = 1.0 - saturate(dot(lightDirection, normalWS));
float scale = invNdotL * _ShadowBias.y;

// normal bias is negative since we want to apply an inset normal offset
positionWS = lightDirection * _ShadowBias.xxx + positionWS;
positionWS = normalWS * scale.xxx + positionWS;
return positionWS;
}

出人意料地,这个函数并没有再调用别的函数,它的作用是将原本片元的positionWS沿着lightingDirection和normalWS分别偏移了一段距离,大家可以自己试验一下加和不加这个偏移造成的结果有什么差距。

而片元着色器中用到的Alpha()和SampleAlbedoAlpha()函数在SurfaceInput.hlsl中。

代码
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

half Alpha(half albedoAlpha, half4 color, half cutoff)
{
#if !defined(_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A) && !defined(_GLOSSINESS_FROM_BASE_ALPHA)
half alpha = albedoAlpha * color.a;
#else
half alpha = color.a;
#endif

#if defined(_ALPHATEST_ON)
clip(alpha - cutoff);
#endif

return alpha;
}

half4 SampleAlbedoAlpha(float2 uv, TEXTURE2D_PARAM(albedoAlphaMap, sampler_albedoAlphaMap))
{
return half4(SAMPLE_TEXTURE2D(albedoAlphaMap, sampler_albedoAlphaMap, uv));
}

half3 SampleNormal(float2 uv, TEXTURE2D_PARAM(bumpMap, sampler_bumpMap), half scale = half(1.0))
{
#ifdef _NORMALMAP
half4 n = SAMPLE_TEXTURE2D(bumpMap, sampler_bumpMap, uv);
#if BUMP_SCALE_NOT_SUPPORTED
return UnpackNormal(n);
#else
return UnpackNormalScale(n, scale);
#endif
#else
return half3(0.0h, 0.0h, 1.0h);
#endif
}

half3 SampleEmission(float2 uv, half3 emissionColor, TEXTURE2D_PARAM(emissionMap, sampler_emissionMap))
{
#ifndef _EMISSION
return 0;
#else
return SAMPLE_TEXTURE2D(emissionMap, sampler_emissionMap, uv).rgb * emissionColor;
#endif
}

这个Alpha()函数很容易理解,它就是在用color的a通道去做一个AlphaTest,clip掉一定阈值下的颜色,而SampleAlbedoAlpha()也就是一个普通的采样2D贴图来得到alpha通道。

自定义ShadowCaster

可以看出,整个shadowmap的写入并没有开放给我们。shadowcaster其实只完成了这样一件事情——做一个带偏移的世界坐标to齐次裁剪坐标的变换,然后做一个alphatest,其它都没有开放,但其实这些实现大部分的设计思路也足够了。

通常需要做的自定义阴影投影多半也就是改变一下阴影的形状、颜色和clip等等,显然,shadowCaster是不支持改变阴影颜色的,形状的改变可以依靠顶点着色器里面进行位移来实现,不过也比较复杂,完全不如在读取阴影的时候直接进行处理。

因此大部分我们需要做的也就是clip一下吧。

DepthNormals

首先依然是查看DepthNormalsPass.hlsl的代码。

代码
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
48
49
50
51
52
53
54
55
56
57
58
59
60

#ifndef UNIVERSAL_DEPTH_NORMALS_PASS_INCLUDED
#define UNIVERSAL_DEPTH_NORMALS_PASS_INCLUDED

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

struct Attributes
{
float4 positionOS : POSITION;
float4 tangentOS : TANGENT;
float2 texcoord : TEXCOORD0;
float3 normal : NORMAL;
UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD1;
float3 normalWS : TEXCOORD2;

UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};

Varyings DepthNormalsVertex(Attributes input)
{
Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);

VertexNormalInputs normalInput = GetVertexNormalInputs(input.normal, input.tangentOS);
output.normalWS = NormalizeNormalPerVertex(normalInput.normalWS);

return output;
}

half4 DepthNormalsFragment(Varyings input) : SV_TARGET
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);

#if defined(_GBUFFER_NORMALS_OCT)
float3 normalWS = normalize(input.normalWS);
float2 octNormalWS = PackNormalOctQuadEncode(normalWS); // values between [-1, +1], must use fp32 on some platforms.
float2 remappedOctNormalWS = saturate(octNormalWS * 0.5 + 0.5); // values between [ 0, 1]
half3 packedNormalWS = PackFloat2To888(remappedOctNormalWS); // values between [ 0, 1]
return half4(packedNormalWS, 0.0);
#else
float3 normalWS = NormalizeNormalPerPixel(input.normalWS);
return half4(normalWS, 0.0);
#endif
}
#endif


代码依然很短,顶点着色器中只是做了个很正常的变换,并且根据设置的渲染质量来判断是否需要归一化法线(NormalizeNormalPerVertex()函数,这个函数没找到在哪,谷歌了一下有什么用……)

片元着色器中,首先依然是进行一个alphatest,然后直接就输出法线了,和shadowcaster一下,具体的映射方式依然没有开放给我们,我们能进行操作的只有改变法线的方向以及进行clip的部分。

值得一提的是,很多朋友都会在发现在使用DepthOnly这个pass的时候没法正确地向深度图写入深度,而对比下来我们会发现这两个pass的函数写得没有任何区别,只是depthNormals多了一计算法线的过程,而DepthOnlyFragment()直接在alphatest之后输出了0.

于是回看Lit.shader,发现这两个Pass唯一的区别只有DepthNormals并没有开启ColorMask 0,而且DepthNormalsFragment()如果同样只输出0的话可以发现,它确实是会输出颜色的。

URP,你做的好啊,我完全无法参透这个里面的逻辑。

结语

到这里其实我们已经可以在URP的框架上进行改动来创造一个属于自己的前向shader,并且尽量使用URP的现有代码来减少代码量了,如果说是给这几天的学习交个报告的话,那就用URP框架来写一个完整的NPR Shader吧。