The most powerful criterion for studying URP shaders “correctly” is the struct.
- Functions come and go, but data contracts (structure fields and meaning) persist longer.
- In particular, in the Lit series,
SurfaceDataandInputDataare effectively “pipeline interfaces.”
This chapter aims to:
- Rather than memorizing structure fields, connect meaning/space/producer/consumer.
LitPassFragment → UniversalFragmentPBRDescribe the data flow “by field.”- When debugging, make it a habit to narrow down the cause to “which field is empty.”
21.0 Accurate Reference (IDE)
Primary source for structure definition/field list:
Function signature (to keep track of who fills it):
Lit core symbol xref (definition + representative caller):
21.1 Lit data flow (big picture)
If you look at Lit Forward as a data flow, it is as follows.
The “accurate reference” for this flow is further narrowed down and fixed in Chapter 16/22.
21.2 VertexPositionInputs: Location (WS/VS/CS/NDC) Agreement
Definition (URP 17.3.0):
<URP>/ShaderLibrary/Core.hlsl:259(@@TOK_4_11a7626e@@)
Generate function:
VertexPositionInputs GetVertexPositionInputs(float3 positionOS)<URP>/ShaderLibrary/ShaderVariablesFunctions.hlsl:8
Field:
| Field | Type | meaning | State Consumer Agency |
|---|---|---|---|
positionWS | float3 | World coordinates | Lighting/Shadow/Cluster (Forward+) |
positionVS | float3 | View (camera) coordinates | Depth-based calculations/special effects |
positionCS | float4 | clip coordinates | rasterizer input (final) |
positionNDC | float4 | Normalized Device Coordinates (NDC) | Screen UV calculation, etc. |
Practical tips:
- “Forward+ cluster loop” consumes
positionWSandnormalizedScreenSpaceUVvery frequently. - It is difficult to debug a shader that is only created by
positionCS. If possible, useGetVertexPositionInputsin URP as a starting point.
21.3 VertexNormalInputs: tangent basis contract (WS tangent/bitangent/normal)
Definition (URP 17.3.0):
<URP>/ShaderLibrary/Core.hlsl:267
Generating function (representative):
VertexNormalInputs GetVertexNormalInputs(float3 normalOS)<URP>/ShaderLibrary/ShaderVariablesFunctions.hlsl:22
VertexNormalInputs GetVertexNormalInputs(float3 normalOS, float4 tangentOS)<URP>/ShaderLibrary/ShaderVariablesFunctions.hlsl:31
Field:
| Field | Type | meaning | State Consumer Agency |
|---|---|---|---|
tangentWS | real3 | World space tangent | Normal map/tangent space transformation |
bitangentWS | real3 | World space bitangent | Normal map/tangent space transformation |
normalWS | float3 | World space normal | Light/BRDF/Shadow |
Practical tips:
- “Normal is flipped/highlight is strange” is mostly a tangent basis problem.
normalWSconsumes virtually everywhere in light loops/shadows/indirect lighting (most expensive bug).
21.4 SurfaceData: Material (surface) input contract
Definition (URP 17.3.0):
<URP>/ShaderLibrary/SurfaceData.hlsl:5
Commonly filled functions in Lit:
InitializeStandardLitSurfaceData(float2 uv, out SurfaceData outSurfaceData)<URP>/Shaders/LitInput.hlsl:252
Field:
| Field | Type | Meaning (Summary) | State Consumer Agency |
|---|---|---|---|
albedo | half3 | Base color (texture/color product result) | PBR |
specular | half3 | specular color (specular workflow) | PBR |
metallic | half | Metallic (Metallic Workflow) | PBR |
smoothness | half | Smoothness (reciprocal roughness series) | PBR |
normalTS | half3 | Tangent space normal (normal map result) | Configure InputData |
emission | half3 | Self-luminous | final color |
occlusion | half | Occlusion | Light Attenuation |
alpha | half | Alpha (transparent/cutout) | OutputAlpha |
clearCoatMask | half | Clear coat mask | PBR (optional) |
clearCoatSmoothness | half | Clearcoat Smoothness | PBR (optional) |
Practical tips (safe customization):
- Step 1 of “Change my material model” is to adjust the
SurfaceDatavalue. - This step is relatively independent from the Pass contract/light loop/Forward+ branch, making it easy to maintain compatibility.
21.5 InputData: Space/Camera/Lighting Preparation Agreement (Core)
Definition (URP 17.3.0):- <URP>/ShaderLibrary/Input.hlsl:43
Commonly filled functions in Lit Forward:
InitializeInputData(Varyings input, half3 normalTS, out InputData inputData)<URP>/Shaders/LitForwardPass.hlsl:72
Fields (23 total, based on IDE):
The table below is for viewing “field meaning + usually who fills it and where it is used” at a glance.
Which fields are actually populated may depend on project features (Decal/APV/Debug/ProbeVolumes, etc.) and keywords.
| Field | Type | meaning/space | Main producer (representative) | State Consumer Agency (Representative) |
|---|---|---|---|---|
positionWS | float3 | World Location | InitializeInputData | Light/Shadow/Cluster |
positionCS | float4 | clip location | Varyings/Pass | LODFade/Decal etc. |
normalWS | float3 | World normal (normalization required) | InitializeInputData | PBR/Shadow |
viewDirectionWS | half3 | World view orientation (usually normalized) | InitializeInputData | PBR |
shadowCoord | float4 | Main light shadow coordinates | GetShadowCoord etc. | Shadow Sampling |
fogCoord | half | fog coefficient | InitializeInputData | MixFog |
vertexLighting | half3 | vertex additional lighting stacked | vertex path | Final color (optional) |
bakedGI | half3 | baked GI(LM/APV/SH) | InitializeBakedGIData | PBR |
normalizedScreenSpaceUV | float2 | 0..1 screen UV | GetNormalizedScreenSpaceUV etc. | Forward+ (cluster), SSR type |
shadowMask | half4 | shadowmask/probe occlusion | GI/Lightmap Path | shadow mixing |
tangentToWorld | half3x3 | TS→WS basis | InitializeInputData | normalTS conversion |
dynamicLightmapUV | half2 | Dynamic Lightmap UV | vertex path | GI |
staticLightmapUV | half2 | static lightmap UV | vertex path | GI |
vertexSH | float3 | SH(vertex) | vertex path | GI |
brdfDiffuse | half3 | BRDF diffuse cache (optional) | PBR Preparation | PBR |
brdfSpecular | half3 | BRDF specular cache (optional) | PBR Preparation | PBR |
uv | float2 | UV for original/tracking (optional) | Debug/Function | Debug/VT etc. |
mipCount | uint | number of mips (optional) | VT/Streaming | Debug |
texelSize | float4 | texel size (optional) | VT/Streaming | Debug |
mipInfo | float4 | mip information (optional) | VT/Streaming | Debug |
streamInfo | float4 | Streaming Information (Optional) | VT/Streaming | Debug |
originalColor | float3 | Original color (optional) | Debug/Post-Processing | Debug |
probeOcclusion | float4 | Probe occlusion (optional) | probe/APV | shadow mixing |
Practical tips:
- If
normalizedScreenSpaceUVis empty in Forward+, the cluster light loop does not work properly. shadowCoordis the key input of “Main Light Shadow”. If the direction/space is misaligned, the entire shadow will be broken.
21.6 Light: Light summary (light consumption contract)
Definition (URP 17.3.0):
<URP>/ShaderLibrary/RealtimeLights.hlsl:12
Field:
| Field | Type | Meaning (Summary) |
|---|---|---|
direction | half3 | Light direction |
color | half3 | Light color/intensity |
distanceAttenuation | float | Distance Attenuation |
shadowAttenuation | half | Shadow Attenuation |
layerMask | uint | light layer matching |
Core API combined with Forward+ loop:
GetMainLight/GetAdditionalLight/LIGHT_LOOP_BEGIN/END- For more information: 07. Forward/Forward+/Lights
21.7 Common mistakes (data flow perspective)1) Space confusion: Mixing normalTS/normalWS and positionCS/positionNDC
- Missing normalization: Specular is broken due to
normalWS,viewDirectionWSnot being normalized - Forward+ input missing:
normalizedScreenSpaceUVis empty, cluster loop fails - DepthNormals contract not involved: SSAO/Outline reads empty texture
A quick practical debugging routine can be found in the following chapters.
- Pass contract: @@TOK_3_b865c9ce@@
- Lit callflow/edit point: @@TOK_4_e356ea28@@
- Troubleshooting Playbook: @@TOK_5_a2e9de9a@@