adevra/unity-shader-agent-skills

urp-hlsl-templates

Production-ready URP HLSL/ShaderLab code templates with SRP Batcher compatibility for Unity

Ver código-fonte
Documento original do Skill

Renderizado do repositório de origem, preservando títulos, exemplos, código, tabelas, links e imagens.

URP HLSL Shader Code Templates

TRIGGER

Use this skill when the user wants to write a Unity shader in HLSL/ShaderLab code (not Shader Graph) for Universal Render Pipeline. Triggers include: "URP shader", "HLSL", "ShaderLab", "write a shader", "custom shader", "vertex fragment shader", "SRP Batcher", "CBUFFER", "custom lit shader", "unlit shader", or any code-based shader authoring in URP. Also trigger when converting Built-in pipeline shaders to URP, or when the user references Cyanilux templates, phi-lira gists, or NedMakesGames tutorials.


CORE RULES — URP HLSL

  1. Always use `HLSLPROGRAM` / `ENDHLSL`, never `CGPROGRAM` / `ENDCG`. URP uses HLSL, not CG. CG blocks will compile but miss URP-specific includes and macros.
  1. All material properties must be in a `CBUFFER_START(UnityPerMaterial)` / `CBUFFER_END` block for SRP Batcher compatibility. Without this, each material breaks batching and gets its own draw call.
  1. Include URP Core, not legacy Unity includes.
hlsl
   // CORRECT (URP)
   #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
   #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
   
   // WRONG (Built-in / legacy)
   #include "UnityCG.cginc"
   #include "Lighting.cginc"
  1. Use URP transform macros, not legacy ones:
hlsl
   // URP                              // Legacy equivalent
   TransformObjectToHClip(posOS)       // UnityObjectToClipPos(v.vertex)
   TransformObjectToWorld(posOS)       // mul(unity_ObjectToWorld, v.vertex)
   TransformObjectToWorldNormal(nOS)   // UnityObjectToWorldNormal(v.normal)
   TransformWorldToHClip(posWS)        // mul(UNITY_MATRIX_VP, float4(posWS, 1))
   GetVertexPositionInputs(posOS)      // (convenience struct)
   GetVertexNormalInputs(nOS, tanOS)   // (convenience struct)
  1. Tags must include `"RenderPipeline" = "UniversalPipeline"` or the shader won't be recognized by URP.
  1. Use `TEXTURE2D` and `SAMPLER` macros instead of sampler2D:
hlsl
   TEXTURE2D(_MainTex);
   SAMPLER(sampler_MainTex);
   // Sample with:
   half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv);

TEMPLATE: URP UNLIT (MOBILE-READY)

Minimal unlit shader with SRP Batcher compatibility. Use as starting point for mobile shaders:

hlsl
Shader "Custom/URP_Unlit_Mobile"
{
    Properties
    {
        _BaseMap ("Base Map", 2D) = "white" {}
        _BaseColor ("Base Color", Color) = (1, 1, 1, 1)
    }
    
    SubShader
    {
        Tags
        {
            "RenderType" = "Opaque"
            "RenderPipeline" = "UniversalPipeline"
            "Queue" = "Geometry"
        }
        
        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode" = "UniversalForward" }
            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            
            // SRP Batcher compatible: all properties in CBUFFER
            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                half4 _BaseColor;
            CBUFFER_END
            
            TEXTURE2D(_BaseMap);
            SAMPLER(sampler_BaseMap);
            
            struct Attributes
            {
                float4 positionOS : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                half fogFactor : TEXCOORD1;
            };
            
            Varyings vert(Attributes input)
            {
                Varyings output;
                VertexPositionInputs posInputs = GetVertexPositionInputs(input.positionOS.xyz);
                output.positionCS = posInputs.positionCS;
                output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
                output.fogFactor = ComputeFogFactor(posInputs.positionCS.z);
                return output;
            }
            
            half4 frag(Varyings input) : SV_Target
            {
                half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor;
                color.rgb = MixFog(color.rgb, input.fogFactor);
                return color;
            }
            ENDHLSL
        }
        
        // Shadow caster pass (required for objects to cast shadows)
        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode" = "ShadowCaster" }
            
            ZWrite On ZTest LEqual ColorMask 0
            
            HLSLPROGRAM
            #pragma vertex ShadowPassVertex
            #pragma fragment ShadowPassFragment
            #include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
            ENDHLSL
        }
        
        // Depth pass (required for depth-based effects)
        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode" = "DepthOnly" }
            
            ZWrite On ColorMask R
            
            HLSLPROGRAM
            #pragma vertex DepthOnlyVertex
            #pragma fragment DepthOnlyFragment
            #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
            ENDHLSL
        }
    }
    
    Fallback "Universal Render Pipeline/Unlit"
}

TEMPLATE: URP LIT WITH MAIN LIGHT

Adds Lambert diffuse lighting from the main directional light:

hlsl
Shader "Custom/URP_SimpleLit_Mobile"
{
    Properties
    {
        _BaseMap ("Base Map", 2D) = "white" {}
        _BaseColor ("Base Color", Color) = (1, 1, 1, 1)
    }
    
    SubShader
    {
        Tags
        {
            "RenderType" = "Opaque"
            "RenderPipeline" = "UniversalPipeline"
            "Queue" = "Geometry"
        }
        
        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode" = "UniversalForward" }
            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE
            #pragma multi_compile _ _SHADOWS_SOFT
            #pragma multi_compile_fog
            
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            
            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                half4 _BaseColor;
            CBUFFER_END
            
            TEXTURE2D(_BaseMap);
            SAMPLER(sampler_BaseMap);
            
            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normalOS : NORMAL;
                float2 uv : TEXCOORD0;
            };
            
            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 normalWS : TEXCOORD1;
                float3 positionWS : TEXCOORD2;
                half fogFactor : TEXCOORD3;
            };
            
            Varyings vert(Attributes input)
            {
                Varyings output;
                VertexPositionInputs posInputs = GetVertexPositionInputs(input.positionOS.xyz);
                VertexNormalInputs normInputs = GetVertexNormalInputs(input.normalOS);
                
                output.positionCS = posInputs.positionCS;
                output.positionWS = posInputs.positionWS;
                output.normalWS = normInputs.normalWS;
                output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
                output.fogFactor = ComputeFogFactor(posInputs.positionCS.z);
                return output;
            }
            
            half4 frag(Varyings input) : SV_Target
            {
                half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor;
                
                // Main light (directional)
                float4 shadowCoord = TransformWorldToShadowCoord(input.positionWS);
                Light mainLight = GetMainLight(shadowCoord);
                
                half3 normal = normalize(input.normalWS);
                half NdotL = saturate(dot(normal, mainLight.direction));
                half3 diffuse = baseColor.rgb * mainLight.color * NdotL * mainLight.shadowAttenuation;
                
                // Ambient
                half3 ambient = SampleSH(normal) * baseColor.rgb;
                
                half3 finalColor = diffuse + ambient;
                finalColor = MixFog(finalColor, input.fogFactor);
                
                return half4(finalColor, baseColor.a);
            }
            ENDHLSL
        }
        
        // Include ShadowCaster and DepthOnly passes (same as unlit template above)
        UsePass "Custom/URP_Unlit_Mobile/ShadowCaster"
        UsePass "Custom/URP_Unlit_Mobile/DepthOnly"
    }
    
    Fallback "Universal Render Pipeline/Simple Lit"
}

BUILT-IN → URP CONVERSION CHEAT SHEET

Built-inURP Equivalent
#include "UnityCG.cginc"#include ".../Core.hlsl"
#include "Lighting.cginc"#include ".../Lighting.hlsl"
UnityObjectToClipPos(v)TransformObjectToHClip(v.xyz)
UnityObjectToWorldNormal(n)TransformObjectToWorldNormal(n)
_WorldSpaceLightPos0GetMainLight().direction
_LightColor0GetMainLight().color
UNITY_LIGHTMODEL_AMBIENTSampleSH(normal)
SHADOW_COORDS / TRANSFER_SHADOWTransformWorldToShadowCoord(posWS)
SHADOW_ATTENUATION(i)GetMainLight(shadowCoord).shadowAttenuation
tex2D(_Tex, uv)SAMPLE_TEXTURE2D(_Tex, sampler_Tex, uv)
sampler2D _TexTEXTURE2D(_Tex); SAMPLER(sampler_Tex);
UNITY_FOG_COORDS / APPLY_FOGComputeFogFactor() / MixFog()
v2f struct conventionVaryings struct convention
appdata struct conventionAttributes struct convention
Tags { "LightMode" = "ForwardBase" }Tags { "LightMode" = "UniversalForward" }
Surface shader #pragma surfaceNo equivalent — write vertex/fragment manually

MULTI-PASS AND ADDITIONAL PASSES

URP requires explicit pass names for the renderer to recognize them:

LightMode TagPurpose
UniversalForwardMain rendering pass
ShadowCasterShadow map generation
DepthOnlyDepth prepass
DepthNormalsDepth + normals prepass (for SSAO, depth-normals effects)
MetaLightmap baking
Universal2D2D renderer pass
SRPDefaultUnlitFallback when no LightMode matches

COMMON PITFALLS

  1. Forgetting CBUFFER → Breaks SRP Batcher. Every draw call becomes unique. Check with Frame Debugger > SRP Batcher column.
  1. Using `sampler2D` instead of `TEXTURE2D` macro → Works but loses cross-platform sampler control and may break on some platforms.
  1. Missing ShadowCaster pass → Object won't cast shadows. Always include it or use UsePass/Fallback.
  1. Missing DepthOnly pass → Depth-based effects (fog, post-processing, water depth) won't see this object.
  1. Using `multi_compile` instead of `shader_feature`multi_compile compiles ALL keyword variants. shader_feature only compiles variants used by materials in the build. For material-specific toggles, always use shader_feature.

REFERENCE REPOS AND GISTS

  • Cyanilux URP Code Templates (CC0) — https://github.com/Cyanilux/URP_ShaderCodeTemplates
  • Cyanilux URP Shader Code Tutorial — https://www.cyanilux.com/tutorials/urp-shader-code/
  • phi-lira URP Lit Template — https://gist.github.com/phi-lira/225cd7c5e8545be602dca4eb5ed111ba
  • NedMakesGames 9-Part URP HLSL Series — https://nedmakesgames.medium.com/
  • Halftone Toon URP Gist — https://gist.github.com/wonkee-kim/3b5c1821975b8414f420f93ef6cdf5a0
  • Wide Outlines URP Gist — https://gist.github.com/ScottJDaley/6cddf0c8995ed61cac7088e22c983de1
  • Toon Shader URP 11 Gist — https://gist.github.com/FaultyPine/08306e52971d77081ea159128cb3e5b5
  • URP Custom Lit BlinnPhong — https://gist.github.com/xCyborg/2923ce9e57e65af9d417706dda4db4be
  • ColinLeung-NiloCat URP Toon Lit — https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample

SOURCES

  • Cyanilux: URP Shader Code Templates — https://github.com/Cyanilux/URP_ShaderCodeTemplates
  • NedMakesGames: Writing URP Shaders with Code — https://nedmakesgames.medium.com/
  • Catlike Coding: Custom SRP — https://catlikecoding.com/unity/tutorials/
  • Daniel Ilett: Shader Graph + HLSL — https://danielilett.com/
  • ColinLeung-NiloCat: Mobile URP Toon — https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample
  • Unity: Shader Performance — https://docs.unity3d.com/Manual/SL-ShaderPerformance.html