第二篇:ShaderLab语法——Unity Shader的脚手架
核心思想
在Unity中,一个.shader文件并不仅仅包含纯粹的HLSL代码。它是由一种名为ShaderLab的声明式语言组织起来的。ShaderLab是Unity为你提供的“脚手架”或“包装纸”,它用一种清晰的结构定义了Shader的属性、渲染设置和多个渲染子版本(SubShader),而你的HLSL代码(写在CGPROGRAM和ENDCG块中)是这个结构中最核心的“内容”。
你可以把它类比成一份产品说明书:
- Properties:是说明书上的可调节旋钮和开关(比如颜色、滑块、纹理选择),暴露给材质Inspector面板。
- SubShader/Pass:是说明书的安装指南,告诉GPU在不同的硬件或渲染环境下应该如何设置和执行这个Shader。
- CGPROGRAM:是说明书最核心的技术原理和工作细节,只有GPU这位“工程师”会仔细阅读并执行它。
ShaderLab 基本结构解剖
一个最基本的ShaderLab文件结构如下,我们逐块解析:
(语言为glsl)
// 1. 定义Shader在菜单中的路径
Shader "MyShaders/SimpleShader"
{
// 2. 属性块:定义在材质面板上可见的可调参数
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Tint Color", Color) = (1,1,1,1)
_Glossiness ("Smoothness", Range(0,1)) = 0.5
}
// 3. 子着色器块:可以定义多个以适应不同硬件,至少一个
SubShader
{
// 4. 标签:告诉引擎如何以及何时渲染这个SubShader
Tags { "RenderType"="Opaque" }
// 5. 渲染通道(Pass):一次完整的渲染流程。一个SubShader可包含多个Pass
Pass
{
// 6. 开始CG代码片段
CGPROGRAM
// 7. 编译指令:声明顶点着色器和片元着色器的函数名
#pragma vertex vert
#pragma fragment frag
// 8. 引入常用的内置函数和变量
#include "UnityCG.cginc"
// 9. 定义与Properties块关联的变量
sampler2D _MainTex;
float4 _MainTex_ST; // 注意:自动生成的纹理缩放偏移变量
fixed4 _Color;
// 10. 定义输入结构体(从CPU到顶点着色器)
struct appdata
{
float4 vertex : POSITION; // 顶点位置(语义绑定)
float2 uv : TEXCOORD0; // 第一套UV坐标
};
// 11. 定义输出结构体(从顶点着色器到片元着色器)
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION; // 裁剪空间位置(必须)
};
// 12. 顶点着色器函数
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex); // 核心变换函数
o.uv = TRANSFORM_TEX(v.uv, _MainTex); // 应用纹理缩放偏移
return o;
}
// 13. 片元着色器函数
fixed4 frag (v2f i) : SV_Target
{
// 采样纹理,并乘以颜色属性
fixed4 col = tex2D(_MainTex, i.uv) * _Color;
return col;
}
ENDCG // 结束CG代码片段
}
}
// 14. 后备Shader(可选)
FallBack "Diffuse"
}
关键部分详解
-
Properties 属性块:
- 这是你与美术师或自己交互的界面。
- 语法:
_VariableName ("Display Name", Type) = DefaultValue {} - 常见类型:
2D(纹理),Color(颜色),Range(min, max)(滑块),Float(浮点数),Vector(四维向量)。
-
SubShader 和 Pass:
- 一个Shader可以有多个SubShader。Unity会从上到下检查第一个能被目标硬件支持的SubShader。这用于为不同性能的显卡提供不同效果的版本。
- 每个SubShader可以包含多个Pass。每个Pass意味着一次完整的渲染流程。多Pass用于实现复杂的、需要渲染多次的效果(如描边、法线 extrusion 等),但性能开销较大。
-
Tags:
- 以键值对的形式提供一些“提示”,告诉渲染引擎如何渲染这个SubShader或Pass。
- 例如:
"RenderType"="Opaque"(是不透明物体),"Queue"="Geometry"(渲染队列是几何体,即2000)。
-
CGPROGRAM 中的关键:
#pragma vertex [functionName]:告诉Unity哪个函数是顶点着色器。#pragma fragment [functionName]:告诉Unity哪个函数是片元着色器。#include "UnityCG.cginc":极其重要!它包含了Unity预提供的大量帮助函数、宏和内置变量(如UnityObjectToClipPos),能极大简化你的代码。- 变量传递:在Properties中定义的变量,必须在CG代码段中重新声明一遍(如
sampler2D _MainTex;)才能使用。注意:声明一个_MainTex纹理时,Unity会自动生成一个_MainTex_ST(ST = Scale & Tiling)变量来存储其缩放和偏移值。
-
结构体 (appdata, v2f):
appdata:输入到顶点着色器的数据模型。用语义(: POSITION) 来标明每个变量的用途。v2f(vertex-to-fragment):顶点着色器输出、并插值后传递给片元着色器的数据。同样使用语义。
-
语义 (Semantics):
- 这是HLSL中连接数据和管线特定角色的桥梁。
: POSITION:表示这是顶点位置。: TEXCOORD0:表示这是一套纹理坐标。: SV_POSITION:表示这个值是裁剪空间的位置(顶点着色器输出必须包含这个)。: SV_Target:表示片元着色器输出的颜色值要渲染到目标渲染纹理(屏幕)。
下一步行动
- 在Unity中,创建一个
Unlit Shader,将其命名为SimpleShader。 - 用上面的代码完全替换掉默认内容。
- 创建一个材质,使用这个Shader,并赋予一个纹理。
- 尝试调节
_Color属性,观察效果。恭喜你,你已经成功创建并运行了你的第一个自定义Shader!
下一篇预告:《第三篇:HLSL语法基础——Shader的编程语言》。我们将暂时离开ShaderLab框架,深入学习了HLSL语言的基本语法、数据类型和核心函数,为你编写更复杂的着色器逻辑打下坚实基础。
1527

被折叠的 条评论
为什么被折叠?



