Unity3D 基於ShadowMap的平滑硬陰影


前言

傳統的ShadowMap在明暗邊緣處都會有很難看的鋸齒,常規的解決辦法都會在使用ShadowMap渲染陰影的時候通過背面剔除把這種缺陷隱藏掉,最后剩下一個影子。但是這樣一來,自陰影就會丟失,因而傳統的做法又會通過局部光照來重新為這個物體添加上部分自陰影,也就是咱們常見的Phone光照模型、Blinn-Phone光照模型。而本文決定通過文獻[1]的一個平滑方法把ShadowMap在明暗邊緣處的鋸齒消除,並和光照模型求並,最后得到了一個包含豐富平滑自陰影效果。

本文讀者默認為有圖形學基礎、編寫Shader基礎和ShadowMap基本原理,若沒有請先去把這些基礎學習一下,再來閱讀本文,否則可能會有閱讀障礙。

 

一、ShadowMap和局部光照模型的缺陷

(1) 傳統ShadowMap能產生豐富的自陰影,但在明暗邊緣處都會有很難看的鋸齒和走樣問題,如圖1.1。

圖1.1 ShadowMap的缺陷

 

(2) 局部光照模型在在明暗邊緣處能產生非常平滑的陰影,但只能產生有限的自陰影,缺陷表現為丟失部分自陰影和投射陰影,如圖1.2。

圖1.2 局部光照模型的缺陷

 

二、ShadowMap缺陷分析

ShadowMap產生這種鋸齒缺陷的原因是,光照攝像機的方向和模型中邊緣處的三角面接近平行,導致這些三角面沒有映射到任何像素點。如下圖2.1、圖2.2

圖2.1 以光照攝像機的正交投影視角觀察需產生陰影的物體

 

圖2.2 以自由的正交投影視角觀察需產生陰影的物體

圖2.1渲染出來的圖像就是ShadowMap,圖2.2是以不同的角度觀察ShadowMap渲染的被光柵化的所有三角形。到這里,我們應該意識到鋸齒的產生是因為三角形光柵化丟失的原因造成的。

兩張圖都是是同一個渲染方法,只是觀察角度不同。即:把光照方向和三角面法向量的點乘結果大於0的三角面渲染出來的結果,也就是隱藏對於光源攝像機不可見部分。這樣大致上能模擬得出構成ShadowMap的所有必須的三角面。

從人的視覺上看圖2.1很完美,理論上也就認為能產生完美的陰影,但實際操作的時候就會發現結果和自己想的不一樣。結合兩張圖得出,造成ShadowMap明暗邊緣鋸齒的罪魁禍首是模型的三角化導致的。因此不管怎么增加ShadowMap的分辨率都是沒用的。

圖2.3 ShadowMap的缺陷指示圖

目前市面上的模型基本都是三角面構成的,不可能因為這個問題就廢棄掉。雖然學術上有一種把三角面模型體素化的方法把模型轉換成空間中的一個個有體積微小正方體,但貌似並不常用。因此問題怎么消除這些鋸齒是本文的重點。

 

三、ShadowMap和局部光照模型的並集

那么仔細觀察兩種模型產生的陰影缺陷后,把兩者求並集后是否就能即擁有局部光照般的邊緣平滑度,又有ShadowMap豐富的陰影呢?立馬動手實現,如下圖3.1

圖3.1 ShadowMap和局部光照模型求並

如果這樣的效果能接受的話,那么到此就結束了。本人在翻了一番國內學術后發現,也是到這一步就結束了,后續貌似沒人再做更多的工作。但其實還可以進一步把平滑做得更完美。

 

四、ShadowMap明暗邊界的平滑

4.1 構造明暗邊界線

ShadowMap的鋸齒原因是由於在明暗邊界的地方三角面不完整,導致深度呈鋸齒狀起伏,因此只要把明暗交接的地方的深度值(像素值)用某個深度值覆蓋就能獲取到非常平滑的明暗邊界。

在文獻[1]~文獻[3]中都闡述到了同一種,方向向量與模型網格(Mesh)在邊緣處求邊緣線的方法。由於本人未對其做深入研究,僅知道通過其提供的公式即可求出邊緣線,進而可構造出比較完美的明暗邊界邊,理論就不多說了免得班門弄斧,建議直接去看原文,不看那就直接抄本文提供的代碼。效果如下圖4.1~圖4.3:

圖4.1 ShadowMap+明暗交界線(紅色)

 

圖4.2 局部光照+明暗交界線(紅色)

 

圖4.2 復雜模型+局部光照+明暗交界線(紅色)

此邊緣線基本上就是局部光照模型的明暗交接的比較完美的逼近了,甚至還比局部光照還能更平滑,這都是得益於數學上的赫米特(Hiemite)插值法。

 

 4.2 平滑ShadowMap明暗邊界的深度值

實際上通過文獻[1]~文獻[3]求出來的是一個一個輪廓三角形上的一條線段,最后把所有這些線段合並起來就得到了明暗邊界線,那么我們就可以通過這些點構造出一條針對於光照攝像機的可控粗細的線條。如下圖4.2.1、圖4.2.2

圖 4.2.1 其他視角

 

圖 4.2.2 光照攝像機視角

具體實現步驟如下:

1.通過明暗邊界線的2個點的位置及其單位法向量(注:這2個數據都可以通過文獻[1]~文獻[3]計算得到)構造出2個各自沿着單位法向量負方向位移一段距離的點,以及2個各自沿着單位法向量正方向位移一段距離的點。

2.通過步驟1得到的4個點構造出2個三角面,進而構造出1個四角面。

這樣我們就得到了一個針對於光照攝像機的可控粗細的明暗邊界線,接下來就是如何進行正確的覆蓋ShadowMap中鋸齒狀起伏的深度值。

 

4.3 覆蓋ShadowMap中鋸齒狀起伏的深度值

關於這一塊本人目前沒有想到太好的辦法,目前的做法是把這些明暗邊界線往光照方向的負方向位移一段距離來覆蓋鋸齒狀起伏的深度值,效果看起來還不錯。

圖4.3.1 ShadowMap+平滑明暗邊界的深度值

 

圖4.3.2 ShadowMap+平滑明暗邊界的深度值+局部光照

 

圖4.3.3 ShadowMap+平滑明暗邊界的深度值+局部光照+復雜模型

 

圖4.3.4 平滑明暗邊界的ShadowMap

可以看到僅僅使用ShadowMap+平滑明暗邊界的深度值(圖4.3.1),就已經比ShadowMap+局部光照陰影(圖3.1)好上不少了。但由於只是簡單的把這些明暗邊界線往光照方向的負方向位移一段距離來覆蓋鋸齒狀起伏的深度值,因此還是有一點點的小缺陷。到這里還沒結束,本文建議再把局部光照加上去,因為局部光照算法非常簡單,1次點乘+1次step即可得到結果,再與本文方法求並,就能得到效果很不錯的陰影了,如圖4.3.2和圖4.3.3。

 

五、實現以及用途

說了這么多,真正動手去實現的時候會發現,並沒有增加多少復雜度,僅僅在傳統的ShadowMap的基礎上,在渲染ShadowMap的時候增加幾何着色器即可。代碼部分不過多說明,后面會給出基於Unity3D的源碼。

那么這種硬陰影有什么用呢,甚至不惜增加一定的復雜度?本文認為這種陰影在卡通渲染上是十分有用的,因為卡通的顏色並不需要過多的漸變,一般只需要明暗2種顏色即可,而卡通渲染又需要豐富陰影,因此將其運用到卡通渲染上是用途之一,如下圖5.1。

圖5.1 本文算法+明暗貼圖+復雜模型

 

六、結束語

雖然是這么說,但實際上二次元精美的插畫都有一定程度的漸變,這是人為主觀意識來添加的。在計算機上要實時實現這中漸變,並且任何角度觀察都能達到插畫級的精美程度是很困難的,這是因為插畫的繪畫人自己也說不出這個數學模型,在計算機里沒有數學模型就不存在合理性,沒有合理性就很難模擬,因此一般都只能用大量人力一幀一幀地把畫面畫出來。

感謝學術界大佬們的精彩文章,本文到此結束,謝謝。

 

附:源碼 

 

參考文獻

Silhouette Smoothing for Real-time Rendering of Mesh Surfaces

基於GPU的網格模型平滑陰影的實時繪制

三角網格模型平滑陰影的實時繪制


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM