NPR中的透明物體bloom

PBR由於要搞線性空間和色調映射,通常會用R16B16G16A16(對Unity而言就是打開Camera上的HDR開關)來直接儲存HDR的顏色值,輝光的blur效果會直接以亮度值為依據,透明物體也能正確blend,不需要特殊處理。

但是NPR卻不能這麼做,因為從一開始光照的部分就不正常,bloom並不是亮度高的地方強烈,而是「想什麼地方強烈就什麼地方強烈」,所以就只能通過在屏幕上標記bloom強度來實現,通常的做法是將bloom的強度直接存在alpha通道上。

fixed4 frag (v2f i) : SV_Target{ fixed4 col = tex2D(_MainTex, i.uv); col.a = _Bloom; return col;}

然後在Bloom後處理的DownSample階段,用Alpha作為權重降低這張Sample圖的顏色值

fixed4 fragDownsample ( v2f_tap i ) : SV_Target{ fixed4 color = tex2D (_MainTex, i.uv20); color += tex2D (_MainTex, i.uv21); color += tex2D (_MainTex, i.uv22); color += tex2D (_MainTex, i.uv23); color /= 4; return max(color - THRESHHOLD, 0) * color.a * ONE_MINUS_THRESHHOLD_TIMES_INTENSITY;}

這種做法在處理不透明物體時是沒問題的,因為原本就不使用alpha通道。但是在透明物體部分,alpha和bloom需要佔用同一個輸出通道,Blend時就會產生衝突。

如果是Add疊加模式還可以處理,因為它實際上也不需要使用Alpha通道,只要需要把透明度預乘到顏色值上。

fixed4 frag (v2f i) : SV_Target{ fixed4 col = tex2D(_MainTex, i.uv); col.rgb *= col.a; col.a *= _Bloom; return col;}

普通的AlphaBlend就沒辦法了。

如果Bloom值固定為1,那麼可以用普通的透明Shader正常繪製

但如果這部分Bloom值需要可定製,不用MRT,目前唯一的辦法是雙PASS。

PASS 1: Blend SrcAlpha OneMinusSrcAlphaColorMask RGBfixed4 frag (v2f i) : SV_Target{ fixed4 col = tex2D(_MainTex, i.uv) * _Color; return col;}PASS 2: Blend SrcAlpha OneMinusSrcAlphaColorMask Afixed4 fragBloom (v2f i) : SV_Target{ fixed alpha = tex2D(_MainTex, i.uv).a; return fixed4(0,0,0,_Bloom * alpha);}

也就是先屏蔽掉Alpha通道繪製正確的顏色混合,然後再補畫Alpha通道,將Bloom值加入。

理論上是可以利用MRT多加張紋理來專門記錄Bloom值,這樣可以迴避掉這個雙Pass,但這就需要考慮設備兼容性了,還有多餘的帶寬成本。考慮到Add,和願意讓Bloom值為1的透明物體數量眾多,使用雙Pass的情況其實也不是那麼多,所以現在的方案其實適用度已經很廣了。


不過上面最後一個Shader其實是錯的,估計不少人都會犯這個錯。

首先要先說明下GPU BLEND的方式:

Blend SrcAlpha OneMinusSrcAlpha

這是典型的Alpha Blend,第一項指的是「要繪製的部分的alpha通道值」,第二項指的是「一減去前面的那個值」。

最終顏色值就是:

要繪製的顏色的rgba * 繪製部分的alpha + 原來屏幕的顏色 *(1 - 繪製部分的alpha)

在這個公式里,雖然顏色值的疊加效果是正常的,但是alpha通道的結果卻搞了一個alpha的平方出來。

如果屏幕原Alpha值是1時,當圖片Alpha為0.5時,結果卻是0.75。而如果屏幕原Alpha值是0的時候(目前的情況),結果是0.25。

總之,這個錯誤的原因是Alpha通道被重複計算了。

最後導致的結果是,同樣的圖片內容,Bloom值都是0.5,如果使用透明Shader,Bloom效果只有非透明的一半。

所以Pass 2的Blend應該換成

Blend One OneMinusSrcAlpha

最終顏色值是:

要繪製的顏色的Bloom + 原來屏幕的Bloom *(1 - 繪製部分的Bloom)

就能避免剛才的問題,結果也能更符合預期。


Bloom部分用的是Unity舊版後處理,BloomOptimized.cs。修改過的Shader見下。

https://pan.baidu.com/s/1uumkosNIDpLasky3tmQjjA?

pan.baidu.com


推薦閱讀:

兩個平均年齡十一歲的小男孩,做了兩款遊戲
換個角度去看棋牌遊戲
《Exploring in UE4》關於網路同步的理解與思考[概念理解]
《伏龍記》ea實錄(week6)
如何讓動作在遊戲中更加流暢(1)

TAG:Unity遊戲引擎 | 遊戲開發 | 計算機圖形學 |