20. URP Pass tags & LightMode contract (Unity 6.3 / URP 17.3.0)
This chapter fixes the Pass contract, which is the most common breaking point when creating “URP compatible shaders”.
It's quicker if you rephrase the question this way.
- “If this LightMode Pass is not present, which step of the URP will fail (or fallback)?”
- “What output (RenderTarget/Stencil/ColorMask) does this Pass promise?”
- “Which project features (Deferred/TAA/XR/2D) consume this Pass?”
Principle
- “Accurate list” is fixed (generated) based on local sources.
- “When/Why is it consumed?” looks at URP Runtime (C#) and shader (Pass states) together.
20.0 Accurate Reference (Generated)
- Lit Pass/Include Map (Exact Pass List): @@TOK_0_14164784@@
- Lit core symbol xref: @@TOK_1_8394c928@@
20.1 ShaderLab Pass selection rules (summary): URP selects a Pass by ShaderTagId list
URP의 “오브젝트 그리기 패스”는 내부적으로 ShaderTagId 리스트를 가지고 있고, 그 리스트에 맞는 ShaderLab Pass를 찾아 그립니다.
URP 17.3.0 example (Forward Opaques/Transparents default tag list):
<URP>/Runtime/Passes/DrawObjectsPass.cs:91SRPDefaultUnlit,UniversalForward,UniversalForwardOnly
When you turn on the Deferred(=GBuffer) renderer, the “GBuffer + ForwardOnly” strategy is mixed.
<URP>/Runtime/UniversalRenderer.cs:388Comment Summary- Deferred possible materials:
UniversalForward+UniversalGBufferrecommended to be provided - Deferred not possible material (unlit/special):
UniversalForwardOnlyrecommended - (Legacy) unnamed pass is considered
SRPDefaultUnlitand can be processed as forward-only
- Deferred possible materials:
In other words, LightMode is not just a tag, but a contract key that is 1:1 linked to a pipeline consumption rule.
20.1.1 Understanding pass selection priority like code (concept)
URP의 오브젝트 드로우 패스는 내부 ShaderTagId 목록을 순회하면서 “첫 매칭 pass”를 선택하는 방식으로 이해하면 디버깅이 빨라집니다.
// 개념 의사코드(URP DrawObjectsPass 동작 모델)
ShaderTagId[] tags =
{
new ShaderTagId("SRPDefaultUnlit"),
new ShaderTagId("UniversalForward"),
new ShaderTagId("UniversalForwardOnly")
};
foreach (var tag in tags)
{
if (material.HasPassWithLightMode(tag))
{
Draw(tag);
break; // 첫 매칭 pass 사용
}
}
In practice, check the final matching result in Frame Debugger's Shader Pass as a standard.
20.1.2 LightMode is the basis for the contract rather than Name
ShaderLab's Pass Name is useful for debugging readability, but URP's pass selection contract basically operates based on Tags { "LightMode"="..." }.
That is:
- If it is
Name "ForwardLit"but withoutLightMode, URP may not be able to find it on the expected path. - Conversely, even if
Nameis different, ifLightModeis correct, normal matching can be achieved at the consumption stage.
As a practical rule, it is safe to manage them separately as “Name is for people, LightMode is for pipeline contracts.”
20.2 URP 17.3.0 9 types of LightMode based on Lit.shader (“actual list”)
LightMode that Lit.shader actually includes (based on Generated):
UniversalForwardUniversalGBufferShadowCasterDepthOnlyDepthNormalsMetaMotionVectorsXRMotionVectorsUniversal2D
Check the exact include roots/number here:
20.2.1 Why is UniversalForwardOnly not in the list of 9 Lit types?
UniversalForwardOnly is a contract that prepares for “Materials that cannot use the Deferred path”.
Since the URP Lit default shader handles Deferred with the combination of UniversalForward + UniversalGBuffer, it is normal for UniversalForwardOnly to not be in the default Pass list.
That is:
- Lit basic type:
UniversalForward+UniversalGBuffer - Special/Unlit/Deferred incompatible shaders: use
UniversalForwardOnlyas a separate strategy
20.3 LightMode Contract Table (as of URP 17.3.0 Lit)
The table below is intended to show both “what steps consume this LightMode” and “what the shader should output” at once.
How to read a table
- Consumption Stage: Representative pipeline stage at which the URP calls its Pass (may vary depending on mode/function)
- Core output: RenderTarget/Stencil/ColorMask perspective contract (learning/debugging core)
include root: Core files that Lit.shader directly includes in the pass (as of URP 17.3.0) LightMode Consumption stage (representative) Key output (summary) include root (representative) Typical symptoms when falling out Required/Optional Guide UniversalForwardForward color (including Forward+/transparent) Color(SV_Target0) + (Option) Rendering Layers(SV_Target1) <URP>/Shaders/LitForwardPass.hlsl,<URP>/Shaders/LitInput.hlslObject is abnormal in default color path (fallback/not printed) Essential for almost every project UniversalGBufferDeferred(GBuffer) Creating GBuffer with MRT <URP>/Shaders/LitGBufferPass.hlsl,<URP>/Shaders/LitInput.hlslLighting/materials break or forward-only fallback increases in Deferred If deferred, it is essential ShadowCastershadow map depth recording (+alpha clip) <URP>/Shaders/ShadowCasterPass.hlsl,<URP>/Shaders/LitInput.hlslCertain material shadows disappear/cutout shadow errors A must if you use shadows DepthOnlyDepthTexture/Depth prepass depth recording (color is usually masked) <URP>/Shaders/DepthOnlyPass.hlsl,<URP>/Shaders/LitInput.hlslSceneDepth based effects are unstable/broken depth dependent path Important when using DepthTexture/priming DepthNormalsDepthNormalsTexture depth+normal recording <URP>/Shaders/LitDepthNormalsPass.hlsl,<URP>/Shaders/LitInput.hlslSSAO/Outline/SSR types are broken immediately SSAO/Outline/SSR is virtually required MetaBaking (Lightmap) meta(albedo/emission) <URP>/Shaders/LitMetaPass.hlsl,<URP>/Shaders/LitInput.hlslBake results are abnormal (dark or missing emissions) A must-have for baking MotionVectorsmotion vector (velocity) RG velocity output <URP>/ShaderLibrary/ObjectMotionVectors.hlsl,<URP>/Shaders/LitInput.hlslTAA/Motion Blur/Reprojection Ghosting/Blurring Important if TAA/motion blur/reprojection XRMotionVectorsXR motion vector RGBA velocity (+stencil contract available) <URP>/ShaderLibrary/ObjectMotionVectors.hlsl,<URP>/Shaders/LitInput.hlslReprojection/motion related artifacts only in XR Important if you need motion vectors in XR Universal2D2D Renderer 2D contract color printing <URP>/Shaders/Utils/Universal2D.hlsl,<URP>/Shaders/LitInput.hlslMaterial renders abnormally in 2D Renderer Required for 2D Renderer compatibility
20.3.1 Minimum pass set for each function (for quick decision-making)
| Project conditions | Minimum Recommended LightMode Set |
|---|---|
| Basic 3D (Forward) | UniversalForward, ShadowCaster, DepthOnly, Meta |
| + Screen-based normal effect (SSAO/Outline/SSR) | Basic + DepthNormals |
| Use Deferred | Basic + UniversalGBuffer |
| + TAA/Motion Blur/Reprojection | Corresponding set + MotionVectors |
| + XR Motion Reprojection | Corresponding set + XRMotionVectors |
| 2D Renderer support | Add Universal2D (or separate dedicated shader) |
When starting quickly, it is safe to leave only the necessary passes based on samples/urp/URP_LitCompatibleTemplate.shader.
20.3.2 Pass State Contract Quick Reference
Even if the LightMode is correct, the result may be broken if the pass status is incorrect. At least check the conditions below.
| LightMode | Status Contract (Representative) | Inspection points | |
|---|---|---|---|
DepthOnly |
ZWrite On, ColorMask 0 |
Is only depth recorded and color writing blocked? | |
ShadowCaster |
ZWrite On, appropriate ZTest |
Whether the alpha clip matches the cutoff in the cutout material | |
MotionVectors |
ColorMask RG |
velocity is recorded in RG channel | |
XRMotionVectors |
ColorMask RGBA + (per project) stencil |
Whether the XR reprojection path includes a stencil contract | |
UniversalForward |
Normal color output + when necessary SV_Target1 |
Maintain additional target output contracts when using Rendering Layers | ## 20.4 MotionVectors contract (particularly frequently broken) |
In URP 17.3.0, Motion Vectors also explicitly consumes the “MotionVectors” tag in C#.
<URP>/Runtime/Passes/MotionVectorRenderPass.cs:14k_MotionVectorsLightModeTag = "MotionVectors"
Key points on the shader side:
- Lit.shader includes
ObjectMotionVectors.hlslas#include_with_pragmas.- If the pragma is inside an include, it may be missed if you just use the plain
#include.
- If the pragma is inside an include, it may be missed if you just use the plain
- MotionVectors is a step that outputs “velocity” rather than “color,” so it must be considered separately from color/alpha logic.
20.4.1 Minimal MotionVectors pass snippet
Pass
{
Name "MotionVectors"
Tags { "LightMode" = "MotionVectors" }
ColorMask RG
HLSLPROGRAM
#pragma shader_feature_local _ALPHATEST_ON
#pragma multi_compile _ LOD_FADE_CROSSFADE
#pragma shader_feature_local_vertex _ADD_PRECOMPUTED_VELOCITY
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ObjectMotionVectors.hlsl"
ENDHLSL
}
If XR response is required, it is safe to separate the XRMotionVectors pass and match the stencil/define contract (sample: samples/urp/URP_LitCompatibleTemplate.shader).
20.4.2 Common misimplementation: Missing pragma by using only #include
Below is a representative example where contract stability changes even if the same file is included.
// 위험: include 내부 pragma가 반영되지 않을 수 있음
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ObjectMotionVectors.hlsl"
// 권장: Lit.shader와 같은 방식
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ObjectMotionVectors.hlsl"
When an issue related to MotionVectors recurs, checking the include type first will help reduce the cause quickly.
20.5 DepthNormals contract (based on SSAO/Outline)
DepthNormals are consumed as a separate pass in URP.
<URP>/Runtime/Passes/DepthNormalOnlyPass.cs:19- Default tag list:
DepthNormals,DepthNormalsOnly
- Default tag list:
Breaking points in practice:
- If the normal space (WS/VS) and encoding method are different from the URP implementation, SSAO/Outline will break immediately.
- If “my shader does not participate in DepthNormals”, the texture expected by the post effect will be an empty value.
20.5.1 Minimum DepthNormals pass snippet
Pass
{
Name "DepthNormals"
Tags { "LightMode"="DepthNormals" }
ZWrite On
HLSLPROGRAM
#pragma target 4.5
#pragma vertex DepthNormalsVertex
#pragma fragment DepthNormalsFragment
#pragma multi_compile_instancing
#pragma shader_feature_local_fragment _ALPHATEST_ON
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitDepthNormalsPass.hlsl"
ENDHLSL
}
When changing to a direct implementation, normal encoding/space conversion must be verified together with the consumer side (SSAO/Outline).
20.6 Deferred Contract: UniversalForward vs UniversalForwardOnly vs UniversalGBuffer
This is a quick way to check compatibility in projects with the Deferred renderer turned on.
- Check whether opaque draw goes into
GBufferin Frame Debugger. - Check if the material provides
UniversalGBufferpass - Check that materials that cannot be deferred are separated by
UniversalForwardOnly
URP Source Comments (Key Summary, URP 17.3.0):
<URP>/Runtime/UniversalRenderer.cs:388- “Deferred-capable materials provide UniversalForward + UniversalGBuffer”
- “Undeferred materials are provided with UniversalForwardOnly”
20.6.1 Minimum Pass Combination Snippet for Deferred
// Deferred 친화형 머티리얼
Pass { Tags { "LightMode"="UniversalForward" } } // 투명/특수 경로 포함
Pass { Tags { "LightMode"="UniversalGBuffer" } } // Opaque deferred 경로
Pass { Tags { "LightMode"="ShadowCaster" } }
Pass { Tags { "LightMode"="DepthOnly" } }
Pass { Tags { "LightMode"="DepthNormals" } } // 화면기반 효과가 있으면 권장
Deferred For incompatible materials (special unlit/custom), it is operationally safer to separate shaders using the UniversalForwardOnly strategy.
20.6.2 UniversalForwardOnly Minimum snippet (for special/non-deferred materials)
Pass
{
Name "ForwardOnly"
Tags { "LightMode"="UniversalForwardOnly" }
HLSLPROGRAM
#pragma target 4.5
#pragma vertex LitPassVertex
#pragma fragment LitPassFragment
#pragma multi_compile_instancing
#pragma multi_compile_fog
#pragma shader_feature_local_fragment _ALPHATEST_ON
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl"
ENDHLSL
}
To reliably separate the material in a forward-only manner even in the deferred renderer, it is safer to specify this pass as a “separation strategy.”
20.7 Debugging routine: “Is URP using my pass?”
A) Frame Debugger
- Select a draw call to see what the “Shader Pass” is (ForwardLit/GBuffer/DepthNormals/MotionVectors, etc.)
- Check whether it is being drawn in the expected LightMode
B) RenderGraph Viewer
- Check whether Depth/Normals/History resources are created and in which path they are used.
- Check the flow to see if MotionVectors depend on depth (reprojection).
C) Quickly narrow down the cause with Generated
- Check the include roots of the LightMode Pass at @@TOK_0_14164784@@
- Check “definition location + representative call destination” at @@TOK_1_8394c928@@
D) Find missing contracts with source search (quick recipe)
rg -n 'LightMode"\\s*=\\s*"(UniversalGBuffer|DepthNormals|MotionVectors|XRMotionVectors)"' samples Assets
rg -n 'include_with_pragmas\\s+".*ObjectMotionVectors.hlsl"' samples Assets
You can quickly check whether the pass exists with the first command and whether the MotionVectors pragma is missing with the second command.
E) Symptom-based primary verification matrix
| Symptoms | What to See First | Next check |
|---|---|---|
| Material broken only in Deferred | UniversalGBuffer pass existence/matching |
UniversalForwardOnly Necessity of separation |
| SSAO/Outline defective | DepthNormals pass matching |
Normal space/encoding matching |
| TAA/Motion Blur Ghosting | MotionVectors pass matching |
include_with_pragmas Use or not |
| More than just reprojection in XR | XRMotionVectors pass matching |
Whether stencil/define contract is included |
| Missing shadows/cutout errors | ShadowCaster pass matching |
_ALPHATEST_ON + _Cutoff Match or not |
20.8 Checklist (acceptance criteria)
- If the project is Deferred, is
UniversalGBufferactually consumed? - Is
MotionVectorspass actually consumed when TAA/motion blur/reprojection is involved? - If SSAO/Outline is present, is
DepthNormalspass actually consumed? - In the Forward+ environment, do the
_CLUSTER_LIGHT_LOOPvariant andLIGHT_LOOP_BEGIN/ENDpatterns exist?
20.8.1 Completion criteria (recommended)
If the four conditions below are satisfied, the “Pass contract” reinforcement can be considered completed.
- The LightMode expected from the Frame Debugger matches the actual drawcall.
- The DepthNormals/MotionVectors resource creation-consumption flow is confirmed in the RenderGraph Viewer.
- The same material operates without functional regression in the Deferred/Forward+/XR ON/OFF combination.
- Compared to
samples/urp/URP_LitCompatibleTemplate.shader, the minimum set leaving only the necessary passes is documented.