mcpField
U
urp-hlsl-templates

urp-hlsl-templates

github.com/adevra开发1654 次安装31 周前更新
代码仓库

关于 urp-hlsl-templates

面向 Unity Universal Render Pipeline 代码着色器编写的参考技能,涵盖 HLSL 和 ShaderLab。提供 URP 模板、转换指引,以及兼容性和渲染常见问题的注意事项。

主要功能

  1. 01URP 无光照与光照着色器模板
  2. 02Built-in 转 URP 参考
  3. 03SRP Batcher 与 HLSL 规范
  4. 04Pass 与着色器变体指引

适用场景

  1. 01开始编写代码版 URP 着色器
  2. 02将 Built-in 管线着色器转换为 URP
  3. 03检查着色器中的常见 URP 问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/adevra/unity-shader-agent-skills --skill urp-hlsl-templates

安装后在 Claude Code 中输入/urp-hlsl-templates确认已加载。

详细介绍

URP HLSL 着色器代码模板

触发条件

当用户想要使用 HLSL/ShaderLab 代码(而不是 Shader Graph)为 Universal Render Pipeline 编写 Unity 着色器时,使用此技能。触发词包括:“URP shader”“HLSL”“ShaderLab”“write a shader”“custom shader”“vertex fragment shader”“SRP Batcher”“CBUFFER”“custom lit shader”“unlit shader”,以及任何基于代码的 URP 着色器编写需求。用户想将 Built-in 管线着色器转换为 URP,或提到 Cyanilux 模板、phi-lira gists 或 NedMakesGames 教程时,也应触发。


核心规则 — URP HLSL

  1. 始终使用 HLSLPROGRAM / ENDHLSL,绝不使用 CGPROGRAM / ENDCG。 URP 使用 HLSL,而不是 CG。CG 代码块虽然可以编译,但会缺少 URP 专用的 include 和宏。

  2. 所有材质属性都必须放在 CBUFFER_START(UnityPerMaterial) / CBUFFER_END 代码块中,以兼容 SRP Batcher。否则,每个材质都会破坏批处理,并单独产生一次绘制调用。

  3. 包含 URP Core,而不是旧版 Unity include。

    // 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"
    
  4. 使用 URP 变换宏,而不是旧版宏:

    // 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)
    
  5. 标签必须包含 "RenderPipeline" = "UniversalPipeline",否则 URP 无法识别该着色器。

  6. 使用 TEXTURE2D 和 SAMPLER 宏,而不是 sampler2D:

    TEXTURE2D(_MainTex);
    SAMPLER(sampler_MainTex);
    // Sample with:
    half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv);
    

模板:URP 无光照(适用于移动设备)

兼容 SRP Batcher 的最简无光照着色器。可作为移动设备着色器的起点:

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"
}

模板:带主光源的 URP 光照着色器

添加来自主方向光的 Lambert 漫反射光照:

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 转换速查表

Built-inURP 等效项
#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 结构体约定Varyings 结构体约定
appdata 结构体约定Attributes 结构体约定
Tags { "LightMode" = "ForwardBase" }Tags { "LightMode" = "UniversalForward" }
表面着色器 #pragma surface无等效项 — 手动编写顶点/片元着色器

多通道与其他通道

URP 要求显式指定通道名称,以便渲染器识别:

LightMode 标签用途
UniversalForward主渲染通道
ShadowCaster生成阴影贴图
DepthOnly深度预通道
DepthNormals深度 + 法线预通道(用于 SSAO、深度法线效果)
Meta烘焙光照贴图
Universal2D2D 渲染器通道
SRPDefaultUnlit没有匹配 LightMode 时使用的回退项

常见问题

  1. 忘记 CBUFFER → 破坏 SRP Batcher。每次绘制调用都会变成独立调用。可在 Frame Debugger > SRP Batcher 列中检查。

  2. 使用 sampler2D 而不是 TEXTURE2D 宏 → 虽然可用,但会失去跨平台采样器控制能力,并且在某些平台上可能出现问题。

  3. 缺少 ShadowCaster 通道 → 对象不会投射阴影。务必添加该通道,或使用 UsePass/Fallback。

  4. 缺少 DepthOnly 通道 → 基于深度的效果(雾效、后期处理、水深效果)将无法检测到此对象。

  5. 使用 multi_compile 而不是 shader_feature → multi_compile 会编译所有关键字变体。shader_feature 只会编译构建中材质实际使用的变体。对于材质专属的开关,始终使用 shader_feature。


参考仓库和 Gist

来源

评论

支持 Markdown