09. Guide to writing “fully compatible” shaders based on URP (HLSL)
The goal of this chapter is to provide a checklist for writing custom HLSL shaders that are compatible with URP's existing shaders/pipeline.
Compatibility is broadly divided into four categories.
- Keyword/Variant Compatibility: URP expects
#pragma multi_compile/@shader_feature - Pass Tag/Light Mode: Rules for URP to find passes
- Constant buffer layout (SRP Batcher): CBUFFER structure/sorting rules
- Forward+/Deferred etc path compatible: light loop branching
If these four conditions are met simultaneously, the custom shader will operate stably “in an environment similar to the URP built-in Lit series.”
9.0 Accurate Reference (Generated, URP 17.3.0)
This chapter is intended to be read in conjunction with references automatically extracted from local URP/Core sources, so as not to end up as a “concept checklist”.
- Lit Pass/Include Map: @@TOK_2_14164784@@
- Function/structure/macro index:
book/generated/urp-17.3.0/symbols/* - Lit core symbol xref: @@TOK_4_8394c928@@
In particular, Pass contracts (which LightMode is consumed and when) are fixed in a separate chapter.
- Pass tags/LightMode contract table: @@TOK_5_b865c9ce@@
Precedence: 08. URP HLSL 라이브러리 지도, 07. Forward/Forward+/Lights
9.1 Pass tag: How URP recognizes passes
URP distinguishes “which pass is for what purpose” with tags such as LightMode.
If you want your custom shader to participate in a specific rendering phase (e.g. DepthOnly, ShadowCaster), You need to match the LightMode tag and pass configuration used by URP.
Practical tip: The safest way is to duplicate (structure only) the Pass section of URP's existing shader (Lit/SimpleLit/Unlit), Start by replacing only the internal HLSL.
9.1.1 There are two types of “passes”: (A) Shader pass vs (B) Render pass
Isolate the points of confusion first.
- (A) ShaderLab Pass: Pass blocks defined within one shader (ForwardLit, ShadowCaster, etc.)
- (B) URP Render Pass(ScriptableRenderPass): Render step inserted into the URP pipeline.
URP's Render Pass selects and uses a specific ShaderLab Pass (=LightMode) of the shader when drawing a specific cue at a specific time.
In other words, “compatible” means:
- The LightMode Pass that URP expects exists and
- The pass satisfies the input/keyword/buffer layout expected by URP.
It means a state of being.
9.1.2 LightMode tag is “Contract” (as of URP 17.3.0 Lit)
URP selects a ShaderLab Pass based on LightMode from the Render Pass (pipeline stage).
So “fully compatible” means:
- Provides a LightMode Pass consumed by the project,
- Satisfies the output/keyword/input structure required by the pass.
means.
The 9 LightModes actually included by Lit.shader in URP 17.3.0 (as of Generated):
| LightMode | Recommended classification | Main consumption stages (summary) | Remarks |
|---|---|---|---|
UniversalForward |
Essential level | Forward color | Includes Forward+ branches |
ShadowCaster |
Essential level | shadow map | Alpha Clip/Bias |
DepthOnly |
Usually required | DepthTexture/Depth prepass | ColorMask Setting Caution |
DepthNormals |
Feature Dependency | DepthNormalsTexture | Based on SSAO/Outline/SSR type |
Meta |
Feature Dependency | Lightmap Baking | Baking is virtually essential |
UniversalGBuffer |
Feature Dependency | Deferred(GBuffer) | Essential for deferred projects |
MotionVectors |
Feature Dependency | velocity | TAA/Motion Blur/Reprojection |
XRMotionVectors |
XR Dependency | XR velocity | Stencil Contract Included |
Universal2D |
2D dependence | 2D Renderer | 2D pipeline contract |
Exact include/entry/definition location:
Contract (when/why consumed) details:- @@TOK_0_b865c9ce@@
9.2 Basic framework of URP shader (ShaderLab)
When creating a URP-compatible shader, it is safe to use the following form as the basic framework “including the Pass contract”.
Shader "Custom/URP/ExampleLit"
{
Properties
{
_BaseMap ("Base Map", 2D) = "white" {}
_BaseColor ("Base Color", Color) = (1,1,1,1)
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"RenderType"="Opaque"
"Queue"="Geometry"
}
// Pass: Forward(컬러)
Pass
{
Name "ForwardLit"
Tags { "LightMode"="UniversalForward" }
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment Frag
// (A) URP 키워드/variant
// (B) include/CBUFFER
// (C) 엔트리 함수
ENDHLSL
}
// Pass: ShadowCaster(그림자)
Pass
{
Name "ShadowCaster"
Tags { "LightMode"="ShadowCaster" }
HLSLPROGRAM
#pragma vertex VertShadow
#pragma fragment FragShadow
ENDHLSL
}
// Pass: DepthOnly(깊이)
Pass { Name "DepthOnly" Tags { "LightMode"="DepthOnly" } }
// Pass: DepthNormals(깊이+노말)
Pass { Name "DepthNormals" Tags { "LightMode"="DepthNormals" } }
// Pass: Meta(베이킹)
Pass { Name "Meta" Tags { "LightMode"="Meta" } }
// (옵션) Deferred를 목표로 한다면:
Pass { Name "GBuffer" Tags { "LightMode"="UniversalGBuffer" } }
// (옵션) TAA/모션블러/리프로젝션을 목표로 한다면:
Pass { Name "MotionVectors" Tags { "LightMode"="MotionVectors" } }
// (옵션) XR에서 모션 벡터를 쓰면:
Pass { Name "XRMotionVectors" Tags { "LightMode"="XRMotionVectors" } }
// (옵션) 2D Renderer 호환이 필요하면:
Pass { Name "Universal2D" Tags { "LightMode"="Universal2D" } }
}
}
The above code is a framework to show the “Pass contract list”.
It is safest for a practical implementation to use the URP Lit Pass configuration as a starting point:
- Template (sample):
samples/urp/URP_LitCompatibleTemplate.shader- Description chapter: 18. URP 완전 호환 Pass 세트 템플릿
9.3 URP include configuration (minimum practical set)
URP shaders usually start based on the following include:
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
In URP 17.3.0, Core.hlsl is effectively a “root include”.
- Forward+ branch switch:
_CLUSTER_LIGHT_LOOP→USE_CLUSTER_LIGHT_LOOP - Pull SRP Core (common) include (
Common.hlsl,Packing.hlsl, etc.) - Include URP
Input.hlsl(global constant/struct/util base)
Related (exact definition location is based on generated):
USE_CLUSTER_LIGHT_LOOP:<URP>/ShaderLibrary/Core.hlsl_CLUSTER_LIGHT_LOOP:<URP>/ShaderLibrary/ForwardPlusKeyword.deprecated.hlsl
For your purposes here:
- If lighting is needed, include Lighting/RealtimesLights series
- If depth sampling is needed, include the DeclareDepthTexture series.
- If you want to do surface texture sampling in URP style, include the SurfaceInput series.
Add . The specific “which files should be included” may vary depending on the URP version. We recommend keeping track of the list that URP Lit includes. (Related: 08. URP HLSL 라이브러리 지도)
Note: If you need
#include_with_pragmas
URP includes some files (DOTS, ObjectMotionVectors, etc.) as#include_with_pragmas.
If that file contains#pragmainside (e.g. MotionVectors pass), the plain#includealone may be “missing a necessary pragma”.
For detailed pass contracts/examples, refer to @@TOK_14_b865c9ce@@.
9.4 Keywords/Variants: “What needs to be adjusted” and “What needs to be reduced”
When matching keywords for URP compatibility, it is easy for variants to explode.
- Advantages: Naturally combined with URP features (additional lights, shadows, soft shadows, lightmaps, fog…)
- Disadvantage: Increased build time/memory/loading time
9.4.1 Lighting/shadow related keywords (representative)
Commonly seen in shaders containing Forward(+)/shadow:
- Additional Lights:
_ADDITIONAL_LIGHTS,_ADDITIONAL_LIGHTS_VERTEX - Additional Light Shadows:
_ADDITIONAL_LIGHT_SHADOWS - Forward+ loop:
_CLUSTER_LIGHT_LOOP - Soft Shadows:
_SHADOWS_SOFT(or similar keyword) - Main Light Shadows/Cascade: Keyword composition varies depending on URP version/shader
Recommended: Turn on “only what you really need” for your first implementation, and verify by increasing keywords each time you add a feature.
9.5 SRP Batcher Compatibility: CBUFFER Rule (Very Important)
SRP Batcher optimizes based on the Material Constant Buffer Layout to reduce material constant upload costs.
So, to bring SRP Batcher to life in a custom shader:
- Put the material property in
CBUFFER_START(UnityPerMaterial) ... CBUFFER_END - Prevent layout (type/order) from changing with conditional compilation
- Avoid placing material values in global (non-CBUFFER) variables unnecessarily.
This is the key.
CBUFFER_START(UnityPerMaterial)
float4 _BaseColor;
float4 _BaseMap_ST;
CBUFFER_END
9.5.1 Patterns that frequently break SRP Batcher
- CBUFFER layout changes by removing the property or changing the type with
#if - CBUFFER content/order varies for each pass
- It is a material value, but is scattered outside the global variable/constant buffer.
Related official documents:
- SRP Batcher Overview: https://docs.unity3d.com/Manual/SRPBatcher.html
9.6 Forward+ Compatible: Light Loop Branching
Supporting Forward+ requires the keywords and branching patterns described in 07. Forward/Forward+/Lights.
In Forward+, there are cases where the shader side should not “simply loop additional lights in the traditional way”. Be sure to check the cluster loop path using URP provided macros/functions.
Key Checks:
- Does
_CLUSTER_LIGHT_LOOPvariant exist? - Did you know that
GetAdditionalLightsCount()in Forward+ can return 0?- Solved: Use
LIGHT_LOOP_BEGIN/ENDpattern (unify Forward/Forward+ loops within URP into macro)
- Solved: Use
The exact definition/reference location is fixed by the generated xref:
Related official documents:
- Forward+ additional light/keyword: https://docs.unity3d.com/6000.3/Documentation/Manual/urp/rendering/additional-lights-fplus.html
9.7 “Fully Compatible” Checklist (Summary)
A) Pipeline/Pass
- Is there
Tags { "RenderPipeline"="UniversalPipeline" } - Is the necessary LightMode Pass (Forward/ShadowCaster/DepthOnly/DepthNormals/Meta) present?
B) Keyword/Variant
- Are the keywords required for the URP functions (Additional Lights, Shadows, Soft Shadows, Fog…) used in the project included?
- When using Forward+, is
_CLUSTER_LIGHT_LOOPincluded?
C) Constant buffer (SRP Batcher)
- Are the material properties gathered in
UnityPerMaterialCBUFFER? - Is the CBUFFER layout consistent between passes?
D) Resource input (Depth/Normals, etc.)
- If you need Depth/Normal/History, have you requested/configured them to actually be created in the pipeline?
- Did you do the correct include/sampling/linearization in the shader?
Further reading (official/authoritative sources)
- Forward+ Additional light keyword/action: https://docs.unity3d.com/6000.3/Documentation/Manual/urp/rendering/additional-lights-fplus.html
- SRP Batcher: https://docs.unity3d.com/Manual/SRPBatcher.html