1. 程式人生 > >Unity Shader入門精要筆記(十八):透明度混合

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

下圖是上面得到的效果: