【Unity】Planar Shadows平面陰影的實現


Plannar Shadows,即平面陰影,是一個適用於平坦地形的假陰影技術。要求陰影的Receiver為平面,Occluder不與其他物體穿插。

實現效果

1.定向光源Planar Shadows

2.點光源Planar Shadows

實現步驟

實現Planar Shadows,這里主要考慮兩種光源,定向光和點光源兩種十分常用的光源類型。實現Planar Shadows的核心就是推導兩種光源的投影矩陣,為了簡化矩陣,將xz平面(y=0)作為Receiver平面。

頂點着色器核心代碼如下:

 1     v2f vert(a2v v)
 2     {
 3         v2f o;
 4         o.uv = v.uv;
 5         half3 wordPos=mul(unity_ObjectToWorld,v.vertex).xyz;
 6         //投影:中心投影(透視投影,點光源),平行投影
 7         //平行投影:斜投影(定向光),正投影
 8 
 9         half3 l=_WorldSpaceLightPos0.xyz;
10         //透視投影矩陣:將點光源投影到xz平面
11         half4x4  Projection=half4x4(
12             l.y,-l.x,0,0,
13             0,0,0,0,
14             0,-l.z,l.y,0,
15             0,-1,0,l.y
16         );
17         //斜投影矩陣
18 //        half4x4  Projection=half4x4(
19 //            l.y,-l.x,0,0,
20 //            0,0,0,0,
21 //            0,-l.z,l.y,0,
22 //            0,0,0,l.y
23 //        );
24         half4 shadowPos=mul(Projection,half4(wordPos,1));
25         shadowPos.x/=shadowPos.w;
26         shadowPos.z/=shadowPos.w;
27         shadowPos.y=_HeightBias;
28         o.pos=UnityWorldToClipPos(shadowPos);
29         return o;
30     }

1.額外的陰影Pass

需要在Shader中,常規渲染Pass外增加新的用於Planar Shadows的額外的渲染Pass。

2.推導定向光和點光源的投影矩陣

定向光的投影類型為斜投影,點光源的投影類型為中心投影,需要提前推導出投影矩陣。為了條理清晰,在頂點着色器中進行了矩陣的計算。實際上,可以預計算矩陣然后腳本傳遞給Shader。

3.矩陣變換

首先將頂點變換到世界空間,然后進行投影變換。由於使用齊次坐標系,需要除w分量來得到正確的結果。

_WorldSpaceLightPos0——是Unity內置着色器變量,在光源為定向光的情況下,xyz分量表示方向,w分量為0,在光源為點光源的情況下,xyz分量表示位置,w分量為1。

_HeightBias——用來解決深度沖突造成的顯示錯誤。

效果優化

以上的步驟雖然完成了陰影的形式上的正確,但仍然有優化的空間。接下來介紹使用模板緩沖,實現更優的效果。

優化效果如下:

上圖實現了陰影邊緣裁剪半透明的效果。

1.邊緣裁剪

為了實現邊緣裁剪的效果,這里用到了模板緩沖,用於Mask掉超出Receiver邊緣的陰影像素。

首先,在Receiver的Shader中,添加模板緩沖的代碼:

1         Stencil {
2             Ref 1
3             Comp always
4             Pass replace
5     }

這段代碼的用處是,將模板值1寫入到模板緩沖中,即Receiver覆蓋像素位置的對應模板緩沖區的模板值均為1。

 

然后在我們Occluder的Shader中,將Queue值調整為Receiver的Queue+1,同時添加一下的代碼:

1     Stencil {
2        Ref 1
3         Comp equal
4         Pass keep 
5     }

這段代碼的用處是,繪制陰影像素的模板值為1,與深度緩存中已有的模板值比較,如果相同的話,那就保持緩沖區值不變,模板測試通過,像素將被繪制。關閉深度緩沖寫入的意義是使得渲染按照先渲染Receiver然后Occluder的順序,讓Receiver的模板值先寫入模板緩沖中。

2.半透明

如果我們直接對陰影進行半透明着色,會發現在簡單物體上效果還可以。但是在復雜的模型上,有很大的問題,如下圖所示:

因此,我們為了解決這個問題,需要用模板緩沖,來保證每個像素被繪制一次。

做法很簡單,除了對一些透明的相關設置外,需要對Occluder的模板緩沖代碼進行修改。

1     Stencil {
2         Ref 1
3         Comp equal
4         Pass incrWrap
5         Fail keep
6     }

這段代碼的和上面的不同是,如果模板測試通過,將模板緩沖中的模板值+1,像素被繪制。這樣的話,如果一個重疊像素要被繪制,因為模板值+1=2,不通過模版測試(條件為Equal),並不會被繪制。

小結

雖然實現了陰影的正確投射,以及邊緣裁剪和半透明問題,現在的效果於Unity中Hard Shadow的效果已經十分接近。對於對於如何實現軟陰影(本影+半影),希望有較好的解決方案。


免責聲明!

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



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