Unity3D實時平面反射的擴充套件與應用

搭建MrsFu123發表於2022-05-21

我們在遊戲中,對鏡面,水面,光滑的大理石地面等物體,需要使用自然反射增加場景真實感,顯然,使用Cubemap或Screen Space Reflection提供的效果都非常有限,Cubemap的反射錯位與SSR的螢幕外無法渲染等缺陷,都會嚴重降低遊戲體驗,因此這時我們就需要通過子攝像機實現平面反射。

Unity官方其實在很久以前就已經給出了平面反射的實現,原理也比較簡單,直接使用反射矩陣對攝像機的相對座標系進行反轉,並且使用斜切投影使近裁面貼合到鏡面上即可,這裡就不再對基本的實現進行闡述了。

單純實現一個實時反射,其實依舊無法正常的在場景中使用,因此,我們需要對反射指令碼,Shader進行全方位的加強,並且從不同的用途進行考慮。對於實時渲染的遊戲來講,比起效果,效能顯得更加重要,因為玩家實際上很難在高速運動的畫面中感受到鏡面的一絲細微的不真實感,但是由於鏡面的出現導致的幀數驟降卻足以毀滅一個玩家的遊戲興趣。如果是CG渲染,效能就不是重點了,而追求效果才是重點。


  1. 實時渲染中效能的優化:

關於合併材質網格,合批降低drawcall,使用Graphics Jobs提高drawcall效率等工程方面操作我們這裡就不多說了,但是道理還是要說明白的,平面反射會直接導致drawcall翻倍,CPU執行Cull和呼叫圖形API的消耗也會翻倍,同時,執行shader所需要的GPU消耗也會提高不少,而且由於斜切投影的原因,近裁面具有不確定性,Unity的遮擋剔除就會失去作用,同時Deferred Shading也將不可以使用,所以我們同時也要嚴格控制畫素光的數量。

在GTAV中,包括水面在內的平面反射,都是使用了單光源(日光,月光)的Forward渲染管線,其他光源均屬頂點光,且沒有陰影,除此之外GTAV的主攝像機是以Deferred Shading管線進行渲染的,我們這裡也建議在Unity中採用這種管線設計。

優化渲染效能,最重要的毫無疑問就是剔除了,剔除特定的層對效能提高顯示效能十分有幫助,在場景設計時,我們可以利用好Unity提供的32個層,比如將室外地形,花草樹木,人造建築,室內場景甚至是LOD的不同模型放到不同的層中,然後將cull掉不需要渲染的層,比如室外的海面,其實並不需要渲染花草這些細節,只需要渲染一下主要地形就可以了。同時,Unity還提供了Per Layer Culling Distance,即對不同層可以使用不同的有效渲染距離,對小物體所在的層,可以進行一個動態的剔除,功能相當於一個巨集觀的LOD,用在這裡是比較合適的。

玩過《戰地4》的朋友應該可以發現,在戰地4中,水面的實時反射並不會原原本本將建築物反射出來,而是反射一層只有漫反射的低模,實際我們平時在大型專案的製作中,為了節約效能這也是完全可以考慮的方法。首先,接手的場景應該是已經擁有完整的LOD模型,並且應將低模放入單獨的一層,使我們的鏡面可以單獨渲染。同時,由於鏡面採用的是forward rendering path,光照計算都是per object的,所以我們可以給低模使用簡單的光照Shader,比如lambda漫反射Shader而不是Unity預設的GGX + DisneyDiffuse,通過犧牲部分效果,節省大量的GPU資源,這一套場景地編的方案,現在也已經被歐美的大作開發者廣泛應用。

另外,降低反射解析度也是可以大幅度降低顯示卡效能消耗的方法,一般將反射解析度降低到螢幕的1/3或1/4,也是可以用效果換效能的,或者可以直接在子攝像機渲染時,改變QualitySettings的設定,然後在渲染結束後再復原,使子攝像機以一套不同的渲染質量進行渲染,控制渲染消耗。

2. 提高反射質量:

有時比起更高的效能,我們需要更高的質量,尤其是在CG中,所以,一些細節就需要反覆推敲使其更加接近完美。

基於物理的鏡面:在Unity中,我們常使用Deferred Shading管線渲染不透明物體,使用Forward管線渲染透明物體,鏡面也不例外,Forward管線由於per object lighting的特性,實現基於物理的鏡面非常容易,只需要把Shader中讀取Reflection Probe的程式碼換成讀取反射render texture的程式碼就可以了,剩下的BRDF,菲涅爾效果等,可以直接呼叫Unity內建的LightingStandardSpecular函式實現standard PBR,比起forward來說,deferred就要稍微複雜一些,我們需要在輸出GBuffer之前,先手動計算實時反射與菲涅爾效應,並且將計算結果賦值給GBuffer3, 也就是自發光貼圖上,然後再將高光值設定為0, 避免Deferred Lighting時鏡面受到反射球或天空的影響。法線自然也是少不了的,可惜我們無法模擬真實的光線反射,只能用近似的方法,對法線進行偏移,所以嚴格的來講並不能叫做基於物理,畢竟這種使用渲染貼圖做平面反射與實際物理上的反射仍然差了十萬八千里,但是,由於依舊使用Standard BRDF進行表面光照運算,因此,這裡可以不嚴謹的稱之為基於物理的反射。


抗鋸齒對反射質量的提升也是比較明顯的,尤其是當反射解析度較低時。在開源中,我們提供了三種抗鋸齒並將它們混合在一起使用,根據渲染管線流程,首先是MSAA,MSAA作為硬體抗鋸齒,清晰而且質量良好,然後是FXAA,作為後處理抗鋸齒的一種,FXAA的效率高且質量也不錯,缺點就是通過畫素色彩進行識別,識別率較低,因此容易導致畫面比較糊,不過既然已經是基於物理的著色,糊一點反而可以增加真實感。Edge Blur AA,通過卷積運算元取樣求出畫素與周圍畫素的顏色差異,從而有選擇性的對畫素點進行模糊,類似於FXAA,我們可以將其看做FXAA的輔助抗鋸齒。在加上這三層抗鋸齒效果以後,鋸齒有了明顯的減弱:


光有抗鋸齒顯然是不行的,我們還需要模擬物體表面的粗糙感,實際上,生活中的平面物體,大都是粗糙的,表面不規則的,因此模擬光線打在物體表面的漫反射與高光反射的融合無疑會讓我們的畫面效果提高一個檔次。

那麼問題就來了,斜切投影導致深度圖反推世界座標非常困難,所以我們這裡就再次使用近(tou)似(lan)的方法,也就是使用另一個子攝像機,並設定replacement shade,最後直接在replacement shader中計算出畫素點到視線與鏡面接觸的點的距離,使用簡單的三角函式與點積即可求得,再把該距離儲存到一個格式為RFloat的貼圖中,一張相對距離的圖就這麼出來了。

有了深度圖以後,我們需要實現基於深度的模糊,而就在這時我想到了,Unity官方已經給我們提供了Depth of Field的指令碼,裡邊已經有根據深度圖進行模糊的實現了


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70009264/viewspace-2895898/,如需轉載,請註明出處,否則將追究法律責任。

相關文章