Unity3D魔改渲染系列之亂改圖形篇
來自專欄 Unity Graphics
Unity作為一款受歡迎的引擎,優點自不必說,然而它同時也有許多缺點一直飽受詬病,比起UE,CE等引擎,有對大場景優化較差,光影效果較差,開發大作工具鏈不完整等。當然,工具鏈屬於工程問題,除非實際切入工程開發,否則我們也沒有什麼辦法。但是光影與渲染的優化卻是可以著手改變的,今天我們就來嘗試魔改一下Unity提供的.cginc文件來實現對引擎底層渲染的一個優化。
國際慣例,先上開源:
MaxwellGengYF/Unity-Optimized-CGInclude-file需要的老鐵們,直接按照裡邊的文檔,把文件copy到自己的引擎目錄下替換就行了(由於Unity版本更迭,有可能出現不兼容的情況,因此請注意備份)。
首先需要魔改的第一個文件就是UnityPBSLighting.cginc,這個文件是幾乎所有使用PBR Shader的項目都會用到的文件,包括內置的Standard Shader也是隱式調用了這個文件里的方法。我們打開這個文件,可以看到裡邊有deferred, forward兩種管線的光照調用,同時為每個管線都提供了Standard和Standard Specular這兩種光照方案。具體這兩種光照方案有何意義在官網的教程可以查到,不再贅述。如果大家仔細看一下deferred shading的光照運算,會發現一個很荒謬的設計,裡邊居然有個BRDF?!WTF?!我們都知道deferred shading管線是在後處理環節計算光照的,所以這裡的設計並不知道是Unity工程師的設計錯誤或者疏忽,這裡要對其進行修改。
修改的核心依據在於對引擎管線的理解,延遲管線將燈光和反射的運算都包含在後處理中,唯一涉及光照的運算是光照貼圖,光照貼圖以自發光的形式,也就是色彩直接疊加到最終結果里。所以我們只需要保留光照貼圖的讀取即可,這一部分直接使用傳入的gi.diffuse的值,從一整個BRDF運算到一行賦值,節約的性能不言而喻。
說完deferred管線,我們再來分析一下forward管線。其實forward管線的BRDF也有諸多缺陷,所以我們將對UnityStandardBRDF.cginc這個文件里的BRDF1公式進行魔改。魔改的主要方向是,通過宏定義區分當前執行的Pass,過濾掉ForwardAdd Pass下的反射和間接光,因為這兩個部分本身就是沒有必要的,這也是Unity的一個重大的設計缺陷,這將直接導致Unity在Forward Path中,多燈光照明時性能驟降,尤其是GPU性能較差的手機平台。所以這裡的修改也是非常必要的。性能優化部分到這裡就差不多了,其他的我暫時也沒找到什麼問題,如果有讀者發現有問題了可以直接留言告訴我。
Unity的軟陰影效果,用過的都知道有多呵呵,其實這也是屬於這個引擎的歷史遺留問題,畢竟要考慮低端平台,不能像UE一樣完全面對高端平台一股腦特效往裡塞,所以我們這裡可以手動魔改一下陰影,讓點光源和平行光的軟陰影好看一點。
陰影的計算方法是在UnityShadowLibrary.cginc中,而平行光的計算是獨立出來的,是利用屏幕的深度圖在屏幕空間進行逐像素計算的。所以我們就需要把平行光的Shader單獨拿出來設置,具體設置位置在菜單的GraphicsSettings中的ScreenSpaceShadow。
介紹完Unity陰影計算的背景,我們可以來考慮一下怎樣對其進行魔改了,要實現軟陰影,就需要進行多次採樣,尋找該像素周邊的像素,並且進行加權平均。Unity中有點光源,聚光源和平行光源這三種,而這三種光源的shadowmap原理基本一樣,都是依靠shadowmap,也就是一個內置的子攝像機生成的。從光源特性可以簡單判斷出來,點光源實際上就是一個cubemap,聚光源就是一個透視投影攝像機,而平行光則是正交投影攝像機,實際上平行光一般有多個不同等級不同繪製面積的攝像機,以此實現基於距離的陰影等級劃分。而Cubemap的渲染原理其實也並不神秘,只不過相當於一個上下左右前後6個面的全方位透視投影攝像機,生成的結果其實也是6張RenderTexture,只不過引擎和圖形API已經給我們封裝成一個可以直接通過向量進行採樣的Cubemap了。
在得到shadowmap以後,shader將像素的世界坐標乘光源的ViewProjectionMatrix,獲得該像素在shadowmap上的UV,然後進行深度比較即可判斷出這個點是否被其他物體遮擋,這就是整個Shadowmap運算的基本流程。
知道了shadowmap的原理,就可以開始下一步的開發了。透視投影秉承近大遠小的原則,而正交投影則秉承遠近一樣大的原則。先說透視投影的軟陰影模糊原理,對一個透視投影採樣的圖片進行模糊時,由於近大遠小,所以遠處物體占像素要比其在近處時少,也就是說遠處物體的模糊程度會比近處物體的模糊程度要高,而正交投影無論遠近受到的模糊程度都是一樣的,根據生活經驗,由於光線具有發散性,一般的陰影都是近處硬,遠處軟,比較符合透視投影的特性,所以在透視投影類的shadowmap上,我們可以直接使用固定距離的UV移就可以了。
然後我們開始嘗試進行多次採樣,這裡我們使用的軟陰影演算法是受到諸多3A大作歡迎的PCSS演算法,PCSS的核心思想可以用一句話概括:胡亂採樣大法。通過像素當前所在的位置作為種子,生成數個隨機數作為偏移量並採樣,在點光源上實現的效果:
單純論效果可以毫不謙虛的說,已經甩官方實現起碼十萬八千里了,為了達到這個目的,我們使用了64次採樣,這個採樣數的性能消耗在一般的PC平台上勉強可以接受。
平行光就相對麻煩一些了,因為正交投影遠近一樣,所以我們需要手動判斷像素點離shadowmap中像素的距離,從而實現基於距離的模糊。這裡我們的解決方法是使用線性插值,指定一個最小的採樣距離(即最硬的陰影)和最大的採樣距離(即最軟的陰影),然後通過線性插值獲取當前的採樣距離,然後對極限距離以及插值變數進行調整,實現對陰影效果的調參,平行光軟陰影效果如下:
這時候,一個不知好歹的男人提出了意見,不行啊,64次採樣太多了啊,PS4,XBOX以及一些中低端的顯卡性能不夠啊,得減少採樣數量,起碼降到32,甚至16或25次。唔。。坦率的講,64次確實有。。emm。。那麼一點點小多?但是美術大佬們又不希望軟陰影的質量降低,不希望出現稀疏的噪點,而隨機採樣率降低的最直觀問題就是會出現難以忍受的噪點。如何兼顧性能和效果呢,這時機智的我靈機一動,想到了Temporal大法。
「在這個世界上如果有什麼問題,Temporal解決不了,那說明你的Temporal寫的不對。」
——著名圖形科學家,沃·茲吉碩德
實現方法如下:首先,在陰影的隨機採樣的種子里,加入當前遊戲時間,使隨機採樣動起來,然後在攝像機下掛載官方的PostProcessing腳本,並使用TAA,然後降低陰影採樣率。
16次採樣率的聚光燈陰影效果,噪點簡直難以忍受:
加上TAA後的效果:
效果已經逼近32次甚至64次採樣了,對於這個結果本人是非常滿意的,除了對TAA有一定的依賴性以外,可以說這樣的實現基本沒有什麼大的毛病。
推薦閱讀:
※關於離線渲染有哪些資料可以推薦的?
※如何設計一個靈活的遊戲渲染系統?
※Redshift是否會對動畫產生革命性的變化?
※keyshot渲染燈具怎麼才能做到光線自然發出?
※為什麼虛幻4的渲染要比unity強,強在哪裡了,想知道底層原理解釋?