Unity Shader基础
在unity中需要配合使用**材质(material)**和Unity Shader 才能达到需要的效果
Unity Shader是Unity为开发者提供的高层级的渲染抽象层
Unity Shader模板:
- Standard Surface Shader: 会产生一个包含了标准光照模型的表面着色器模板
- Unlit Shader: 会产生一个不包含光照(但包含雾效)的基本的顶点/片元着色器
- Image Effect Shader: 为实现各种屏幕后处理效果提供了一个模板
- Compute Shader: 会产生一个特殊的Shader文件,旨在利用GPU的并行性来进行一些与常规渲染流水线无关的计算
ShaderLab
ShaderLab是Unity提供的编写Unity Shader的一种说明性语言。
Unity Shader的结构
Shader Name
Shader "Custom/MyShader"
Properties
Properties语义块中包含了一系列属性(property) ,这些属性将会出现在材质面板中
Properties语义块支持的属性类型
属性类型 | 默认值的定义语法 | 例子 |
Int | number | _Int (“Int”, Int) = 2 |
Float | number | _Float (“Float”, Float) = 1.5 |
Range(min,max) | number | _Range (“Range”, Range(0.0,5.0)) = 3.0 |
Color | (number,number,number,number) | _Color (“Color”, Color) = (1,1,1,1) |
Vector | (number,number,number,number) | _Vector (“Vector”, Vector) = (2,3,6,1) |
2D | “defaulttexture”{} | _2D (“2D”, 2D) = “white” {} |
Cube | “defaulttexture”{} | _Cube (“Cube”, Cube) = “black” {} |
3D | “defaulttexture”{} | _3D (“3D”, 3D) = “white” {} |
SubShader
每一个Unity Shader文件可以包含多个SubShader语义块,至少有一个。当Unity需要加载这个Unity Shader时,unity会扫描所有的SubShader语义块,然后选择一个能在目标平台上运行的SubShader。如果都不支持,Unity会使用Fallback语义指定的Unity Shader。
Unity提供这些语义的原因在于,不同的显卡有不同的能力。
SubShader语义块
SubShader {
[Tags] //可选
[RenderSetup] //可选
Pass { }
//Other Passes
}
状态设置
常见的渲染状态设置选项
状态名称 | 设置指令 | 解释 |
Cull | Cull Back | Front | Off | 设置剔除模式:剔除背面/正面/关闭剔除 |
ZTest | ZTest Less Greater | LEqual | GEqual | Equal | NotEqual | Always | 设置深度测试时使用的函数 |
ZWrite | ZWrite On | Off | 开启/关闭深度写入 |
Blend | Blend SrcFactor DstFactor | 开启并设置混合模式 |
Tags
SubShader的标签(Tags)是一个键值对。这些键值对是SubShader和渲染引擎之间的沟通桥梁,用来告诉Unity渲染引擎:希望怎样以及何时渲染这个对象。
标签类型 | 说明 | 例子 |
Queue | 控制渲染顺序,指定该物体属于哪一个渲染队列,通过这种方式可以保证所有的透明物体可以在所有不透明物体后面被渲染。也可以自定义使用的渲染队列来控制物体的渲染顺序 | Tags { “Queue” = “Transparent” } |
RenderType | 对着色器进行分类 可以被用于着色器替换(Shader Replacement)功能 | Tags { “RenderType”=“Opaque” } |
DisableBatching | 一些SubShader在使用Unity的批处理功能时会出现问题,可以通过该标签来直接指明是否对该SubShader使用批处理 | Tags { “DisableBatching” = “True” } |
ForceNoShadowCasting | 是否会投射阴影 | Tags { “ForceNoShadowCasting” = “True” } |
IgnoreProjector | True: 使用该SubShader的物体将不会受Projector影响。通常用于半透明物体 | Tags { “IgnoreProjector” = “True” } |
CanUseSpriteAtlas | 当该SubShader是用于精灵(sprites)时,将该标签设为"False" | Tags { “CanUseSpriteAtlas” = “False” } |
PreviewType | 指明材质面板将如何预览该材质。默认情况下,材质显示为一个球形,可以把该标签值设为"Plane" "SkyBox"来改变预览类型 | Tags { “PreviewType” = “Plane” } |
注意: 这些标签仅可以在SubShader中声明,不可以在Pass块中声明
Pass语义块
Pass {
[Name] //定义pass名称, 如:Name "MyPassName"
[Tags]
[RenderType]
//Other Code
}
通过[Name] 可以使用UsePass命令来直接使用其他Unity Shader中的Pass
UsePass "MyShader/MYPASSNAME"
Unity 内部会把所有Pass的名称转换为大写字母的表示,在使用UsePass命令时必须使用大写字母的形式
对Pass可以设置渲染状态。SubShader的状态设置适用于Pass,在Pass中还可以使用固定管线的着色器命令
Pass可以设置标签,但标签不同于SubShader的标签
标签类型 | 说明 | 例子 |
LightMode | 定义该Pass在Unity的渲染流水线中的角色 | Tags { “LightMode” = “ForwardBase” } |
RequireOptions | 用于指定当满足某些条件时才渲染该Pass | Tags { “RequireOptions” = “SoftVegetation” } |
Fallback
Unity Shader的形式
表面着色器(Surface Shader)
是Unity自己创造的一种着色器代码类型。当给Unity提供一个表面着色器的时候,仍旧把它转换成对应的顶点/片元着色器。
表面着色器是Unity对顶点/片元着色器的一种抽象
Shader "Custom/Simple Surface Shader"
{
SubShader
{
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input
{
float4 color : COLOR;
};
void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = 1;
}
ENDCG
}
FallBack "Diffuse"
}
表面着色器被定义在SubShader语义块(而非Pass语义块)中的CGPROGRAM和ENDCG之间。
原因:表面着色器不需要开发者关心使用多少个Pass、每个Pass是如何渲染等问题,Unity会做好这些事情。
CGPROGRAM和ENDCG之间的代码是Cg/HLSL编写的,也就是说需要把Cg/HLSL语言嵌套在ShaderLab语言中。
顶点/片元着色器(Vertex/Fragment Shader)
在unity中可以使用Cg/HLSL语言来编写顶点/片元着色器
Shader "Custom/Simple Vertex Fragment Shader"
{
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v : POSITION) : SV_POSITION {
return UnityObjectToClipPos(v);
}
float4 frag() : SV_Target {
return fixed4(1.0,0.0,0.0,1.0);
}
ENDCG
}
}
}
顶点/片元着色器的代码也需要定义在CGPROGRAM和ENDCG之间,但顶点/片元着色器是写在Pass语义块内
原因是:我们需要自己定义每个Pass需要使用的Shader代码,灵活性更高,可以控制渲染的实现细节。
固定函数着色器(Fixed Function Shader)
较旧的设备不支持可编程管线着色器,使用此来渲染,往往只可以完成一些非常简单的效果。
对于固定函数着色器来说,需要完全使用ShaderLab的语法来编写,而非Cg/HLSL
Unity Shader != 真正的Shader
Unity Shader实际上指的是一个ShaderLab文件
- 在传统的shader中,仅可以编写特定类型的Shader,而在Unity Shader中,可以在同一个文件中同时包含需要的顶点着色器和片元着色器代码
- 在传统的shader中,无法设置一些渲染设置,例如是否开启混合、深度测试等,需要开发者在另外的代码中自行设置。而在Unity Shader中,通过一行特定的指令就可以完成这些设置
- 在传统的shader中,需要编写冗长的代码来设置着色器的输入和输出,要小心的处理这些输入输出的位置对应关系等。而在Unity Shader中,只需要在特定语义块中声明一些属性,就可以依靠材质来方便地改变这些属性,而且对于模型自带的数据,也提供了直接访问的方法,不需要开发者自行编码传给着色器。
缺点:由于Unity Shader的高度封装性,可以编写的Shader类型和语法被限制了。对于一些类型的Shader,如曲面细分着色器(Tessellation Shader)、几何着色器(Geometry Shader)等,Unity支持相对较差。另外一些高级的Shader语法Unity Shader也不支持。