Understanding Unity Shader Semantics and Function Signatures

System Semantics and Function Bodies

System semantic are annotations that indicate how the GPU pipeline should process the marked data types.

When using a structure as a return value, system semantics should not be declared in the function header. Declaring semantics both out side and inside a structure creates ambiguity, so modern versions only permit internal declarations.

Incorrect Usage Example

Shader "Tutorial/Basic/ShaderSemantics" {
    Properties {
        _MainColor ("Base Color", Color) = (1.0,1.0,1.0,1.0)
    }
    SubShader {
        Pass {
            CGPROGRAM

            #pragma vertex ProcessVertex
            #pragma fragment ProcessFragment

            fixed4 _MainColor;

            struct VertexInput {
                float4 position : POSITION;
                float3 normalVector : NORMAL;
                float4 uvCoordinates : TEXCOORD0;
            };

            struct VertexToFragment {
                float4 clipPosition : SV_POSITION;
                fixed3 vertexColor : COLOR0;
            };

            VertexToFragment ProcessVertex(VertexInput inputData) : SV_POSITION {  // Error: Cannot declare semantic outside struct
                VertexToFragment outputData;
                outputData.clipPosition = mul(UNITY_MATRIX_MVP, inputData.position);
                outputData.vertexColor = inputData.normalVector * 0.5 + fixed3(0.5, 0.5, 0.5);
                return outputData;
            }

            fixed4 ProcessFragment(VertexToFragment interpolatedData) : SV_Target {
                fixed3 finalColor = interpolatedData.vertexColor;
                finalColor *= _MainColor.rgb;
                return fixed4(finalColor, 1.0);
            }

            ENDCG
        }
    }
}

Key issues with modern shader compilers:

  • No longer tolerant of ambiguous or incorrect semantic usage
  • Semantic annotations on entire structures are not permitted (e.g., struct VertexToFragment : SV_POSITION)

The mul operation is deprecated in favor of UnityObjectToClipPos

Corrected Implementation

Shader "Tutorial/Basic/ShaderSemantics" {
    Properties {
        _MainColor ("Base Color", Color) = (1.0,1.0,1.0,1.0)
    }
    SubShader {
        Pass {
            CGPROGRAM

            #pragma vertex ProcessVertex
            #pragma fragment ProcessFragment

            fixed4 _MainColor;

            struct VertexInput {
                float4 position : POSITION;
                float3 normalVector : NORMAL;
                float4 uvCoordinates : TEXCOORD0;
            };

            struct VertexToFragment {
                float4 clipPosition : SV_POSITION;
                fixed3 vertexColor : COLOR0;
            };

            VertexToFragment ProcessVertex(VertexInput inputData) {
                VertexToFragment outputData;
                outputData.clipPosition = UnityObjectToClipPos(inputData.position);
                outputData.vertexColor = inputData.normalVector * 0.5 + fixed3(0.5, 0.5, 0.5);
                return outputData;
            }

            fixed4 ProcessFragment(VertexToFragment interpolatedData) : SV_Target {
                fixed3 finalColor = interpolatedData.vertexColor;
                finalColor *= _MainColor.rgb;
                return fixed4(finalColor, 1.0);
            }

            ENDCG
        }
    }
}

Input structure semantics are automatically captured and assigned by the GPU, while return structure semantics are automatically unpacked during pipeline processing.

Two Valid Function Signature Patterns

Pattern 1: Semantics declared within structure members, no semantic annotation in function header

struct VertexInput {
	float4 position : POSITION;
	float3 normalVector : NORMAL;
	float4 uvCoordinates : TEXCOORD0;
};

struct VertexToFragment {
	float4 clipPosition : SV_POSITION;
	fixed3 vertexColor : COLOR0;
};

VertexToFragment ProcessVertex(VertexInput inputData) {
	VertexToFragment outputData;
	outputData.clipPosition = UnityObjectToClipPos(inputData.position);
	outputData.vertexColor = inputData.normalVector * 0.5 + fixed3(0.5, 0.5, 0.5);
	return outputData;
}

Pattern 2: Basic type with semantic annotation in function header

fixed4 ProcessFragment(VertexToFragment interpolatedData) : SV_Target {
	fixed3 finalColor = interpolatedData.vertexColor;
	finalColor *= _MainColor.rgb;
	return fixed4(finalColor, 1.0);
}

Tags: unity ShaderLab HLSL CGPROGRAM VertexShader

Posted on Mon, 11 May 2026 07:28:07 +0000 by BillyMako