第七章 透明效果(3)

@緩存

1.開啓深度寫入的半透明效果

在上一節,咱們給出了一種因爲關閉深度寫入而形成的錯誤排序狀況。一種解決方法是使用兩個Pass來渲染模型:
第一個Pass開啓深度寫入,但不輸出顏色,它的目的僅僅是爲了把該模型的深度值寫入深度緩衝中;第二個Pass進行正常的透明度混合,因爲上一個Pass已經獲得了逐像素的正確的深度信息,該Pass就能夠按照像素級別的深度排序結果進行透明渲染。但這種方法的缺點在於,多使用一個Pass會對性能形成必定的影響。在本節最後,咱們能夠獲得相似下圖的效果:
在這裏插入圖片描述
能夠看出,使用這種方法,咱們仍然能夠實現模型與它後面的背景混合的效果,但模型內部之間不會有任何真正的半透明效果。
本節使用的代碼和AlphaBlend幾乎同樣,咱們只需在原來的基礎上再增長一個新的Pass便可。性能

Properties{
_Color("Main Tint",Color)=(1,1,1,1)
_MainTex("Main Tex",2D)="white"{}
_AlphaScale("Alpha Scale",Range(0,1))=1
}
SunShader{
Tags{"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transpraent"}
//Extra pass that renders to depth buffer only
Pass{
Zwrite On
ColorMaxk 0
}
Pass{
//和上一節同樣的代碼
}
}
Fallback"Diffuse"

這個新添加的Pass的目的僅僅是爲了把模型的深度信息寫入深度緩衝中,從而剔除模型中被自身遮擋的片元。所以,Pass的第一行開啓了深度寫入。在第二行,咱們使用了一個新的渲染命令——ColorMask。在ShaderLab中,ColorMask用於設置顏色通道的寫掩碼(write mask)。它的語義以下:
ColorMask RGB|A|0|其它任何R、G、B、A的組合
當ColorMask設爲0時,意味着該Pass不寫入任何通道,即不會輸出任何顏色。這正是咱們須要的——該Pass只需寫入深度緩存便可。測試

2.ShaderLab的混合命令

在前面,咱們已經看到如何利用Blend命令進行混合。實際上,混合還有不少其餘的用處,不只僅是用於透明度混合。在本節裏,咱們將更加詳細的瞭解混合中的細節問題。
咱們首先來看一下混合時如何實現的。當片元着色器產生一個顏色的時候,能夠選擇與顏色緩衝中的顏色進行混合。這樣一來,混合就和兩個操做數有關:源顏色(source color)和目標顏色(destination color)。源顏色,咱們用S表示,指的是由片元着色器產生的顏色值;目標顏色,咱們用D表示,指的是從顏色緩衝中讀取到的顏色值。對它們進行混合後獲得的輸出顏色,咱們用O表示,它會從新寫入到顏色緩衝中。咱們須要注意的是,當咱們談及混合中的源顏色、目標顏色和輸出顏色時,它們都包含了RGBA四個通道的值,而並不是僅僅是RGB通道。
想要使用混合,咱們必須首先開啓它。在Unity中,咱們使用Blend(Blend Off命令除外)命令時,除了設置混合狀態外也開啓了混合。可是,在其餘圖形API中咱們是須要手動開啓的。例如在OpenGl中,咱們須要使用glEnable(GL_BLEND)來開啓混合。但在Unity中,它已經在背後爲咱們作了這些工做。spa

2.1 混合等式和參數

前面咱們提到過,混合是一個逐片元的操做,並且它是不可編程的,但倒是高度可配置的。也就是說,咱們能夠設置混合時使用的運算操做、混合因子等來影響混合。那麼,這些配置又是如何實現的呢?
如今,咱們已知兩個操做數:源顏色S和目標顏色D,想要得出輸出顏色O就必須使用一個等式來計算。咱們把這個等式稱爲混合等式(blend equation)。進行混合時,咱們須要兩個混合等式:一個用於混合RGB通道,一個用於混合A通道。當設置混合狀態時,咱們實際上設置的就是混合等式中的操做和因子。在默認狀況下,混合等式使用的操做都是加操做(咱們也可使用其它操做),咱們只需再設置一下混合因子便可。因爲須要混合兩個等式(分別用於混合RGB通道和A通道),每一個等式有兩個因子(一個用於和源顏色相乘,一個用於和目標顏色相乘),所以一共須要四個因子。下表給出了ShaderLab中設置混合因子的命令。
code

能夠發現,第一個命令只提供了兩個因子,這意味着將使用一樣的混合因子來混合RGB通道和A通道,即此時SrcFactorA將等於SrcFactor,DstFactorA將等於DstFactor。下面就是使用這些因子進行加法混合時使用的混合公式:
在這裏插入圖片描述
那麼,這些混合因子能夠由哪些值呢?下表給出了ShaderLab支持的幾種混合因子:
orm

使用上面的指令進行設置時,RGB通道的混合因子和A通道的混合因子都是同樣的,有時咱們但願可使用不一樣的參數混合A通道,這時就能夠利用Blend SrcFactor DstFactor,SrcFactorA DstFactorA指令。例如,咱們想要在混合後,輸出顏色的透明度值就是源顏色的透明度,就可使用下面的指令:blog

Blend SrcAlpha OneMinusSrcAlpha, One Zero

2.2 混合操做

在上面涉及的混合等式中,當把源顏色和目標顏色與它們對應的混合因子相乘後,咱們都是把它們的結果加起來做爲輸出顏色的。那麼可不能夠選擇不使用加法,而使用減法呢?答案是確定的,咱們可使用ShaderLab的BlendOp BlendOperation命令,即混合操做命令。下表給出了ShaderLab中支持的混合操做。
排序

混合操做命令一般是與混合因子命令一塊兒工做的。但須要注意的是,當使用Min或Max混合操做時,混合因子實際上是不齊任何做用的,它們僅會判斷原始的源顏色和目的顏色的比較結果。圖片

2.3 常見的混合類型

經過混合操做和混合因子命令的組合,咱們能夠獲得一些相似Photoshop混合模式中的混合效果:

//正常(Normal),即透明度混合
Blend SrcAlpha OneMinusSrcAlpha
//柔和相加(soft Additive)
Blend OneMinusDstColor 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

下圖給出了上面不一樣設置下獲得的結果。
在這裏插入圖片描述
須要注意的是,雖然上面使用的Min和Max混合操做時仍然設置了混合因子,但實際上它們並不會對結果有任何影響,由於Min和Max混合操做會忽略混合因子。另外一點是,雖然上面有些混合模式並無設置混合操做的類型,可是它們默認就是使用加法操做,至關於設置了BlendOp Add。

3.雙面渲染的透明效果

在現實生活中,若是一個物體是透明的,意味着咱們不只能夠透過它看到其它物體的樣子,也能夠看到它的內部結構。但在前面實現的透明效果中,不管是透明度測試仍是透明度混合,咱們都沒法觀察到正方體內部及其背面的形狀,致使物體看起來好像只有半個同樣。這是由於,默認狀況下,渲染引擎剔出了物體背面(相對於攝像機的方向)的渲染圖元,而只渲染了物體的正面。若是咱們想要獲得雙面渲染的效果,可使用Cull指令來控制須要剔除哪一個面的渲染圖元。在Unity中,Cull指令的語法以下:

Cull Back| Front | Off

若是設置爲Back,那麼那些背對着攝像機的渲染圖元就不會被渲染,這也是默認狀況下的剔除狀態;若是設置爲Front,那麼那些朝向攝像機的渲染圖元就不會被渲染;若是設置爲Off,就會關閉剔除功能,那麼全部的渲染圖元都會被渲染,但因爲這時須要渲染的圖元數目會成倍增長,所以除非是用於特殊效果,例如這裏的雙面渲染的透明效果,一般狀況下是不會關閉剔除功能的。

3.1 透明度測試的雙面渲染

咱們首先來看一下,如何讓使用了透明度測試的物體實現雙面渲染的效果。這很是簡單,只需在Pass的渲染設置中使用Cull指令來關閉剔除便可。

Pass{
Tags{"LightMode"="ForwardBase"}
//Turn off culling
Cull Off
}

如上所示,這行代碼的做用是關閉剔除功能,是的該物體的全部渲染圖元都會被渲染。由此,咱們能夠獲得下圖的效果:
在這裏插入圖片描述
此時,咱們能夠經過正方體的鏤空區域看到內部的渲染結果。

3.2透明度混合的雙面渲染

和透明度測試相比,想要讓透明度混合實現雙面渲染會更復雜一些,這是由於透明度混合須要關閉深度寫入,而這是「一切混亂的開端」。咱們知道,想要獲得正確的透明效果,渲染順序是很是重要的——咱們想要保證圖元是從後往前渲染的。對於透明度測試來講,因爲咱們沒有關閉深度寫入,所以能夠利用深度緩衝按逐像素的粒度進行深度排序,從而保證渲染的正確性。然而一旦關閉了深度寫入,咱們就須要當心的控制渲染順序來獲得正確的深度關係,若是咱們讓然採樣上面的方法,直接關閉剔除功能,那麼咱們就沒法保證同一個物體的正面和背面的渲染順序,就有可能獲得錯誤的半透明效果。
爲此,咱們選擇把雙面渲染的工做分紅兩個Pass——第一個Pass只渲染背面,第二個Pass只渲染正面,因爲Unity會順序執行SubShader中的各個Pass,所以咱們能夠保證背面老是在正面渲染以前渲染,從而能夠保證正確的深度渲染關係。
主要代碼:

Properties{
_Color("Main Tint",Color)=(1,1,1,1)
_MainTex("Main Tex",2D)="white"{}
_AlphaScale("Alpha Scale",Range(0,1))=1
}
SubShader{
Tags{"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass{
Tags{"LightMode"="ForwardBase"}
//First pass renders only back faces
Cull Front
//和以前同樣的代碼
}
Pass{
Tags{"LightMode"="ForwardBase"}
//Second pass renders only front faces
Cull back
//和以前同樣的代碼
}
}
Fallback"Transparent/VertexLit"

經過上面的代碼咱們能夠獲得下圖的效果:
在這裏插入圖片描述

相關文章
相關標籤/搜索