新建Shader的分类
- Standard Surface Shader - 产生一个包含标准光照模型的表面着色器模板。
- Unlit Shader(推荐) - 产生一个不包含光照(但包含雾效)的基本顶点/片元着色器。
- Image Effect Shader - 为实现各种屏幕处理后效果提供的基本模板。
- Compute Shader - 特殊Shader,利用GPU的并行特性计算一些计算。
- ShaderLab 是 Unity 提供的编写 Unity Shader 的一种说明性语言。在 Unity 中,所有的 Unity Shader 都是使用 ShaderLab 来编写的。
Shader 的基本结构
Shader "MyShader/Temp01"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
ENDCG
}
}
}
Shader 基本结构说明
1. 给Shader起个名字
Shader "MyShader/Temp01"
这一行代码设置了 Shader 在材质中出现的位置及名称:
2. 属性
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
声明这些属性是为了在材质面板中能够方便地调整各种材质属性。在 Unity 中,这些属性的名字通常由一个下划线开始。括号中的第一个参数,是属性显示在材质面板上的名字。第二个参数,是数据类型。
下面的代码给出了一个展示所有属性类型的例子:
Shader "MyShader/Temp01"
{
Properties
{
// Unmbers and Sliders
_Int("Int", Int) = 2
_Float("Float", Float) = 1.5
_Range("Range", Range(0.0, 5.0)) = 3.0
// Colors and Vectors
_Color("Color", Color) = (1,1,1,1)
_Vector("Vector", Vector) = (2,3,6,1)
// Textures
_2D("2D", 2D) = ""{}
_Cube("Cube", Cube) = "white"{}
_3D("3D", 3D) = "black"{}
}
FallBack "Diffuse"
}
TIPS:编译器对 Shader 的支持普遍不好,其中比较好用的还是 JetBrans 公司的 Rider。
3. SubShader
每一个 Unity Shader 文件可以包含多个 SubShader 语义块,但至少一个。当 Unity 需要加载这个 Unity Shader 时, Unity 会扫描所有的SubShader 语义块,然后选择第一个能够在目标平台上运行的 SubShader。如果都不支持,Unity 会使用 Fallback 语义指定的 Unity Shader。
每个Pass定义了一次完整的渲染流程,但如果Pass的数目过多,往往会造成渲染性能的下降。因此,应当尽量使用最小数目的Pass。
SubShader 语义块中包含的定义通常如下:
···
Subshader{
// 标签,可选的
[Tags]
// 状态,可选的
[RenderSetup]
Pass{
}
// Other Passes
}
···
SubShader 内的具体内容放到以后再写。
最简单的 Unity Shader
尽管 Unity Shader 可以做的事情非常多(例如设置渲染状态等),但其最重要的任务还是指定各种着色器所需的代码。这些着色器代码可以写在 SubShader 语义块中(表面着色器的做法),也可以写在 Pass 语义块中(顶点/片元着色器和固定函数着色器的做法)。
在 Unity 中,我们可以使用2种最常用的形式来编写 Unity Shader。
1. 表面着色器(Surface Shader)
表面着色器是 Unity 自己创造的一种着色器代码类型,它需要的代码量很少,Unity 在背后做了很多工作,但渲染的代价比较大。其本质与接下来的顶点/片元着色器一样,我们可以理解成 Unity 对顶点/片元着色器的更高一层的抽象。它存在的价值在于,Unity为我们处理了很多光照细节,使得我们不需要再操心这些“烦人的事情”。
一个非常简单的例子:
Shader "MyShader/Temp01"
{
Subshader{
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input{
float4 clolr : COLOR;
};
void surf (Input IN, inout SurfaceOutput o){
o.Albedo = 1;
}
ENDCG
}
FallBack "Diffuse"
}
可见,表面着色器被定义在 SubShader 语义块(而非Pass语义块)中的 CGPROGRAM 和 ENDCG 之间。原因是:表面着色器不需要开发者关心使用多少个 Pass、每个 Pass 如何渲染等问题,Unity 会在背后为我们做好这些事情。我们要做的只是告诉它:“嘿,使用这些纹理去填颜色,使用这个法线纹理去填充法线,使用Lambert光照模型,其他的不要来烦我!”
2. 顶点/片元着色器(Vertex/Fragment Shader)
顶点/片元着色器更加复杂,但也更加灵活。
一个非常简单的例子:
Shader "MyShader/Temp01"
{
Subshader{
Pass{
CGPROGRAM
#pragma vertex vertex
#pragma fragment fra
float4 vert(float4 v : POSITION) : SV_POSITION {
return UnityObjectToClipPos (v);
}
fixed4 frag() : SV_Target {
return fixed4(1.0, 0.0, 0.0, 1.0);
}
ENDCG
}
}
}
我们将主要使用顶点/片元着色器。
扩展阅读
Unity官网的 Unity Shader 文档:官方文档