前言

【注】因为是概述,很多细节和需要查询的内容请参考《Unity Shader入门精要》

上一章描述了什么是渲染流水线,这一章主要讨论什么是Unity Shader。

Shader 翻译为着色器,GPU流水线中对于高度可编程的阶段来说,Shader是可以在GPU上运行的渲染代码;而对于固定流水线来说,则起着渲染设置的作用。前者有顶点着色器,片元着色器等。

正常情况下,开发者需要与下图中各种操作、文件打交道。

因为我们是在引擎上使用shader,Unity 提供了名为Unity Shader的抽象。于是我们只需要使用unity shader就可以控制所有操作。并且必要的内部功能已经写好。

与其它着色语言的关系

我们知道着色器语言主要有CG,HLSL,GLSL。

不同于它们,Unity中开发者使用的着色器语言叫做ShaderLab,语法上shaderlab和CG,HLSL没有太大区别,部分函数Unity没有提供;本质上shaderlab是基于它们的封装。

虽然Unity只支持封装后的CG,HLSL,但是如果要写GLSL也是可行的(不推荐)。

Unity Shader 基本介绍

Unity Shader 结构

Unity主要有Standard Surface Shader, Unlit Shader, Image Effect Shader 以及 Compute Shader

Unity Shader基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Shader "ShaderName"{
Properties{

}

SubShader{// SubShader的Tag会作用所有Pass
//显卡A
Pass{
//一次完整渲染流程,可以有多个,但是性能会下降
}
}
SubShader{
//显卡B
}
FallBack "VertexLit"
}

Properties 声明

Properties 语义块的作用仅仅是为了让这些属性可以出现在材质面板中。

Sub Shader

真正意义上的Shader语义块都在这里面。

Tags可以进行设置

Tags{"Queue" = "Transparent"} 设置渲染顺序

Pass

Pass包含一次完整渲染流程

设置渲染状态

首先Pass的命名Name"MyPassName"

调用其他Unity Shader中的Pass UsePass "MyShader/MyPassName"

Pass中的Tags与SubShader中的一样,都是用于设置渲染状态,具体的控制内容不同

详情见《Unity Shader入门精要》P3。下图为Pass中的标签类型。

【补充】GrabPass关键字可以将渲染结果存储到纹理中,详情参照原书。

Fall Back

FallBack "name",如果其它Sub Shader都没用,就用这个指定的

FallBack off关闭,不管了

FallBack "VertexLit"调用内置的Shader中的Pass

结构

Unity上的着色器

表面着色器

(Surface Shader) 已经写好,开发者可编程内容少。不需要Pass包裹,引擎自行处理

本质上可以理解为顶点、片元着色器的进一步抽象,Unity中的Shader代码一般如上图所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
Shader "Custom/Simple Surface Shader"{
SubShader{
Tags{"RenderType" = "Opaque"}

CGPROGRAM
#pragma surface surf Lambert
struct Input{
float4 color : COLOR;
};
ENDCG
}
Fallback "Diffuse"
}

顶点/片元着色器

(Vertex/Fragment Shader)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Shader "Custom/Simple VertexFragment Shader"{
SubShader{
Pass{
CGPROGRAM //用来包裹ShaderLab语言
#pragma vertex vert
#pragma fragment frag

float4 vert(float4 v : POSITION) :SV_POSITION{
return mul(UNITY_MATRIX_MVP,v);
}

float4 frag() : SV_Target{
return fixed4(1.0,1.0,0.0,1.0);
}
ENDCG
}
}
Fallback "Diffuse"
}

固定函数着色器落后不讨论

从结果上来讲,Unity Shader只包含顶点片元两种可编写部分

表面着色器中SubShader的CG代码会被Unity细分到Pass。

Unity Shader语法与语义

基础语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Shader "Unity Shaders Book/Chaper5/Simple Shader" { 
SubShader {
Pass { //自定义渲染设置
CGPROGR
#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, 1.0,1.0,1.0);
}
ENDCG
}
}
}
  • 顶点着色器中

POSITION和SV_POSITION是CG/HLSL中的语义(semantic)不可省略。

POSITION告诉Unity将模型的坐标作为输入参数输入到v中。

而SV_POSITION则告诉Unity返回裁剪坐标。

  • 片元着色器中

SV_TARGET也是一个语义,告诉系统将渲染输出存储到渲染目标,默认输出到帧缓存。(1,1,1)表示白色,这里返回了白色。

结构体用来定义着色器的输入

1
2
3
4
5
6
7
8
struct a2v{   //a2v意思是将应用阶段数据传到顶点着色器
//用模型坐标填充
float4 vertex : POSITION;
//用法线方向填充
float3 normal : NORMAL;
//TEXCOORD0告诉系统用第一条纹理坐标填充
float4 texcoord : TEXCOORD0;
};

Unity 支持的语义有: POSITION, TANGENT, NORMAL, TEXCOORDO, TEXCOORDJ, TEXCOORD2, TEXCOORD3, COLOR 等。

struct StructName{TypeName name : Semantic;};

顶点与片元着色器通信

1
2
3
4
5
6
struct v2f{ //从顶点着色器到片元
//pos中包含了裁剪空间的位置信息
float4 pos : SV_POSITION;
//COLOR0用来存储颜色信息
float3 color : COLOR0;
}

以下为完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Shader "Unity Shaders Book/Simple Shader"{
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//将模型坐标输入到顶点着色器
struct a2v{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
//将顶点数据输出到片元
struct v2f{
float4 pos : SV_POSITION;
fixed3 color : COLOR0;
};
//顶点着色器,逐顶点调用
v2f vert(a2v v){
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
o.color = v.normal * 0.5+fixed3(0.5,0.5,0.5);
return o;
}
//逐片元调用
fixed4 frag(v2f i) : SV_TARGET{
return fixed4(i.color,1.0);
}
ENDCG
}

}
}

属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Properties{
// shaderlab的变量
_Color("Color Tint",Color) = (1.0,1.0,1.0,1.0);
}
//需要注意的是,在CG代码中需要定义出名称类型都一致的变量

SubShader{
Pass{
// CG片段
CGPROGRAM
fixed4 _Color;
ENDCG
}
}

区别ShaderLab和CG的变量

文件

已知真正的Shader不是简单的一个.shader文件。

UnityShader 的方便性使得不用开发者自己转换法线方向,处理光照阴影等。

类似于C++的头文件,写在CG中,根据情况下载

1
2
3
4
5
CGPROGRAM
//...
#include "UnityCG.cginc"
//...
ENGCG

Debug

ColorPicker.cs绑定摄像机,获取屏幕RGB值

其他

有关CG/HLSL的语义,可参照微软DX文档

语义主要是用来限定输入输出的字符串,决定输入输出来源去向,方式等。

SV(system value semantics)

定义复杂变量类型时,需要注意,一个语义,寄存器只能存储4个浮点数,所以可以采取分成4个变量表示一个矩阵

【注意】shader中不要除以0,会造成意想不到的错误。