关于 urp-hlsl-templates
面向 Unity Universal Render Pipeline 代码着色器编写的参考技能,涵盖 HLSL 和 ShaderLab。提供 URP 模板、转换指引,以及兼容性和渲染常见问题的注意事项。
主要功能
- 01URP 无光照与光照着色器模板
- 02Built-in 转 URP 参考
- 03SRP Batcher 与 HLSL 规范
- 04Pass 与着色器变体指引
适用场景
- 01开始编写代码版 URP 着色器
- 02将 Built-in 管线着色器转换为 URP
- 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
-
始终使用
HLSLPROGRAM/ENDHLSL,绝不使用CGPROGRAM/ENDCG。 URP 使用 HLSL,而不是 CG。CG 代码块虽然可以编译,但会缺少 URP 专用的 include 和宏。 -
所有材质属性都必须放在
CBUFFER_START(UnityPerMaterial)/CBUFFER_END代码块中,以兼容 SRP Batcher。否则,每个材质都会破坏批处理,并单独产生一次绘制调用。 -
包含 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" -
使用 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) -
标签必须包含
"RenderPipeline" = "UniversalPipeline",否则 URP 无法识别该着色器。 -
使用
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-in | URP 等效项 |
|---|---|
#include "UnityCG.cginc" | #include ".../Core.hlsl" |
#include "Lighting.cginc" | #include ".../Lighting.hlsl" |
UnityObjectToClipPos(v) | TransformObjectToHClip(v.xyz) |
UnityObjectToWorldNormal(n) | TransformObjectToWorldNormal(n) |
_WorldSpaceLightPos0 | GetMainLight().direction |
_LightColor0 | GetMainLight().color |
UNITY_LIGHTMODEL_AMBIENT | SampleSH(normal) |
SHADOW_COORDS / TRANSFER_SHADOW | TransformWorldToShadowCoord(posWS) |
SHADOW_ATTENUATION(i) | GetMainLight(shadowCoord).shadowAttenuation |
tex2D(_Tex, uv) | SAMPLE_TEXTURE2D(_Tex, sampler_Tex, uv) |
sampler2D _Tex | TEXTURE2D(_Tex); SAMPLER(sampler_Tex); |
UNITY_FOG_COORDS / APPLY_FOG | ComputeFogFactor() / MixFog() |
v2f 结构体约定 | Varyings 结构体约定 |
appdata 结构体约定 | Attributes 结构体约定 |
Tags { "LightMode" = "ForwardBase" } | Tags { "LightMode" = "UniversalForward" } |
表面着色器 #pragma surface | 无等效项 — 手动编写顶点/片元着色器 |
多通道与其他通道
URP 要求显式指定通道名称,以便渲染器识别:
| LightMode 标签 | 用途 |
|---|---|
UniversalForward | 主渲染通道 |
ShadowCaster | 生成阴影贴图 |
DepthOnly | 深度预通道 |
DepthNormals | 深度 + 法线预通道(用于 SSAO、深度法线效果) |
Meta | 烘焙光照贴图 |
Universal2D | 2D 渲染器通道 |
SRPDefaultUnlit | 没有匹配 LightMode 时使用的回退项 |
常见问题
-
忘记 CBUFFER → 破坏 SRP Batcher。每次绘制调用都会变成独立调用。可在 Frame Debugger > SRP Batcher 列中检查。
-
使用
sampler2D而不是TEXTURE2D宏 → 虽然可用,但会失去跨平台采样器控制能力,并且在某些平台上可能出现问题。 -
缺少 ShadowCaster 通道 → 对象不会投射阴影。务必添加该通道,或使用
UsePass/Fallback。 -
缺少 DepthOnly 通道 → 基于深度的效果(雾效、后期处理、水深效果)将无法检测到此对象。
-
使用
multi_compile而不是shader_feature→multi_compile会编译所有关键字变体。shader_feature只会编译构建中材质实际使用的变体。对于材质专属的开关,始终使用shader_feature。
参考仓库和 Gist
- Cyanilux URP 代码模板(CC0)— https://github.com/Cyanilux/URP_ShaderCodeTemplates
- Cyanilux URP 着色器代码教程 — https://www.cyanilux.com/tutorials/urp-shader-code/
- phi-lira URP Lit 模板 — https://gist.github.com/phi-lira/225cd7c5e8545be602dca4eb5ed111ba
- NedMakesGames URP HLSL 系列(共 9 篇)— 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 自定义 Lit BlinnPhong — https://gist.github.com/xCyborg/2923ce9e57e65af9d417706dda4db4be
- ColinLeung-NiloCat URP Toon Lit — https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample
来源
- Cyanilux:URP 着色器代码模板 — https://github.com/Cyanilux/URP_ShaderCodeTemplates
- NedMakesGames:使用代码编写 URP 着色器 — https://nedmakesgames.medium.com/
- Catlike Coding:自定义 SRP — https://catlikecoding.com/unity/tutorials/
- Daniel Ilett:Shader Graph + HLSL — https://danielilett.com/
- ColinLeung-NiloCat:移动端 URP Toon — https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample
- Unity:着色器性能 — https://docs.unity3d.com/Manual/SL-ShaderPerformance.html