Unity提供了5种Unity Shader的模板供我们选择:
Standard Surface Shader:会产生一个包含了标准光照模型的表面着色器模板。
Unlit Shader:会产生一个不包含光照(但包含雾效)的基本的顶点/片元着色器。
Image Effect Shader:为我们实现各种屏幕后处理效果提供了一个基本模板。
Compute Shader:会产生一种特殊的Shader文件,这类Shader旨在利用GPU的并行性来进行一些与常规渲染流水线无关的计算。
Ray Tracing Shader:会创建一个光线追踪着色器。
一个Unity Shader的基础结构如下所示:
Shader "ShaderName"
{
Properties
{
//所需的各种属性
}
SubShader
{
//显卡A使用的子着色器
//真正意义上的Shader代码会出现在这里
//表面着色器(Surface Shader)或者
//顶点/片元着色器(Vertex/Fragment Shader)或者
//固定函数着色器(Fixed Function Shader)
}
SubShader
{
//显卡B使用的子着色器
//和上一个SubShader类似
}
Fallback "VertexLit"
}
通过代码第一行的shadername可以在Unity窗口中找到对应的shader材质,如:
Shader "Unlit/FirstShader"
Properties语义块中包含了一系列属性,这些属性将会出现在材质面板中:
Properties
{
Name("display name", PropertyType) = DefaultValue
Name("display name", PropertyType) = DefaultValue
}
Name是每个属性的名字,在Unity中这些属性的名字通常由一个下划线开始。
displayname是显示的名称,就是出现在材质面板上的名字。
PropertyType就是属性类型。除此之外,我们还需要为每个属性制定一个默认值,即DefaultValue。
下面代码给出了一个展示所有属性类型的例子:
Properties
{
//Name("display name", PropertyType) = DefaultValue
_Int("Int", Int) = 1
_Float("Float",Float) = 1.5
_Range("Range",Range(0.0, 5.0)) = 3.0
_Color("Color",Color) = (1, 1, 1, 1)
_Vector("Vector",Vector) = (2, 3, 6, 1)
_2D("2D",2D) = ""{}
_Cube ("Cube",Cube) = "white" {}
_3D("3D",3D) = "black"{}
}
然后我们就可以在材质面板中看到对应的显示:
SubShader语义块:每一个Unity Shader文件可以包含多个SubShader,但最少要有一个。当Unity需要加载这个Unity Shader的时候,会扫描所有的SubShader然后选择第一个能够在目标平台上运行的,如果都不支持的话,就会使用Fallback语义指定的Unity Shader。
这种语义的主要功能就是在不同显卡上显示不同的画面,在旧的显卡上使用计算复杂度较低的着色器,而再高级显卡上使用计算复杂度较高的着色器,以便提供更出色的画面。
SubShader
{
//可选的
[Tags]
//可选的
[RenderSetup]
Pass
{
//Other Passes
}
}
SubShader中定义了一系列Pass以及可选的状态(RenderSetup)和标签(Tags)设置。每个Pass定义了一次完整的渲染流程,但是如果Pass数目过多会造成渲染性能的下降。
状态设置:设置之后会应用到所有的Pass,如果想要单独渲染的话可以在Pass语义块中单独进行上面的设置。
标签:标签是一个键值对,它的键和值都是字符串类型。这些键值对是SubShader和渲染引擎之间沟通的桥梁,它告诉Unity渲染引擎该怎么样以及何时渲染这个对象。标签只可以在SubShader中实现,而不可以在Pass块中声明。
Tags { "TagName1" = "Value1" "TagName2" = "Value2"}
Pass语义块:
Pass
{
[Name]
//此处的Tags和上面SubShader的Tags是不一样的
[Tags]
[RenderSetup]
//Other code
}
我们可以定义该Pass的名称,然后通过这个名称在其他Unity Shader中调用Pass。
//声明Pass名称
Name "MyPassName"
//在其他Unity Shader中调用
UsePass "FirstShader/MYPASSNAME"
Fallback指令:它用于告诉Unity如果上述所有的SubShader在这块显卡上都无法运行,那就使用这个最低级的Shader吧。它还会影响阴影的投射,Fallback使用的内置Shader中包含了这样一个通用的Pass。
//调用最低级的Unity Shader的名字
Fallback "name"
//或者关闭Fallback,即使跑不了所有的SubShader也不管他
Fallback Off
Pass
{
//嵌套CG/HLSL语言
CGPROGRAM
//指定顶点阶段函数为vert
#pragma vertex vert
//指定片元阶段函数为frag
#pragma fragment frag
//引入Unity中的函数库
#include "UnityCG.cginc"
//顶点阶段输入的信息,这里指定了输入的有顶点的位置信息
struct appdata
{
//可以把 float4 理解为一个长度等于4的 float 数组,用 xyzw 或者 rgba 来取它的每一个元素值
//POSITION 是这个字段的语义,它的意思是指明这个字段传入的是顶点位置信息。
float4 vertex : POSITION;
};
//是片元阶段输入的信息,也是顶点阶段输出的信息
struct v2f
{
//SV_POSITION 指定了顶点的位置是经过顶点坐标空间变换过的,直接进行光栅化处理。
float4 vertex : SV_POSITION;
};
//这个声明通常和Properties语句块里的属性相对应
float4 _TintColor;
//vert函数输入的是appdata结构体,输出的是v2f结构体,这里个函数里只做了一件事,就是转换顶点空间并返回。
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
//frag函数输入的是v2f结构体,输出的是一个颜色值,这个颜色值是我们在Properties里声明的。
float4 frag(v2f i) : SV_Target
{
return _TintColor;
}
ENDCG
}