参考书籍: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中的ShaderUnity 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 属性 PropertiesProperties语义块的作用:仅将属性可视呈现于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 语义块支持的属性类型
属性类型默认值的定义语法案例Intnumber_Int ("Int", Int) = 2
Floatnumber_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
SubShader {
[Tags] //可选标签
[RenderSetup] //可选状态
Pass {
//实现A
}
Pass {
//实现B
}
... //更多的实现
}
Tags
:标签。出现多个SubShader{}
的区分命名 RenderSetup
:工作状态设置
注意:一个 Unity Shader 可以包含多个SubShader语义块。但至少有一个SubShader{}
在其中。
答:”Unity加载该Shader时会扫描所有SubShader{}
,选择第一个能在目标平台工作的SubShader{}
。若都不支持,则选择Fallback
语义指定的Unity Shader。
答:受限制于不同版本的显卡中所支持的指令数目,配置合适的着色器,以保障画面的出色能力。
3.3.3 SubShader中的标签 TagsSubShader的标签设置
标签类型说明案例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{}
不同。
常见的渲染状态设置选项
状态名称设置指令解释CullCull Back/Front/Off设置剔除模式:剔除 背面/正面/关闭ZTestZTest Less/Greater/LEqual/Equal/NotEqual/Always设置深度测试使用的函数ZWriteZWrite On/Off深度写入 开启/关闭BlendBlend SrcFactor DstFactor混合模式 开启并设置 特别注意:SubShader{}
中设置上述状态时,会将应用到所有Pass{}
。如果仅仅希望不同的Pass{}
使用的状态不一,可在各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渲染引擎我们希望如何来渲染物体。
其他特殊Pass
- UsePass:使用其他Shader中的Pass
- GrabPass:抓取屏幕并将结果存储至一张纹理中,以用于后续Pass处理
Fallback "name" //返回指定SubShader的pass
Fallback Off //不作任何操作
紧跟于各个SubShader{}
语义块后面,用于告诉Unity,当上面所有的SubShader在这块显卡上都不能下运行时,就使用字符串命名的最低级Shader吧。
例如:Fallback "VertexLit"
,我的显卡都不能用上面的Shader,所以用这个最低级的Shader—“VertexLit”。
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光照模型,不需要其他"等。
简单的顶点/片元着色器示例代码:
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代码,可能存在需求编写更多的代码,带来的好处是灵活性更高,更重要的是可以控制渲染的实现细节。
适用对象:旧设备、不支持可编程管线着色器的设备。(该着色器几乎被弃用)
简单的固定函数着色器代码示例:
Shader "Tutorial/Basic" {
Properties {
_Color ("Main Color", Color) = (1, 0.5, 0.5, 1)
}
SubShader {
Pass {
Material {
Diffuse [_Color]
}
Lighting On
}
}
}
注意:固定函数着色器需要完全使用ShaderLab语法来编写。
4.4 如何选择- 有明确需求必须要使用 固定函数着色器。(运行于少见,旧时代的设备)
- 与各种光源打交道,使用 表面着色器。(需小心注意其在移动平台的表现)
- 需要使用的光照数目非常少,使用 顶点/片元着色器。
- 有很多自定义的渲染效果,使用 顶点/片元着色器。