在 ShaderLab 中,通道(Pass)是着色器对象的核心执行单元,负责定义一次完整的渲染流程,包括 GPU 状态设置(如混合模式、深度测试)和着色器程序(顶点 / 片元着色器)。每个子着色器(SubShader)可包含多个 Pass,用于实现复杂渲染效果(如多光源处理、阴影投射)。以下是 Pass 的核心概念、语法规则及跨管线实践:
一、Pass 的核心作用
-
渲染状态控制:
- 配置 GPU 状态(如
Blend SrcAlpha OneMinusSrcAlpha
透明混合、Cull Off
关闭背面剔除)。 - 示例:透明物体需开启混合并调整渲染队列,阴影投射 Pass 需禁用颜色输出仅写入深度。
- 配置 GPU 状态(如
-
着色器程序载体:
- 包含顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)代码,实现坐标变换、光照计算等核心逻辑。
- 支持 GLSL、HLSL 等着色语言,通过
CGPROGRAM
/ENDCG
块嵌入 HLSL 代码(内置管线 / URP/HDRP)。
-
多 Pass 渲染:
- 单个 SubShader 通过多个 Pass 实现多重渲染(如先渲染主颜色,再叠加高光或环境光)。
- 示例:正向渲染中,主光源 Pass(
LightMode="ForwardBase"
)与附加光源 Pass(LightMode="ForwardAdd"
)分离。
二、Pass 的语法与结构
hlsl
Pass {
// 可选:Pass名称(用于被其他着色器引用)
Name "PassName"
// 可选:标签(控制光照模式、渲染顺序等)
Tags { "LightMode"="ForwardBase" }
// 可选:渲染状态指令
ZTest LEqual
Blend One One
// 可选:着色器代码块
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// 顶点/片元着色器函数
ENDCG
}
三、关键组件详解
1. 标签(Tags)
Pass 标签用于控制渲染流程,核心标签包括:
标签 | 作用 | 典型值 |
---|---|---|
LightMode | 定义光照模式(内置管线 / URP/HDRP) | ForwardBase (主光源)、ShadowCaster (阴影投射) |
RenderType | 配合 SubShader 标签实现着色器替换(如渲染深度纹理) | Opaque 、TransparentCutout |
Queue | 覆盖 SubShader 的渲染队列(较少使用,优先在 SubShader 层级设置) | Transparent+1 |
URP/HDRP 光照模式示例:
hlsl
// URP 正向渲染主光源
Tags { "LightMode"="UniversalForward" }
// HDRP 体积渲染
Tags { "LightMode"="HDRenderPipeline" }
2. 渲染状态指令
直接在 Pass 中设置 GPU 状态,常见指令:
- 深度与模板缓冲:
hlsl
ZWrite On // 开启深度写入 ZTest LessEqual // 深度测试:当前片元深度小于等于缓冲深度时通过 Stencil { Ref 1 ZFail Keep } // 模板测试
- 混合模式:
hlsl
Blend SrcAlpha OneMinusSrcAlpha // 透明混合 Blend One One // 相加混合(如粒子发光)
- 剔除与面渲染:
hlsl
Cull Back // 剔除背面(默认) Cull Front // 剔除正面 Cull Off // 渲染双面
3. 着色器代码块
通过CGPROGRAM
/ENDCG
嵌入 HLSL 代码,需声明顶点 / 片元着色器入口:
hlsl
CGPROGRAM
#pragma vertex vert // 顶点着色器函数名
#pragma fragment frag // 片元着色器函数名
#include "UnityCG.cginc" // 引用Unity内置函数库
// 顶点输入结构(对应模型顶点数据)
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
// 顶点输出结构(传递给片元着色器)
struct v2f {
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
// 顶点着色器:坐标转换
v2f vert (appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 模型空间→裁剪空间
o.uv = v.uv;
return o;
}
// 片元着色器:纹理采样
fixed4 frag (v2f i) : SV_TARGET {
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
4. 特殊 Pass 类型
- UsePass:引用其他着色器的 Pass,避免重复代码:
hlsl
UsePass "ExampleShader/ShadowCasting" // 引用名为ShadowCasting的Pass
- GrabPass:抓取屏幕图像到纹理,用于反射 / 折射效果(内置管线常用,URP/HDRP 建议使用 RenderTexture 替代):
hlsl
GrabPass { "GrabTex" } // 抓取结果存储到名为GrabTex的纹理
四、跨渲染管线的 Pass 设计
1. 内置管线(Built-in RP)
- 特点:支持传统光照模式(Forward/Deferred),Pass 需明确声明
LightMode
标签。 - 示例:正向渲染主光源 Pass
hlsl
Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma multi_compile_fwdbase // 主光源编译变体 // 包含光照计算代码 ENDCG }
2. 通用渲染管线(URP)
- 特点:轻量级,使用
UniversalForward
等标签,需引用 URP HLSL 库。 - 示例:URP 不透明物体 Pass
hlsl
Pass { Tags { "LightMode"="UniversalForward" } HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" // 使用URP光照函数 UniversalForwardLighting() ENDCG }
3. 高清渲染管线(HDRP)
- 特点:支持高级渲染,Pass 需匹配 HDRP 光照模式,使用集群光照等特性。
- 示例:HDRP 体积雾 Pass
hlsl
Pass { Tags { "LightMode"="HDRenderPipeline" } HLSLPROGRAM #include "Packages/com.unity.render-pipelines.high-definition/ShaderLibrary/Volumetric.hlsl" // 体积雾计算逻辑 ENDCG }
4. 自定义 SRP
- 策略:通过标签匹配自定义管线的渲染逻辑,例如:
hlsl
Pass { Tags { "CustomSRP"="MyPipeline" } // 自定义管线专属渲染指令 }
五、Pass 的性能优化
-
减少 Pass 数量:
- 合并渲染状态相同的 Pass,使用
MultiCompile
生成变体而非多个独立 Pass。 - 示例:通过
#pragma multi_compile _ USE_SPECULAR
控制是否包含高光计算,避免拆分 Pass。
- 合并渲染状态相同的 Pass,使用
-
状态复用:
- 相邻 Pass 尽量复用渲染状态(如相同的混合模式、深度设置),减少 GPU 状态切换开销。
-
管线适配优化:
- URP:避免使用
GrabPass
,改用ScriptableRenderContext
抓取屏幕。 - HDRP:利用 GPU 实例化(GPU Instancing)减少多 Pass 渲染的 Draw Call 次数。
- URP:避免使用
-
移动端优化:
- 避免复杂的多 Pass 渲染(如延迟渲染),优先使用单 Pass 完成光照计算。
- 使用
half
精度变量(如half2 uv
)替代float
,减少寄存器占用。
六、调试与验证
-
Pass 可视化:
- 在 Unity 编辑器中,通过 Frame Debugger 查看每个 Pass 的渲染结果,定位渲染顺序或状态错误。
- 示例:检查阴影投射 Pass 是否正确写入深度缓冲。
-
编译错误处理:
- 着色器代码块需严格遵循 HLSL 语法,注意括号匹配和变量声明顺序。
- 常见错误:未引用必要的头文件(如 URP 需
#include "Core.hlsl"
)、标签拼写错误。
-
性能分析:
- 使用 Unity Profiler 查看
Render.Pass
耗时,识别瓶颈 Pass。 - 移动端建议单 SubShader 的 Pass 数量不超过 3 个,PC / 主机可适当增加。
- 使用 Unity Profiler 查看
七、完整示例:多 Pass 着色器
hlsl
Shader "Example/MultiPassShader" {
Properties {
_MainTex ("基础纹理", 2D) = "white" {}
_GlowTex ("发光纹理", 2D) = "black" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
// 主Pass:漫反射光照
Pass {
Name "BASE_PASS"
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata_base v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_TARGET {
return tex2D(_MainTex, i.uv);
}
ENDCG
}
// 发光Pass:叠加自发光纹理(混合模式为相加)
Pass {
Name "GLOW_PASS"
Tags { "LightMode"="ForwardAdd" "Queue"="Transparent" }
Blend One One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _GlowTex;
float4 _GlowTex_ST;
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata_base v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _GlowTex);
return o;
}
fixed4 frag (v2f i) : SV_TARGET {
return tex2D(_GlowTex, i.uv);
}
ENDCG
}
}
Fallback "Diffuse"
}
八、注意事项
-
标签作用域:
- Pass 标签仅影响当前 Pass,不继承 SubShader 标签(如 SubShader 设置
Queue=Geometry
,Pass 可通过Queue=Transparent
覆盖)。
- Pass 标签仅影响当前 Pass,不继承 SubShader 标签(如 SubShader 设置
-
材质参数共享:
- 多个 Pass 间共享材质属性(如
_MainTex
)时,需确保 HLSL 变量名一致,且在每个 Pass 中正确声明。
- 多个 Pass 间共享材质属性(如
-
管线兼容性测试:
- 跨管线项目需为每个 Pass 添加条件编译(如
#if UNITY_URP
),避免不同管线的语法冲突。
- 跨管线项目需为每个 Pass 添加条件编译(如
总结
Pass 是 ShaderLab 实现复杂渲染效果的基础,通过合理设计 Pass 的渲染状态、着色器逻辑和多 Pass 组合,可在不同渲染管线和硬件上实现高效渲染。在实践中,需结合项目的管线类型和性能目标,优化 Pass 数量与状态切换,并利用 Unity 的调试工具确保每个 Pass 按预期执行。掌握 Pass 的定义规则,是开发高性能着色器和复杂视觉效果的关键。