作者:王子饼干 ——镇江虹视游戏科技有限公司联合创始人、《多洛可小镇》制作人
原文链接:UnityShader入门精要笔记2:Shaderlab语法
原文发布时间:2020年2月22日
文章类型:授权转载
UnityShader为我们提供了非常便利的工具,也就是Shaderlab,一种用于在Unity平台编写着色器的语言。
ShaderLab有效的组织了不同类型的文件到Unity中,例如纹理,顶点着色器,片元着色器等。而在一般的OpenGL或者DirectX当中,这些都是分离的,需要通过OpenGL或者DX的API一步步的加载。所以ShaderLab对这些操作进行了非常高度的封装,用户只需要考虑着色器的实现细节即可。
从设计上来说,ShaderLab类似于CgFX和Direct3D Effects(.FX)语言,他们都定义了要显示一个材质所需要的所有东西,而不仅仅是着色器代码。
一个UnityShader的基础结构如下图所示
Shader "ShaderNmae"{
Properties{
//在此处定义你的着色器需要调整的参数,纹理等
}
SubShader{
//着色器1号,针对显卡A
}
SubShader{
//着色器2号,针对显卡B,功能同着色器1号
}
Fallback "Diffuse" //上述着色器都不管用,就使用Unity默认的表面着色器
}
着色器的文件名和其中的内容可以毫无关联,我们需要在着色器中定义其名称,该名称也是该着色器唯一的编号。
我们可以组合字符与“/”来控制它在Shader面板下拉菜单的位置,比如我们定义下面的名称
Shader "MyShaders/TestShader"
就可以在Shader下拉菜单中找到该Shader的名称
在Properties中定义的属性都会出现在材质面板当中。Properties语义块的定义通常如下:
Properties{
Name("displayname",PropertyType) = DefaultValue
Name("displayname",PropertyType) = DefaultValue
}
其中displayname指的是在材质面板中所显示的label,PropertyType是属性的类型,是纹理,还是一个浮点数或者一个Slider等,DefaultValue即默认值啦。下表是ShaderLab支持的属性类型。
下面的代码,给出了一些使用的案例
Shader "MyShaders/TestShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
FallBack "Diffuse"
}
每个UnityShader文件可以包含多个SubShader语义块,但是最少要有一个。当Unity需要加载这个UnityShader时,会扫描所有的SubShader语义块,选择第一个能够在目标平台运行的SubShader,如果都不支持,会选用Fallback语义指定的Shader。
SubShader的结构通常如下
SubShader{
// optional
[Tags]
// optional
[RenderSetup]
Pass{
}
//other passes
}
SubShader中定义了一系列Pass以及可选的状态和标签,每个Pass定义了一次完整的渲染流程,但如果Pass的数目过多,往往会造成渲染性能的下降。
SubShader中的设置会应用到所有的Pass中,不过在Pass中可以继续设定,以覆盖SubShader中的设定。
pass语义块通常包含如下的内容:
Pass{
[Name]
[Tags]
[RenderSetup]
// other codes
}
通过Name可以定义该pass的名称, 通过UsePass命令可以直接使用其他UnityShader中的pass比如
UsePass "MyShader/MYPASSNAME"
这样可以提高代码的复用性。需要注意的是,Unity内部会把所有的Pass名称转换为大写字母表示,因此在使用UsePass命令时必须使用大写形式的名字。
另外,我们可以给Pass设置它自己的渲染状态,但它的标签不同于SubShader的标签。
除了普通的pass之外,UnityShader还支持一些特殊的pass,一遍进行代码的复用或者实现更加复杂的效果。
它定义了这个着色器最次的一种形式,也就是如果上面所有的子着色器都用不了的话,就会用Fallback语义定义的着色器,你也可以关闭Fallback,这样一来就相当于是,如果所有的SubShader都不管用的话,那就随他去吧。
此处省略了表面着色器和固定管线着色器
顶点/片元着色器是我们定义一个有效的着色器所必须的内容。它们更加复杂,然而灵活性也更高。
Shahder "Custom/Simple VertexFragment Shader"{
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v : POSITION):SV_POSITION{
return mul(UNITY_MATRIX_MVP,v);
}
fixed4 frag():SV_TARGET{
return fixed4(1.0,0,0,1.0);
}
ENDCG
}
}
}
我们可以看到,着色器的主要渲染代码都写在了CGRPGRAM/ENDCG之间,并且有两个编译指令,一者是顶点着色器,一者是片元着色器。