在Properties{}语句块中定义了着色器属性,在这里定义的属性将被作为输入提供给所有的子着色器。就和在C#脚本中定义一些int值一样。
定义语法为:
_Name("Display Name", type) = defaultValue[{options}]
示例:
_MainColor ("Main Color", Color) = (0,0,0,0)
_Texture ("Texture", 2D) = "white" {}
_WaveScale ("Wave scale", Range (0.02,0.15)) = 0.07
_ReflectionTex ("Environment Reflection", 2D) = "" {}
这两句话的意思是:
定义了一个_MainColor属性,在面板中显示为Main Colo,他的类型是Color,默认为(0,0,0,0)(纯黑色)
定义了一个_Texture属性,在面板中显示为Texture,他的类型是2D图片,默认为white(白色)
定义了一个_WaveScale 属性,在面板中显示为Wave scale,他的类型是Range (0.02,0.15),默认为0.07
定义了一个_ReflectionTex 属性,在面板中显示为Environment Reflectio,他的类型是2D图片,默认为空
Tags标签是一个键值对,键和值都是字符串类型。Tags标签的作用是告诉渲染引擎,什么时候、怎么样调用这个SubShader。
注意:它只能写在 Subshader 语句块里面,不能写在 Pass 语句块里面。
1.一些常用的Tags渲染队列标签:
(1)Queue:指定渲染顺序,指定该物体属于哪一个渲染队列,例如Tags{“Queue” = “Transparent”}
(2)RenderType:对着色器分类。例如:这是一个不透明着色器,例如 Tags{“RenderType” = “Opaque”}
(3)DisableBatching: 一些SubShader在使用Unity批处理时会出现问题。可以用该标签直接表明是否使用批处理,例如Tags{“DisableBatching” = “True”}
(4)ForecNoShadowCasting:控制该SubShader的物体是否会投射阴影,例如Tags{“ForceNoShadowCasting” = “True”}(从不产生阴影)
(5)IgnoreProjector:设置该SubShader的物体是否受Projector影响。True常用于半透明物体,例Tags{“IgnoreProjector” = “True”}
(6)CanUseSpriteAtlas:当该SubShader用于“sprite”时,将该标签设为False,例如Tags{“CanUseSpriteAtlas” = “False”}
(7)PriviewType:材质面板的预览类型,一般默认材质预览效果是球形,还可以该为"Plane" “SkyBox”,例如Tags{“PreviewType” = “Plane”}
2.修改渲染顺序
渲染顺序的示意图如下:
如果想让对象在 Background 之后 Geometry 之前渲染,我们可以这么写:
Tags { “Queue” = “Geometry-1”}
它等价于
Tags { “Queue” = “Background+999”}
因为因为 2000 - 1 = 1000 + 999。
2.一些常用的Tags灯光模式标签:
(1)Always:永远都渲染,但不处理光照。
(2)ShadowCaster,用于渲染产生阴影的物体。
(3)ShadowCollector,用于收集物体阴影到屏幕坐标Buff里。
(4)ForwardBase:用于前向渲染一种渲染路径,它根据影响对象的光线在一个或多个过程中渲染每个对象。灯光本身也可以通过正向渲染进行不同的处理,具体取决于它们的设置和强度。
(5)ForwardAdd:用于前向渲染;应用添加的每像素光,每个光通过一次。延迟:用于延迟着色
(6)MotionVectors:用于计算每个对象的运动矢量。
(7)PrepassBase:用于旧版延迟光照,渲染法线和镜面反射指数。
(8)PrepassFinal:用于传统延迟光照,通过组合纹理,光照和发射来渲染最终颜色。
(9)VertexLMRGBM:在对象进行光照贴图时用于旧版Vertex Lit渲染;在lightmap是RGBM编码的平台上(PC和控制台)。
(10)VertexLM:在对象进行光照贴图时用于旧版Vertex Lit渲染;在光照贴图采用双LDR编码的平台上(移动平台)。
(11)Vertex:当对象未进行光照贴图时,用于传统的顶点渲染渲染,应用所有顶点灯。
Tags{ "LightMode"="Always" }
3.Tags传递标志标签:(不常用)
(1)OnlyDirectional:在ForwardBase传递类型中使用时,此标志使得只有主方向灯和ambient / lightprobe数据才会传递到着色器。 这意味着非重要光源的数据不会传递到顶点光或球面谐波着色器变量中。
Tags{ "PassFlags "="OnlyDirectional" }
4.Tags请求选项标签:(不常用)
(1)SoftVegetation:仅在“质量”窗口中启用“软植被”时才渲染此过程。
Tags{ "RequireOptions "="SoftVegetation" }
LOD很简单,它是Level of Detail(详细程度)的缩写,一般的初始Shader脚本指定了其为200(其实这是Unity的内建Diffuse着色器的设定值)。这个数值决定了我们能用什么样的Shader。在Unity的Quality Settings中我们可以设定允许的最大LOD,当设定的LOD小于SubShader所指定的LOD时,这个SubShader将不可用。Unity内建Shader定义了一组LOD的数值,我们在实现自己的Shader的时候可以将其作为参考来设定自己的LOD数值,这样在之后调整根据设备图形性能来调整画质时可以进行比较精确的控制。
一般的Shader脚本设置200即可。
首先,我们可以在Pass块中定义该Pass的名称,例如
Name "MyPass"
定义好名称后,可以在其他Shader脚本中使用该Pass,提高代码的复用性,例如
UsePass “MyShader/MYPASS”
注意:在使用UsePass命令时,Pass的名字必须使用大写。
此外也可以设置Pass块的渲染状态,SubShader的状态设置也适用于Pass块。
Tags{ "LightMode" = "Always" }
注意:Pass要尽量的少,每多一个pass,那么dc就会多一次,性能就会更低一个档次!
Pass块的其他指令:
(1)Cull指令(面的剔除):
在渲染的时候,默认情况下是只有朝向摄像机的面才会被渲染,可以告诉Unity,我想渲染哪一个朝向的面,使用Cull命令在计算体积阴影的时候会用到对Cull的操作来计算和物体相交的投影
Cull指令有三种:
1.Cull Off:不剔除 。
2.Cull Back: 剔除背面(背向摄像机的面) 。
3.Cull Front :剔除前面 (朝向摄像机的面)。
之后介绍的顶点、片元着色器都是写在Pass块中的。如果在没有Pass块的Standard Surface Shader中,直接写到SubShader块中。
(2)Blend指令(混合效果):
Blending就是控制透明的。处于光栅化的最后阶段,通过此指令可以控制最后光栅化的渲染。
常见的几种Blend混合命令:
(接下来涉及数学公式,在此不再赘述)
1.Blend Off:不混合。
2.Blend SrcFactor DstFactor:片元产生的颜色乘以SrcFactor,加上屏幕上已有的颜色乘以DstFactor,
得到最终的颜色(写入颜色缓存)
3.Blend SrcFactor DstFactor, SrcF。actorA DstFactorA:同上,只不过使用单独的因子SrcFactorA和DstFactorA来混合透明度通道。
4.BlendOp BlendOperation:用其他的操作来取代加法混合。
5.BlendOp OpColor, OpAlpha:同上,只不过对于透明度通道使用不同的操作。
6.Blend SrcAlpha OneMinusSrcAlpha:传统透明度
7.Blend One OneMinusSrcAlpha:预乘透明度
8.Blend One One:叠加
9.Blend OneMinusDstColor One:柔和叠加
10.Blend DstColor Zero:相乘——正片叠底
11.Blend DstColor SrcColor:两倍相乘
一些常用的混合因子:
1.One:混合因子1,表示完全的源颜色或目标颜色
2.Zero:混合因子0,舍弃掉源颜色或目标颜色
3.SrcColor:源颜色值
4.SrcAlpha:源透明度
5.DstColor :目标颜色值
6.DstAlpha :目标透明度
7.OneMinusSrcColor:1-SrcColor
8.OneMinusSrcAlpha :1-SrcAlpha
10.OneMinusDstColor:1-DstColor
12.OneMinusDstAlpha:1-DstAlpha
(3)ZTest深度测试:
ZTest可以取的值有:Greater/GEqual/Less/LEqual/Equal/NotEqual/Always/Never/Off
默认是LEqual,表示通过比较深度来更改颜色缓存的值。
注意:当ZTest取值为Off时,表示的是关闭深度测试,等价于取值为Always,而不是Never
Always指的是直接将当前像素颜色(不是深度)写进颜色缓冲区中;而Never指的是不要将当前像素颜色写进颜色缓冲区中,相当于消失。
(4)ZWrite深度写入:
ZWrite On | Off 默认值为On,代表是否要将像素的深度写入深度缓存中.
注意:ZWrite的设置同时还要看ZTest是否通过。
ZTest和ZWrite的配合:
1.当ZWrite为On时,ZTest为On时,该像素的深度才能成功写入深度缓存,同时因为ZTest通过了,该像素的颜色值也会写入颜色缓存。
2.当ZWrite为On时,ZTest为Off时,该像素的深度不能成功写入深度缓存,同时因为ZTest不通过,该像素的颜色值不会写入颜色缓存。
3.当ZWrite为Off时,ZTest为On时,该像素的深度不能成功写入深度缓存,同时因为ZTest通过了,该像素的颜色值会写入颜色缓存。
4.当ZWrite为Off时,ZTest为Off时,该像素的深度不能成功写入深度缓存,同时因为ZTest不通过,该像素的颜色值不会写入颜色缓存。
因为ZWrite默认值为On,ZTest默认值为LEqual,所以这很好地解释了为什么在unity中,距离相机近的东西会阻挡住距离相机远的东西。如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这时我们可以通过修改ZWrite和ZTest来改变物体的遮挡关系!
(5)其他指令:
1.Color Color:设定对象的纯色。颜色即可以是括号中的四值(RGBA),也可以是被方框包围的颜色属性名。
2.Material { Material Block }:材质块被用于定义对象的材质属性。
3.Lighting On | Off :定义材质块中的设定是否有效,你必须使用Lighting On 命令开启光照,而颜色则通过Color命令直接给出。
4.SeparateSpecular On | Off:这个命令会添加高光光照到着色器通道的末尾,因此贴图对高光没有影响。只在光照开启时有效。
5.ColorMaterial AmbientAndDiffuse | Emission :使用每顶点的颜色替代材质中的颜色集。AmbientAndDiffuse 替代材质的阴影光和漫反射值;Emission 替代 材质中的光发射值。
6.Material Block 材质块:包含材质如何和光线产生作用的设定。这些属性都是可以被忽略的,默认为值都被设定为黑色(不产生作用)
7.Diffuse Color:漫反射颜色构成。这是对象的基本颜色。
8.Ambient Color:环境色颜色构成.这是当对象被RenderSettings. 中设定的环境色所照射时对象所表现的颜色。
9.Specular Color :对象反射高光的颜色。
10.Shininess Number:加亮时的光泽度,在0和1之间。0的时候你会发现更大的高亮也看起来像漫反射光照,1的时候你会获得一个细微的亮斑。
11.Emission Color:自发光颜色,当不被任何光照所照到时,对象的颜色
它声明了我们要写一个表面Shader,并指定了光照模型。
这是unity创建的初始Standard Surface Shader脚本中的两行#pragma编译指令
格式为:
#pargma 关键词 函数名 光照模型 [其它选项]
函数名在cginc中,实际上会在前部加上 Lighting ,如 LightingStandard , 我们用的时候只取后部分 , 也就是 Standard
使用model 3.0 可以得到一个更好的光照效果, 默认是2.0
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
上面两条语句表示:
当前是一个 surface 着色器, 函数名是 surf, 使用 Standard 基于物理系统光照模式, 有一个完整的向前的阴影。
使用model 3.0 可以得到一个更好的光照效果。(默认是2.0)
又如
#pragma surface surf Lambert addshadow
表示当前是一个 surfac 着色器, 函数名是 surf, 使用 Lambert 兰伯特光照模型, addshadow 表示给物体添加一个阴影
此外如果在有Pass块的Shader中必须在#pragma指令处定义函数,否则不必。
#pragma vertex vert
#pragma fragment frag
定义了顶点着色器和片元着色器。
之后需要声明变量,只有这样才可以在下面的函数中使用他们
定义属性的类型:
1.sampler2D:声明2D图片。
2.float3:声明向量。
3.float4:声明颜色。
4.Vector4:声明点。
5.half4(是float的一半):声明图片,颜色。
6.fixed4(是half的一半):声明图片,颜色。
7.float(32位高精度浮点数):声明浮点数,范围。
8.half(16位中精度浮点数):声明浮点数,范围。
9.fixed(11位低精度浮点数):声明浮点数,范围。
10.sampler3D:声明3D图片。
例如这里就声明了_Glossiness、_Metallic、_Color三个变量。
half _Glossiness;
half _Metallic;
fixed4 _Color;
注意:定义变量尽量选择适宜的精度,在Shader运算很复杂中,不适宜的精度将会大大降低性能。
在Shade中也会用结构体来定义变量:
例如
float4 vert(float4:POSITION):SV_POSITION
{
return mul(UNITY_MATRIX_MVP,v);
}
POSITION与SV_POSITION都是Cg/HLSL中的语义,是不可省略的。
float4:POSITION就是将模型的顶点坐标作为vert()的参数。
又例如
struct input_data
{
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
float3 normal:NORMAL;
};
struct output_data
{
float4 pos: SV_POSITION;
fixed3 color : COLOR0;
};
在input_data中定义了模型顶点坐标、第0套材质的纹理坐标、法线,在output_data中定义了pos里包含了顶点在裁剪空间的位置信息、第0套顶点颜色。
上文中SV_POSITION,POSITION,COLOR0,NORMAL等。这些是CG/HLSL提供的语义。语义实际上就是一个赋给Shader输入输出的字符串,这个字符串表达了这个参数的含义。这些语义可以让Shader知道从哪里读取数据,并把数据输出到哪里。
注意:定义结构体时记得语义绑定,不然会报错。
一些常用的语义(顶点着色器):
1.POSITION 模型空间中的顶点位置,通常是float4类型。
2.TANGENT 顶点切线,float4。
3.TEXCOORD0 顶点纹理坐标, float2,float4。
4.COLORn 顶点颜色,float4,fixed4。
5.NORMAL 顶点法线,float3。
6.SV_POSITION 裁剪空间中的顶点位置 ,结构体中必须包含一个用该语义的变量。
一些常用的语义(片元着色器):
1.SV_Target 输出值存储在渲染目标中。
顶点着色器是在GPU上运行的小程序。顾名思义,顶点着色器是用来处理顶点数据的。一个顶点着色器就是一个处理顶点的小程序。
顶点着色器的输入是一个或多个顶点属性(Vertex Attribute)组成的VertexBuffer。VertexBuffer中的顶点应至少指出了顶点的位置属性,这些位置属性通常指的是每个3D模型(每个模型都有其自身的原点)本身的坐标。顶点着色器将这些位置信息转换为屏幕位置,以便可以正确的显示。VertexBuffer中可能还包括一些其它顶点属性,例如,顶点颜色或纹理UV坐标等。顶点着色器将这些作为输出(最终经过处理的),以便其可在光栅化单元进行插值,然后作为输入传递给片元着色器。
这是Unlit Shader的默认顶点着色器:
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
这里进行了一系列的运算,实现一些希望得到的效果,运算过程在此不再赘述。
就像顶点着色器一样,片段着色器也是运行在GPU上的小程序。顾名思义,用它来处理片段。它们负责输出每个三角形像素的最终颜色。
基本上来说,它是这样运行的:片段着色器将顶点着色器输出的片段作为输入,片段的顶点属性已被光栅化单元进行了插值处理。
片段着色器是可编程管道中的最最核心的部分。其普遍的作用就是计算各种各样的三角形像素颜色,从为着色顶点图形(vertex-colored geometries)计算的顶点属性颜色,和为纹理图形为计算的纹理及相关的UV纹理坐标。
在这里多提一句。
例如,动态光源效果通常都是由片段着色 器完成。思考一下就会明白,动态光源意味着根据场景中已有的光源计算像素颜色,这与几何图形的位置、材料都有很大的关系,所以片段着色器是制作动态光源效 果的不二之选。
像水体环境映射之类的反射特效也都是由片段着色器完成的。片段着色器能生成世界上几乎所有的光影特效,以上提及的只不过是它的冰山一角。
最后要提一下的是,片段着色器决定了你在屏幕上能看到什么,所以,片段着色器才是影响渲染的核心代码。
这是Unlit Shader的默认顶点着色器:
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
这里进行了一系列的运算,实现一些希望得到的效果,运算过程同样在此不再赘述。
语法规则:
Fallback “name”
如果所有子着色器都无法在该硬件上运行,请尝试使用另一个名为name的子着色器。
Fallback Off
如果所有子着色器都无法在该硬件上运行,则不运行任何备用的着色器。
一般在SubShader块结束后写:
SubShader
{
……
}
FallBack "Diffuse"
意为:如果该Shader所编写的所有子着色器都无法在该硬件上运行,尝试使用Diffuse着色器代替运行。
(1)头文件
1、UnityCG.cginc 包含最常用的帮助函数、宏和结构体
2、UnityShaderVariables.cginc 在编译Shader时,会被自动包含进来,包含了许多内置的全局变量,如UNITY_MATRIX_MVP
3、Lighting.cginc 包含了各种内置光照模型,如果编写SurfaceShader的话,会被自动包含进来
4、HLSLSurport.cginc 在编译Shader时,会被自动包含进来,声明了很多跨平台编译的宏和定义
(2)函数
1.float3 WorldSpaceViewDir(float4 v):输入一个模型顶点坐标,得到世界空间中从该点到摄像机的观察方向
2.float3 ObjSpaceViewDir(float4 v):输入一个模型顶点坐标,得到模型空间中从该点到摄像机的观察方向
3.float3 WorldSpaceLightDir(float4 v):输入一个模型顶点坐标,得到世界空间中从该点到光源的光照方向(方向没有归一化,且只可用于前向渲染)
4.float3 ObjSpaceLightDir(float4 v): 输入一个模型顶点坐标,得到模型空间中从该点到光源的光照方向(方向没有归一化,且只可用于前向渲染)
5.float3 UnityObjectToWorldNormal(float3 norm):将法线从模型空间转换到世界空间
6.float3 UnityObjectToWorldDir(in float3 dir):把方向矢量从模型空间转换到世界空间
7.float3 UnityWorldToObjectDir(float3 dir):把方向矢量从世界空间转换到模型空间
(3)内置变量
1.UNITY_MATRIX_MVP:当前模型观察投影矩阵,用于将模型顶点/方向矢量从模型空间转换到裁剪空间
2.UNITY_MATRIX_MV:当前模型观察矩阵,用于将模型顶点/方向矢量从模型空间转换到观察空间
3.UNITY_MATRIX_V:当前观察矩阵,用于将顶点/方向矢量从世界空间变换到观察空间
4.UNITY_MATRIX_P:当前投影矩阵,用于将顶点/方向矢量从观察空间变换到裁剪空间
5.UNITY_MATRIX_VP:当前观察投影矩阵,用于将顶点/方向矢量从世界空间变换到裁剪空间
6.UNITY_MATRIX_T_MV:UNITY_MATRIX_MV转置矩阵
7.UNITY_MATRIX_IT_MV:UNITY_MATRIX_MV逆转置矩阵,可将法线矢量从模型空间转换到观察空间
8._Object2World:当前模型的矩阵,用于将模型顶点/方向矢量从模型空间转换到世界空间
9._World2Object:_Object2World逆矩阵,用于将模型顶点/方向矢量从世界空间转换到模型空间
(4)内置数学函数
1.radians(degree) : 角度变弧度;
2.degrees(radian) : 弧度变角度;
3.sin(angle), cos(angle), tan(angle)
4.asin(x): arc sine, 返回弧度 [-PI/2, PI/2];
5.acos(x): arc cosine,返回弧度 [0, PI];
6.atan(y, x): arc tangent, 返回弧度 [-PI, PI];
7.atan(y/x): arc tangent, 返回弧度 [-PI/2, PI/2];
8.pow(x, y): x的y次方;
9.exp(x): 指数, log(x):
10.exp2(x): 2的x次方, log2(x):
11.sqrt(x): x的根号; inversesqrt(x): x根号的倒数
12.abs(x): 绝对值
13.sign(x): 符号, 1, 0 或 -1
14.floor(x): 底部取整
15.ceil(x): 顶部取整
16.fract(x): 取小数部分
17.mod(x, y): 取模, x - yfloor(x/y)
18.min(x, y): 取最小值
19.max(x, y): 取最大值
20.clamp(x, min, max): min(max(x, min), max);
21.mix(x, y, a): x, y的线性混叠, x(1-a) + ya;
22.step(edge, x): 未知
23.smoothstep(edge0, edge1, x): threshod smooth transition时使用。 edge0<=edge0时为0.0, x>=edge1时为1.0
24.length(x): 向量长度
25.distance(p0, p1): 两点距离, length(p0-p1);
26.dot(x, y): 点积,各分量分别相乘 后 相加
27.cross(x, y): 差积,x[1]*y[2]-y[1]*x[2], x[2]y[0] - y[2]x[0], x[0]y[1] - y[0]x[1]
28.normalize(x): 归一化, length(x)=1;
29.faceforward(N, I, Nref): 如 dot(Nref, I)< 0则N, 否则 -N
30.reflect(I, N): I的反射方向, I -2dot(N, I)N, N必须先归一化
31.refract(I, N, eta): 折射,k=1.0-etaeta(1.0 - dot(N, I) * dot(N, I)); 如k<0.0 则0.0,否则 etaI - (etadot(N, I)+sqrt(k))*N
32.matrixCompMult(matX, matY): 矩阵相乘, 每个分量 自行相乘, 即 r[j] = x[j]*y[j];
33.lessThan(vecX, vecY): 向量 每个分量比较 x < y
34.lessThanEqual(vecX, vecY): 向量 每个分量比较 x<=y
35.greaterThan(vecX, vecY): 向量 每个分量比较 x>y
36.greaterThanEqual(vecX, vecY): 向量 每个分量比较 x>=y
37.equal(vecX, vecY): 向量 每个分量比较 x==y
38.notEqual(vecX, vexY): 向量 每个分量比较 x!=y
39.any(bvecX): 只要有一个分量是true, 则true
40.all(bvecX): 所有分量是true, 则true
41.not(bvecX): 所有分量取反
编写于2019.4.27 21.26分
张晓文