實踐篇:簡單透明度

前言:本篇博客只是一個簡單的實現透明度功能的例子,主要是當做筆記使用。

核心要點:如下所示:
1.要使模型具有透明度效果,必須在Tags中將Queue指定爲Transparent,並且在pass裏面使用blend srcalpha oneminussrcalpha。

2.透明度渲染隊列(Queue = Transparent)在渲染模型時,會先渲染不透明模型再渲染透明模型。並且在渲染時會按照深度值從大到小(也就是離攝像機由遠到近)的順序渲染。

3.模型的每個像素在渲染時會先進行深度測試,也就是將像素的深度值與深度緩衝區中該像素的同位置處的深度值進行比較。當滿足深度測試條件時,會將該像素寫入到顏色緩衝區中。如果pass裏面開啓了深度寫入的話,就會將顏色的深度值寫入到深度緩衝區中對應位置處。

4.如果pass裏面開啓了裁剪功能的話,當開啓了深度寫入時,距離攝像機遠的透明模型會被裁剪掉,如果我們不想被裁剪的話,此時就需要關閉深度寫入。所以一般情況下,對於不透明模型要開啓深度寫入,對於透明模型要關閉深度寫入。但也不是絕對的,具體情況具體分析,可以寫多個pass來控制深度寫入和深度測試,從而達到需要的效果。

5.當像素深度值相同時,往往會產生錯位混亂的效果。所以當同一個模型的網格上的頂點深度值相同時,可以採用分割網格的方式來使深度值不同。當不同的模型上的頂點深度值相同時,可以採用分割模型的方式來使深度值不同。

6.攝像機會將不透明的模型渲染到一張深度紋理中,所以我們可以藉助這一點來做透明模型和不透明模型之間的深度處理。

7.攝像機可以調用SetReplacementShader接口,並傳遞替換着色器對象和替換標籤來對場景中所有滿足替換着色器中指定的替換標籤和替換標籤值的着色器進行替換,按照替換着色器進行渲染,場景中其他不滿足替換條件的着色器將不會進行渲染。

製作流程:如下所示:
1.創建一個場景,裏面建立兩個立方體作爲牆面,一個膠囊體作爲角色。如圖所示:
在這裏插入圖片描述
2.編寫一個具有透明度並輸出藍色的着色器,並掛載Wall1上,如上圖所示。核心代碼如下所示:

Shader "Custom/Transparent_2" {
	SubShader {
		// 隊列爲透明度類型。rendertype爲transparent目的是讓替換着色器進行替換
		Tags { "Queue"="Transparent" "rendertype" = "transparent" }

		pass {
			// 使用透明度混合:當前顏色與顏色緩衝區中顏色混合
			blend srcalpha oneminussrcalpha
			// 關閉深度寫入
			ZWrite off

			CGPROGRAM
			#pragma vertex vert 
			#pragma fragment frag 
			#include "unitycg.cginc"

			struct v2f {
				float4 pos:POSITION0;
			};

			v2f vert(appdata_base v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

				return o;
			}

			fixed4 frag(v2f IN):COLOR0
			{
				// 具有透明度的藍色 
				fixed4 col = fixed4(0, 0, 1, 0.5);

				return col;
			}

			ENDCG
		}
	}
}

3.創建一個具有透明度並遮罩部分輸出黃色,不遮罩部分輸出紅色的着色器,並掛載Role上,如上圖所示。核心代碼如下所示:

Shader "Custom/Transparent_3" {
	SubShader {
		// 渲染隊列爲透明度類型
		Tags{ "Queue" = "Transparent" }

		pass {
			// 和顏色緩衝區中的顏色進行透明度混合
			blend SrcAlpha OneMinusSrcAlpha
			// 由於牆是不透明的,所以深度值寫入到深度緩衝區中了。而角色的深度值大於牆的深度值,所以就會滿足深度
			// 測試從而將牆遮擋部分的像素渲染處理,並和牆渲染到顏色緩衝區的顏色進行透明度混合。
			ZTest greater
			// 開啓深度寫入,從而牆遮擋部分(角色下部分)的深度值會寫入到深度緩衝區中對應像素所在位置處。
			ZWrite on

			CGPROGRAM
			#pragma vertex vert 
			#pragma fragment frag 
			#include "unitycg.cginc"

			struct v2f {
				float4 pos:POSITION0;
			};

			v2f vert(appdata_base v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

				return o;
			}

			fixed4 frag(v2f IN):COLOR0
			{
				// 遮擋部分爲透明黃色
				return fixed4(1, 1, 0, 0.5);
			}
			ENDCG
		}

		pass {
			// 由於角色下部分已經輸出顏色並寫入深度值,此時沒有必要再處理角色下部分。
			// 所以使用less做深度測試,這樣角色下部分由於深度值相同,深度測試失敗不會再做角色下部分渲染。
			// 由於角色上部分沒有其他模型遮擋,深度值小於天空深度值1的,滿足深度測試,此時直接將角色上部分
			// 輸出到顏色緩衝區中
			ZTest less
			// 開啓深度寫入,從而角色不被遮擋部分(角色上部分)的深度值會由寫入到深度緩衝區對應像素所在位置處。
			ZWrite on

			CGPROGRAM
			#pragma vertex vert 
			#pragma fragment frag 
			#include "unitycg.cginc"

			struct v2f {
				float4 pos:POSITION0;
			};

			v2f vert(appdata_base v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

				return o;
			}

			fixed4 frag(v2f IN):COLOR0
			{
				// 輸出不透明的紅色
				return fixed4(1, 0, 0, 1);
			}
			ENDCG
		}
	}
}

4.創建一個替換着色器,用來對具有透明效果的牆按照替換着色器進行處理。而該替換着色器就是一個按照不透明方式輸出模型的深度值。
替換着色器核心代碼如下所示

Shader "Custom/ReplementTransparentShader" {
	SubShader {
		// 場景中需要被替換的shader類型:標籤類型爲rendertype,值爲transparent
		Tags { "rendertype" = "transparent" }

		pass {
			CGPROGRAM
			#pragma vertex vert 
			#pragma fragment frag 
			#include "unitycg.cginc"

			struct v2f {
				float4 pos:POSITION0;
				// 齊次空間下的深度和寬度值
				float2 depth:TEXCOORD0;
			};

			v2f vert(appdata_base v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				o.depth = o.pos.zw;

				return o;
			}

			fixed4 frag(v2f IN):COLOR0
			{
				// 獲取透視除法後的深度值,並限定在0~1之間
				float depth = Linear01Depth(IN.depth.x / IN.depth.y);
				// 將深度值寫入到顏色r分量中
				fixed4 col = fixed4(depth, 0, 0, 0);

				return col;
			}

			ENDCG
		}
	}
}

替換腳本核心代碼如下所示

using UnityEngine;
using System.Collections;

public class ReplementTransparentShader : MonoBehaviour {

	// Use this for initialization
	void Start () {
        // 使用Custom/ReplementTransparentShader路徑的shader中的標籤爲rendertype,值爲該shader該標籤對應值,來對
        // 場景中所有的同標籤同標籤值的shader進行置換,由於置換shader是不透明的,所以該置換shader的渲染結果會存放
        // 在一張深度紋理中。其他標籤和標籤值不同的shader不會進行替換渲染。
        Camera.main.SetReplacementShader(Shader.Find("Custom/ReplementTransparentShader"), "rendertype");
	}
	
	// Update is called once per frame
	void Update () {
	
	}
}

5.將替換腳本掛在角色對象上,運行遊戲會產生一張記錄透明牆的深度紋理,如下圖所示:
在這裏插入圖片描述
6.將替換着色器對透明牆生成的深度紋理與角色遮擋部分(角色下部分)進行深度測試,從而得到和不透明牆遮罩部分一樣的效果。

7.運行結果如下圖所示:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章