unity-doc-内置shader手册

基础结构

顶点/片元着色器

  • 基础结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
Shader "MyShaderName" {
Properties {
// 属性
}

// 针对显卡A的SubShader
SubShader {
// 针对所有pass的标签设置
// 可选
Tags {
"LightMode"="ForwardBase"
...
}

// 可选
// RenderState,针对所有的pass
ZWrite Off
....


Pass {
// name 可选
Name "MyPassName1"

// tags,可选,针对该pass
// 设置渲染状态和标签
Tag {
"LightMode"="ForwardBase"
...
}

// 可选
// RenderState,针对该pass
ZWrite Off
....


// 开始CG代码片段
CGPROGRAM
// 函数定义
#pragma vertex vert
#pragma fragment frag

// CG代码

ENDCG
}


// 可选,通过名字使用pass
UsePass "MyPassName2"

// 可选,抓取屏幕存储到纹理中
GrabPass

// 其他需要的Pass
...
}

// 针对其他显卡的SubShader
...

// SubShader都失败后用于回调的Unity Shader
Fallback "VertexLit"
}
  • 顶点着色器和片元着色器之间通信
    顶点着色器的输出结构中,必须包含一个变量,它的语义是SV_POSITION或POSITION。否则,渲染器将无法得到裁剪空间中的顶点坐标,也就无法把顶点渲染到屏幕上。
    顶点着色器是逐顶点调用的,而片元着色器是逐片元调用的。片元着色器实际上是把顶点着色器的输出进行插值后得到的结果。

SubShader

SubShader包装了一个渲染方案,而这个方案是由一个个pass块来执行的。SubShader可以包含多个Pass块,每个Pass块都包含了渲染一个几何体的具体代码。
SuberShader可以写多个,一般写2-3个,即针对目前最流行的一代显卡写一个,针对老旧的显卡写一个。从第一个开始找满足机器的subshader,只会有一个生效。

在SubShader块中设置的渲染状态会应用到该SubShader下的所有pass

在别的SubShader重复使用pass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Shader "AA/Shader1" {
SubShader {
Pass {
Name "PASS1"
Material {
Diffuse(1, 0.7, 0.4, 1)
Ambient(1, 0.7, 0.4, 1)
}
}
}
}

Shader "AA/Shader2" {
SubShader {
UsePass "AA/Shader1/PASS1"
}
}

Pass(通道)

1
Pass {[Name and Tags] [RenderSetUp(RenderState)] [TextureSetup]}

Pass是用来控制被渲染的几何体对象,每一个pass都是一个完整的渲染过程,有多少个就执行多少个。

  • Pass光照渲染规则
    Deffered渲染模式:先找Deffered,没找到就找Forward,最后是VertexLit,不执行Always
    Forward渲染模式:先找Forward,没找到就找VertexLit,如果有Always在前,那么会先执行Always,然后在执行其它有效的pass,如果Always在后,则先执行有效的pass,然后在执行Always
    VertexLit渲染模式:只会找VertexLit的pass,如果有Always在前,那么会先执行Always,然后在执行VertexList的pass,如果Always在后,则先执行VertexList的pass,然后在执行Always

  • Name

1
Name "MyPass"

Properties

  • 参考
1
2
3
4
5
6
7
8
9
10
[PerRendererData]_MainTex("MainTex",2D) = "white"{}
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255

_ColorMask ("Color Mask", Float) = 15

[Toggle]_GrayEnabled("Gray Enabled",int) = 0

Properties

类型 描述 参数
Int 类型:整型
取决于在Cg/HLSL中是用float还是int来声明的,如果定义为float则实际使用的就是浮点数,字义为int会被识别为int类型(去小数点直接取整)
_Int("Int", Int) = 1
Float 类型:浮点数值
Cg/HLSL:可根据需要定义不同的浮点精度
float 32位精度,常用于世界坐标位置以及UV坐标
half 范围[-6W,6W],常用于本地坐标位置,方向等
fixed 范围[-2,2],常用于纹理与颜色等低精度的情况
_Float ("Float", Float ) = 0
Range 类型:数值滑动条
本身还是Float类型,只是通过Range(min,max)来控制滑动条的最小值与最大值
_Slider ("Slider", Range(0, 1)) = 0
Color 类型:颜色属性
Cg/HLSL:float4/half4/fixed4
_Color("Color", Color) = (1,1,1,1)
Vector 类型:四维向量
在Properties中无法定义二维或者三维向量,只能定义四维向量
_Vector ("Vector", Vector) = (0,0,0,0)
2D 类型:2D纹理
Cg/HLSL:sampler2D/sampler2D_half/sampler2D_float
默认值有white、black、gray、bump以及空,空就是white
_MainTex ("Texture", 2D) = "white" {}
2DArray 类型:2D纹理数组
UNITY_DECLARE_TEX2DARRAY(_MainTexArry);
默认值有white、black、gray、bump以及空,空就是white
仅支持DX10、OpenGL3.0、Metal及以上版本
_MainTexArry ("TextureArry", 2DArray) = "white" {}
3D 类型:3D纹理
Cg/HLSL:sampler3D/sampler3D_half/sampler3D_float
_MainTex3D ("Texture", 3D) = "white" {}
Cube 类型:立方体纹理
Cg/HLSL:samplerCUBE/samplerCUBE_half/samplerCUBE_float
_MainCube ("Texture", Cube) = "white" {}
Any 类型:通用纹理
支持2D、3D、Cube.
_MainTex ("Texture", Any) = "white" {}

Attributes

属性 描述
[Header(xxx)] 用于在材质面板中当前属性的上方显示标题xxx,注意只支持英文、数字、空格以及下划线
[HideInInspector] 在材质面板中隐藏此条属性,在不希望暴露某条属性时可以快速将其隐藏
[Space(n)] 使材质面板属性之前有间隔,n为间隔的数值大小
[HDR] 标记为属性为高动态范围
[PowerSlider(3)] 滑条曲率,必须加在range属性前面,用于控制滑动的数值比例
[IntRange] 必须使用在Range属性之上,以使在材质面板中滑动时只能生成整数
[Toggle] 开关,加在数值类型前,可使材质面板中的数值变成开关,0是关,1是开
[ToggleOff] 与Toggle相当,0是开,1是关
[Enum(Off, 0, On, 1)] 数值枚举,可直接在cg中使用此关键字来替代数字.
[KeywordEnum (Enum0, Enum1, xxx)] 关键字枚举,可最多定义8个,需要#pragma multi_compile _ENUM_ENUM0 _ENUM_ENUM1 …来依次声明变体关键字.
[Enum (UnityEngine.Rendering.CullMode)] 内置枚举,可在Enum()内直接调用Unity内部的枚举.
[NoScaleOffset] 只能加在纹理属性前,使其隐藏材质面板中的Tiling和Offset
[Normal] 只能加在纹理属性前,标记此纹理是用来接收法线贴图的,当用户指定了非法线的纹理时会在材质面板上进行警告提示
[Gamma] Float和Vector属性默认情况下不会进行颜色空间转换,可以通过添加[Gamma]来指明此属性为sRGB值
[PerRendererData] 标记当前属性将以材质属性块的形式来自于每个渲染器数据
[MainTexture] 标记当前纹理为主纹理,便于C#通过Material.mainTexture调用
[MainColor]] 标记当前颜色为主颜色,便于C#通过Material.color调用

CG变量

  • 浮点变量
1
2
3
精度最大 float4、float  通常是32位
精度较大 half4、half 通常是16位
精度最小 fixed4、fixed 通常是11位
  • ShaderLab属性类型和CG变量类型的匹配关系
ShaderLab属性类型 CG变量类型
Color、Vector float4、half4、fixed4
Range、Float float、half、fixed
2D sampler2D
Cube samplerCube
3D sampler3D

Semantics

  • 参考
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct appdata
{
float4 vertex:POSITION; // 顶点的本地坐标
uint vid:SV_VertexID; // 顶点的索引id
float3 normal : NORMAL; // 顶点的法线信息
float4 tangent : TANGENT; // 顶点的切线信息
float4 texcoord : TEXCOORD0; // 顶点的UV1信息
float4 texcoord1 : TEXCOORD1; // 顶点的UV2信息
float4 texcoord2 : TEXCOORD2; // 顶点的UV3信息
float4 texcoord3 : TEXCOORD3; // 顶点的UV4信息
fixed4 color : COLOR; // 顶点的颜色信息
};

struct v2f
{
float4 pos:SV_POSITION; // 顶点的齐次裁剪空间下的坐标,默认情况下用POSITION也可以(PS4下不支持),但是为了支持所有平台,所以最好使用SV_POSITION.
float2 uv:TEXCOORD;
fixed4 color:COLOR;
float4 vertex:TEXCOORD1;
};
  • 应用程序到顶点着色器的数据
语义 描述 参考
POSITION 模型空间的顶点 float4 vertex : POSITION;
SV_VertexID 顶点的索引ID uint vid : SV_VertexID;
NORMAL 顶点的索引ID float3 normal : NORMAL;
TANGENT 顶点的法线信息 float4 tangent : TANGENT;
TEXCOORD0 顶点的UV1信息 float4 texcoord : TEXCOORD0;
TEXCOORD1 顶点的UV2信息 float4 texcoord1 : TEXCOORD1;
TEXCOORD2 顶点的UV3信息 float4 texcoord2 : TEXCOORD2;
TEXCOORD3 顶点的UV4信息 float4 texcoord3 : TEXCOORD3;
COLOR 顶点的顶点色信息 fixed4 color : COLOR;
  • 顶点着色器到片段着色器的数据
语义 描述 参考
SV_POSITION 顶点的齐次裁剪空间下的坐标 float4 pos:SV_POSITION;
TEXCOORD0~N 例如TEXCOORD0、TEXCOORD1、TEXCOORD2…等等,主要用于高精度数据
1.OpenGL ES2.0支持最多8个
2.OpenGL ES3.0支持最多16个
float4 texcoord : TEXCOORD0;
VFACE 如果渲染表面朝向摄像机,则Face节点输出正值1,如果远离摄像机,则输出负值-1 float face:VFACE
VPOS 1.当前片断在屏幕上的位置(单位是像素,可除以_ScreenParams.xy来做归一化),此功能仅支持#pragma target 3.0及以上编译指令
2.大部分平台下VPOS返回的是一个四维向量,部分平台是二维向量,所以需要用UNITY_VPOS_TYPE来统一区分.
3.在使用VPOS时,就不能在v2f中定义SV_POSITION,这样会冲突,所以需要把顶点着色器的输入放在()的参数中,并且SV_POSITION添加out.
UNITY_VPOS_TYPE screenPos : VPOS
SV_VertexID 顶点着色器可以接收具有“顶点编号”作为无符号整数的变量,当需要从纹理或ComputeBuffers中获取额外的顶点数据时比较有用,此语义仅支持#pragma target 3.5及以上 uint vid : SV_VertexID
  • 片段着色器输出的数据
语义 描述 参考
SV_Target 默认RenderTarget,也是默认输出的屏幕上的颜色
同时支持SV_Target1、SV_Target2…等等
fixed4 color : SV_Target;
SV_Depth 通过在片断着色器中输出SV_DEPTH语义可以更改像素的深度值,注意此功能相对会消耗性能,在没有特别需求的情况下尽量不要用 fixed depth : SV_Depth;

一个语义可以使用的寄存器只能处理4个浮点值(float)。因此,如果我们想要定义矩阵类型,如float3X4等变量就需要使用更多的空间。一种方法是,把这些变量拆分成多个变量,例如对于float4X4矩阵类型,我们可以拆分成4个float4类型的变量,每个变量存储了矩阵中的一行数据。

Tags

  • 参考
1
2
3
4
5
6
7
8
9
SubShader
{
Tags { "RenderType"="Opaque" }

Pass
{
Tags {"LightMode" = "ForwardBase"}
}
}

Queue

渲染队列直接影响性能中的重复绘制,合理的队列可极大的提升渲染效率。
渲染队列数<=2500的对象都被认为是不透明的物体(从前往后渲染),>2500的被认为是半透明物体(从后往前渲染)。
"Queue" = "Geometry+1" 可通过在值后加数字的方式来改变队列。

仅可以在SubShader中声明

1
2
3
4
Tags {
"Queue" = "Gemotry"
"Queue" = "Gemotry+500"
}
变量 描述
Background 1000 这个队列最先渲染,它被用于skyboxes等
Geometry 2000 这个是默认的渲染队列,它被用于绝大多数对象。不透明几何体使用该队列
AlphaTest 2450 通道检查的几何体使用该队列。它和Geometry队列不同,对于在所有立体物体绘制后渲染的通道检查的对象,它更有效
Transparent 3000 该渲染队列在Geometry和AlohaTest队列后被渲染,任何通道混合的(也就是说,那些不写入深度缓存的Shaders)对象使用该队列,例如玻璃和例子效果
Overlay 4000 该渲染队列是为了覆盖物效果服务的。任何最后被渲染的对象使用该队列,例如镜头光晕

LightMode

设置光照模型

仅可以在pass中声明

1
2
3
Tags {
"LightMode"="Always"
}
变量 描述
ForwardBase 用于正向渲染,环境主要方向灯和点光/SH等的应用(修饰第1个Pixel平行光源
ForwardAdd 用于正向渲染,附加的像素光被应用,每个光照一个pass(必须和ForwardBase一起使用,修饰除ForwardBase修饰的Pixel平行光源外的Pixel光源
Deferred 用于延迟渲染。
ShadowCaster 将物体当做阴影产生者来渲染,对自身也会有产生阴影
ShadowCollector 正向渲染对象的路径,将对象阴影收集到屏幕空间缓冲区中
MotionVectors 运动矢量。
PrepassBase 旧版,用于延迟光照,渲染法线/镜面指数
PrepassFinal 旧版,用于延迟光照,通过结合纹理,光照和自发光渲染最终验收
Vertex 旧版,用于顶点光照渲染,当物体没有光照映射(Light Map)时,所有顶点光照被应用
VertexLMRGBM 旧版,用于顶点光照渲染,当物体有光照映射(Light Map)的时候,使用顶点光照渲染,当物体有light map才会被渲染
VertexLM 旧版,用于顶点光照渲染,当物体有光照映射(Light Map)的时候使用顶点光照渲染
Always 总是渲染。没有光照应用
  • 对于只有ForwardBase的Pass的Shader,ForwardBase的Pass内的unity_LightPos[X,Y,Z]0和unity_LightColor[4]只含有点光源,Pixel光源的排名会更靠前。
  • 对于即含有ForwardBase又含有ForwardAdd的Pass的Shader来说,Unity会在ForwardBase Pase内的 unity_4LightPos[X,Y,Z]0中放置Vertex点光源。而且 unity_4LightPos[X,Y,Z]0的数据都是世界坐标而不是相机空间坐标。
  • 在Forward的Pass内,unity_4LightPos[X,Y,Z]0组合和对应颜色数据只包含Vertex点光源,不包含任何Pixel光源或者平行光

RenderType

用来区别这个Shader要渲染的对象是属于什么类别的,可以想像成是我们把各种不同的物体按我们需要的类型来进行分类一样。
当然也可以根据需要改成自定义的名称,这样并不会影响到Shader的效果。
此Tag多用于摄像机的替换材质功能(Camera.SetReplacementShader)。

仅可以在SubShader中声明

1
2
3
Tags {
"RenderType"="Opaque"
}
变量 描述
Opaque 不透明。最常用的一种。(Normal, Selfllluminated, Reflective, terrain shaders(地形的泥土纹理等))
Transparent 半透明物体。(Transparent, Particle, Font, terrain additive pass shaders(地形的草地纹理等))
TransparentCutout 透明遮罩shader。(Transparent Cutout, two pass vegetation shaders)
Background 背景。(天空shader)
Overlay 叠加的。(GUITexture, Halo, Flare shaders).
TreeOpaque 树干的不透明(terrain engine tree bark(地形的树干等))
TreeTransparentCutout 树的不透明遮罩(terrain engine tree leaves(地形的树叶等))
TreeBillboard 树的布告板,一直对着摄像机(terrain engine billboarded trees(面片树等))
Grass 草(terrain engine grass(地形的草)
GrassBillboard 草的布告板(terrain engine billboarded grass(面片草))

DisableBatching

关闭批处理。在利用Shader在模型的顶点本地坐标下做一些位移动画,而当此模型有批处理时会出现效果不正确的情况,这是因为批处理会将所有模型转换为世界坐标空间,因此“本地坐标空间”将丢失。

仅可以在SubShader中声明

1
2
3
4
5
Tags {
"DisableBatching"="True" // 禁用批处理
"DisableBatching" = "False" // 不禁用
"DisableBatching" = "LODFading" // 仅当LOD激活时禁用批处理。
}

ForceNoShadowCasting

设置当前物体是否会投射阴影

仅可以在SubShader中声明

1
2
3
4
Tags {
"ForceNoShadowCasting"="True" // 强制关闭阴影投射。
"ForceNoShadowCasting" = "False" // 不关闭阴影投射。
}

IgnoreProjector

设置忽略投影的影响,通常用于半透明物体

仅可以在SubShader中声明

1
2
3
4
Tags {
"IgnoreProjector"="True" // 不受投影器影响。
"IgnoreProjector" = "False" // 受投影器影响。
}

CanUseSpriteAtlas

是否可用于打包图集的精灵

仅可以在SubShader中声明

1
2
3
4
Tags {
"CanUseSpriteAtlas" = "True" // 支持精灵打包图集。
"CanUseSpriteAtlas"="False" // 不支持精灵打包图集。
}

PreviewType

指明材质面板将如何预览该材质。默认情况下,材质将显示为一个球形,我们可以通过标签的值设为PlaneSkyBox来改变预览

仅可以在SubShader中声明

1
2
3
4
Tags {
"PreviewType"="Plane" // 平面
"PreviewType" = "Skybox" // 天空盒
}

PerformanceChecks

是否对shader在当前平台进行性能检测,并在材质面板进行警告提示

仅可以在SubShader中声明

1
2
3
4
Tags {
"PerformanceChecks" = "True" // 开启性能检测提示
"PerformanceChecks" = "False" // 关闭性能检测提示
}

RequireOptions

用于指定当满足某些条件时才渲染该pass,它的值是一个空格分隔的字符串。目前,unity支持选项有SoftVegetation

仅可以在pass中声明

1
2
3
Tags {
"RequireOptions"="SoftVegetation"
}

RenderState

renderState写在Pass内,用于设定各种状态,例如能打开alpha混合,能使用雾等等

Cull

设置多边形剔除模式,控制多边形的哪一面应该被剔除(即不绘制),默认是Back

1
2
3
4
5
Pass {
...
Cull Front
...
}
说明
Back 表示剔除背面,也就是显示正面,这也是最常用的设置。
Front 表示剔除前面,只显示背面。
Off 关闭剔除,正反面都渲染

Stencil

模板缓冲区(StencilBuffer)可以为屏幕上的每个像素点保存一个无符号整数值,这个值的具体意义视程序的具体应用而定.在渲染的过程中,可以用这个值与一个预先设定的参考值相比较,根据比较的结果来决定是否更新相应的像素点的颜色值.这个比较的过程被称为模板测试.
将StencilBuffer的值与ReadMask与运算,然后与Ref值进行Comp比较,结果为true时进行Pass操作,否则进行Fail操作,操作值写入StencilBuffer前先与WriteMask与运算.
模版缓冲中的默认值为:0
公式:(Ref & ReadMask) Comp (StencilBufferValue & ReadMask)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Stencil
{
// 设定的参考值,这个值将用来与模板缓冲中的值进行比较
// 取值范围位为0-255的整数.
Ref [_Stencil]

// ReadMask的值将和Ref的值以及模板缓冲中的值进行按位与(&)操作,
// 取值范围也是0-255的整数,默认值为255(二进制位11111111),即读取的时候不对Ref的值和模板缓冲中的值产生修改,读取的还是原始值.
ReadMask [_StencilReadMask]

// WriteMask的值是当写入模板缓冲时进行的按位与操作
// 取值范围是0-255的整数,默认值也是255,即不做任何修改.
WriteMask [_StencilWriteMask]

// 定义Ref与模板缓冲中的值比较的操作函数,默认值为always.
Comp [_StencilComp] ((UnityEngine.Rendering.CompareFunction))

// 当模板测试(和深度测试)通过时,则根据(stencilOperation值)对模板缓冲值进行处理,默认值为keep.
Pass [_StencilOp] (UnityEngine.Rendering.StencilOp)

// 当模板测试(和深度测试)失败时,则根据(stencilOperation值)对模板缓冲值进行处理,默认值为keep.
Fail [_Fail]

// 当模板测试通过而深度测试失败时,则根据(stencilOperation值)对模板缓冲值进行处理,默认值为keep.
ZFail [_ZFail]
}
  • Comp选项
选项 描述
Less 相当于“<”操作,即仅当左边<右边,模板测试通过,渲染像素.
Greater 相当于“>”操作,即仅当左边>右边,模板测试通过,渲染像素.
Lequal 相当于“<=”操作,即仅当左边<=右边,模板测试通过,渲染像素.
Gequal 相当于“>=”操作,即仅当左边>=右边,模板测试通过,渲染像素.
Equal 相当于“=”操作,即仅当左边=右边,模板测试通过,渲染像素.
NotEqual 相当于“!=”操作,即仅当左边!=右边,模板测试通过,渲染像素.
Always 不管公式两边为何值,模板测试总是通过,渲染像素.
Never 不管公式两边为何值,模板测试总是失败 ,像素被抛弃.
  • Pass选项
选项 描述
Keep 保留当前缓冲中的内容,即stencilBufferValue不变.
Zero 将0写入缓冲,即stencilBufferValue值变为0.
Replace 将参考值写入缓冲,即将referenceValue赋值给stencilBufferValue.
IncrSat 将当前模板缓冲值加1,如果stencilBufferValue超过255了,那么保留为255,即不大于255.
DecrSat 将当前模板缓冲值减1,如果stencilBufferValue超过为0,那么保留为0,即不小于0.
NotEqual 相当于“!=”操作,即仅当左边!=右边,模板测试通过,渲染像素.
Invert 将当前模板缓冲值(stencilBufferValue)按位取反.
IncrWrap 当前缓冲的值加1,如果缓冲值超过255了,那么变成0,(然后继续自增).
DecrWrap 当前缓冲的值减1,如果缓冲值已经为0,那么变成255,(然后继续自减).

ZTest

深度测试,拿当前像素的深度值与深度缓冲中的深度值进行比较,默认值为LEqual。可通过在属性中添加枚举UnityEngine.Rendering.CompareFunction

说明
Less 小于,表示如果当前像素的深度值小于深度缓冲中的深度值,则通过,以下类同。
Greater 大于。
Lequal 小于等于。
Gequal 大于等于。
Equal 等于。
NotEqual 不等于。
Never 永远不通过.
Always 永远通过。

ZTest[unity_GUIZTestMode]用于UI材质中,此值默认为LEqual,仅当UI中Canvas模式为Overlay时,值为Always.

Alpha Test

透明度测试设置,只要当透明度与比较的值为true的才渲染

1
2
3
4
5
6
Properties{
_CutOut ("CutOut", range(0, 1)) = 0.4
}

// 当贴图该位置的像素的透明度小于_CutOut的时候,都像素不会渲染,而变成渲染物体遮挡的物体
AlphaTest Less[_CutOut]
说明
Less 低于该值得像素都不渲染
Greater
LEqual
GEqual
Equal
NotQual
Always

ZWrite

控制是否将对象的像素写入深入缓存(默认是ON)。如果要绘制实心对象,则使其处于on状态,如果要绘制半透明效果,则切换至off。

1
2
3
4
5
Pass {
...
ZWrite Off
...
}
说明
On 记录深度
Off 不记录此深度,通常用于半透明物体

ZClip

设置GPU的深度剪辑模式以此来决定如何处理近平面和远平面之外的片元深度.
False表示将GPU的深度剪辑模式设置为Clmap,这对于模板阴影渲染很有用,这意味着当几何体超出远平面时不需要特殊处理,从而减少渲染操作。但是,它可能会导致不正确的Z排序。

1
2
3
4
5
Pass {
...
ZClip False
...
}

Offset

设置深度偏移, 当2个物体的深度一样的时候,进行的偏移量,默认值为0,0

offset = (m * factor) + (r * units)

  • m:指多边形的深度斜率(在光栅化阶段计算得出)中的最大值,多边形越是与近裁剪面平行,m值就越接近0。
  • r:表示能产生在窗口坐标系的深度值中可分辨的差异的最小值,r是由具体实现OpenGL的平台指定的一个常量。
  • 结论:一个大于0的offset会把模型推远,一个小于0的offset会把模型拉近。
1
2
3
4
5
Pass {
...
Offset 0,0
...
}

ColorMask

设置关闭颜色通道的渲染,默认值为:RGBA,表示写入RGBA四个通道。

说明
RGB
A
O 将关闭所有颜色通道的渲染
R,G,B,A任意组合 R:表示除了红色,GBA的效果都过滤掉

Blend

设置混合模式
颜色混合,源颜色与目标颜色以给定的公式进行混合出最终的新颜色,源颜色*SrcFactor + 目标颜色*DstFactor
源颜色:当前Shader计算出的颜色。
目标颜色:已经存在颜色缓存中的颜色。默认值为Blend Off,即表示关闭混合。
在混合时可以针对某个RT做混合,比如Blend 3 One One,就是对RenderTarget3做混合。
可在Properties中添加这个实现下拉列表选择:[Enum(UnityEngine.Rendering.BlendMode)]

1
2
Blend SrcFactor DstFactor // SrcFactor为源颜色,DstFactor为目标颜色,将两者按Op中指定的操作进行混合。
Blend SrcFactor DstFactor, SrcFactorA DstFactorA // 对RGB和A通道分别做混合操作。
1
2
3
4
5
Pass {
...
Blend SrcAlpha OneMinusSrcAlpha
...
}
语义 描述
Blend Off 关闭混合
Blend SrcFactor DstFactor 开启混合,并设置混合因子(该片元参数的颜色)会乘以SrcFactor,而且模板颜色(已经存在于颜色缓冲区的颜色)会乘以DstFactor,然后把两者相加后再存入颜色缓冲中
Blend SrcFactor DstFactor, SrcFactorA DstFactorA 和上面几乎一样,只是使用不同的因子来混合透明通道
参数 描述
One 值为1,使用此设置来让源或目标颜色完全通过
Zero 值为0,使用此设置来删除源或目标值
SrcColor 因子为源颜色值。当用于混合RGB的混合等式时,使用SrcColor的RGB分量作为混合因子,当用于混合A的混合等式时,使用SrcColor的A分量作为混合因子
SrcAlpha 因子为源颜色的透明度值(A通道)
DstColor 因子为源颜色值。当用于混合RGB通道的混合等式时,使用DstColor的RGB分量作为混合因子;当用于混合A通道的混合等式时,使用DstColor的A分量作为混合因子。
OneMinusSrcColor 因子为(1-源颜色)
OneMinusSrcAlpha 因子为(1-源颜色的透明度值)
OneMinusDstColor 因子为(1-目标颜色)
OneMinusDstAlpha 因子为(1-目标颜色的透明度)

BlendOp

混合时的操作运算符,默认值为Add(加法操作)。

1
2
BlendOp Op // 混合时的操作运算符,默认值为Add(加法操作)。
BlendOp OpColor, OpAlpha // 对RGB和A通道分别指定混合运算符。
参数 描述
Add 将混合后的源颜色和目的颜色相加。默认的混合操作。
Sub 用混合后的源颜色减去混合后的目的颜色。
RevSub 用混合后的目的颜色减去混合后的源颜色。
Min 使用源颜色和目的颜色中较小的值,是逐分量比较的。
Max 使用源颜色和目的颜色中较大的值,是逐分量比较的。

AlphaToMask

是否启用GPU上的alpha-to-coverage模式(当开启MSAA时,减少AlphaTest产生的过度锯齿感).

1
2
AlphaToMask On
AlphaToMask Off

Conservative

是否启用保守光栅化(指GPU对被三角形部分覆盖的像素进行光栅化,无论覆盖范围如何,这会导致更多片元着色器的调用).

1
2
Conservative True
Conservative False

Fog {}

1
2
3
4
5
fog {
Mode Linear
Color(1,1,1,1)
Density 1000
}
变量 说明
Mode Off | Globa | Linear | Exp | Exp2 设置雾的模式
Color 颜色值 设置雾的颜色
Density 密度值 对exp有效
Range 范围 对linear模式有效

Color

设置当顶点光照关闭时所使用的颜色

1
2
3
4
5
Pass {
...
Color 颜色值
...
}

SeparateSpecular

开启或关闭顶点光照相关的平行高光颜色

说明
On
Off

ColorMaterial

当计算顶点颜色时使用的顶点颜色

说明
AmbientAndDiffuse
Emission

Material {}

定义一个使用顶点光照管线的材质

  • Lighting

开启或关闭顶点光照

说明
On 开启
Off 关闭

SetTexture

设置纹理,主要用在固定管线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlendTex ("Alpha Blended (RGBA)", 2D) = "white" {}
}

SubShader {
Pass {
// 应用基础纹理
SetTexture[_MainTex] {
combine texture
}

// 使用lerp操作符混合alpha纹理
SetTexture[_BlendTex] {
combine texture lerp(texture) previous
}
}
}

Other

LOD

Shader LOD,可利用脚本来控制LOD级别,通常用于不同配置显示不同的SubShader。

1
2
3
4
5
6
SubShader
{
...
LOD 600
...
}

Fallback

备用,当Shader中没有任何SubShader可执行时,则执行FallBack。默认值为Off,表示没有备胎。

1
2
3
4
5
6
SubShader
{
...
}

FallBack "Diffuse"

CustomEditor

自定义材质面板,name为自定义的脚本名称。可利用此功能对材质面板进行个性化自定义。

1
2
3
4
5
6
SubShader
{
...
}

CustomEditor "LegacyIlluminShaderGUI"

Name

给当前Pass指定名称,以便利用UsePass进行调用。

UsePass

调用其它Shader中的Pass,注意Pass的名称要全部大写!Shader的路径也要写全,以便能找到具体是哪个Shader的哪个Pass。另外加了UsePass后,也要注意相应的Properties要自行添加。

GrabPass

GrabPass{} 抓取当前屏幕存储到_GrabTexture中,每个有此命令的Shader都会每帧执行。
GrabPass { “TextureName” } 抓取当前屏幕存储到自定义的TextureName中,每帧中只有第一个拥有此命令的Shader执行一次。
GrabPass也支持Name与Tags。

Category{}

定义一组所有SubShader共享的命令,位于SubShader外面。

#Pragma

target

Shader编绎目标级别,默认值为2.5
可以通过#if (SHADER_TARGET < 30)来做分支判断

1
#pragma target 2.0
版本 描述
2.0
2.5 derivatives
3.0 2.5 + interpolators10 + samplelod + fragcoord
3.5 3.0 + interpolators15 + mrt4 + integers + 2darray + instancing
4.0 3.5 + geometry
4.5 3.5 + compute + randomwrite
4.6 4.0 + cubearray + tesshw + tessellation
5.0 4.0 + compute + randomwrite + tesshw + tessellation

require

表明shader需要的特性功能

特效 描述
interpolators10 至少支持10个插值器(从顶点到片断)
interpolators15 至少支持15个插值器(从顶点到片断)
interpolators32 至少支持32个插值器(从顶点到片断)
mrt4 至少支持4个Multiple Render Targets
mrt8 至少支持8个Multiple Render Targets
derivatives 片断着色器支持偏导函数(ddx/ddy)
samplelod 纹理LOD采样
fragcoord 将像素的位置(XY为屏幕上的坐标,ZW为齐次裁剪空间下的深度)传入到片断着色器中
integers 支持真正的整数类型,包括位/移位操作
2darray 2D纹理数组
cubearray Cubemap纹理数组
instancing GPU实例化
geometry 几何着色器
compute Compute Shader
randomwrite 可以编写任意位置的一些纹理和缓冲区 (UAV,unordered access views)
tesshw GPU支持硬件的tessellation
tessellation Tessellation hull/domain Shader
msaatex 能够访问多采样纹理
framebufferfetch 主要用于在延迟渲染中减少采样的带宽消耗

shader_feature

变体声明,常用于不需要程序控制开关的关键字,在编缉器的材质上设置,打包时会自动过滤

1
#pragma shader_feature _ _MASKENABLED_ON

shader_feature_local

声明本地变体(shader_feature),unity2019才支持的功能,每个Shader最多可以有64个本地变体,不占用全局变体的数量.

multi_compile

变体声明,在打包时会把所有变体都打包进去,这是它与feature的区别.
定义关键字时如果加两个下划线,则表示定义一个空的变体,主要目的是为了节省关键字.
当使用shader变体时,记住在unity中全局关键字最多只有256个,而且在内部已经用了60个了,所以记得不要超标了.

1
#pragma multi_compile _ _DISSOLVEENABLED_ON

multi_compile_local

声明本地变体(multi_compile),unity2019才支持的功能,每个Shader最多可以有64个本地变体,不占用全局变体的数量.

skip_variants

剔除指定的变体,可同时剔除多个

1
#pragma skip_variants DIRECTIONAL DIRECTIONAL_COOKIE POINT_COOKIE

multi_compile_fog

描述
FOG_EXP
FOG_EXP2
FOG_LINEAR

multi_compile_fwdbase

定义在LightMode = ForwardBase的Pass中,在此Pass中仅只持一个平行灯(逐像素)以及其它逐顶点灯和SH当照.这个指令的作用是一次性生成Unity在ForwardBase中需要的各种内置宏.

描述
DIRECTIONAL 主平行灯下的效果开启,fowwardBase下必开宏
DIRLIGHTMAP_COMBINED 烘焙界面中的DirecitonalMode设置为Directional
DYNAMICLIGHTMAP_ON RealtimeGI是否开启
LIGHTMAP_ON 当对象标记为LightMap Static并且场景烘焙后开启
LIGHTMAP_SHADOW_MIXING 当灯光设置为Mixed,光照烘焙模式设置为Subtractive或者shadowMask时开启,Baked Indirect情况下无效
LIGHTPROBE_SH 开启光照探针,动态物体会受到LightProbe的影响,静态物体与此不相关
SHADOWS_SCREEN 在硬件支持屏幕阴影的情况下,同时处理阴影的距离范围内时开启
SHADOWS_SHADOWMASK 当灯光设置为Mixed,光照烘焙模式设置为shadowMask时开启
VERTEXLIGHT_ON 是否受到逐顶点的照明

multi_compile_fwdadd

定义在LightMode=ForwardAdd的Pass中,在此Pass中用来计算其它的逐像素光照.而此指令的作用是一次性生成Unity在ForwardAdd中需要的各种内置宏.

描述
DIRECTIONAL 判断当前灯是否为平行灯.
DIRECTIONAL_COOKIE 判断当前灯是否为Cookie平行灯
POINT 判断当前灯是否为点灯
POINT_COOKIE 判断当前灯是否为Cookie点灯
SPOT 判断当前灯是否为聚光灯

multi_compile_shadowcaster

定义在LightMode=ShadowCaster的Pass中,会自动生成两个宏:

描述
SHADOWS_DEPTH 用于生成直线光和聚光灯阴影.
SHADOW_CUBE 用于生成点光源阴影.

enable_d3d11_debug_symbols

开启d3d11调试,加此命令后相关的名称与代码不会被剔除,便于在调试工具中进行查看分析

shader_feature EDITOR_VISUALIZATION

开启Material Validation,Scene视图中的模式,用于查看超出范围的像素颜色

fragmentoption ARB_precision_hint_fastest

最快的,意思就是会用低精度(一般是指fp16),以提升片段着色器的运行速度,减少时间.

only_renderers

仅编译指定平台的Shader

描述
d3d11 Direct3D 11/12
glcore OpenGL 3.x/4.x
gles OpenGL ES 2.0
gles3 OpenGL ES 3.x
metal iOS/Mac Metal
vulkan Vulkan
d3d11_9x Direct3D 11 9.x feature level, as commonly used on WSA platforms
xboxone Xbox One
ps4 PlayStation 4
psp2 PlayStation Vita
n3ds Nintendo 3DS
wiiu Nintendo Wii U

exclude_renderers

剔除掉指定平台的相关代码

描述
d3d11 Direct3D 11/12
glcore OpenGL 3.x/4.x
gles OpenGL ES 2.0
gles3 OpenGL ES 3.x
metal iOS/Mac Metal
vulkan Vulkan
d3d11_9x Direct3D 11 9.x feature level, as commonly used on WSA platforms
xboxone Xbox One
ps4 PlayStation 4
psp2 PlayStation Vita
n3ds Nintendo 3DS
wiiu Nintendo Wii U

other#

用法 描述
#define NAME 定义一个叫NAME的字段,在CG代码中可以通过#if defined(NAME)来判断走不同的分支。
#define NAME 1 定义一个叫NAME的字段并且它的值为1.
可以通过#if defined(NAME)来判断走不同的分支。
可以通过#if NAME来判断走不同的分支。(此时值为非0时才有效,为0时不走此分支)
还可以直接通过NAME来得到它的值,比如上面的1。
#error xxx 多用于分支的判断中,利用此语句可直接输出一条报错信息,内容为xxx

Macro

Target Platform

描述
SHADER_API_D3D11 Direct3D 11
SHADER_API_GLCORE 桌面OpenGL核心(GL3/4)
SHADER_API_GLES OpenGl ES 2.0
SHADER_API_GLES3 OpenGl ES 3.0/3.1
SHADER_API_METAL IOS/Mac Metal
SHADER_API_VULKAN Vulkan
SHADER_API_D3D11_9X IOS/Mac Metal
SHADER_API_PS4 PS4平台,SHADER_API_PSSL同时也会被定义
SHADER_API_XBOXONE Xbox One
SHADER_API_MOBILE 所有移动平台(GLES/GLES3/METAL)

Shader Target Model

1
#if SHADER_TARGET < 30 // 对应于#pragma target的值,2.0就是20,3.0就是30
描述
SHADER_TARGET shader 目标平台

Unity Version

1
#if UNITY_VERSION >= 500 // Unity版本号判断,500表示5.0.0
描述
UNITY_VERSION unity的版本号

Light

描述
UNITY_SHOULD_SAMPLE_SH 是否进行计算SH(光照探针与顶点着色)
-当静态与动态Lightmap启用时,此项不激活.
-当静态与动态Lightmap没有启用时,此项激活.
-除ForwardBase其它Pass都不激活,每个Pass需要指定UNITY_PASS_FORWARDADD、UNITY_PASS_SHADOWCASTER等.
UNITY_SAMPLE_FULL_SH_PER_PIXEL 光照贴图uv和来自SHL2的环境颜色在顶点和像素内插器中共享,在启用静态lightmap和LIGHTPROBE_SH时,在像素着色器中执行完整的SH计算。
HANDLE_SHADOWS_BLENDING_IN_GI 当同时定义了SHADOWS_SCREEN与LIGHTMAP_ON时开启.
UNITY_SHADOW_COORDS(N) 定义一个float4类型的变量_ShadowCoord,语义为第N个TEXCOORD.
V2F_SHADOW_CASTER; 用于”LightMode” = “ShadowCaster”中,相当于定义了float4 pos:SV_POSITION.

Platform

描述
UNITY_UV_STARTS_AT_TOP 一般此判断当前平台是DX(UV原点在左上角)还是OpenGL(UV原点在左下角)
UNITY_NO_SCREENSPACE_SHADOWS 定义移动平台不进行Cascaded ScreenSpace Shadow.

UI

描述
UNITY_UI_CLIP_RECT 当父级物体有Rect Mask 2D组件时激活.
需要先手动定义此变体#pragma multi_compile _ UNITY_UI_CLIP_RECT
同时需要声明:_ClipRect(一个四维向量,四个分量分别表示RectMask2D的左下角点的xy坐标与右上角点的xy坐标.)
UnityGet2DClipping (float2 position, float4 clipRect)即可实现遮罩.

Other

描述
UNITY_SHADER_NO_UPGRADE 另Shader不自动更新API,只需把语句用注释的形式写在shader中任意位置即可.

Transformations

变量 描述
UNITY_MATRIX_M 模型变换矩阵,模型空间>>世界空间
UNITY_MATRIX_I_M 模型变换逆矩阵,世界空间>>模型空间
UNITY_MATRIX_V 视图变换矩阵,世界空间>>相机空间
UNITY_MATRIX_I_V 视图变换逆矩阵,相机空间>>世界空间
UNITY_MATRIX_P 投影变换矩阵,相机空间>>投影空间
UNITY_MATRIX_I_P 投影变换逆矩阵,投影空间>>相机空间
UNITY_MATRIX_VP 视图投影变换矩阵,世界空间>>投影空间
UNITY_MATRIX_I_VP 视图投影变换逆矩阵,投影空间>>世界空间
UNITY_MATRIX_MV 模型视图变换矩阵,模型空间>>相机空间
UNITY_MATRIX_T_MV 模型视图变换转置矩阵,transpose(UNITY_MATRIX_MV)
UNITY_MATRIX_IT_MV 模型视图变换转置逆矩阵,transpose(mul(UNITY_MATRIX_I_M, UNITY_MATRIX_I_V))
UNITY_MATRIX_MVP 模型视图投影变换矩阵,模型空间>>投影空间
unity_WorldToCamera 世界空间到视图空间的矩阵
unity_CameraToWorld 视图空间到世界空间的矩阵,UNITY_MATRIX_V
unity_ObjectToWorld 视图空间到世界空间的矩阵,UNITY_MATRIX_I_V
unity_ObjectToWorld 模型变换矩阵,UNITY_MATRIX_M
unity_WorldToObject 模型变换逆矩阵,UNITY_MATRIX_I_M

方法

方法 描述
UnityObjectToClipPos(v.vertex) 将模型空间下的顶点转换到齐次裁剪空间
UnityObjectToWorldNormal(v.normal) 将模型空间下的法线转换到世界空间(已归一化)
UnityObjectToWorldDir (v.tangent) 将模型空间下的法线转换到世界空间(已归一化)
UnityWorldSpaceLightDir (i.worldPos) 世界空间下顶点到灯光方向的向量(未归一化)
UnityWorldSpaceViewDir (i.worldPos) 世界空间下顶点到视线方向的向量(未归一化)
ComputeScreenPos(float4 pos) pos为裁剪空间下的坐标位置,返回的是某个投影点下的屏幕坐标位置
由于这个函数返回的坐标值并未除以齐次坐标,所以如果直接使用函数的返回值的话,需要使用:tex2Dproj(_ScreenTexture, uv.xyw);
也可以自己处理其次坐标,使用:tex2D(_ScreenTexture, uv.xy / uv.w);

基础变化矩阵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 平移矩阵
float4x4 M_translate = float4x4(
1, 0, 0, T.x,
0, 1, 0, T.y,
0, 0, 1, T.z,
0, 0, 0, 1);

// 缩放矩阵
float4x4 M_scale = float4x4(
S.x, 0, 0, 0,
0, S.y, 0, 0,
0, 0, S.z, 0,
0, 0, 0, 1);

// 旋转矩阵(x轴)
float4x4 M_rotationX = float4x4(
1, 0, 0, 0,
0, cos(θ), -sin(θ), 0,
0, sin(θ), cos(θ), 0,
0, 0, 0, 1);

// 旋转矩阵(y轴)
float4x4 M_rotationY = float4x4(
cos(θ), 0, sin(θ), 0,
0, 1, 0, 0,
-sin(θ), 0, cos(θ), 0,
0, 0, 0, 1);

// 旋转矩阵(z轴)
float4x4 M_rotationZ = float4x4(
cos(θ), -sin(θ), 0, 0,
sin(θ), cos(θ), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);

// 变换规则
// 将P点从A空间变换到B空间
P_B = M_AB * P_A
= (M_BA)^-1 * P_A
= (M_BA)^T * P_A)

BuildIn

Time

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 时间,主要用于在Shader做动画,类型:float4
// x = t/20
// y = t
// z = t*2
// w = t*3
_Time

// t是时间的正弦值,返回值(-1~1):
// x = t/8
// y = t/4
// z = t/2
// w = t
_SinTime

// t是时间的余弦值,返回值(-1~1):
// x = t/8
// y = t/4
// z = t/2
// w = t
_CosTime

// dt是时间增量,smoothDt是平滑时间
// x = dt
// y = 1/dt
// z = smoothDt
// z = 1/smoothDt
unity_DeltaTime

Light

FowardBase 和 ForwardAdd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 主平行灯的颜色值
// rgb = 颜色x亮度
// a = 亮度
_LightColor0

// 平行灯: (xyz=位置,z=0)),已归一化
// 其它类型灯: (xyz=位置,z=1)
_WorldSpaceLightPos0

// 从世界空间转换到灯光空间下,等同于旧版的_LightMatrix0.
unity_WorldToLight

// 返回顶点到灯光的向量
float3 UnityWorldSpaceLightDir( float3 worldPos )

Camera

1
2
3
4
5
6
7
8
9
10
11
12
// 主相机的世界坐标位置,类型:float3
_WorldSpaceCameraPos

// 世界空间下的相机方向(顶点到主相机),类型:float3
UnityWorldSpaceViewDir(i.worldPos)


// 摄像机深度图
// 1.在脚本中开启相机的深度:Camera.main.depthTextureMode = DepthTextureMode.Depth;
// 2.sampler2D_float _CameraDepthTexture;
// 3.float depth = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
_CameraDepthTexture

Screen

1
2
3
4
5
6
7
8
9
10
11
// 屏幕的相关参数,单位为像素。
// x表示屏幕的宽度
// y表示屏幕的高度
// z表示1+1/屏幕宽度
// w表示1+1/屏幕高度
_ScreenParams

// pos为裁剪空间下的坐标位置,返回的是某个投影点下的屏幕坐标位置
// 由于这个函数返回的坐标值并未除以齐次坐标,所以如果直接使用函数的返回值的话,需要使用:tex2Dproj(_ScreenTexture, uv.xyw);
// 也可以自己处理其次坐标,使用:tex2D(_ScreenTexture, uv.xy / uv.w);
ComputeScreenPos(float4 pos)

Ambient

1
2
3
4
5
6
7
8
9
10
11
// 环境光(Gradient)中的Sky Color.
unity_AmbientSky

// 环境光(Gradient)中的Equator Color.
unity_AmbientEquator

// 环境光(Gradient)中的Ground Color.
unity_AmbientGround

// 环境光(Color)中的颜色,等同于环境光(Gradient)中的Sky Color.
UNITY_LIGHTMODEL_AMBIENT

Fog

1
2
// 内置雾颜色
unity_FogColor

Texture

1
2
3
4
5
// 对UV进行Tiling与Offset变换
TRANSFORM_TEX(i.uv,_MainTex)

// 去色,内部公式为:dot(rgb,fixed3(0.22,0.707,0.071))
Luminance(float rgb)

Platform

1
2
// 由于HLSL编缉器不接受没有初始化的数据,所以为了支持所有平台,从而需要使用此方法进行初始化.
UNITY_INITIALIZE_OUTPUT(type,name)

Platform Differences

裁剪空间

OpenGL下裁剪空间坐标范围(-1,1),DirectX下裁剪空间坐标范围(1,0)。可以使用UNITY_NEAR_CLIP_VALUE来获取当前平台裁剪空间下的近裁剪值(DX为1,OpenGL为-1).

ReversedZ

DirectX 11、DirectX 12、PS4、Xbox One、Metal这些平台都属于反向方向.深度值从近裁剪面到远裁剪面的值为[1 ~ 0],裁剪空间下的Z轴范围为[near,0]

除以上反向方向的平台以外都属于传统方向。深度值从近裁剪面到远裁剪面的值为[0 ~ 1],裁剪空间下的Z轴范围为:DX平台=[0,far],OpenGL平台=[-near,far]

可以通过使用UNITY_REVERSED_Z来判断当前平台是否开启ReversedZ。

在c#可以使用SystemInfo.usesReversedZBuffer来判断当前平台是否支持ReversedZ

常见实现

GPU Instancing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 实现步骤
// 用于对多个对象(网格一样,材质一样,但是材质属性不一样)合批,单个合批最大上限为511个对象.
1.#pragma multi_compile_instancing 添加此指令后会使材质面板上曝露Instaning开关,同时会生成相应的Instancing变体
2.UNITY_VERTEX_INPUT_INSTANCE_ID 在顶点着色器的输入(appdata)和输出(v2f,可选项)中添加
3.UNITY_INSTANCING_BUFFER_START(arrayName) / UNITY_INSTANCING_BUFFER_END(arrayName) 将每个你需要实例化的属性都封装在这个常量寄存器中
4.UNITY_DEFINE_INSTANCED_PROP(type, name) 在上面的START和END间把需要的每条属性加进来
5.UNITY_SETUP_INSTANCE_ID(v); 需放在顶点着色器/片断着色器(可选)中最开始的地方,这样才能访问到全局的unity_InstanceID
6.UNITY_TRANSFER_INSTANCE_ID(v, o); 当需要将实例化ID传到片断着色器时,在顶点着色器中添加
7.UNITY_ACCESS_INSTANCED_PROP(arrayName, propName) 在片断着色器中访问具体的实例化变量

// Instancing选项
// 对GPU Instancing进行一些设置
#pragma instancing_options forcemaxcount:batchSize 强制设置单个批次内Instancing的最大数量,最大值和默认值是500
#pragma instancing_options maxcount:batchSize 设置单个批次内Instancing的最大数量,仅Vulkan, Xbox One和Switch有效

使用切线空间下的法线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. appdata中定义NORMAL与TANGENT语义.
2. v2f中声明三个变量用于组成成切线空间下的旋转矩阵.
float3 tSpace0:TEXCOORD3;
float3 tSpace1:TEXCOORD4;
float3 tSpace2:TEXCOORD5;
3. 在顶点着色器中执行:
half3 worldTangent = UnityObjectToWorldDir(v.tangent);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
half3 worldBinormal = cross(o.worldNormal, worldTangent) * tangentSign;
o.tSpace0 = float3(worldTangent.x,worldBinormal.x,o.worldNormal.x);
o.tSpace1 = float3(worldTangent.y,worldBinormal.y,o.worldNormal.y);
o.tSpace2 = float3(worldTangent.z,worldBinormal.z,o.worldNormal.z);
4. 在片断着色器中计算出世界空间下的法线,然后再拿去进行需要的计算:
half3 normalTex = UnpackNormal(tex2D(_NormalTex,i.uv));
half3 worldNormal = half3(dot(i.tSpace0,normalTex),dot(i.tSpace1,normalTex),dot(i.tSpace2,normalTex));

线性深度转换

1
2
3
4
从深度图中得到顶点的线性深度值(相机位置=0,相机远裁剪面=1)
Linear01Depth(depthMap, _ZBufferParams);
从深度图中得到顶点的线性深度值(不是0-1的范围)
LinearEyeDepth(depthMap, _ZBufferParams);

Lambert光照模型

1
2
3
4
5
6
// Diffuse:最终物体上的漫反射光强.
// Kd:物体材质对光的反射系数.
// LightColor:光源的强度.
// N:顶点的单位法线向量.
// L:顶点指向光源的单位向量.
Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L))

Phong光照模型

1
2
3
4
5
6
7
8
9
// Specular:最终物体上的反射高光光强.
// SpecularColor:反射光的颜色.
// Ks:反射强度系数.
// R:反射向量,可使用2 * N * dot(N,L) - L或者reflect (-L,N)获得.
// V:观察方向.
// N:顶点的单位法线向量.
// L:顶点指向光源的单位向量.
// Shininess:乘方运算来模拟高光的变化.
Specular = SpecularColor * Ks * pow(max(0,dot(R,V)), Shininess)

Blinn-Phong光照模型

1
2
3
4
5
6
7
// Specular:最终物体上的反射高光光强.
// SpecularColor:反射光的颜色.
// Ks:反射强度系数.
// N:顶点的单位法线向量.
// H:入射光线L与视线V的中间向量,也称为半角向量H = normalize(L+V).
// Shininess:乘方运算来模拟高光的变化.
Specular = SpecularColor * Ks * pow(max(0,dot(N,H)), Shininess)

Disney Principled BRDF

1
2
3
4
5
6
7
8
9
10

// f(l,v):双向反射分布函数的最终值,l表示光的方向,v表示视线的方向.
// diffuse:漫反射.
// D(h):法线分布函数(Normal Distribution Function),描述微面元法线分布的概率,即朝向正确的法线浓度.h为半角向量,表示光的方向与反射方向的半角向量,只有物体的微表面法向m = h时,才会反射到视线中.
// D(h) = roughness^2 / π((n·h)^2(roughness^2-1)+1)^2
// F(v,h):菲涅尔方程(Fresnel Equation),描述不同的表面角下表面所反射的光线所占的比率.
// F(v,h) = F0 + (1-F0)(1-(v·h))^5(F0是0度入射角的菲涅尔反射值)
// G(l,v,h):几何函数(Geometry Function),描述微平面自成阴影的属性,即微表面法向m = h的并未被遮蔽的表面点的百分比.
// 4cos(n·l)cos(n·v):校正因子(correctionfactor)作为微观几何的局部空间和整个宏观表面的局部空间之间变换的微平面量的校正.
f(l,v) = diffuse + D(h)F(v,h)G(l,v,h)/4cos(n·l)cos(n·v)

菲涅尔

1
2
3
4
5
fixed4 rimColor = fixed4 (0, 0.4, 1, 1);
half3 worldViewDir = normalize (UnityWorldSpaceViewDir (i.worldPos));
float ndotv = dot (i.normal, worldViewDir);
float fresnel = (0.2 + 2.0 * pow (1.0 - ndotv, 2.0));
fixed4 col = rimColor * fresnel;

ShadowMap阴影

1
2
3
4
5
6
7
8
9
10
11
// 生成阴影
添加"LightMode" = "ShadowCaster"的Pass.
1.appdata中声明float4 vertex:POSITION;和half3 normal:NORMAL;这是生成阴影所需要的语义.
2.v2f中添加V2F_SHADOW_CASTER;用于声明需要传送到片断的数据.
3.在顶点着色器中添加TRANSFER_SHADOW_CASTER_NORMALOFFSET(o),主要是计算阴影的偏移以解决不正确的Shadow Acne和Peter Panning现象.
4.在片断着色器中添加SHADOW_CASTER_FRAGMENT(i)

// 采样阴影
1.在v2f中添加UNITY_SHADOW_COORDS(idx),unity会自动声明一个叫_ShadowCoord的float4变量,用作阴影的采样坐标.
2.在顶点着色器中添加TRANSFER_SHADOW(o),用于将上面定义的_ShadowCoord纹理采样坐标变换到相应的屏幕空间纹理坐标,为采样阴影纹理使用.
3.在片断着色器中添加UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos),其中atten即存储了采样后的阴影.

全局照明GI

1
2
3
// 产生间接光照
添加"LightMode" = "Meta"的Pass.
可参考内置Shader中的Meta Pass.

光照探针

1
2
3
4
5
// 规则1
当逐像素平行灯标记为Mixed时,同时场景内有LightProbe时,那么当前平行灯的光照值会自动被LightProbe影响,所以不管物体Shader中是否有SH相关的运算,都会受到LightProbe的影响.

// 规则2
当逐像素平行灯标记为Baked时,同时场景内有LightProbe时,那么需要自行在物体Shader中添加SH相关的运算,才会受到LightProbe的影响.

反射探针

1
2
3
4
5
6
// 反射探针的采样
反射探针中当前激活的CubeMap存储在unity_SpecCube0当中,必须要用UNITY_SAMPLE_TEXCUBE进行采样,然后需要对其进行解码
half3 worldView = normalize (UnityWorldSpaceViewDir (i.worldPos));
half3 R = reflect (-worldView, N);
half4 cubemap = UNITY_SAMPLE_TEXCUBE (unity_SpecCube0, R);
half3 skyColor = DecodeHDR (cubemap, unity_SpecCube0_HDR);

雾效

1
2
3
4
5
6
7
8
9
10
11
12
// 方案一
1.#pragma multi_compile_fog声明雾效所需要的内置变体:FOG_LINEAR FOG_EXP FOG_EXP2.
2.UNITY_FOG_COORDS(idx): 声明顶点传入片断中的雾效插值器(fogCoord).
3.UNITY_TRANSFER_FOG(o,o.vertex): 在顶点着色器中计算雾效采样.
4.UNITY_APPLY_FOG(i.fogCoord, col): 在片断着色器中进行雾效颜色混合.

// 方案二
当在v2f中有定义worldPos时,可以把worldPos.w利用起来做为雾效值.
1.#pragma multi_compile_fog声明雾效所需要的内置变体:FOG_LINEAR FOG_EXP FOG_EXP2.
2.UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,o.positionCS): 在顶点着色器中添加,会自动取o.worldPos.z=裁剪空间下的坐标z值.
3.UNITY_EXTRACT_FOG_FROM_WORLD_POS(i): 在片断着色器中添加.
4.UNITY_APPLY_FOG(_unity_fogCoord, c): 在片断着色器中进行雾效颜色混合.

去色

1
2
3
4
方法1:Luminance(float rgb)
方法2:dot(rgb,fixed3(0.22,0.707,0.071))
方法3:dot(rgb,half3(0.299,0.587,0.114))
方法4:(r+g+b)/3

Matcap

1
2
o.normalWS = TransformObjectToWorldNormal(v.normalOS);
o.uv.zw = mul(UNITY_MATRIX_V, float4(o.normalWS, 0.0)).xy * 0.5 + 0.5;

XRay射线

1
2
3
4
1. 新建一个Pass
2.设置自己想要的Blend
3.Zwrite Off关闭深度写入
4.Ztest greater深度测试设置为大于

Dither

另一种透明消失实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//先求出整数型的屏幕像素UV(求余后)
float2 uv = (uint2)i.positionCS.xy%行数;
//然后调用相应的方法
float Dither2x2(float2 uv)
{
float D[4] =
{
0,2,
3,1
};
uint index = uv.x * 2 + uv.y;
return D[index] / 5;
}

float Dither4x4(float2 uv)
{
float D[16] =
{
0,8,2,10,
12,4,14,6,
3,11,1,9,
15,7,13,5
};
uint index = uv.x * 4 + uv.y;
return D[index] / 17;
}

float Dither8x8(float2 uv)
{
float D[64] =
{
0,32,8,40,2,34,10,42,
48,16,56,24,50,18,58,26,
12,44,4,36,14,46,6,38,
60,28,52,20,62,30,54,22,
3,35,11,43,1,33,9,41,
51,19,59,27,49,17,57,25,
15,47,7,39,13,45,5,37,
63,31,55,23,61,29,53,21
};
uint index = uv.x * 8 + uv.y;
return D[index] / 65;
}

模型中心点坐标

1
2
3
4
5
6
方法1: float3 objCenterPos = mul( unity_ObjectToWorld, float4( 0, 0, 0, 1 ) ).xyz;
方法2: float3 objCenterPos = float3(UNITY_MATRIX_M[0][3], UNITY_MATRIX_M[1][3], UNITY_MATRIX_M[2][3]);
方法3: float3 center = float3(unity_ObjectToWorld[0].w, unity_ObjectToWorld[1].w, unity_ObjectToWorld[2].w);
方法4: float3 center = float3(unity_ObjectToWorld._m03, unity_ObjectToWorld._m13, unity_ObjectToWorld._m23);
方法5: float3 center = unity_ObjectToWorld._14_24_34;
在Shader中获取当前模型中心点在世界空间下的坐标位置.

BillBoard

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在Properties中添加:

[Enum(BillBoard,1,VerticalBillboard,0)]BillBoardType("BillBoard Type",float) = 1

在顶点着色器中添加:

//将相机从世界空间转换到模型的本地空间中,而这个转换后的相机坐标即是点也是模型中心点(0,0,0)到相机的方向向量,如果按照相机空间来定义的话,可以把这个向量定义为相机空间下的Z值
float3 cameraOS_Z = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
//BillBoardType=0时,圆柱形BillBoard;BillBoard=1时,圆形BillBoard;
cameraOS_Z.y = cameraOS_Z.y * BillBoardType;
//归一化,使其为长度不变模为1的向量
cameraOS_Z = normalize(cameraOS_Z);
//假设相机空间下的Y轴向量为(0,1,0)
cameraOS_Y = float3(0,1,0);
//利用叉积求出相机空间下的X轴向量
float3 cameraOS_X = normalize(cross(cameraOS_Z,cameraOS_Y));
//再次利用叉积求出相机空间下的Y轴向量
cameraOS_Y = cross(cameraOS_X,cameraOS_Z);
//通过向量与常数相乘来把顶点的X轴与Y对应到cameraOS的X与Y轴向上
float3 billboardPositionOS = cameraOS_X * v.vertex.x + cameraOS_Y * v.vertex.y;
o.pos = UnityObjectToClipPos(billboardPositionOS);

网格阴影

1
2
3
4
half4 worldPos = mul(unity_ObjectToWorld, v.vertex);
worldPos.y = 2.47;
worldPos.xz += fixed2(阴影X方向,阴影Z方向)*v.vertex.y;
o.pos = mul(UNITY_MATRIX_VP,worldPos);

其他记录

ps中的混合公式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// A、B为ps中图层

// 正常
A*(1-B.a)+B*(B.a)
// 变暗
min(A,B)
// 变亮
max(A,B)
// 正片叠底
A*B
// 滤色
1-((1-A)*(1-B))
// 颜色加深
A-((1-A)*(1-B))/B
// 颜色减淡
A+(A*B)/(1-B)
// 线性加深
A+B-1
// 线性减淡
A+B
// 叠加
half4 a = step(A,0.5);
half4 c = a*A*B*2+(1-a)*(1-(1-A)*(1-B)*2);
// 强光
half4 a = step(B,0.5);
half4 c =a*A*B*2+(1-a)*(1-(1-A)*(1-B)*2);
// 柔光
half4 a = step(B,0.5);
half4 c =a*(A*B*2+A*A*(1-B*2))+(1-a)*(A*(1-B)*2+sqrt(A)*(2*B-1)
// 亮光
half4 a = step(B,0.5);
half4 c =a*(A-(1-A)*(1-2*B)/(2*B))+(1-a)*(A+A*(2*B-1)/(2*(1-B)));
// 点光
half4 a = step(B,0.5);
half4 c =a*(min(A,2*B))+(1-a)*(max(A,( B*2-1)));
// 线性光
A+2*B-1
// 排除
A+B-A*B*2
// 差值
abs(A-B)
// 深色
half4 a = step(B.r+B.g+B.b,A.r+A.g+A.b);
half4 c =a*(B)+(1-a)*(A);
// 浅色
half4 a = step(B.r+B.g+B.b,A.r+A.g+A.b);
half4 c =a*(A)+(1-a)*(B);
// 减去
A-B
// 划分
A/B

UV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// UV重映射到中心位置
// 将UV值重映射为(-1,-1)~(1,1),也就是将UV的中心点从左下角移动到中间位置。
float2 centerUV = uv * 2 - 1

// 画圆
// 利用UV来画圆,通过_Radius来调节大小,_CircleFade来调节边缘虚化程序。
float circle = smoothstep(_Radius, (_Radius + _CircleFade), length(uv * 2 - 1));

// 画矩形
// 利用UV来画矩形,_Width调节宽度,_Height调节高度,_RectangleFade调节边缘虚化度。
float2 centerUV = abs(i.uv.xy * 2 - 1);
float rectangleX = smoothstep(_Width, (_Width + _RectangleFade), centerUV.x);
float rectangleY = smoothstep(_Heigth, (_Heigth + _RectangleFade), centerUV.y);
float rectangleClamp = clamp((rectangleX + rectangleY), 0.0, 1.0);

// 黑白棋盘格
float2 uv = i.uv * 格子密度;
uv = floor(uv) * 0.5;
float c = frac(uv.x + uv.y) * 2;
return c;

// 极坐标
float2 centerUV = (i.uv * 2 - 1);
float atan2UV = 1 - abs(atan2(centerUV.g, centerUV.r) / 3.14);
利用UV来实现极坐标.

// 将0-1的值控制在某个自定义的区间内
// 比如frac(i.uv*3.33+3.33);就是将0-1的uv值重新定义为0.33-0.66
frac(x*n+n);

// 随机
1.frac(sin(dot(i.uv.xy, float2(12.9898, 78.233))) * 43758.5453);
2.frac(sin(x)*n);

// 旋转
fixed t=_Time.y;
float2 rot= cos(t)*i.uv+sin(t)*float2(i.uv.y,-i.uv.x);

// 从中心缩放纹理
half2 offset = (0.5-i.uv.xy)*_Offset;
half4 baseMap = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv.xy + offset);

// 序列图
// splitUV是把原有的UV重新定位到左上角的第一格UV上,_Sequence.xy表示的是纹理是由几x几的格子组成的,_Sequence.z表示的是走序列的快慢.
float2 splitUV = uv * (1/_Sequence.xy) + float2(0,_Sequence.y - 1/_Sequence.y);
float time = _Time.y * _Sequence.z;
uv = splitUV + float2(floor(time *_Sequence.x)/_Sequence.x,1-floor(time)/_Sequence.y);

// HSV2RGB方法01
half3 hsv2rgb (half3 c)
{
float2 rot= cos(t)*i.uv+sin(t)*float2(i.uv.y,-i.uv.x);
float3 k = fmod (float3 (5, 3, 1) + c.x * 6, 6);
return c.z - c.z * c.y * max (min (min (k, 4 - k), 1), 0);
}

// HSV2RGB方法02(更优化)
half3 hsv2rgb (half3 c)
{
float4 K = float4 (1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
float3 p = abs (frac (c.xxx + K.xyz) * 6.0 - K.www);
return c.z * lerp (K.xxx, saturate (p - K.xxx), c.y);
}