Unity Shader入門精要筆記(十八):透明度混合
本系列文章由Aimar_Johnny編寫,歡迎轉載,轉載請標明出處,謝謝。
前面章節介紹過,透明度混合是實現了真正的半透效果。它會以當前片元透明度作為混合因子,與已經儲存在顏色緩衝區中的顏色進行混合,得到新的顏色。同時要關掉深度寫入,小心物體的渲染順序。
為了進行混合,我們用Unity提供的混合命令——Blend。如下表:
本節用Blend SrcFactor DstFactor來進行混合。這個命令在設定混合因子的同時也開啟了混合模式,否則不會有混合效果,因為開啟了混合模式,片元的透明通道才有意義。我們把源顏色的混合因子設為SrcAlpha,目標的設為OneMinusSrcAlpha。則混合後的新顏色為:
DstColor = SrcAlpha * SrcColor + (1 - SrcAlpha) * DstColor
在上程式碼之前,我們先看看要實現的效果:
我們用上一章提供的紋理,配合立方體實現如上效果,程式碼如下:
Shader "CustomShader/Transparent/AlphaBlendShader" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Color Tint", Color) = (1, 1, 1, 1) _AlphaScale ("Alpha Scale", Range(0, 1)) = 1 } SubShader { Tags {"Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True"} Pass { Tags {"LightMode" = "ForwardBase"} ZWrite Off Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #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; fixed4 _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).xyz; return o; } fixed4 frag (v2f i) : SV_Target { fixed3 worldNormal = normalize(i.worldNormal); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed4 texColor = tex2D(_MainTex, i.uv); fixed3 albedo = texColor.rgb * _Color.rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir)); return fixed4(ambient + diffuse, texColor.a * _AlphaScale); } ENDCG } } FallBack "Transparent/VertexLit" }
上面的程式碼去掉光照和紋理取樣部分,剩下的透明處理部分其實很少,我們著重看一下。
首先要注意的是標籤,我們用瞭如下標籤:
Tags {"Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True"}
Queue渲染佇列用Transparent表明透明混合佇列,RenderType也用Transparent把這個Shader歸入到提前定義的組(Transparent)中,用來指明該Shader是一個使用了透明度混合的shader。IgnoreProjector設為True來不受到投影器的影響。
然後Pass裡我們關閉了深度寫入:
ZWrite Off
開啟並設定混合模式:
Blend SrcAlpha OneMinusSrcAlpha
最後我們在片元著色器中設定了透明通道
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
_AlphaScale可以用來調節透明的強度。
這樣就可以用紋理的alpha和我們自定義的AlphaScale疊加控制最後的透明效果了。
上面我們只是用最常見的方法實現了透明效果,下面要重點講一下透明引數的各種設定。
當進行混合時,我們需要兩個混合等式,一個用於混合RGB通道,一個用於混合A通道。當設定混合狀態時,我們實際上設定的是混合等式中的操作和因子。預設情況下,混合等式使用的操作都是加操作(當然也可以使用其他操作),我們可以不用管,大多數情況我們只設置混合因子就可以了。由於需要兩個等式,每個等式有兩個因子(一個和源顏色相乘,一個和目標顏色相乘),所以一共需要4個因子。下表給出了ShaderLab設定混合因子的命令:
第一個命令只提供了兩個因子,這意味著使用SrcFactor代替SrcFactorA,DstFactor代替DstFactorA。下面就是混合公式:
下表是ShaderLab提供的幾種混合因子:
上面提到過,混合操作預設是加法,如果我們有其他需求,可以用ShaderLab的BlendOp BlendOperation設定混合操作,下表是ShaderLab支援的混合操作:
需要注意的是,當使用Min或Max時,混合因子是不起作用的,它只會判斷源顏色和目標顏色的比較結果。
最後我們列舉出幾種常見的混合型別,得到類似Photoshop混合模式中的效果:
//正常(Normal)
Blend SrcAlpha OneMinusSrcAlpha
//柔和相加(Soft Addtive)
Blend OneMinusDstAlpha One
//正片疊底(Multiply),即相乘
Blend DstColor Zero
//兩倍相乘(2x Multiply)
Blend DstColor SrcColor
//變暗(Darken)
BlendOp Min
Blend One One
//變亮(Lighten)
BlendOp Max
Blend One One
//濾色(Screen)
Blend OneMinusDstColor One
//等同於
Blend One OneMinusSrcColor
//線性減淡(Linear Dodge)
Blend One One
下圖是上面得到的效果: