Unity移動端動態陰影總結

一、基於Cubemap的動態軟陰影

ARM公司曾利用Unity開發過兩款技術Demo(Ice Cave和Chess Room),裡面充分發揮了Cubemap的強大威力,既用來做地面反射、冰塊折射,還用來做動態軟陰影,利用簡單的技術做出了高品質的畫面。下面是Ice Cave的效果:

其中反射、折射部分參考:Reflections Based on Local Cubemaps in Unity、ARM Guide for Unity Developers,下面主要介紹下軟陰影部分原理。

以此國際象棋屋為例,屋子中間放置一個Reflect probe來拍攝周圍環境,只用了Cubemap的RGB通道,而周圍環境的Alpha其實也代表了光是穿透了窗戶還是被牆壁遮擋,那就可以利用Cubemap剩餘的Alpha通道就可以來存儲光和周圍環境的遮擋情況,Alpha通道圖如下:

生成Cubemap細節可以參考AssetStore中的源碼:Asset Store

利用生成的Cubemap渲染陰影主要分為兩步,一是向量L(vertex-to-light)轉換為Lp(校準過的vertex-to-light,用來採樣Cubemap用),二是軟陰影處理。

1.L到Lp向量校準:

輸入參數:

_EnviCubeMapPos – cubemap 中心坐標

_BBoxMax – 包圍盒最大坐標,生成Cubemap時自動生成

_BBoxMin – 包圍盒最小坐標,生成Cubemap時自動生成

V – 頂點坐標

L – vertex-to-light向量,已normalized

輸出參數:

Lp – 校準後的vertex-to-light向量,作為UV去採樣Cubemap

校準過程:

先利用線和包圍盒求交點,從包圍盒位置到交點的向量就是Lp,然後利用Lp去採樣Cubemap用於著色。

另外背面要特殊處理下,防止陰影穿透問題。

2.軟陰影:

陰影平滑的過程比較有趣,首先Cubemap過濾方式選擇tri-linear filtering,然後計算vertex-to-intersection-point(頂點到交點)向量的長度,然後乘以外部傳入係數:

為了平滑陰影,我們用texCUBElod 去採樣Cubemap,其中UV的XYZ來自Lp,W來自vertex-to-intersection-point(頂點到交點)的距離。

下圖也可以看到離窗戶越遠處的陰影越模糊。

這種做陰影方法的限制是比較適合室內環境、點光源位置不變、內部有移動物體的情況。

二、地面雲陰影

對於地面上雲陰影,用實時燈光照射出陰影顯然是不划算,可以直接在地面shader中混合一個運動的雲圖就能達到類似效果。

gif圖顯示不了,看這裡吧:雲陰影

我用shaderforge拖出了一個簡單的版本

另外這種方法也可以用來做地面風雪效果。

三、植物搖曳陰影

對於樹、草、旗子這類位置不變但有搖曳動畫的物體,可以預先把陰影烘焙到貼圖中,然後把陰影圖作為單獨貼圖、或地面貼圖Alpha通道傳送到地面shader中,然後只需要添加陰影晃動的特性的就可以,隨植物晃動而晃動,會有一種真實陰影的感覺。另外注意陰影的方向、和植物晃動的同步登細節。

具體細節可以參考:手機遊戲中大量植物圖像的偽陰影渲染

四、結合Projector和Rendertexture的實時陰影

創建一個跟隨主相機的陰影相機,改為正交投影,設置單獨的shadow Layer,將需要投射陰影物體設置到shadow layer,為此陰影相機設置渲染目標到一個渲染紋理RTT_Shadow。另外創建一個Projector,為它設置一個材質Mat_Proj,並將RTT_Shadow傳到Mat_Proj的shader中進行著色,另外為防止投影相機邊緣的刺刺的長線,要設置一個陰影衰減紋理,如果需要軟陰影則需要另外Blur。

這是最近幾年手游應用比較廣泛的方法,網上有很多相關文章,比如:結合Projector和Rendertexture實現實時陰影、ProjectorShadow(手游上的實時陰影方案)

另外AssetStore也有不少類似插件:Fast Shadow Projector

五、角色腳下陰影面片

對於遊戲中的NPC、雜兵、野怪這些非關鍵性角色可以直接用防止一個陰影面片來模擬陰影,當然如果地面起伏比較大可能會有穿插問題。

六、Light Probe

具體細節參考Unity手冊不贅述了:Light Probes

Shadow Maps

1.Standard Shadow Mapping:的基本思想是在光源位置放置一個相機(Light space Camera),畫一遍深度得到深度圖,在渲染場景時將pixel坐標轉換Light Space計算深度,然後比較它深度和深度圖中的深度,如果比深度圖中深度大就意味著在陰影中,否則在被照亮。

陰影的鋸齒有兩類:透視導致的鋸齒(Perspective alias)和投影導致的鋸齒(Project alias)。

2.PCF:投影導致的鋸齒是因為燈光投射方向和物體表面夾角過小時多pixel對應陰影圖的一個texel,這可以通過提高陰影圖的大小來解決,也可以通過Percentage Closer Filtering來柔化邊緣。PCF就是在繪製時,除了繪製當前點還會對周圍像素進行多次採樣、混合來柔化鋸齒,常用PCF有:使用隨機採樣實現soft shadow、泊松採樣等。

3.PSM:透視導致的鋸齒是因為透視的近大遠小所帶來的,於是就有了Perspective Shadow Map,它將整個Shadow Map的計算過程轉到歸一化設備空間(NDC)來計算,這就消除了近大遠小的問題。下圖是Standard Shadow Map和經過Perspective Shadow Map優化過的陰影,陰影明顯更細緻。

可是PSM本身有很大局限性,比如影子質量比較依賴視角方向、近處陰影與遠處陰影Z分布過大。

4.LISPSM:在PSM的基礎上又有了新的陰影技術Light Space Perspective Shadow Maps,它是在和燈光方向垂直的方向構建View Frustrum,然後將燈光、場景都轉到這個View Frustrum的Perspective space,然後再計算Shadow Map,這樣無論是點光、聚光、平行光就都轉為平行光。

左圖是Uniform(近處精度不足),中間是LISPSM(近處、遠處都不錯),右面是PSM(遠處精度不足)。

LISPSM具體細節參考:cg.tuwien.ac.at/researc

5.VSM(方差陰影):在使用PCF時一般不能提前對Shadow Map進行模糊處理,因為這會導致PCF計算不準,而Variance Shadow Maps則沒有這樣的限制。VSM存儲的Shadow Map不近包括深度,還有深度的平方,這時可以對Shadow Map做過濾,然後利用切比雪夫不等式計算出大於當前深度的概率上限,也就是陰影區的概率。切比雪夫不等式:

左圖是Standard Shadow Map,右圖是Variance Shadow Map

具體細節參考:Variance Shadow Mapping、VSM的demos、Matt"s Variance Shadow Maps

6、CSM/PSSM:這是兩種分別研究發表但是原理幾乎一樣的陰影技術,Unity用的就是CSM,而其中PSSM是幾個中國人(Zhang F, Sun H Q, Xu L L, et al,觀摩大佬風采)提出的。它們的原理如下:

a)對攝像機視錐體內沿著Z由近到遠切陰影圖分為多張,而切分是兩種切分規則的混合,一種是均勻切分,一種是指數切分,兩者按照一定比率混合起來。

b)對每一塊分別計算一個光源投影空間內平移、縮放的矩陣cropMatrix,它可以將切分的多塊移動、縮放到光源的視椎中,這個矩陣和正交投影矩陣非常像。

// Build a matrix for cropping light"s projection // Given vectors are in light"s clip spaceMatrix Light::CalculateCropMatrix(Frustum splitFrustum){ Matrix lightViewProjMatrix = viewMatrix * projMatrix; // Find boundaries in light"s clip space BoundingBox cropBB = CreateAABB(splitFrustum.AABB, lightViewProjMatrix); // Use default near-plane value cropBB.min.z = 0.0f; // Create the crop matrix float scaleX, scaleY, scaleZ; float offsetX, offsetY, offsetZ; scaleX = 2.0f / (cropBB.max.x - cropBB.min.x); scaleY = 2.0f / (cropBB.max.y - cropBB.min.y); offsetX = -0.5f * (cropBB.max.x + cropBB.min.x) * scaleX; offsetY = -0.5f * (cropBB.max.y + cropBB.min.y) * scaleY; scaleZ = 1.0f / (cropBB.max.z - cropBB.min.z); offsetZ = -cropBB.min.z * scaleZ; return Matrix( scaleX, 0.0f, 0.0f, 0.0f, 0.0f, scaleY, 0.0f, 0.0f, 0.0f, 0.0f, scaleZ, 0.0f, offsetX, offsetY, offsetZ, 1.0f);}

c)針對切分的每一塊渲染陰影圖,一般陰影圖大小一樣的,比如都是1024*1024,而近處包含的場景範圍比遠處小,所以近處陰影圖的精度會更高。

d)渲染場景陰影

關於CSM和PSSM具體細節參考:Cascaded Shadow Maps、Parallel-split shadow maps for large-scale virtual environments、PSSM from GPU Gems 3

另外Shadow Maps還有很多其他變種:已知Shadow Maps名稱匯總
推薦閱讀:

《Splatoon 2》繪製效果的簡單實現
Instagram濾鏡,影視級調色演算法實現
2D遊戲開發時有那些驚艷的效果是一定要學會著色器(shader)編程?
編寫Unity Shader的時候,語義POSITION和SV_POSITION的區別?
Shader model 5.0 的片段著色器能不能得到該片段所在的三角圖元實際光柵化後有多少像素?

TAG:计算机图形学 | shader | Unity游戏引擎 |