1. Unity提供的四种shader模板
- Standard Surface Shader:unity自己创建的shader类型,提供了基本的光照处理逻辑,使用者不需要自己实现vertext/fragment着色器,只要指定光照模型,unity会自动编译生成对应的vertext/fragment
- shader Unlit Shader:一个不含光照处理的基础的vertext/fragment shader
- Image Effect Shader:用于屏幕后处理效果的基础模板
- Compute Shader:一种特殊的shader文件,与渲染过程本身无关,主要是为了利用GPU强大的并行计算能力进行快速计算
2. Unity Shader是什么
unity shader就是对渲染流水线中可编程或可配置部分进行实际控制的文件,这里面包括了两个语言的概念
- ShaderLab
unity提供的,用于描述shader文件本身的一种语言,可以理解为这种语言是与Unity引擎交互的,用来通知Unity引擎这个shader是谁、都包含哪些内容 - Cg/HLSL/GLSL
三种用于编写着色器代码的语言,是相对于通过汇编直接与GPU交互而产生的更易于理解和编写的高级语言,可以理解为Unity用这部分语言的代码通知显卡如何真正进行渲染
UnityShader中可以使用这三种语言中的任意一种,只需要在shader文件中指明使用的类型即可,为了描述方便,无论实际使用的是哪种语言,后面的文章中都会笼统称之为“CG代码段”
3. Unity Shader的基础结构
Shader "PathA/PathB/.../ShaderName_1"
{Properties {}SubShader{}SubShader{}...FallBack "PathC/PathD/.../ShaderName_2"
}
名字
字符串指定shader路径和名字
路径部分决定了在材质面板下拉框中shader所在的层级位置
属性
PropertyName("DisplayName", PropertyType) = DefaultValue
- PropertyName:属性名
- DisplayName:面板显示名
- PropertyType:属性类型
- DefaultValue:默认值
常用的属性类型可以分成三大类:
- 基本数值类
包括Int, Float, Range
默认值为一个具体的数值
_IntPro("IntPro", Int) = 1
_FloatPro("FloatPro", Float) = 1.0
_RangePro("RangePro", Range(0.0, 1.0)) = 0.5
- 矢量类
包括Vector 和Color
默认值为一个四维矢量
_VecPro("VecPro", Vector) = (0, 0, 0, 1)
_ColorPro("ColorPro", Color) = (1, 1, 1, 1)
- 纹理类
包括2D, 3D, Cube
默认值为字符串 + 花括号
字符串可以填内置的纹理名(如"White" “Black” "Gray"等)或空字符串
_Tex2D("Tex2D", 2D) = "White"{}
_Tex3D("Tex3D", 3D) = ""{}
_TexCube("TexCube", Cube) = ""{}
注意:Properties{}是ShaderLab的语义,其作用是通知unity引擎当前shader中有这些属性,需要unity在面板上显示出来可以编辑。但是如果在着色器中要使用的话,还需要在CG代码块中声明变量
4. SubShader
一个SubShader可以理解为一个渲染方案
每个UnityShader中可以有多个SubShader,但至少要有一个
Unity会从上往下依次查找当前平台支持的第一个SubShader使用
如果没有找到可以使用的SubShader,会使用FallBack指定的Shader
SubShader的基本结构
SubShader
{Tags{}[Render Setup]Pass{}Pass{}...
}
Tags
当前SubShader的渲染标签,可选项,如果设置了Tag,会应用于当前SubShader下的所有Pass
Tags{"Tag1Name" = "Tag1Value" "Tag2Name" = "Tag2Value" ...}
Tag | 说明 | 示例 |
---|---|---|
Queue | 指定渲染队列 | “Queue” = “Transparent” |
RenderType | 指定着色器类型,其实并没有什么实际作用,可以用来筛选shader,可以自己任意指定 | “RenderType” = “Opaque” |
DisableBatching | 是否关闭合批 | “DisableBatching” = “True” |
ForceNoShadowCasting | 关闭阴影 | “ForceNoShadowCasting” = “True” |
IgnoreProjector | 不受Projector影响,比如在做透明度检测时使用 | “IgnoreProjector” = “True” |
CanUseSpriteAtlas | 用于sprite | “CanUseSpriteAtlas” = “True” |
PreviewType | 材质在面板中的预览类型 | “PreviewType” = “Plane” |
Render Setup
状态设置,可选项,用于对渲染流水线中的各种可配置项进行配置,如果配置了状态,会应用于当前SubShader下的所有Pass,但会被单个Pass中的设置覆盖
配置项 | 说明 | 示例 |
---|---|---|
Cull | 剔除模式 | Cull Back/Cull Front/Cull Off |
ZTest | 深度测试类型 | ZTest Greater/ZTest Less/ZTest Equal/ZTest GEqual/ZTest LEqual/ZTest NotEqual/ZTest Always |
ZWrite | 深度写入 | ZWrite Off/ ZWrite On |
Blend | 透明度混合因子 | Blend SrcAlpha OneMinusSrcAlpha |
BlendOp | 透明度混合操作模式 | BlendOp Add |
5. Pass
一个Pass对应着一个完整的渲染流程,当一个SubShader中配置了N个Pass时,就代表在这种渲染方案中,需要进行N遍渲染才能得到最终的渲染效果
Pass的基本结构
Pass
{Name ""Tags{}[Render Setup][CGCode]
}
Name
可以为Pass指定名字,方便在其他shader中使用之前已经写好的pass
在跨shader使用Pass的时候,需要通过shader名 + “/” + pass名的方式指定要使用的Pass
Pass的名字会被Unity转化为大写,因此在使用的时候,指定的Pass名字也要用大写,如“PathA/PathB/ShaderA/PASS_1”
Tags
当前Pass的渲染标签,Pass可以使用的Tag与SubShader使用的Tag不同,不会覆盖SubShader的标签,只是补充
Tag | 说明 | 示例 |
---|---|---|
LightMode | 指定渲染路径以及在渲染路径中的角色 | “LightMode” = “ForwardBase” |
RequireOptions | 指定满足条件时才使用Pass,值为空格或分开的字符串 | “RequireOptions” = “SoftVegetation” |
Render Setup
状态设置,可选项,对当前Pass的渲染流水线配置做单独针对性设置,会覆盖SubShader中的同类设置。可设置的内容也与上文SubShader中一样。
CG代码段
XXPROGRAM
#pragma vertex vertFunc
#pragma fragment fragFunc
#pragma ...
...fixed4 _ColorPro;
...struct a2v
{...
};
struct v2f
{...
};
...v2f vertFunc(a2v _input)
{v2f _output;...return _output;
}...fixed4 fragFunc(v2f _input)
{...return (1, 1, 1, 1)
}ENDXX
- 通过 XXPROGRAM 开启CG代码段,其中XX代表使用的语言,如CGPROGRAM,HLSLPROGRAM
- 通过ENDXX关闭CG代码段,其中XX代表使用的语言,如ENDCG
- 通过#pragma … 指定编译指令,如 #pragma vertext vertFunc 指定顶点着色器为verFunc,#pragma fragment fragFunc指定片元着色器为fragFunc
- 通过类型和变量名声明变量,如果要声明的变量是使用Properties中定义的属性的值,则声明的变量名需要与Properties中定义的属性名一样,变量类型也需要跟属性的类型对应, 如 fixed4 _ColorPro; 就是声明了 _ColorPro 变量,且使用了上文Properties中定义的_ColorPro属性值
- 通过struct定义自定义结构 实现定义的顶点着色器和片元着色器,如果要加入可选着色器
- 也可以在CG代码段中加入可选着色器,与顶点/片元着色器方式一样
后面章节中会对CG代码段内的内容做更详细的介绍
6. 表面着色器
与普通的顶点/片元着色器不同,表面着色器直接在SubShader中定义(而非Pass中),也就是直接在SubShader中添加CG代码段
表面着色器除了指定着色器类型和名字外,还需要指定使用的光照模型,如 #pragma surface surfaceFunc Lambert 就是制定了表面着色器名字为surfaceFunc,且使用的光照模型为Lambert
表面着色器会被unity自动编译成普通的顶点/片元着色器,因此不需要返回值
SubSader
{Tags{"Queue" = "Transparent"}CGPROGRAM#pragma surface surfaceFunc Lambert;struct Input{float4 color : COLOR;}void surfaceFunc(Input _input, inout SurfaceOutput o){o.Albedo = 1;}ENDCG
}