《Unity Shader入門精要》自學筆記(五)第八章 透明效果

AZeGurio發表於2020-11-21

官方API:ShaderLab: Blending

這一章的內容巨多啊,資訊量比五六七章加起來還多

不過整清楚渲染順序、深度測試、深度寫入、透明度測試、透明度混合,以及它們對渲染的影響,這一章理起來也就順了

 

 

雜七雜八但是很重要的知識點

在這裡插入圖片描述

深度緩衝區Z-Buffer
儲存螢幕上的每個畫素當前的深度值,值越大,離螢幕越遠

深度測試
將當前片元的深度值與深度緩衝區中的深度值比較,若比深度緩衝區中的值大,則說明這個片元會被之前渲染過的片元擋住,沒有必要再繼續渲染了,所以會被丟棄(當然也可以手工設定遠被捨棄,還是近被捨棄,還是一些其他的判斷標準)

深度寫入ZWrite
當一個片元通過了深度測試,並且pass開啟了深度寫入,這個片元的深度值就會覆蓋深度緩衝區中的深度值;否則不會對深度緩衝區做修改

顏色緩衝區和顏色寫入

通過了各種測試的片元會將自己的顏色存入顏色緩衝區(前提是開啟了顏色寫入)。當有新的片元通過了重重測試,會根據渲染模式來決定,是直接用新片元的顏色覆蓋顏色緩衝區,還是將新老顏色根據透明度做一個混合

 

 

透明度測試和透明度混合

透明度測試
沒通過透明度測試,片元也會被直接丟棄。但和深度測試不一樣的是,它沒有緩衝區,只是將片元的透明度和一個設定的值相比較,是否滿足一個設定的條件(< > = ≥ ≤ 等),不滿足直接丟棄
在這裡插入圖片描述
也就是“要麼看得見要麼看不見”的簡單粗暴效果,還會因為浮點數精度問題有明顯的鋸齒

一般會使用Clip()函式:給定引數的任意一個分量是負數,都會捨棄該片元

透明度混合
簡單來講,它要做的就是上面提到的“顏色緩衝區新老顏色按照透明度進行混合”,這個才是比較像真正的透明效果

在這裡插入圖片描述
 

 

渲染順序、關閉深度寫入後的一系列填坑操作

渲染順序
就是渲染不同型別物體的順序:背景、不透明物體、透明物體等
unity中準備了5個佇列,為SubShader提供了Queue標籤:
在這裡插入圖片描述

開啟深度寫入的情況下,完全不用關心渲染順序,因為每個片元的覆蓋與否都會經過比較得出結果
但是為了獲得透明效果,需要在渲染透明物體時關閉深度寫入,這個時候就體現了渲染順序的重要:
如果透明物體A和不透明物體B一前一後,先渲染了透明物體A,B來的時候A並沒有把深度值寫入,所以B按照流程理所應當的把自己的顏色整個填進了顏色緩衝區,覆蓋了A的顏色,並且沒有執行透明度混合
如果先渲染了不透明的B,再渲染透明的A的話,A發現自己沒被遮擋後,會執行透明度混合,達到透明的效果

誰讓不透明物體的shader就是不會管這麼多呢,關閉深度寫入和透明度混合只寫在透明物體自己的shader裡呀,那隻能你殿後了鴨

那…

為啥要關閉深度寫入
首先,深度寫入被關閉的是透明物體
因為我們是可以看到透明物體背後的其他物體的,也就是說我們不能直接丟棄深度值大於透明物體的物體
一旦開啟了深度寫入,深度檢測就會丟棄透明物體後面的物體
因此只好關閉深度寫入了,雖然帶來了一系列的麻煩

開啟深度寫入的透明效果

一些情況下就算安排好了渲染順序,還是會有意外發生

因為渲染順序是針對每一個模型的,而每一個模型可以是各種形狀,就導致了模型之間的前後順序並不絕對
在這裡插入圖片描述

而對於一些本身就比較複雜的模型,自己一個人也可以玩出這種錯亂效果

在這裡插入圖片描述

這時可以將物體切分成多個網格,但是還是無法完全避免穿幫,因為它依然不是針對每一個畫素進行深度測試

或者,乾脆在不影響透明度混合的前提下把可愛的深度寫入安回來:

現在的shader是這樣的

SubShader
{
	Tags{"Queue" = "Transparent", "IgnoreProjector" = "True", "RenderType" = "Transparentut"}
	Pass
	{
		Tags{"LightMode" = "ForwardBase"}
		ZWrite Off
		Blend SrcAlpha OneMinusSrcAlpha
		
		// ...
	}
}

嗯?ZWrite off和Blend命令寫在了Pass裡,那再寫一個ZWrite On的命令在另一個Pass裡不就好了嘛!先做個深度測試,再上色!(代價是需要多渲染一遍,因為增加了一個pass)

SubShader
{
	Tags{"Queue" = "Transparent", "IgnoreProjector" = "True", "RenderType" = "Transparentut"}
	Pass
	{
		ZWrite On
		ColorMask 0
	}
	Pass
	{
		Tags{"LightMode" = "ForwardBase"}
		ZWrite Off
		Blend SrcAlpha OneMinusSrcAlpha
		
		// ...
	}
}

效果:
不會錯亂了,但是細節也少了:

在這裡插入圖片描述
自己被自己遮擋的部分完全看不見了,只能看見下面的不透明平面

在這裡插入圖片描述
被另一個透明物體遮擋的部分也看不見了,只能看見下面的不透明平面

這些都是因為被遮擋的片元被深度測試卡掉了

 

雙面渲染的透明效果 —— Cull命令

上面的透明效果都是隻能看到正面,物體內部和背面的情況完全看不到,是因為引擎自動剔除了物體背面(相對於攝像機的方向)的渲染圖元

想要雙面效果,就要渲染出背面的圖元,unity中要用到Cull指令:

Cull Back | Front | Off

分別代表剔除背面(預設情況)、前面、關閉剔除

對於雙面的透明度測試,只要在Pass內關閉剔除就可以了

但是對於雙面的透明度混合,就要考慮到渲染順序,誰讓萬惡之源是ZWrite Off呢…

由於Pass是按順序執行的,所以可以將背面和正面的渲染分開到先後兩個Pass中,使用Cull Front和Cull Back分別渲染

在這裡插入圖片描述

不過這樣物體之間的順序還是亂的,因為沒有了深度寫入

 

混合型別:混合因子、混合命令

應該會有很多高階玩法待開發呢

在這裡插入圖片描述
混合因子就是混合命令中的SrcFactor和DstFactor,分別代表源顏色(該片元的顏色)的混合因子和目標顏色(顏色緩衝區中的顏色)的混合因子
表格第三行的命令寫成式子就是:
O r g b O_{rgb} Orgb = S r c F a c t o r SrcFactor SrcFactor * S r g b S_{rgb} Srgb + D s t F a c t o r DstFactor DstFactor * D r g b D_{rgb} Drgb
O a O_a Oa = S r c F a c t o r A SrcFactorA SrcFactorA * S a S_a Sa + D s t F a c t o r A DstFactorA DstFactorA * D a D_a Da
表格第二行的命令中SrcFactorA就是SrcFactor,DstFactorA就是DstFactor

混合因子SrcFactor、DstFactor可以是什麼呢
在這裡插入圖片描述
emm其實就是按照目標值&源值、rgb值&alpha值、原值&1-原值、0&1啦…

混合操作BlendOp Operation都有啥?
BlendOp Add就是預設的混合操作,需要改變的話要在指定混合因子前新增BlendOp命令

在這裡插入圖片描述
可以看出Min和Max只與源顏色和目標顏色的rgba值相關,所以在用他們的時候混合因子可以直接指定為One或者Zero

未列出的DirectX混合操作見官方API:ShaderLab: Blending

 

混合型別有哪些呢?
書中和工程檔案中提供了一些常見的型別
在這裡插入圖片描述
再分開看看
在這裡插入圖片描述

Blend SrcAlpha OneMinusSrcAlpha

O r g b = S r c A l p h a ∗ S r g b + ( 1 − S r c A l p h a ) ∗ D r g b O a = S r c A l p h a ∗ S a + ( 1 − S r c A l p h a ) ∗ D a \begin{aligned} O_{rgb} = SrcAlpha * S_{rgb} + (1 - SrcAlpha )* D_{rgb}\\ O_a = SrcAlpha * S_a + (1 - SrcAlpha ) * D_a \end{aligned} Orgb=SrcAlphaSrgb+(1SrcAlpha)DrgbOa=SrcAlphaSa+(1SrcAlpha)Da

 

在這裡插入圖片描述

Blend OneMinusDstColor One

O r g b = ( 1 − D s t C o l o r ) ∗ S r g b + D r g b O a = ( 1 − D s t C o l o r ) ∗ S a + D a \begin{aligned} O_{rgb} = ( 1 - DstColor)* S_{rgb} + D_{rgb}\\ O_a = ( 1 - DstColor)* S_a + D_a \end{aligned} Orgb=(1DstColor)Srgb+DrgbOa=(1DstColor)Sa+Da

 

在這裡插入圖片描述

Blend DstColor Zero

O r g b = D s t C o l o r ∗ S r g b O a = D s t C o l o r ∗ S a \begin{aligned} O_{rgb} = DstColor * S_{rgb}\\ O_a = DstColor * S_a \end{aligned} Orgb=DstColorSrgbOa=DstColorSa

 

在這裡插入圖片描述

Blend DstColor SrcColor

O r g b = D s t C o l o r ∗ S r g b + S r c C o l o r ∗ D r g b O a = D s t C o l o r ∗ S a + S r c C o l o r ∗ D a \begin{aligned} O_{rgb} = DstColor * S_{rgb} + SrcColor* D_{rgb}\\ O_a = DstColor * S_a + SrcColor* D_a \end{aligned} Orgb=DstColorSrgb+SrcColorDrgbOa=DstColorSa+SrcColorDa

 

在這裡插入圖片描述

BlendOp Min
Blend One One

O = ( m i n ( S r , D r ) , m i n ( S g , D g ) , m i n ( S b , D b ) , m i n ( S a , D a ) ) \begin{aligned} O = (min(S_r, D_r), min(S_g, D_g), min(S_b, D_b), min(S_a, D_a)) \end{aligned} O=(min(Sr,Dr),min(Sg,Dg),min(Sb,Db),min(Sa,Da))

 

在這裡插入圖片描述

BlendOp Max
Blend One One

O = ( m a x ( S r , D r ) , m a x ( S g , D g ) , m a x ( S b , D b ) , m a x ( S a , D a ) ) \begin{aligned} O = (max(S_r, D_r), max(S_g, D_g), max(S_b, D_b), max(S_a, D_a)) \end{aligned} O=(max(Sr,Dr),max(Sg,Dg),max(Sb,Db),max(Sa,Da))

 

在這裡插入圖片描述

Blend OneMinusDstColor One
(Blend One OneMinusSrcColor)

O r g b = ( 1 − D s t C o l o r ) ∗ S r g b + D r g b O a = ( 1 − D s t C o l o r ) ∗ S a + D a \begin{aligned} O_{rgb} = (1 - DstColor)* S_{rgb} + D_{rgb}\\ O_a = (1 - DstColor)* S_a + D_a \end{aligned} Orgb=(1DstColor)Srgb+DrgbOa=(1DstColor)Sa+Da

 

在這裡插入圖片描述

Blend One One

O r g b = S r g b + D r g b O a = S a + D a \begin{aligned} O_{rgb} = S_{rgb} + D_{rgb}\\ O_a = S_a + D_a \end{aligned} Orgb=Srgb+DrgbOa=Sa+Da

 

總結
感覺自己的筆記記得越來越亂了…果然知識多了就要勤記著點…
透明度混合這裡有很多可以做實驗的地方,各種混合操作的效果還不是特別有印象,抽空要多玩玩這裡,回來補一下筆記

這一章終於開始更多的接觸到tags啦,以及多個pass相互協助的使用,也讓shader變得有趣起來了,後面的學習一定也會更有意思的吼吼吼!
也算是終於好好的理解一把ps的圖層型別哈哈哈

相關文章