讓角色半透明:後期模糊(二)
上一篇,我們實現了用 Screen-Door Transparency 淡入淡出角色,但效果太粗糙,點陣太明顯了。解決方案就是在 Post-Processing 中對 Screen-Door Transparency 的區域(物體)加模糊效果。
說起模糊,另一種後期效果——景深(Depth of Field)也使用到了,是否可以直接借用景深來達到目的呢?本來我是這麼想的,但從原理上說景深是基於深度而不是物體計算的,效果並不理想。於是還是選擇單獨做一個後期處理。或許可以將二者合併處理,不過這是另外一回事了。
用 Stencil Buffer 標記需要模糊的物體
如何在後期效果中識別哪些區域需要模糊呢?一種辦法就是使用 Stencil Buffer。
左:原始輸出;右:期待的 Stencil 輸出
然而柴犬屁股一沉,發覺事情並不簡單。還記得上一篇文章說過,clip / discard 不僅會拋棄顏色,也會拋棄深度嗎?這裡再加一條:還會拋棄 Stencil。所以只繪製一次得到的 Stencil 會是這樣的:
兩倍大小觀察 Stencil 輸出
也就是說,不能指望靠一次繪製就達到目的(在 Unity 中若有辦法拋棄顏色但留下 depth / stencil 請告訴我)。
然而麻煩還沒有結束。Unity 傳統的 Post-Processing 實現方式,即實現 MonoBehaviour 的 OnRenderImage,是無法訪問 Stencil Buffer 的,因為在此之前 Stencil Buffer 已經被清空了(這是 Unity 的回復,不是我瞎猜的)。不過還有辦法,就是使用 Unity 5 引入的 Command Buffer 實現後期效果,可以指定在傳統後期處理之前做我們想做的事情。
好了,總之 Stencil Buffer 是用上了。接下來就是模糊了。
模糊
這裡需要的模糊半徑並不大,因此不宜做過分的 downsampling,也意味著不需要做太多次數的卷積。在我的實現中,我使用了兩個半解析度的臨時 RenderTexture,對 4x4 的 Ordered Dithering 矩陣只需要兩次處理就可以完全消除點陣,對 8x8 的 Ordered Dithering 矩陣只需要三次處理就可以完全消除點陣。
完全消除 Ordered Dithering 的模糊效果
進一步改進
為何沒有做 16x16 的 Ordered Dithering 矩陣呢?雖然加大矩陣尺寸,就能表現出更豐富的透明度級數,但隨之而來的問題是,難以通過模糊消除點陣,即使成功,也需要大量的卷積操作且帶來誇張的鋸齒效果。最終,我選擇了 8x8 的矩陣,這個矩陣可以產生 65 級不透明度基本看不出不連續的感覺,但還是需要處理不透明度太高或太低帶來的鋸齒問題。
隨著距離變化,不透明度不是簡單的線性變化
在圖中,不透明度目標是綠色曲線。可以看出它不是連續的,為了保證畫面過度平滑,實際應用的不透明度是按一定速度移向目標的。這樣一來,就可以快速跳過不透明度太高或太低的區間,一方面可以掩蓋鋸齒,另一方面用戶體驗也會更好。
既然不透明度是按速度變化的,那麼就是有延遲的,這會不會導致來不及變化到 0 就與相機穿插了呢?會的,為了解決這個問題,紅色曲線出現了。它的意義是限制不透明度的上限,負責將不安全的過度值「壓」下去。
類似的處理還有模糊度的淡入淡出,這裡就不展開了。
最終效果
如果只是做物體或角色的半透明,到這裡就結束了。但現實應用場景更複雜,比如女孩可以撿起盒子,有更多細節需要處理。下一篇繼續。
(遊戲 原生體 Protoform)
推薦閱讀:
※推薦幾款適合孩子玩的編程遊戲
※C++對象模型(2)構造函數語義學
※讓角色半透明:從 Ordered Dithering 說起(一)
※C++對象模型(1) 關於對象