UnityShader常用语义和变量
Properties语义块Properties
{
_Int ("Int", Int) = 1
_Float ("Float", Float) = 1.5
_Range ("Range", Range(0.0, 5.0)) = 3.0
_Color ("Color", Color) = (1, 1, 1, 1)
_Vector ("Vector", Vector) = (1, 2, 3, 4)
_2D ("2D", 2D) = "" {}
_Cube ("Cube", Cube) = "white" {}
_3D ("3D", 3D) = "black" {}
}
······
CGINCLUDE
int _Int;
float _Float;
float _Range;
fixed4 _Color;
float4 _Vector;
sampler2D _2D;
float4 _2D_ST; //2D纹理在inspector窗口上的缩放Tiling和偏移Offset
samplerCUBE _Cube;
sampler3D _3D;
ENDCG
······
Color和Vector是四维向量,
2D、3D、Cube是复杂的结构,由字符串和花括号指定,字符串要么是空的要么是"white"、"black"等内置的纹理的名称,CubeMap可以使用 “_Skybox” 字符串指定当前场景里的天空盒花括号内容通常为空。
fixed、half、float 都是浮点数,精度各不同,分别是:11位、16位、32位
UnityShader文件的结构
Shader "Unlit/MyShader"
{
Properties
{
//定义属性变量
}
//第一个SubShader
SubShader
{
//Subshader渲染状态
//Subshader标签
//第一个Pass
Pass
{
//Pass的渲染状态
//Pass的标签
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata{};
struct v2f{};
v2f vert (appdata v){}
fixed4 frag (v2f i) : SV_Target{}
ENDCG
}
//第二个Pass
Pass{······}
}
//第二个Subshader
SubShader{······}
Fallback Off //Fallback "name"
}一个完整的shader文件,至少要有一个SubShader语义块,第一个SubShader如果无法在机器上执行,就执行第二个,直到调用Fallback
SubShader和Pass都可以进行渲染状态设置和标签设置,语法相同,SubShader的设置会应用于其下的所有没有进行设置的Pass
每一个Pass就是一个完整的渲染流程(具备顶点着色器和片元着色器)越多Pass性能越低
状态设置 - (SubShader和Pass通用)
CullCull Back / Front / Off设置剔除模式,剔除背面(默认)、剔除正面、关闭剔除ZTestZTest LessGreater / LEqual / GEqual / Equal / NotEqual / Always设置深度测试时使用的函数ZWriteZWrite On / Off开启、关闭深度写入BlendBlend SrcFactor DstFactor开启和设置混合模式,Src\DstFactor是两个参数,具体参考Blend的详细说明以上设置可以再SubShader和Pass中使用,SubShader的设置影响其下所有Pass,Pass里的设置则覆盖SubShader提供的设置。
[*]ZTest参数详解:
ZTest Less深度小于当前缓存则通过ZTest Greater深度大于当前缓存则通过ZTest LEqual深度小于等于当前缓存则通过(默认值)ZTest GEqual深度大于等于当前缓存则通过ZTest Equal深度等于当前缓存则通过ZTest NotEqual深度不等于当前缓存则通过ZTest Always不论如何都通过ZTest Off相当于Always,关闭深度测试,无论如何都通过默认值为 LEqual ,注意深度测试时间点是片元着色器之后,和预先深度测试不是同一个东西。预先深度测试在片元着色器之前
2.Blend状态详细:
Blend Off关闭混合Blend SrcFactor DstFactor开启混合并设置混合因子Blend SrcFactor DstFactor SrcFactorA DstFactorA开启混合并设置混合因子,但是用另外的参数去混合颜色的透明通道BlendOp BlendOperation修改混合的计算公式,默认是Add,混合颜色后相加混合的公式是:
最终颜色ColorRGBA = Pass输出颜色RGBA * SrcFactor + 目标颜色RGBA * DstFactor
当使用 SrcFactorA 的时候,意味着alpha通道分开来计算,也就是:
最终颜色ColorRGB = Pass输出颜色RGB * SrcFactor + 目标颜色RGB * DstFactor
最终颜色ColorA = Pass输出颜色A * SrcFactorA + 目标颜色A * DstFactorA
Pass输出颜色就是 源颜色Src, 目标颜色就是当前屏幕上颜色缓冲里的颜色Dst
而 Factor 的可用参数有这些:
One因子是1Zero因子是0SrcColor源颜色值,用于RGB混合时,使用RGB分量作为因子,用于Alpha混合时,用A分量作为因子SrcAlpha源颜色值的Alpha分量DstColor同上,但是是目标颜色DstAlpha同上,但是是目标颜色OneMinusSrcColor因子为(1 - 源颜色),使用RGB分量作为因子,用于Alpha混合时,用A分量作为因子OneMinusSrcAlpha因子为(1 - 源颜色A分量)OneMinusDstColor同上,但是是目标颜色OneMinusDstAlpha同上,但是是目标颜色BlendOp 的 BlendOperation 可用参数有:
Add默认的操作,将和各自的因子混合后的源颜色和目标颜色相加Sub混合后,源颜色 减去 目标颜色RevSub混合后,目标颜色 减去 源颜色Min混合后,在源颜色和目标颜色中逐个颜色分量去进行比较,选出最小的RGBA作为最终颜色Max混合后,在源颜色和目标颜色中逐个颜色分量去进行比较,选出最大的RGBA作为最终颜色其他仅在 DirectX 11.1 中支持标签类型Tags - (SubShader)
Queue控制渲染的顺序,主要目的是控制透明物体在不透明物体全渲染完毕之后再渲染、自定义物体的渲染顺序RenderType对着色器进行分类,可以分为不透明的着色器或透明的着色器,这个主要和着色器替换(Shader Replacement)功能相关DisableBatching指明是否对该SubShader进行批处理,一些着色器可能在使用Unity的批处理功能时出现问题(特别是在模型空间下进行顶点动画的时候)ForceNoShadowCasting控制这个物体是否会投射阴影IgnoreProjector如果此标签为True,则此物体不会受Projector的影响,通常用于半透明物体CanUseSpriteAtlas该SubShader用于精灵Sprite时,将该标签设置为FalsePreviewType可以指明UnityInspector面板如何预览该材质,默认是球形,但是可以设置为”Plane“或”SkyBox“来改变预览类型以上Tags仅可以在SubShader中使用,多个Tags用逗号隔开,所有的Tags都是 string - string 的键值对的形式
比如:
Tags { "RenderType"="Opaque" "DisableBatching" = "True" }
[*]Queue的参数
"Background"1000这个队列里的物体在任何其他队列之前先渲染,通常用来绘制背景物体"Geometry"2000默认的渲染队列,大部分不透明物体使用此队列"AlphaTest"2450需要进行透明度测试的使用此队列"Transparent"3000此队列的透明物体,在前面的队列渲染完毕后,再按从后往前的顺序进行渲染。任何使用透明度混合、关闭深度写入的物体都应该使用此队列"Overlay"4000通常用来实现一些叠加效果。要最后渲染的物体使用该队列渲染队列Queue设置影响物体的渲染顺序,且你可以在material的inspector面板里手动设置这个材质的渲染队列:
根据Unity上的队列分布,2500以下是不透明物体的渲染队列,所以这些物体是先渲染距离摄像机近的,再渲染距离摄像机远的
队列2501以上是透明物体的渲染队列,所以这部分的透明物体为了保证透明效果正确,是先渲染距离摄像机远的,再渲染距离摄像机近的
2.RenderType的参数
RenderType是Unity为替换shader准备的一个内建标签集合,对一个SubShader进行一个正确的RenderType的设定,那么在使用着色器替换功能的时候,就能使用同样的RenderType类型的SubShader对这个物体进行替换。
着色器替换功能需要手动写C#代码并通过调用Camera提供的函数来执行,将场景中的物体进行一次替换,然后再渲染。这个概念和屏幕后处理不一样。如果找不到对应的RenderType,那么就会选取第一个,如果平台无法支持SubShader,就找下一个(如果没有对应的)或者直接Fallback
具体可以参考这篇文章:https://www.jianshu.com/p/d8356533fcc2
Opaque用于大多数着色器Transparent用于半透明着色器TransparentCutout蒙皮透明着色器Background天空盒着色器Overlay光晕着色器、闪光着色器TreeOpaque、TreeTransparentCutout、TreeBillboard、Grass、GrassBillboard地形引擎中的树皮、树叶、树、草等实际上你也可以自定义RenderType的名称,来区分shader要渲染的对象,这个设置本身并不会给shader带来任何影响。
3.DisableBatching
Unity会自动对一些符合条件的物体进行动态合批处理,节省性能,但如果我们的顶点动画在模型空间中进行顶点变换的时候,可能会因动态合批而引发错误,解决办法之一就是把这个Tag设置为False
具体可以参考:https://blog.csdn.net/qq_39574690/article/details/105400316
4.ForceNoShadowCasting
= “True” 时,代表此物体不受其他物体投射的阴影的影响
= “False” 时,代表此物体会受到其他物体投射的阴影的影响(默认值)
5.IgnoreProjector
默认为False,当设置为True的时候则表示我们不希望任何投影类型材质或贴图影响这个着色器,这个属性主要配合unity的一个组件 Projector(投射器、投影仪) 使用
标签类型Tags - (Pass)
LightMode定义这个Pass在Unity渲染流水线中的角色RequireOptions指定当满足某些条件时才渲染这个Pass1.Unity的LightMode部分选项
Tags { "LightMode" = "Always" }不管使用哪种渲染路径,该Pass都会渲染,但不会计算任何光照,默认使用此选项Tags { "LightMode" = "ForwardBase" }用于前向渲染。该Pass会计算环境光,最重要的平行光、逐顶点/SH光源和LightmapsTags { "LightMode" = "ForwardAdd" }用于前向渲染。该Pass会计算额外的逐像素光源,每个Pass对应一个光源Tags { "LightMode" = "ShadowCaster" }把物体的深度信息渲染到阴影映射纹理或一张深度纹理中Tags { "LightMode" = "Deferred" }用于延迟渲染中Unity默认使用前向渲染,如果Unity使用前向渲染,而Pass使用延迟渲染(Deferred),那么这个Pass是不会进行渲染的
不声明LightMode,则默认使用 “Always” 这个模式,此时光照参数比如_LightColor0、_WorldSpaceLightPos0 没有进行正确的计算,直接使用可能会引发不可预见的错误
简单说下正确使用前向光照应该:
#pragma multi_compile_fwdbase
#include "Lighting.cginc"用这两句话告诉unity我们在这里使用前向渲染,声明后,unity会自动处理物体的光照计算,处理完毕后变成一堆参数提供在Pass里用,使用Unity提供的光照参数时有些参数需要引入光照文件
前向渲染中,unity会根据场景中各个光源对物体的影响程度进行一个重要度排序,为了性能默认不会全处理所有光,而是会按照光源的重要程度舍弃一些不重要的
前向渲染提供的变量关键字:
_LightColor0float4该Pass处理的逐像素光源颜色_WorldSpaceLightPos0float4_WorldSpaceLightPos0.xyz 是该Pass处理的像素光源的位置,如果是平行光,则w分量为0,xyz代表平行光的方向,其他光源则w为1,xyz代表光源的位置_LightMatrix0float4x4从世界空间到光源空间的变换矩阵,可以用于采样cookie和光强衰减纹理unity_4LightPosX0
unity_4LightPosY0unity_4LightPosZ0float4仅用于Base Pass,前4个非重要的点光源在世界空间中的位置unity_4LightAtten0float4仅用于Base Pass,存储前4个非重要的点光源的衰减因子unity_LightColorhalf4仅用于Base Pass,存储前4个非重要的点光源颜色前向渲染常用的函数:(这些函数的定义在UnityCG.cginc中)
float3 WorldSpaceLightDir (float4 v)输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向,没有归一化float3 UnityWorldSpaceLightDir (float4 v)输入一个世界空间中的顶点位置,返回世界空间中从该点到光源的光照方向,没有归一化float3 ObjSpaceLightDir (float4 v)输入一个模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向,没有归一化float3 Shade4PointLights(...)计算四个点光源的光照,具体使用方式有待研究,参数是上面的内置变量UnityCG.cginc中一些常用的函数
float3 UnityObjectToWorldNormal
(float3 normal)输入:模型空间的法线
输出:世界空间中的该法线的归一化内部实现会判断模型是否发生了形变float3 UnityObjectToWorldDir(flaot3 dir)输入:模型空间中的矢量
输出:世界空间中的该矢量的归一化内部实现使用了unity_ObjectToWorldfloat3 UnityWorldToObjectDir(flaot3 dir)输入:世界空间中的向量
输出:模型空间中的该向量的归一化内部实现使用了unity_WorldToObjectfloat3 WorldSpaceViewDir(float4 v)输入:模型空间中的顶点位置
输出:世界空间中从该顶点到摄像机的观察方向向量,即获得这个顶点的视线方向,没有归一化内部实现是_WorldSpaceCameraPos.xyz - 参数转换到世界空间的位置float3 UnityWorldSpaceViewDir(float4 v)输入:世界空间中的一个顶点的位置
输出:世界空间中这个顶点到摄像机的观察方向内部实现是_WorldSpaceCameraPos.xyz - 参数float3 ObjSpaceViewDir(float4 v)输入:模型空间中的顶点的位置
输出:模型空间中该顶点到摄像机的观察方向内部实现是_WorldSpaceCameraPos转换到模型空间后 - 参数UnityObjectToViewPos输入:模型空间中的顶点的位置
输出:观察空间中该顶点的位置内部实现是顶点变换到世界空间后再和UNITY_MATRIX_V变换到观察空间fixed3 UnpackNormal(fixed4 p)输入:从法线贴图上采样得到的颜色值
输出:颜色值各个分量经过 * 2 - 1 处理后的向量值,即把颜色值从区间 0~1 变换到 -1~1 (法线的分量的取值范围)UnityShader内置矩阵
UNITY_MATRIX_MVP模型空间 -> 观察空间 -> 裁剪空间
通常把模型顶点转换到裁剪空间(就是那个必备的操作)UnityObjectToClipPos()函数里就是用的这个矩阵UNITY_MATRIX_MV模型空间 -> 观察空间
可以用来将模型空间的顶点转换到观察空间里UNITY_MATRIX_V世界空间 -> 观察空间
可以把世界空间中的顶点转换到观察空间中UNITY_MATRIX_P观察空间 -> 裁剪空间
可以把观察空间中的顶点转换到裁剪平面(裁剪空间)里UNITY_MATRIX_VP世界空间 -> 观察空间 -> 裁剪空间
可以把顶点从世界空间中转换到裁剪空间里UNITY_MATRIX_T_MVUNITY_MATRIX_MV矩阵的转置矩阵UNITY_MATRIX_IT_MVUNITY_MATRIX_MV矩阵的逆转置矩阵,通常用来把法线从模型空间转换到视角空间unity_ObjectToWorld模型空间 -> 世界空间
把顶点从模型空间转到世界空间里unity_WorldToObject世界空间 -> 模型空间
把顶点从世界空间转到模型空间里M - Model 模型矩阵
V - View 观察矩阵、视图矩阵
P - Projection 投影矩阵
矩阵的操作当然都适用于顶点和矢量
Unity推荐使用 UnityObjectToClipPos() 来替代 mul(UNITY_MATRIX_MVP, v.vertex),因为直接使用UNITY_MATRIX_MVP,会引入一次额外的矩阵计算,因此最好用Unity提供的函数以便优化。他的内部实现是:
return mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(pos, 1.0)));其次,不可以直接把法线和UNITY_MATRIX_MV矩阵进行变换,因为法线在模型发生非等比缩放的时候,法线的方向会发生变化,为了正确获得世界空间中的法线,应该使用UnityObjectToWorldNormal 函数让法线变换到世界空间的时候是正确计算的(垂直于曲面)
因此 UnityObjectToWorldNormal 和 UnityObjectToWorldDir 的区别是,一个仅负责把模型空间的点或矢量转换到世界空间,一个是专门处理法线,正确计算法线,让即使模型发生了缩放拉伸,法线也能正确垂直于曲面
UnityObjectToWorldNormal的内部实现会判断模型是否进行了非等比缩放,如果是,则这样处理:
return normalize(mul(norm, (float3x3)unity_WorldToObject));如果没有的话则调用 UnityObjectToWorldDir (norm) 返回
这样特殊处理的原因可以参考:https://www.zhihu.com/question/400660113/answer/1343469757
UnityShader内置摄像机和屏幕参数
_WorldSpaceCameraPosfloat3世界空间相机的位置_ProjectionParamsfloat4x = 1.0(或如果当前使用翻转投影矩阵渲染则为-1.0),
y是相机的近平面,z是相机的远平面,w是1 / FarPlane_ScreenParamsfloat4x是相机的渲染目标在像素里的宽度,
y是相机的渲染目标在像素里的高度,z是1.0 + 1.0 /宽度,w是1.0 + 1.0 /高度_ZBufferParamsfloat4用于线性化Z缓冲区的值。x(1-far /near),
y(far/near),z(x /far),w(y /far)unity_OrthoParamsfloat4x是正交的相机的宽度,
y是正交的相机的高度,z是未使用的,为正交的相机时w为1.0,透视相机时w为0.0unity_CameraProjectionfloat4x4摄像机的投影矩阵unity_CameraInvProjectionfloat4x4摄像机的投影矩阵的逆矩阵unity_CameraWorldClipPlanesfloat4相机锥平面世界空间方程,按顺序为:左、右、底部、顶部、近、远Unity提供的CG/HLSL语义
应用阶段传递到顶点着色器语义\POSITION模型空间中顶点的位置 float4NORMAL顶点的法线 float3TANGENT顶点切线 float4,w分量代表这个切线空间下的副切线的方向TEXCOORDn (TEXCOORD0、TEXCOORD1...)此顶点的纹理坐标,TEXCOORD0则代表第一组纹理坐标,以此类推,可以是float2或float4类型COLOR顶点的颜色 fixed4顶点着色器传递到片元着色器语义\SV_POSITION裁剪空间中的顶点的坐标COLOR0~COLOR1\TEXCOORD0~TEXCOORD1\片元着色器输出到Unity的语义\SV_Target输出值会存储到渲染目标中,即最后的一帧中的颜色UnityShader内置时间变量
_Time场景开始加载后所经过的时间,4个分量分别是(t/20, t, 2t, 3t)_SinTime时间的正弦值,四个分量值分别是
(t/8, t/4, t/2, t)的正弦值_CosTime时间的余弦值,四个分量值分别是
(t/8, t/4, t/2, t)的余弦值unity_DeltaTimedt是时间增量,4个分量的值分别是
(dt, 1/dt, smoothDt, 1/smoothDt)这些时间变量都是float4,4个分量分别具有不同的值,直接使用即可,时间值会在游戏运行的时候自动变化
ShaderTarget版本的差别
#pragma target 2.5默认的target版本#pragma target 3.0支持对顶点纹理的采样等#pragma target 4.0支持几何着色器#pragma target 4.6支持曲面细分着色器还有其他的target版本,不同的target版本支持的着色器功能、shader能使用的运算指令数目和对应Direct3D的ShaderModel版本也各不相同,不同的unity版本支持的target版本也可能不同,详情请查阅对应Unity版本的Manual文档,搜索“Shader Target”
环境光和雾
unity_AmbientSkyfixed4梯度环境照明的情况下的天空环境照明颜色。unity_AmbientEquatorfixed4梯度环境照明的情况下赤道环境照明颜色。unity_AmbientGroundfixed4梯度环境照明的情况下地面环境照明颜色。UNITY_LIGHTMODEL_AMBIENTfixed4环境照明颜色(或梯度环境情况下的天空颜色)。unity_FogColorfixed4雾色unity_FogParamsfloat4雾计算参数:
(density / sqrt(ln(2)), density / ln(2), –1/(end-start), end/(end-start)) x值用于Exp2雾模式,y值用于Exp模式,z和w用于线性模式。所谓梯度环境照明,指的是你可以在Unity里设置环境颜色为梯度颜色:
打开Window -> Rendering -> Lighting 打开 Lighting 面板,然后在 Environment 里可以找到环境颜色的设置,把 source 设置为 Gradient(梯度),那么这三个颜色:天空、赤道、地面就对应了上面表格里的3个变量
最后是一些入门的时候经常用到的函数的研究和提示:
贴图 tiling 计算函数
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); //使用实例
o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; //函数原型TRANSFORM_TEX() 函数用于正确处理纹理坐标的缩放和偏移
贴图颜色采样函数
tex2D函数用于传入UV以便对2D纹理进行采样,得到颜色值
tex2D(_MainTex, i.uv).rgb; //用法举例
float4 tex2D(sampler2D x, float2 v); //函数原型另一个函数是 tex2Dlod,也是获得颜色值
tex2Dlod(sampler2D, float4(i.uv.xy, 0, 0)); //用法举例
float4 tex2Dlod(sampler2D x, in float4 t); //函数原型注意tex2Dlod的第二个参数传入的是一个float4,里面 t.xy 是采样颜色的坐标,t.w 代表的是纹理的mipmap层级,0层是没有压缩过的纹理,层级越高(最高8层)则纹理压缩程度越大。
tex2Dlod函数只能用于#pragma target 3.0或以上的shader目标版本
值得注意的是,tex2D 这个函数不能在顶点着色器中使用,想在顶点着色器对纹理进行采样得使用:tex2Dlod 函数。在Unity文档中对此是这样描述的:
Using in the Vertex Shader. This is not valid, because UV derivatives don't exist in the vertex Shader.You need to sample an explicit mip level instead; for example, use ().You also need to add as is a Shader model 3.0 feature. 再结合这篇文章:https://zhuanlan.zhihu.com/p/144434084
大意是 tex2D 函数在片元着色器使用的时候,会自动根据物体距离摄像机的距离来计算应该使用的纹理mipmap层级,他的底层实现也是调用了 tex2Dlod 函数,只不过层级是片元自动计算出来的。
而顶点着色器不能自动计算这个层级,所以你得手动指定mipmap层级,也就是直接调用 tex2Dlod ,而不能使用 tex2D 否则 unity 会报错。
关于什么是 mipmap 可以看:https://blog.csdn.net/u010019717/article/details/91352180
法线函数
UnpackNormal 函数把法线贴图采样得到的颜色值转化为法线的扰乱值
UnpackNormal(tex2D(_BumpTex, i.uv.zw)).xyz; //使用示例
inline fixed3 UnpackNormal(fixed4 packednormal); //函数原型其实本质就是进行 packednormal.xyz * 2 - 1 的操作,把参数的取值范围从颜色值的 0~1 转换到法线的取值范围 -1~1
法线贴图记录的是法线相比垂直向上的法线的差异,也就是扰乱值,这部分我之后出一篇文章另谈
Clip函数
在片元着色器里,如果传入的参数(float4)中的任何一个分量小于0,则丢弃这个像素,不进行渲染
void clip(float4 x)
{
if (any(x < 0))
discard;
}
写shader要用到的数学函数可以查看别人总结的文章,这里就不列举啦。
刚入门shader的时候总是会忘记一些变量名和语义等,所以萌生了写这篇文章的想法,有什么错漏感谢指出!一起加油~
页:
[1]