ShaderLab语法基础
ShaderLab 语法不区分大小写。真正意义上的Shader代码则是在CGPROGRAM代码块中编写的
Shader 的组织结构
通常情况下,Shader 的大致结构如下:
Shader "Name"
{
Properties
{
//开放到材质面板的属性
}
SubShader
{
//顶点-片段着色器
//或者表面着色器
//或者固定函数着色器
}
SubShader
{
//更加精简的版本
//为了在旧的设备上运行
}
...
Fallback"Name"
}
Shader中可以 编写多个子着色器(SubShader),但至少需要一个。
如果编写的是顶点-片段着色器(Vertex-Fragment Shader),每个着色器中还会包含一个甚至多个Pass。在运行的过程中,如果某个子着色器能够在当前GPU上运行,那么该子着色器内的所有Pass会依次执行,每个Pass输出的结果会以指定的方式与上一步的结果进行混合,最终输出。
表面着色器不会再嵌套Pass。系统在编译表面着色器的时候,会自动生成多个对应的Pass,最终编译出来的Shader本质上就是顶点-片段着色器。
Shader 的名称
Shader 程序的第一行代码用来声明该Shader的名称以及所在路径。
名称就是指该Shader在选择使用的时候所显示的名称,而路径则是指Shader在材质面板上Shader下拉列表里的保存路径。
例如:Shader "Custom/Simple Shader"
这一行代码的意思是:这个Shader位于Custom路径里,名称为Simple Shader。
Properties
开放的属性 不同类型的变量或贴图等资源
Unity Shader 的属性主要分为三大类:数值、颜色和向量、纹理贴图,每一条属性都是按照以下语法进行定义的:_Name("Display Name",type) = defaultValue[{options}]
- _Name: 属性的名字,为了方便获取,通常在名字的最前面加一个下划线,后续在整个Shader中都将使用这个名称来获取该属性。
- Display Name:在材质面板中显示出来的名称。
- type:属性的类型。
- defaultValue:将Shader指定给材质的时候初始化的默认值。
数值类属性
Unity Shader 的数值类属性基本都是浮点型(Float)数据,虽然Unity提供了整数(Int)数据,但是在编译的时候最终都会转化为浮点型数据。
数值类型的数据有以下两种:
{
name("display name", Color) = (number,number.number,number)
name("display name", Vector) = (number1,number2,number3,number4)
}
Color 是颜色类型的数据,由R、G、B、A四个分量定义,在材质面板上显示为取色器(Color Picker)。
需要注意的是:Ps处理图片一般会使用8位深度图,每个通道的亮度最大值为2的8次方=256,由于从0开始计算,因此数值范围是[0,255]。而在Shader中,每个分量的数值范围是[0,1],于是它们之间需要按照对应关系进行线性映射,当数值为0,颜色为黑色,当数值为1,中间部分以线性关系对应。
Vector 是向量类的属性,是一个四维数组,在材质面板上作显示为4个连续的数值输入框,分别为X、Y、Z、W。
颜色和向量类型属性的默认值都是由括号括住的4个浮点数组成,其中颜色属性每个分量的数值区间为[0,1],例如中度灰:(0.5,0.5,0.5,1),而向量属性没有范围限制。
纹理贴图类型
{
name("display name",2D) = "defaulttexture"{}
name("display name",Cube) = "defaulttexture"{}
name("display name",3D) = "defaulttexture"{}
}
2D属性是纹理类属性中最常使用的,漫反射贴图、法线贴图等都属于2D类型。
Cube全称Cube map texture(立方体纹理),是由前、后、左、右、上、下6张有联系的2D贴图拼成的立方体,主要用作反射,例如Skybox和Reflection Prob。
3D纹理只能被脚本创建,在实际使用中很少。
2D类型的属性,默认值可以可以为空字符串,也可以是内置的表示颜色的字符串:“white”(RGBA:1,1,1,1),”black”(RGBA:0,0,0,0),”gray”(RGBA:0.5,0.5,0.5,0.5),”bump”(RGBA:0.5,0.5,1,0.5)和“red”(RGBA:1,0,0,0)。其中“bump”通常用于法线体贴图的默认值。
至于非2D类型的属性(Cube,3D,2DArray),默认值为空字符串。当材质没有指定Cubemap或者3D或者2DArray纹理的时候,会默认使用(RGBA:0.5,0.5,0.5,0.5)
所有纹理贴图类的属性最后都有一对空的花括号(是Unity 5.0之前的功能)。
所有类型属性汇总
Shader "Custom/Properties"
{
Properties
{
_MyFloat("Float Property", Float) = 1 //浮点类型
_MyRange("Range Property", Range(0,1)) = 0.1 //范围类型
_MyColor("Color Property", Color) = (1,1,1,1) //颜色类型
_MyVector("Vector Property",Vector) = (0,1,0,0) //向量类型
_MyTex("Texture Property",2D) = "white" {} //2D贴图类型
_MyCube("Cube Property",Cube) = ""{} //立方体贴图类型
_My3D("3D Property",3D) = ""{} //3D贴图类型
}
SubShader
{
Pass
{
//pass中的代码
}
}
FallBack "Diffuse"
}
Properties代码在Shader中并不是必须的。如果在实际编写过程中没有开放参数的必要,完全可以在Shader中省略Properties这一部分的代码。
SubShader
在Unity中,每一个Shader都会至少包含一个SubShader。当Unity想要显示一个物体时,它就会去检测这些SubShader,然后选择第一个能够在当前显卡运行的SubShader。
通常情况下,SubShader的大致结构如下所示:
SubShader
{
//标签
Tags{"TagName1" = "Value1" "TagName2" = "Value2" ...}
//渲染状态
Cull Back
...
Pass
{
//第一个Pass
}
Pass
{
//第二个Pass
}
...
}
每个SubShader都可以设置一个或者多个标签(Tags)和渲染状态(States),然后定义至少一个Pass。在SubShader中设置的渲染状态会影响到该SubShader中所有Pass,如果想要某些状态下不影响其他Pass,可以针对某个Pass单独设置渲染状态。但是需要主要的是,部分渲染状态在Pass中并不支持。
当Unity选择了某个SubShader来渲染某个物体的时候,SubShader中每定义一个Pass都会使这个物体执行一次渲染,当物体受到灯光影响的时候,渲染次数还会增加。所以考虑这方面的影响,应该尽可能地减少Pass的数量。当然,如果某些效果无法通过单个Pass来实现,那么只能使用多个Pass,但是这种情况一定要少出现。
Tags(标签)
States(状态)
SubShader Pass - 1
Pass - 2
…. ….
SubShader的标签**Tags{"TagNamel" = "Value1""TagName2" = "Value2"}**
标签通过键值对的形式进行声明,并且没有使用数量的限制。如果有需要,可以使用任意数量多个标签。
1.渲染队列
在SubShader中可以用Queue(队列)标签确定物体的渲染顺序,Unity预先定义了五种渲染队列
可以使用的渲染队列:
队列名称 | 描述 | 队列号 |
Background | 最先执行渲染,一般用来渲染天空盒(Skybox)或者背景 | 1000 |
Geometry | 非透明的几何体通常使用这个队列,当没有声明渲染队列的时候,Unity会默认使用这个队列。 | 2000 |
AlphaTest | Alpha测试的几何体会使用这个队列,之所以从Geometry队列单独拆分出来,是因为当所有实体都绘制完之后再绘制Alpha测试会更高效 | 2450 |
Transparent | 在这个队列的几何体按由远及近的顺序进行绘制,所有进行Alpha混合的几何体都应该使用这个队列,例如玻璃材质,粒子特效等 | 3000 |
Overlay | 用来叠加渲染的效果,例如镜头光晕等,放在最后渲染 | 4000 |
除了使用Unity预定义的渲染队列, 使用者也可以自己指定一个队列,例如:Tags{"Queue" = "Geometry + 1"}
这个队列的队列号其实就是2001,表示在所有非透明几何体绘制完成之后再进行绘制。
使用自定义的渲染队列在某些情况下非常有用,例如:透明的水应该在所有不透明几何体之后,透明几何体之前被绘制,所以透明水的渲染队列一般会使用”Queue” = “Transparent -1 “‘
2.渲染类型
RenderType(渲染类型)标签可以将Shader划分为不同的类别,用于后期进行Shader替换或者产生摄像机的深度纹理。
可以设置的渲染类型
类型名称 | 描述 |
Opaque | 用于普通Shader,例如:不透明、自发光、反射、地形Shader |
Transparent | 用于半透明Shader,例如:透明、粒子 |
TransparentCutout | 用于透明测试Shader,例如:植物叶子 |
Background | 用于Skybox Shader |
Overlay | 用于GUI纹理、Halo、Flare Shader |
TreeOpaque | 用于地形系统中的树干 |
TreeTransparentCutout | 用于地形系统中的树叶 |
TreeBillboard | 用于地形系统中的Billboarded树 |
Grass | 用于地形系统中的草 |
GrassBillboard | 用于地形系统中的Billboarded草 |
3.禁用批处理
当使用批处理(Batching)的时候,几何体会被变换到世界空间,模型空间会被丢弃。这会导致某些使用模型空间的顶点数据的Shader最终无法实现所希望的效果。而开启DisableBatching(禁用批处理)可以解决这个问题。
禁用批处理有三个数值可以使用
- “DisableBatching” = “True”: 总是禁用批处理。
- “DisableBatching” = “False”: 不禁用批处理,这是默认数值。
- “DisableBatching” = “LODFading”: 当LOD效果激活的时候才会禁用批处理,主要用于地形上的树。
4.禁止阴影投射
在游戏中,有很多特效类的物体并不需要对其他物体产生投影,这个时候可以使用”ForceNoShadowCasting”(禁止阴影照射)标签来达到需要实现的效果。只要将这个标签的数值设为true,那么使用这个Shader的物体就不会对其他物体产生投射阴影了。
5.忽略Projector
如果不希望物体受到Projector(投影机)的投射,可以在Shader中添加IgnoreProjector标签。
它有两个数值可以使用:”True”和”False”,分别为忽略投射机和不忽略投射机。一般半透明的Shader都会开启这个标签。
6.其他标签
除了以上标签之外,Unity还提供了很多不常用的标签,例如CanUseSpriteAtlas、PreviewType。
Pass的渲染状态
如果想某些Pass的渲染状态不影响到其他的Pass,可以在该Pass中单独设置渲染状态。
在SubShader中使用会影响到该SubShader中的所有Pass。
可以设置的渲染状态(这些渲染状态在SubShader中同样被允许使用)
渲染状态 | 数值 | 作用 |
Cull | Cull Back | Front | Off | 设置多边形的剔除方式,有背面剔除、正面剔除、不剔除,默认为Back |
ZTest | ZTest(Less | Greater | LEqual | GEqual | Equal | NotEqual | Always) | 设置深度测试的对比方式,默认为LEqual |
ZWrite | ZWrite On | Off | 设置是否写入深度缓存,默认为On |
Blend | Blend sourceBlendMode destBlendMode | 设置渲染图像的混合方式 |
ColorMask | ColorMask RGB | A | 0 | 或者R、G、B、A的任意组合 | 设置颜色通道的写入蒙版,默认蒙版为RGBA,当设置为0时,则无法写入任何颜色。 |
FallBack
FallBack在所有SubShader之后进行定义。
当所有的SubShader都不能在当前显卡上运行的时候,就会运行Fallback定义的Shader。
语法如下:Fallback "name"
最常用于Fallback的Shader为Unity为Unity内置的Diffuse。
如果觉得某些Shader肯定可以在目标显卡上运行,没有指定Fallback的必要,可以使用Fallback Off关闭Fall back 功能,或者直接什么都不写。