ShaderLab 是 Unity 中用于定义着色器(Shader)结构的声明式语言,通过嵌套括号语法描述着色器对象的各个组成部分。它不直接实现渲染逻辑,而是作为容器整合 HLSL 代码、设置渲染状态,并暴露可编辑的材质参数。以下是 ShaderLab 的核心功能与使用指南:
一、ShaderLab 的核心职责
ShaderLab 主要用于定义着色器的框架结构和元数据,具体包括:
-
着色器整体结构:
- 声明着色器名称、属性、子着色器(SubShader)和回退方案(Fallback)。
- 示例:
hlsl
Shader "Custom/MyShader" { // 属性定义 Properties { _MainTex ("Texture", 2D) = "white" {} } // 子着色器列表 SubShader { // 渲染状态与 Pass 定义 Pass { ... } } // 回退着色器 Fallback "Legacy Shaders/Diffuse" }
-
整合 HLSL 代码:
- 通过
CGPROGRAM
/ENDCG
代码块嵌入顶点、片元等着色器程序。
hlsl
Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // HLSL 代码 ENDCG }
- 通过
-
设置 GPU 渲染状态:
- 定义混合模式、深度测试、剔除模式等渲染状态指令(如
Blend SrcAlpha OneMinusSrcAlpha
)。
- 定义混合模式、深度测试、剔除模式等渲染状态指令(如
-
暴露材质参数:
- 在
Properties
块中声明可在材质面板编辑的参数(如纹理、颜色、数值)。
- 在
二、ShaderLab 关键语法与结构
1. 着色器声明(Shader Declaration)
hlsl
Shader "Shader路径/名称" {
// 内容块
}
- 路径命名规则:决定材质面板中的分类层级(如 "Custom/Effects/Glow" 会显示在 Custom > Effects 下)。
- 示例:
Shader "URP/MyLitShader"
2. 属性块(Properties Block)
定义材质面板可见的参数,格式为 属性名 ("显示名称", 类型) = 默认值
。
类型 | 说明 | 示例 |
---|---|---|
Color | 颜色(RGBA) | _TintColor ("Tint", Color) = (1,1,1,1) |
2D | 二维纹理(带 Alpha 通道) | _MainTex ("Texture", 2D) = "white" {} |
Range(min, max) | 浮点范围滑块 | _GlowIntensity ("Glow Intensity", Range(0, 5)) = 1 |
Vector | 四维向量(XYZW) | _UVScale ("UV Scale", Vector) = (1,1,0,0) |
- 用途:参数值会被传递给 HLSL 代码,通过同名变量引用。
3. 子着色器块(SubShader Block)
每个着色器可包含多个 SubShader
,Unity 会按顺序尝试渲染,直到找到硬件支持的版本。
hlsl
SubShader {
Tags { "渲染标签"="值" } // 定义渲染队列、光照模式等
LOD 200 // 细节层级(Level of Detail)
Pass { ... } // 至少包含一个 Pass
// 可选:其他 Pass 或 UsePass 引用
}
- 核心标签(Tags):
RenderType
:定义渲染队列(如 "Opaque"=2000,"Transparent"=3000)。LightMode
:指定 Pass 的光照模式(如 "ForwardBase" 用于正向渲染主光源)。
- LOD 作用:当材质 LOD 等级低于当前 SubShader 的 LOD 值时,会跳过该 SubShader。
4. 通道块(Pass Block)
每个 Pass
定义一次完整的渲染流程,包含渲染状态指令和 HLSL 代码。
hlsl
Pass {
// 渲染状态指令(如混合、深度测试)
Blend SrcAlpha OneMinusSrcAlpha
ZTest LEqual
// 嵌入 HLSL 代码
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// 顶点和片元着色器函数
ENDCG
}
- 常用渲染状态指令:
Cull Off
:关闭背面剔除(显示双面)。Offset 2, 2
:设置深度偏移,避免 Z 冲突。ColorMask RGB
:仅渲染 RGB 通道,忽略 Alpha。
5. 回退机制(Fallback)
当所有 SubShader
均不被支持时,使用指定的回退着色器。
hlsl
Fallback "Legacy Shaders/VertexLit"
- 最佳实践:回退至简单着色器(如
VertexLit
),确保低端设备可渲染。
6. 包依赖(Package Requirements)
通过 ShaderLab: requires
声明 SubShader 或 Pass 依赖的资源包。
hlsl
SubShader {
Requires "PackageName" // 如 "PostProcessing"
Pass {
Requires "ShaderFeatureName" // 如 "DIRECTIONAL_LIGHT"
}
}
- 用途:仅当项目安装指定包时,才编译对应的 SubShader/Pass。
三、HLSL 代码嵌入与交互
ShaderLab 通过 CGPROGRAM
块整合 HLSL 代码,并支持以下指令:
-
编译指令:
#pragma vertex 函数名
:指定顶点着色器函数。#pragma fragment 函数名
:指定片元着色器函数。#include "UnityCG.cginc"
:导入 Unity 内置函数库(如坐标转换、光照计算)。
-
引用 ShaderLab 属性:
在 HLSL 中声明与Properties
同名的变量,类型需匹配:hlsl
sampler2D _MainTex; // 对应 2D 纹理属性 float4 _MainTex_ST; // Unity 自动生成的 UV 变换参数(ST 代表 Scale/Translate) fixed4 _TintColor; // 对应 Color 属性
四、典型应用场景
1. 基础漫反射材质
hlsl
Shader "Custom/Diffuse" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 100
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
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 = TRANSFORM_TEX(v.uv, _MainTex); // 应用 UV 变换
return o;
}
fixed4 frag (v2f i) : SV_TARGET {
fixed4 tex = tex2D(_MainTex, i.uv);
return tex * _Color; // 颜色混合
}
ENDCG
}
}
Fallback "Diffuse"
}
2. 透明混合材质
hlsl
Shader "Custom/Transparent" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha // 开启透明混合
LOD 100
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
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 {
return tex2D(_MainTex, i.uv);
}
ENDCG
}
}
Fallback "Transparent/Diffuse"
}
五、最佳实践与注意事项
-
性能优化:
- 减少 SubShader 数量:避免为每个平台单独编写 SubShader,优先通过
#pragma multi_compile
生成变体。 - 合并 Pass:使用
MultiCompile
或ShaderVariantCollection
减少重复 Pass。
- 减少 SubShader 数量:避免为每个平台单独编写 SubShader,优先通过
-
跨平台兼容:
- 使用 Unity 内置宏(如
UNITY_SAMPLE_TEX2D
)进行纹理采样,避免直接调用平台特定函数。 - 通过
Tags { "RenderType"="Opaque" }
统一渲染队列,避免排序问题。
- 使用 Unity 内置宏(如
-
调试技巧:
- 在
frag
函数中返回中间变量(如法线、UV),辅助定位问题:hlsl
fixed4 frag(v2f i) : SV_TARGET { return fixed4(i.uv, 0, 1); // 可视化 UV 分布 }
- 使用
ShaderLab: debug
指令输出渲染状态信息(如Debug("Draw Calls", 100)
)。
- 在
六、与 Shader Graph 的关系
Shader Graph 本质上是 ShaderLab 的可视化封装,其生成的代码结构与手动编写的 ShaderLab/HLSL 代码一致。例如:
- Shader Graph 中的节点参数会映射为 ShaderLab 的
Properties
。 - 节点逻辑会转换为 HLSL 函数,并嵌入
Pass
的CGPROGRAM
块中。
对于复杂效果,可通过 Shader Graph 快速搭建框架,再手动编辑生成的 HLSL 代码进行优化。
总结
ShaderLab 是 Unity 着色器开发的基础,其核心价值在于结构化定义渲染流程和抽象硬件差异。通过组合 Properties
、SubShader
、Pass
和 HLSL 代码,开发者可灵活控制材质的外观与性能。尽管 Shader Graph 简化了多数场景的开发,但深入理解 ShaderLab 仍是掌握高级渲染技术(如自定义光照模型、多 Pass 渲染)的必经之路。