book/21-urp-hlsl-structs-dataflow.md

21. URP HLSL Structs & Dataflow

In Unity 6.3 (6000.3) / URP 17.3.0, dissect VertexPositionInputs/VertexNormalInputs/SurfaceData/InputData/Light by field and fix “who fills it and where it is written” by data flow.

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, SurfaceData and InputData are effectively “pipeline interfaces.”

This chapter aims to:

  1. Rather than memorizing structure fields, connect meaning/space/producer/consumer.
  2. LitPassFragment → UniversalFragmentPBR Describe the data flow “by field.”
  3. 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.

flowchart TD A[Attributes\n(vertex input)] --> B[GetVertexPositionInputs\nGetVertexNormalInputs] B --> C[Varyings\n(interpolators)] C --> D[InitializeStandardLitSurfaceData\n=> SurfaceData] C --> E[InitializeInputData\n=> InputData] D --> F[UniversalFragmentPBR] E --> F F --> G[SV_Target0 outColor]

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):

Generate function:

  • VertexPositionInputs GetVertexPositionInputs(float3 positionOS)
    • <URP>/ShaderLibrary/ShaderVariablesFunctions.hlsl:8

Field:

FieldTypemeaningState Consumer Agency
positionWSfloat3World coordinatesLighting/Shadow/Cluster (Forward+)
positionVSfloat3View (camera) coordinatesDepth-based calculations/special effects
positionCSfloat4clip coordinatesrasterizer input (final)
positionNDCfloat4Normalized Device Coordinates (NDC)Screen UV calculation, etc.

Practical tips:

  • “Forward+ cluster loop” consumes positionWS and normalizedScreenSpaceUV very frequently.
  • It is difficult to debug a shader that is only created by positionCS. If possible, use GetVertexPositionInputs in 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:

FieldTypemeaningState Consumer Agency
tangentWSreal3World space tangentNormal map/tangent space transformation
bitangentWSreal3World space bitangentNormal map/tangent space transformation
normalWSfloat3World space normalLight/BRDF/Shadow

Practical tips:

  • “Normal is flipped/highlight is strange” is mostly a tangent basis problem.
  • normalWS consumes 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:

FieldTypeMeaning (Summary)State Consumer Agency
albedohalf3Base color (texture/color product result)PBR
specularhalf3specular color (specular workflow)PBR
metallichalfMetallic (Metallic Workflow)PBR
smoothnesshalfSmoothness (reciprocal roughness series)PBR
normalTShalf3Tangent space normal (normal map result)Configure InputData
emissionhalf3Self-luminousfinal color
occlusionhalfOcclusionLight Attenuation
alphahalfAlpha (transparent/cutout)OutputAlpha
clearCoatMaskhalfClear coat maskPBR (optional)
clearCoatSmoothnesshalfClearcoat SmoothnessPBR (optional)

Practical tips (safe customization):

  • Step 1 of “Change my material model” is to adjust the SurfaceData value.
  • 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.

FieldTypemeaning/spaceMain producer (representative)State Consumer Agency (Representative)
positionWSfloat3World LocationInitializeInputDataLight/Shadow/Cluster
positionCSfloat4clip locationVaryings/PassLODFade/Decal etc.
normalWSfloat3World normal (normalization required)InitializeInputDataPBR/Shadow
viewDirectionWShalf3World view orientation (usually normalized)InitializeInputDataPBR
shadowCoordfloat4Main light shadow coordinatesGetShadowCoord etc.Shadow Sampling
fogCoordhalffog coefficientInitializeInputDataMixFog
vertexLightinghalf3vertex additional lighting stackedvertex pathFinal color (optional)
bakedGIhalf3baked GI(LM/APV/SH)InitializeBakedGIDataPBR
normalizedScreenSpaceUVfloat20..1 screen UVGetNormalizedScreenSpaceUV etc.Forward+ (cluster), SSR type
shadowMaskhalf4shadowmask/probe occlusionGI/Lightmap Pathshadow mixing
tangentToWorldhalf3x3TS→WS basisInitializeInputDatanormalTS conversion
dynamicLightmapUVhalf2Dynamic Lightmap UVvertex pathGI
staticLightmapUVhalf2static lightmap UVvertex pathGI
vertexSHfloat3SH(vertex)vertex pathGI
brdfDiffusehalf3BRDF diffuse cache (optional)PBR PreparationPBR
brdfSpecularhalf3BRDF specular cache (optional)PBR PreparationPBR
uvfloat2UV for original/tracking (optional)Debug/FunctionDebug/VT etc.
mipCountuintnumber of mips (optional)VT/StreamingDebug
texelSizefloat4texel size (optional)VT/StreamingDebug
mipInfofloat4mip information (optional)VT/StreamingDebug
streamInfofloat4Streaming Information (Optional)VT/StreamingDebug
originalColorfloat3Original color (optional)Debug/Post-ProcessingDebug
probeOcclusionfloat4Probe occlusion (optional)probe/APVshadow mixing

Practical tips:

  • If normalizedScreenSpaceUV is empty in Forward+, the cluster light loop does not work properly.
  • shadowCoord is 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:

FieldTypeMeaning (Summary)
directionhalf3Light direction
colorhalf3Light color/intensity
distanceAttenuationfloatDistance Attenuation
shadowAttenuationhalfShadow Attenuation
layerMaskuintlight layer matching

Core API combined with Forward+ loop:

21.7 Common mistakes (data flow perspective)1) Space confusion: Mixing normalTS/normalWS and positionCS/positionNDC

  1. Missing normalization: Specular is broken due to normalWS, viewDirectionWS not being normalized
  2. Forward+ input missing: normalizedScreenSpaceUV is empty, cluster loop fails
  3. DepthNormals contract not involved: SSAO/Outline reads empty texture

A quick practical debugging routine can be found in the following chapters.