找回密码
 立即注册
查看: 689|回复: 0

UnityShader常用语义和变量

[复制链接]
发表于 2021-12-2 19:48 | 显示全部楼层 |阅读模式
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渲染状态[RenderSetup]
         //Subshader标签[Tags]
         
         //第一个Pass
         Pass
         {
             [NAME]
             //Pass的渲染状态[RenderSetup]
             //Pass的标签[Tags]
            
             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性能越低
状态设置[RenderSetup] - (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因子是1
Zero因子是0
SrcColor源颜色值,用于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时,将该标签设置为False
PreviewType可以指明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指定当满足某些条件时才渲染这个Pass
1.Unity的LightMode部分选项
Tags { "LightMode" = "Always" }不管使用哪种渲染路径,该Pass都会渲染,但不会计算任何光照,默认使用此选项
Tags { "LightMode" = "ForwardBase" }用于前向渲染。该Pass会计算环境光,最重要的平行光、逐顶点/SH光源和Lightmaps
Tags { "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_4LightPosZ0
float4仅用于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_ObjectToWorld
float3 UnityWorldToObjectDir(flaot3 dir)输入:世界空间中的向量
输出:模型空间中的该向量的归一化内部实现使用了unity_WorldToObject
float3 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.0
unity_CameraProjectionfloat4x4摄像机的投影矩阵
unity_CameraInvProjectionfloat4x4摄像机的投影矩阵的逆矩阵
unity_CameraWorldClipPlanes[6]float4相机锥平面世界空间方程,按顺序为:左、右、底部、顶部、近、远
Unity提供的CG/HLSL语义

应用阶段传递到顶点着色器语义\
POSITION模型空间中顶点的位置 float4
NORMAL顶点的法线 float3
TANGENT顶点切线 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的时候总是会忘记一些变量名和语义等,所以萌生了写这篇文章的想法,有什么错漏感谢指出!一起加油~

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-23 03:23 , Processed in 0.109163 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表