参考书籍:UNITY SHADER入门精要
一、材质(Material)与 Unity Shader
效果的实现需要材质和Unity Shader配合使用。常见流程为:
1. 创建一个材质
2. 创建一个Unity Shader,并赋予给上一步新建材质中
3. 将材质赋予要需要渲染的物体对象上
4. 在材质面板中调整Unity Shader属性,以达到预期效果
1.1 Unity中的材质
工作原理:结合一个GameObject的Mesh或者Particle Systems组件来工作。
1.1 Unity中的Shader
Unity Shader:Unity中的Shader,不同于渲染管线中描述的Shader。
Unity Shader 本质:是一个文本文件。
Unity提供了4种Unity Shader模板供选择:
Unity Shader | 描述 |
---|---|
Standard Surface Shader | 产生一个包含了标准光照模型的表面着色器模板 |
Unlit Shader | 产生一个不包含光照(但包含雾效)的基本的顶点/片元着色器 |
Image Effect Shader | 实现各种屏幕或处理效果的基本模板 |
Compute Shader | 产生一种特殊的Shader文件(非重要,详情自行参考Unity手册) |
二、什么是ShaderLab
答:服务于Unity Shader的语言。
三、Shader基础结构
Shader "ShaderName" {
Properties {
//属性
}
SubShader {
//显卡A使用的子着色器
}
SubShader {
//显卡B使用的子着色器
}
Fallback "VertexLit"
}
3.1 标题
Shader "MyShader/Shader01" {}
上代码内容指:路径"MyShader/"下,该Shader命名为 Shader01
3.2 属性 Properties
Properties语义块的作用:仅将属性可视呈现于Inspector材质面板中。
下图为Properties{}
基本格式:
Properties {
Name {"display name", PropertyType} = DefaultValue
Name {"display name", PropertyType} = DefaultValue
...
Name {"display name", PropertyType} = DefaultValue
// 更多属性
}
Name
:属性名,用于后面SubShader中。通常以"_"开头命名,例如:_Color
、_Texture
等。
display name
:显示名称,即显示在Inspector窗口上的属性名称。
PropertyType
:Unity Shader提供的可用属性类型,需要我们选择使用,并与前面的属性、名称相符。
DefaultValue
:默认数值,指第一次将Shader赋予至材质时的初始数值。
;Properties 语义块支持的属性类型
属性类型 | 默认值的定义语法 | 案例 |
---|---|---|
Int | number | _Int ("Int", Int) = 2 |
Float | number | _Float ("Float", Float) = 1.5 |
Range(min, max) | number | _Range ("Range", Range(0, 1)) = 0.5 |
Color | (number, number, number, number) | _Color ("Color" Color) = (1, 1, 1, 1) |
Vector | (number, number, number, number) | _Vector ("Vector" Vector) = (2, 3, 4, 5) |
2D | “defaulttexture” {} | _2D ("2D", 2D) = "" {} |
Cube | “defaulttexture” {} | _Cube ("Cube", Cube) = "white" {} |
3D | “defaulttexture” {} | _3D ("3D", 3D) = "black" {} |
特别的,2D
、Cube
、3D
的默认值是 "字符串" {}
。其中字符串有以下:
- 空,即""
- 内置文理名称,如"white"
/black
/gray
/bump
3.3 子着色器 SubShader
SubShader {
[Tags] //可选标签
[RenderSetup] //可选状态
Pass {
//实现A
}
Pass {
//实现B
}
... //更多的实现
}
Tags
:标签。出现多个SubShader{}
的区分命名
RenderSetup
:工作状态设置
注意:一个 Unity Shader 可以包含多个SubShader语义块。但至少有一个SubShader{}
在其中。
3.3.1 工作原理
答:”Unity加载该Shader时会扫描所有SubShader{}
,选择第一个能在目标平台工作的SubShader{}
。若都不支持,则选择Fallback
语义指定的Unity Shader。
3.3.2 使用目的
答:受限制于不同版本的显卡中所支持的指令数目,配置合适的着色器,以保障画面的出色能力。
3.3.3 SubShader中的标签 Tags
SubShader的标签设置
标签类型 | 说明 | 案例 |
---|---|---|
Queue | 控制渲染顺序,指定该物体属于哪一个渲染队列 | Tags { “Queue” = “Transparent” } |
RenderType | 着色器分类,指明使用的着色器对象 | Tags { “RenderType” = “Qpaque” } |
DisableBatching | 指明是否对该SubShader使用批处理(配合Unity批处理) | Tags { “DisableBatching” = “True”} |
ForceNoShadowCasting | 控制使用该SubShader的物体是否会投射阴影 | Tags { “ForceNoShadowCasting” = “True”} |
IgnoreProjector | 控制使用该SubShader的物体是否不受Projector的影响(常用于半透明物体) | Tags { “IgnoreProjetctor” = “True”} |
CanUseSpriteAtlas | 该SubShader是否没有被用于sprites(精灵) | Tags { “CanUseSpritesAtlas” = “False”} |
PreviewType | 材质面板预览该材质的类型,默认为球型。(如 “Plane”、“SkyBox”) | Tags { “PreviewType” = “Plane”} |
标签的声明仅在SubShader{}
中进行,不可在Pass{}
进行。
附注:Pass{}
可以声明标签,但声明的标签类型与SubShader{}
不同。
3.3.4 状态设置 RenderSetup
常见的渲染状态设置选项
状态名称 | 设置指令 | 解释 |
---|---|---|
Cull | Cull Back/Front/Off | 设置剔除模式:剔除 背面/正面/关闭 |
ZTest | ZTest Less/Greater/LEqual/Equal/NotEqual/Always | 设置深度测试使用的函数 |
ZWrite | ZWrite On/Off | 深度写入 开启/关闭 |
Blend | Blend SrcFactor DstFactor | 混合模式 开启并设置 |
特别注意:SubShader{}
中设置上述状态时,会将应用到所有Pass{}
。如果仅仅希望不同的Pass{}
使用的状态不一,可在各Pass{}
语义块中单独进行设置。
3.4 Pass语义块
Pass {
[Name]
[Tage]
[RenderSetup]
// Other code
}
定义该Pass的名称
Name "MyPassName"
使用指定Pass
UsePass "MyShader/MyPassName"
通过使用ShaderLab中UsePass
命令直接使用其他Unity Shader中的Pass(即 提高代码的复用性)。其中UsePass "MyShader/MyPassName"
为 使用"MyShader中的名称为MyPassName"的Pass。
特别注意:Unity会把所有Pass名称转为大写字母表示,使用UsePass
命令时必须使用大写名字。
标签
不同于SubShader{}
中标签。Pass{}
中的标签用于告诉Unity渲染引擎我们希望如何来渲染物体。
标签类型 | 说明 | 案例 |
---|---|---|
LightMode | 定义 Pass 在Unity的渲染流水线中的角色 | Tags { “LightMode” = “ForwardBase” } |
RequireOptions | 指定满足条件时渲染该Pass | Tags { “RequireOptions” = “SoftVegetation”} |
其他特殊Pass
- UsePass:使用其他Shader中的Pass
- GrabPass:抓取屏幕并将结果存储至一张纹理中,以用于后续Pass处理
3.5 Fallback
Fallback "name" //返回指定SubShader的pass
Fallback Off //不作任何操作
紧跟于各个SubShader{}
语义块后面,用于告诉Unity,当上面所有的SubShader在这块显卡上都不能下运行时,就使用字符串命名的最低级Shader吧。
例如:Fallback "VertexLit"
,我的显卡都不能用上面的Shader,所以用这个最低级的Shader—“VertexLit”。
3.6 其他语义
CustomEidtor语义:扩展编辑界面
Category语义:对Unity Shader中的命令进行分组
四、Unity Shader 的形式
SubShader{}
:表面着色器的做法
Pass{}
:顶点/片元着色器和固定函数着色器的做法
真正意义上的Shader代码:
Shader "MyShader" {
Properties {
// 所需的各种属性
}
SubShader {
// 真正意义上的Shader代码出现于这里
// 表面着色器(Surface Shader)
// 或者 顶点/片元着色器(Vertex/Fragment Shader)
// 或者 固定函数着色器(Fixed Function Shader)
}
}
4.1 表面着色器 Surface Shader
理解:表面着色器是Unity对顶点/片元着色器的更高一层的抽象。其存在是Unity为我们处理了很多光照细节的繁琐操作。
简单的表面着色器示例代码:
Shader “Custom/Simple Surface Shader” {
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float4 color : COLOR;
};
void Surf (Input input, inout SurfaceOutput out) {
out.Albedo = 1;
}
ENDCG
}
Fallback "Diffuse"
}
附注:了解 CGPROGRAM-CG
由代码内容所示,表面着色器被定义于SubShader{}
中的CGPROGRAM
和ENDCG
中。
原因:开发中,不需要表面着色器关心使用多少个Pass()
与如何渲染等问题,只需告诉其,“使用这些纹理去填充颜色”、“使用这个法线纹理去填充法线”、"使用Lambert光照模型,不需要其他"等。
4.2 顶点/片元着色器 VertexFragment Shader
简单的顶点/片元着色器示例代码:
Shader "Custom/Simple VertexFragment Shader" {
SubShader {
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, 0.0, 1.0);
}
ENDCG
}
}
注意:与表面着色器类似,顶点/片元着色器的代码也需要定义至CGPROGEAM-CG
中。但却是写在Pass()
中。
原因:开发中需要定义各Pass()
中需求使用的Shader代码,可能存在需求编写更多的代码,带来的好处是灵活性更高,更重要的是可以控制渲染的实现细节。
4.3 固定函数着色器 Fixed Function Shader
适用对象:旧设备、不支持可编程管线着色器的设备。(该着色器几乎被弃用)
简单的固定函数着色器代码示例:
Shader "Tutorial/Basic" {
Properties {
_Color ("Main Color", Color) = (1, 0.5, 0.5, 1)
}
SubShader {
Pass {
Material {
Diffuse [_Color]
}
Lighting On
}
}
}
注意:固定函数着色器需要完全使用ShaderLab语法来编写。
4.4 如何选择
- 有明确需求必须要使用 固定函数着色器。(运行于少见,旧时代的设备)
- 与各种光源打交道,使用 表面着色器。(需小心注意其在移动平台的表现)
- 需要使用的光照数目非常少,使用 顶点/片元着色器。
- 有很多自定义的渲染效果,使用 顶点/片元着色器。