最近因工作需要研究了Unity自带的Standard.shader。标注一下,整个Unity内嵌的shader及库文件(cginc文件)都可以在官网下到。这里整理下之前没学到的知识,方便以后查看。
1.shader的构成
整体的Unity Shader结构如下:
Shader "ShaderName"
{
Properties{}
SubShader{
Tags {}
Pass
{
Tags { "LightMode" = "ForwardBase"}
CGPROGRAM
#pragma shader_feature _NORMALMAP
vertOutput vert()
color frag()
ENDCG
}
Pass ....
}
SubShader...
}
其中一个Shader文件可能包含多个SubShader,这是根据硬件支持适配用的。实际执行时,只执行一个SubShader。
但是一个SubShader中的多个Pass是会依次执行的。
2.LightMode
第一部分中Pass块里Tags的LightMode是用来申明当前Pass渲染在光照模型中充当的角色。通常有一下几种:
1.Always:肯定会被执行,但是不渲染任何光源。
2.ForwardBase:渲染环境光(ambient light),主光源,顶点光源及Spherical Harmonics (SH),
3.ForwardAdd: 按照逐像素渲染light,每个光执行一次该Pass
4.Deferred:延迟渲染,用到了g-buffer
5.ShadowCaster:将深度或者阴影信息渲染到shadowmap或者深度纹理中
3.前向渲染(Forward Rendering)
前向渲染中,最亮的光源会按照逐像素渲染,至多有4个点光源按照逐顶点渲染,剩下其他的按照SH方式进行渲染(渲染效果 逐像素>逐顶点>SH,消耗亦然)。有以下几个原则决定光源按照何种方式渲染:
1.Light设置为Not Important肯定会按照逐顶点或者SH渲染
2 . 最亮的Light肯定是按照逐像素渲染的
3 . Light设置为Important按照逐像素渲染
4 .Quanlity Setting中的Pixel Light Count可以规定逐像素光源的上限
SH的优点及缺点:
优点:性能低消耗,没有任何GPU消耗
缺点:由于是在Vertex上进行计算,所以无法应用LIght Cookies和Normal Map。不合适应用在Surface很近的场景(显示会不正常)
一般来说,SH适合用在离光源较远的小物件上。
4.shader中的宏控制
可以通过#pragma multi_compile或者#progma shader_feature来声明宏开关。
使用类似于
#pragma multi_compile FANCY_STUFF_OFF FANCY_STUFF_ON
上面的语句会产生两个shader变体,一个定义了FANCY_STUFF_OFF,另一个定义了FANCY_STUFF_ON。只实际运行的时候,Unity会根据材质或keywords激活并使用其中的一个(如果两个keyword都没有Enable,则默认会使用第一个,即FACY_STUFF_OFF)。另外可以通过”__"的方式定义无宏开关的版本,例如:
#pragma multi_compile __ FOO_ON
执行上,shader_feature和multi_compile会有不同,具体表现在当宏是通过shader_feature定义的时候,如果宏没有被使用过,那么这个shader的变体不会包含在最后的build中。所以一般来说,通过Material设置的宏开关通过shader_feature定义,而通过Code控制的宏开关使用multi_compile来定义
有几点需要注意的地方:
1 .当有多行定义多个宏开关的时候,会产生爆发式数量的shader变体,例如:
#pragma multi_compile A B C
#pragma multi_compile D E
上面的语句总共会产生6个shader变体(A+D, B+D,C+D,A+E,B+E,C+E)
2 .宏开关的数量Unity是有限制的,其中全局256个,local 64个。可以使用shader_feature_loca及multi_compile_local定义local keywords。
3.常见的Unity自带的宏定义
multi_compile_fwdbase:编译所有会在PassType.ForwardBase
中用到的变量。
multi_compile_fwdadd:编译所有会在 PassType.ForwardAdd
中使用的变量。
#pragma skip_variants:用来跳过编译一些已知不会用到的变量,例如:
#pragma multi_compile_fwdadd
#pragma skip_variants POINT POINT_COOKIE
5.StandardShaderGUI
可以在Unity Shader的末尾声明以下语句来自定义Shader在Inspector上的显示
CustomEditor "BumpSpecularGUI"
BumpSpecularGUI继承自ShaderGUI,下面列举下自定义类的声明及一些关键函数
namespace UnityEditor
{
internal class BumpSpecularGUI : ShaderGUI
{
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props)
{
//这里执行自定义Inspector的逻辑
//另外一些材质宏开关也可以在这里控制,例如targetMat.EnableKeyword("REDIFY_ON");
}
public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader)
{
// 这个函数在赋予材质新的Shader时调用
//重写这个函数时,务必调用基类实现
base.AssignNewShaderToMaterial(material, oldShader, newShader);
}
}
}