基础结构
顶点/片元着色器
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 { }
SubShader { Tags { "LightMode"="ForwardBase" ... }
ZWrite Off ....
Pass { Name "MyPassName1"
Tag { "LightMode"="ForwardBase" ... }
ZWrite Off ....
CGPROGRAM #pragma vertex vert #pragma fragment frag
ENDCG }
UsePass "MyPassName2"
GrabPass
... }
...
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
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; float3 normal : NORMAL; float4 tangent : TANGENT; float4 texcoord : TEXCOORD0; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; float4 texcoord3 : TEXCOORD3; fixed4 color : COLOR; };
struct v2f { float4 pos: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类型的变量,每个变量存储了矩阵中的一行数据。
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" }
|
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
指明材质面板将如何预览该材质。默认情况下,材质将显示为一个球形,我们可以通过标签的值设为Plane、SkyBox来改变预览
仅可以在SubShader中声明
1 2 3 4
| Tags { "PreviewType"="Plane" "PreviewType" = "Skybox" }
|
是否对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 { Ref [_Stencil] ReadMask [_StencilReadMask] WriteMask [_StencilWriteMask] Comp [_StencilComp] ((UnityEngine.Rendering.CompareFunction)) Pass [_StencilOp] (UnityEngine.Rendering.StencilOp) Fail [_Fail] ZFail [_ZFail] }
|
选项 |
描述 |
Less |
相当于“<”操作,即仅当左边<右边,模板测试通过,渲染像素. |
Greater |
相当于“>”操作,即仅当左边>右边,模板测试通过,渲染像素. |
Lequal |
相当于“<=”操作,即仅当左边<=右边,模板测试通过,渲染像素. |
Gequal |
相当于“>=”操作,即仅当左边>=右边,模板测试通过,渲染像素. |
Equal |
相当于“=”操作,即仅当左边=右边,模板测试通过,渲染像素. |
NotEqual |
相当于“!=”操作,即仅当左边!=右边,模板测试通过,渲染像素. |
Always |
不管公式两边为何值,模板测试总是通过,渲染像素. |
Never |
不管公式两边为何值,模板测试总是失败 ,像素被抛弃. |
选项 |
描述 |
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 }
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 Blend SrcFactor DstFactor, SrcFactorA DstFactorA
|
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 BlendOp OpColor, OpAlpha
|
参数 |
描述 |
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
开启或关闭顶点光照相关的平行高光颜色
ColorMaterial
当计算顶点颜色时使用的顶点颜色
值 |
说明 |
AmbientAndDiffuse |
… |
Emission |
… |
Material {}
定义一个使用顶点光照管线的材质
开启或关闭顶点光照
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 }
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)
来做分支判断
版本 |
描述 |
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
宏 |
描述 |
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
宏 |
描述 |
SHADER_TARGET |
shader 目标平台 |
Unity Version
1
| #if UNITY_VERSION >= 500
|
宏 |
描述 |
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. |
宏 |
描述 |
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中任意位置即可. |
宏
变量 |
描述 |
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);
float4x4 M_rotationX = float4x4( 1, 0, 0, 0, 0, cos(θ), -sin(θ), 0, 0, sin(θ), cos(θ), 0, 0, 0, 0, 1);
float4x4 M_rotationY = float4x4( cos(θ), 0, sin(θ), 0, 0, 1, 0, 0, -sin(θ), 0, cos(θ), 0, 0, 0, 0, 1);
float4x4 M_rotationZ = float4x4( cos(θ), -sin(θ), 0, 0, sin(θ), cos(θ), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
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
|
_Time
_SinTime
_CosTime
unity_DeltaTime
|
Light
FowardBase 和 ForwardAdd
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
_LightColor0
_WorldSpaceLightPos0
unity_WorldToLight
float3 UnityWorldSpaceLightDir( float3 worldPos )
|
Camera
1 2 3 4 5 6 7 8 9 10 11 12
| _WorldSpaceCameraPos
UnityWorldSpaceViewDir(i.worldPos)
_CameraDepthTexture
|
Screen
1 2 3 4 5 6 7 8 9 10 11
|
_ScreenParams
ComputeScreenPos(float4 pos)
|
Ambient
1 2 3 4 5 6 7 8 9 10 11
| unity_AmbientSky
unity_AmbientEquator
unity_AmbientGround
UNITY_LIGHTMODEL_AMBIENT
|
Fog
Texture
1 2 3 4 5
| TRANSFORM_TEX(i.uv,_MainTex)
Luminance(float rgb)
|
1 2
| UNITY_INITIALIZE_OUTPUT(type,name)
|
裁剪空间
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
|
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) 在片断着色器中访问具体的实例化变量
#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 = Ambient + Kd * LightColor * max(0,dot(N,L))
|
Phong光照模型
1 2 3 4 5 6 7 8 9
|
Specular = SpecularColor * Ks * pow(max(0,dot(R,V)), Shininess)
|
Blinn-Phong光照模型
1 2 3 4 5 6 7
|
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) = 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
| 当逐像素平行灯标记为Mixed时,同时场景内有LightProbe时,那么当前平行灯的光照值会自动被LightProbe影响,所以不管物体Shader中是否有SH相关的运算,都会受到LightProbe的影响.
当逐像素平行灯标记为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
| 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*(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
|
float2 centerUV = uv * 2 - 1
float circle = smoothstep(_Radius, (_Radius + _CircleFade), length(uv * 2 - 1));
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来实现极坐标.
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);
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);
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); }
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); }
|