Shadow Mapping已經成為當前3D游戲的標配。傳統SM+PCF可以很好地實現日光環境下的Hard Shadow,但如果要實現由昏暗燈光所產生的Soft Shadow,則要么效果過於生硬(sample次數少)要么效率低下(sample次數多)。因此,越來越多的游戲開始使用能夠充分利用硬件特性的軟陰影算法。
這里主要總結各種Soft Shadow Mapping的算法思想和推導過程,不提及實現上的細節和具體代碼,等有空再寫寫Demo。
本文為原創內容,轉載請注明出處。
Standard Shadow Maps
Shadow Map的基本思想:Light View畫一遍Depth,然后Camera View渲場景的時候,把Pixel坐標變換到Light Space,比較Depth即可(Pixel的Depth大於Shadow Map的Depth即在陰影區)。也可以直接是Screen Space對Depth Map做Post-processing。
傳統方法可以用Percentage Closer Filtering解決鋸齒問題,也很簡單,Pixel坐標對應過去之后,比較周圍的多個點,計算出陰影區的百分比。此外,還可以通過Bilinear Filter讓陰影更平滑,跟PCF差不多,比較相鄰的4個點,然后根據Texel的Offset做插值。值得注意的是,現在的GPU都支持Hardware Shadow Map,可以直接通過tex2Dproj( u, v, z, p )這條指令實現2x2PCF+BF。
Perspective Shadow Maps & Trapezoidal Shadow Maps
這兩種方法沿用Shadow Map的基本思想。為了解決Shadow Map精度問題,對Depth Pass的變換矩陣做做手腳。
Variance Shadow Maps
VSM的想法很棒,可以說Soft Shadow是從VSM開始才往利用硬件Filter的方向發展。首先,也是Light View的Depth Pass,但是這次寫入2個值,一個通道寫入Depth,另一個通道寫入Depth2。接着對出來的這張 [Depth, Depth2] 的Texture做Blur。直觀上來講,做Blur的結果就是取周圍一定范圍內的點的平均值(期望值),也就是Blur完之后得到一張 [E(Depth), E(Depth2)] 的Texture。有了這張Texture,后面便可以根據方差公式 V(Depth)=E(Depth2)-E(Depth)2 計算得到方差。
最后,根據Chebychev’s inequality(切比雪夫不等式)
P(x≥t) ≤ σ2/(σ2+(t-μ)2) 式中σ2為方差V(Depth),μ為期望E(Depth)
將Pixel坐標變換到Light Space之后的Depth代入t,即得到某個點周圍一定范圍內大於深度t的點的比率(注意切比雪夫不等式得到的只是一個范圍≤,而這里近似的取不等式右側的結果Pmax)。其實就是一個微分級別的PCF值,於是可以非常高效地得到平滑陰影。
很可惜的是,在Depth變化較大的區域(方差較大),VSM會有缺陷(Light bleeding)。因為根據切比雪夫不等式,在方差較大的情況下,(t-μ)必須足夠大,才能讓不等式右邊趨於0。於是又有了LVSM來解決這個問題。
Layered Variance Shadow Maps
為了解決VSM的Light bleeding,對VSM的Shadow Map進行分層,將每一層次里面的Depth區間控制在一定范圍內,也就是降低可能出現的方差值。
Convolution Shadow Maps
CSM提出了一種非常好的圖形學算法思路。其推導過程比較華麗,用到了幾個數學上的概念:卷積,傅里葉級數。(注意還有另外一種CSM – Cascaded Shadow Maps,兩者作用是不一樣的)
首先,假設x⊆R3為某一點的世界坐標,d(x)為該點到Light Source的距離(也就是Light Space的Depth)。p⊆R2為該點對應到Shadow Map上的坐標,z(p)為該點對應的Shadow Map上的Depth。於是可以定義一個Shadow Test函數:
s(x) = f(d(x), z(p)) 當d>z時,s為0,反之為1
為了得到軟陰影,我們希望能對s進行卷積,即希望得到:
sf(x) = ∑w(q)f(d(x),z(p-q)) = [w*f(d(x), z)](p)(亮點1)
如果能對上式運用卷積定理,得到如下式的結果,則表明可以通過對z(p)做卷積而得到軟陰影。(即可以利用Blur、硬件Bilinear Filter和Mipmap來達到軟陰影)
[w*f(d(x), z)](p) = f(d(x), [w*z](p))
可惜的是,f(d(x), z(p))並不是線性函數(而是階躍函數),無法對其運用卷積定理。為此,CSM轉而采用數值分析方法,用傅立葉級數來逼近函數f。(亮點2)
雖然CSM的推導過程相當華麗,但是本身缺點太多(比如:內存開銷較大,Shadow Map Pass需要輸出的數據量太大,帶寬內存都吃得太緊),導致其實用價值很低。
Exponential Shadow Maps
明白CSM的原理之后,ESM就顯得相當簡單了。