21. URP HLSL Structs & Dataflow (URP 17.3.0)
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 (Generated)
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.
Parse error on line 1: flowchart TD A[Att ^ Expecting 'NEWLINE', 'SPACE', 'GRAPH', got 'ALPHA'
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 Generated):
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@@