混合操作指的是源颜色*因子后和目标颜色*因子后的值的操作方式,一般默认是+起来的,但是我们可以使用混合操作命令修改操作。
BlendOp BlendOperation
BlendOperation有:
对于混合,就会有一个等式来计算。结果颜色就是O,S就是源颜色(片元的颜色),D为目标颜色(颜色缓冲读取出来的)。这时候就需要一些因子来计算出结果O了,RGB和A是使用不同的因子的,所以将会有4个因子的存在。
O(rgb) = 因子1 * S(rgb) + 因子2 * D(rgb)
O(a) = 因子3 * S(a) + 因子4 * D(a)
我们能控制的,就是对这些因子的配置来控制混合的计算。
第一种设置表示 因子3 = 因子1,因子4 = 因子2。第二种就是一一对应了。
因子的值可设置为:
Shader "Unlit/pass2" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color("Color",Color) = (1,1,1,1) _AlphaScale("Alpha Scale",Range(0,5)) = 0.5 } SubShader { Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } //第一个通道用来写入深度值,但是不写入颜色值 Pass { ZWrite on ColorMask 0 } Pass { Tags{ "LightMode" = "ForwardBase" } ZWrite Off //关闭深度测试 Blend SrcAlpha OneMinusSrcAlpha //开启混合,设置混合因子 CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal:NORMAL; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float3 worldNormal:TEXCOORD1; float3 worldPos : TEXCOORD2; }; sampler2D _MainTex; float4 _MainTex_ST; float3 _Color; float _AlphaScale; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); fixed3 wn = normalize(i.worldNormal); fixed3 wl = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 diffuse = (dot(wn, wl) * 0.5 + 0.5) * _LightColor0.rgb * _Color.rgb * col.rgb; return fixed4(diffuse,col.a * _AlphaScale);//只有开启了Blend,返回透明度才有用 } ENDCG } } }
这里和混合实例是差不多的,就是多了一个Pass通道来写入深度值,达到的效果就是在一些互相交叉的物体上有了深度值,剔除一些元素,使自身透明不能看到自身。
看图:左边双通道,右边单纯的混合
Shader "Unlit/blend" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color("Color",Color) = (1,1,1,1) _AlphaScale("Alpha Scale",Range(0,5)) = 1 } SubShader { Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } Pass { Tags{ "LightMode" = "ForwardBase" } ZWrite Off //关闭深度写入 Blend SrcAlpha OneMinusSrcAlpha //开启混合,设置混合因子 CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal:NORMAL; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float3 worldNormal:TEXCOORD1; float3 worldPos:TEXCOORD2; }; sampler2D _MainTex; float4 _MainTex_ST; float3 _Color; float _AlphaScale; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); fixed3 wn = normalize(i.worldNormal); fixed3 wl = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 diffuse = (dot(wn, wl) * 0.5 + 0.5) * _LightColor0.rgb * _Color.rgb * col.rgb; return fixed4(diffuse,col.a * _AlphaScale);//只有开启了Blend,返回透明度才有用 } ENDCG } } }
Shader "Unlit/transparent" { Properties { _Color("Color",Color)=(1,1,1,1) _MainTex ("Texture", 2D) = "white" {} _Cutoff("CutOff",Range(0,1))=0.5 } SubShader { Tags{ "Queue" = "AlphaTest" "IgnoreProjector"="True" "RenderType" = "TransparentCutout" } Pass { Tags{"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal:NORMAL; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float3 worldNormal:TEXCOORD1; float3 worldPos:TEXCOORD2; }; sampler2D _MainTex; float4 _MainTex_ST; float3 _Color; float _Cutoff; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); clip(col.a - _Cutoff);//透明度测试 /*if ((col.a - _Cutoff) < 0.0) { discard;//不通过直接舍弃 }*/ fixed3 wn = normalize(i.worldNormal); fixed3 wl = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 diffuse = (dot(wn, wl) * 0.5 + 0.5) * _LightColor0 * _Color * col; return fixed4(diffuse,1); } ENDCG } } }
Tags{ "Queue"="AlphaTest" }
在Unity中有两种方式可以实现透明效果:一是使用透明度测试(Alpha Test)、一个是透明度混合(Alpha Blending)。要实现透明的效果就需要注意渲染的顺序了,在没有透明物体的时候,渲染顺序是不重要的,因为有一个深度测试,就是离摄像机的远近程度,深度值小的颜色值就会覆盖深度大的buff,反之直接舍弃了,所以无论怎么渲染都是对的。但是如果有透明物体后,透明物体就应该在不透明的物体后面渲染,在渲染透明物体时要关闭深度写入(更新深度值buff)。对于透明物体需要注意重叠的情况,尽量细分模型。
常用的办法是:
先渲染所有不透明物体,并开启它们的深度测试和深度写入。
把半透明物体按它们距离摄像机的远近进行排序,然后按照从后往前的循序渲染这些半透明物体,并开启他们的深度测试,但关闭深度写入。
遮罩纹理是为了具体的控制某些光照的影响,也就是可以逐像素的控制表面的属性。比如说,一张纹理有4个通道RGBA,我们可以利用A通道存储高光反射的强度,G通道存储边缘光照的强度,B通道存储高光反射的指数,A通道存储自发光的强度。这样,使用遮罩纹理就能很好的控制每一个顶点受到的光照影响。
Shader "Unlit/jianbian" { Properties { _Color ("Color",Color) = (1,1,1,1) _RampTex ("Texture", 2D) = "white" {} } SubShader { Tags { "LightMode"="ForwardBase" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float3 normal:NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 worldNormal:TEXCOORD1; float3 worldPos:TEXCOORD2; }; fixed4 _Color; sampler2D _RampTex; float4 _RampTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; return o; } fixed4 frag (v2f i) : SV_Target { fixed3 worldNormal = normalize(i.worldNormal); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed z = 0.5 * dot(worldNormal, worldLightDir) + 0.5; //利用光照影响因子构建uv来取样渐变纹理,从而影响漫反射 fixed3 diffuse = tex2D(_RampTex, fixed2(z, z)).rgb * _Color.rgb * _LightColor0.rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; return fixed4( diffuse.rgb + ambient.rgb,1); } ENDCG } } }
在法线贴图导入到Unity后,可以设置为Normal map类型,这一步操作Unity会根据平台来看看是否对法线贴图进行压缩,随后中需要使用UnpackNormal函数来映射为法线。
inline fixed3 UnpackNormalDXT5nm (fixed4 packednormal) { fixed3 normal; normal.xy = packednormal.wy * 2 - 1; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));//根号(1-(x^2 + y^2)) return normal; } inline fixed3 UnpackNormal(fixed4 packednormal) { #if defined(UNITY_NO_DXT5nm) return packednormal.xyz * 2 - 1; #else return UnpackNormalDXT5nm(packednormal); #endif }
法线可以压缩,只用两个通道就可以的原因在于:
一、法线是一个单位向量,也就是x^2+y^2+z^2=1
二、法线是一个只有正方向的向量