如何用Unity實現超多面渲染?其實只需這三招

放牛的星星發表於2020-05-18
本文來自Unity Connect博主放牛的星星。

UE5宣傳片釋出之後,沸騰的不只是技術行業。朋友圈的刷屏,令眾多行業外人士一臉懵逼。Nanite宣稱可以渲染160億的三角面,這些對行外人來說似乎有些看不懂,其實對於我們這些行內人也是一樣。

在官方沒有放出技術細節之前,不少大佬都在猜測背後的實現原理。主要來說,分兩個方向,一個是 Mesh Shader的渲染管線,另一個是Geometry Image的技術方案。

關於Mesh Shader,此前的文章大概說了下原理。
(通過文末二維碼關注博主檢視往期內容)

而有關Geometry Image的方案,知乎上張心欣大佬說的比較詳細。
https://zhuanlan.zhihu.com/p/140943267

但這兩個方案其實是不衝突的,Geometry Image 可以在 Task Shader 階段去做。

如果需要破功,就先要了解下Geometry Image這麼神祕的專業詞彙到底是什麼。以下一小部分內容摘自張心欣的知乎文章。

和Texture一樣, Geometry Image實質上是一種能夠增加物體表面細節的貼圖方法, 只不過, 紋理貼圖貼的是圖案, Geometry Image,貼的是幾何。什麼叫貼的是幾何?

如何用Unity實現超多面渲染?其實只需這三招

比如像這種, 原來好好一個兔子, 往它身上貼一個高度紋理, 並按照那個高度把頂點拉伸出來, 就出現了新的幾何凹凸, 這就是Geometry Image貼圖。
我想說不管哪個行業,做實驗都要用小兔子。。。

至於Epic宣傳的160億面,是虛的,是不可能全部真實去渲染的。所以他們自己也提到,Nanite的超多面渲染技術叫做虛擬微多邊形幾何體 。

這麼多面的場景可以放入工程,但是絕對不可能全部進入渲染管線裡面。

也就是說,他們的技術方案厲害的地方不是在於為什麼能支援這麼多的多邊形渲染,而是如何將這麼多的多邊形處理成能用Image表達的資料,並且能夠在執行時快速的尋找和對映這些資料。

這裡已經提過的一個方案叫做,Virtual Texture。這個技術方案知乎的李兵大佬也有詳細講解過。

淺談Virtual Texture
https://zhuanlan.zhihu.com/p/138484024

主要內容就是將超大紋理分塊儲存在磁碟上,這部分叫做虛擬紋理,而在記憶體中有若干個紋理,這些紋理叫做物理紋理。任何時候當遊戲視野發生變化的時候,需要根據一些方案或者規則去維護這些物理紋理,不用的剔除掉,用的再從虛擬紋理中載入。

如何用Unity實現超多面渲染?其實只需這三招      

這樣的機制不僅僅減少了頻寬消耗和記憶體(視訊記憶體)消耗,也帶來了其他好處,比如有利於合批,不用因為使用不同的Texture而打斷合批,這樣可以根據需求來組織幾何,使得更利於Culling,當然合批的好處是states change 變少。LightMap也可以預計算到一張大的Virtual Texture上,用來合批。

配合SSD的高速硬碟的時候,這些操作並不會特別耗時。

技術方案不可能憑空的誕生,都是逐漸迭代出來的。所以這個方案應該是靠譜的。如果官方公佈的方案和大家猜測的不一樣,那也沒關係,再去學習新知識就好了。(接受理性討論,不接受嘲諷、抬槓)

知道(猜測)了Nanite的實現之後,我們也可以使用Unity現有的技術嘗試去理解,都是Unity2017就已經支援的技術。同樣這裡要感謝 Jasper 貢獻的教程。

我的譯製工作已經全部完成,正在Unity官方Connect:放牛的星星上連載,歡迎關注。

接下來我們要使用的組合拳法包括:

一、Unity標準著色器的功能,視差。這是基礎渲染教程第二十篇要介紹的內容。

二、曲面細分,OpenGL ES 目標級別4.6及以上可以支援的功能,Unity2017.1即可支援。

三、表面移位,基於曲面細分的具體應用,實現動態調整GPU頂點位置,處理曲面細分之後的陰影,以及剔除不可見細分。

其中,第一步闡述了Geometry Image在Unity上的實現方式,第二步介紹如何動態的生成超多面,第三步則是支援巨量“虛擬”三角面的核心,表現和剔除。

以上的這幾個步驟理論上都是可以放入 Mesh Shading管線的Task階段去完成的。

詳細內容

» 視差

由於視角的原因,當我們調整觀測點時,觀察到的事物的相對位置會發生變化。這種視覺現象稱為視差。可能我們平時遊戲開發叫透視。比如,在高鐵上看窗外的風景,附近的物體看起來很大並且移動迅速,而遠處的背景看起來很小並且移動較慢。

遊戲開發中常用的一個方式叫法線,它可以幫助我們產生一定程度的立體感和光照計算,比如下面就是一張正常的紋理和它的法線:

如何用Unity實現超多面渲染?其實只需這三招

如何用Unity實現超多面渲染?其實只需這三招

當我們只使用正常紋理的時候,Unity裡的表現如下:

如何用Unity實現超多面渲染?其實只需這三招

匯入法線之後,會好非常非常多,如下:

如何用Unity實現超多面渲染?其實只需這三招

法線非常好用也非常重要,但是由於它是向量,只能表示該點的或者面的方向,無法表達高度。就好比一張紙平放在1樓和平放在10樓法線都是一樣的。

要真正的能夠表達海拔高度,需要另外一個貼圖叫做高度貼圖。有了這個資訊之後,配合法線我們就能準確的進行“偽造”視差效果。

如何用Unity實現超多面渲染?其實只需這三招

這是一張灰度圖,白色表示最高點,黑色表示最低點。因為這張貼圖,通常用來做視差效果,所以我們叫它視差貼圖而不是高度貼圖。

有了高度貼圖之後,我們就可以把它和原紋理、法線進行取樣,調和計算之後,形成如下效果。

如何用Unity實現超多面渲染?其實只需這三招

這個技術並不只是這麼簡單,它涉及了非常多的方面和技術來解決由高度差帶來的投影,自陰影和接受投影,光照等等技術內容,這會在我更新到具體章節的時候介紹,因為和主題無關先略過。

到這裡的話我們可以想象一下這個方案的極致運用,是不是隻要有足夠精度和數量的法線貼圖,高度貼圖就能建立出非常完美的真實場景?當然是在完美處理的光影的情況下。

» 曲面細分

答案當然不是,因為現在這個技術是基於平面的,3A遊戲當然不可能只有一個平面。那麼接下來要打的第二拳叫曲面細分。

看看心欣的例子,低模的小兔子:

如何用Unity實現超多面渲染?其實只需這三招

曲面細分後動態生成的小兔子:

如何用Unity實現超多面渲染?其實只需這三招

曲面細分的原理和我們遊戲開發中常用的高模低模類似。只不過我們常用的都是兩套資源,先做高模烘法線,光影等資訊,然後再朝低模上面貼。

曲面細分則是根據演算法動態生成。好處就是我們完全不需要將我們不關心的東西載入進來。如果演算法合理,極致情況下,我們可以全部用一個平面來表示,然後根據需要來生成模型和頂點。

這是OpenGL ES的新的渲染管線。在頂點程式之前,還有一個Tessellation的過程。

如何用Unity實現超多面渲染?其實只需這三招

下面可以看一看這個例子,這是一個普通的正方形,有2個三角面。

如何用Unity實現超多面渲染?其實只需這三招

通過一些演算法(具體演算法和程式碼教程更新到這裡的時候都會介紹),我們可以讓它變成這樣:

如何用Unity實現超多面渲染?其實只需這三招

這樣:

如何用Unity實現超多面渲染?其實只需這三招

或者是這樣:


如何用Unity實現超多面渲染?其實只需這三招          

來個動圖看看:

如何用Unity實現超多面渲染?其實只需這三招

既然我們可以控制細分三角的數量,當然也可以把視距、光照、遮擋等其他因素考慮進來,作為因子共同影響曲面對三角形的細分。比如下面就是根據攝像機距離來控制細分的數量。

如何用Unity實現超多面渲染?其實只需這三招

那麼介紹到這裡,曲面細分的這拳打完了,你們可能會說,這不還是平面麼?

嚴格來說這是個招式,為後面的拳法做初始準備。在極致情況下,我們可以用高模的法線貼圖、視差貼圖、攝像機的相關引數或者其他因素來控制三角面的生成。

背面或者被遮擋的地方,甚至可以完全不用生成。

再來看下心欣的例子:

如何用Unity實現超多面渲染?其實只需這三招

原理幾乎是一致的。只是我們的例子太簡陋了。

» 表面移位

接下來就是最後一招了。我們現在已經能夠自動生成面數了,但是所有的面和頂點都在一個平面上,就像這隻兔子被液壓機壓成了一張紙一樣。

如果我們能根據某些演算法或者貼圖或者參考將這些頂點移位到合適的位置,是不是就能還原出整個兔子了?

要實現這一個目標,就需要把前面的視差貼圖和曲面細分結合使用。視差貼圖實際上就是一個置換貼圖,前面的動圖裡我們可以看到它可以用來偽造位移,既然可以偽造,那麼當然也可以將相同的貼圖用於實際的移位。

我們仍然使用前面的視差貼圖:

如何用Unity實現超多面渲染?其實只需這三招

一般來說把視差貼圖按照法線方向移動就沒什麼問題了。  

如何用Unity實現超多面渲染?其實只需這三招

曲面細分是有基礎三角形上做演算法計算出來的,理論上演算法足夠好的時候,生成的三角形和高模的差距不會太大,但是如果演算法不夠好的情況下,基礎三角形的數量會影響曲面生成的質量。越多的基礎三角形生成的質量越高。

比如下面兩個效果:

如何用Unity實現超多面渲染?其實只需這三招

如何用Unity實現超多面渲染?其實只需這三招

在淺視角下看看我們動態生成的效果,要記得這個是根據視差貼圖動態生成出來的哦。

如何用Unity實現超多面渲染?其實只需這三招          

好了,到這裡三套拳都打完了。當然所有其他不相關的內容都略去了,比如陰影,光照、剔除等等內容,細節實現都會在教程更新的時候,和原始碼一起給出。

下面給出稍微正式一點的效果,如下:

如何用Unity實現超多面渲染?其實只需這三招

這裡是已經考慮了光照和陰影的效果。

小結

如果目前藉助Unity實現Nanite類似的效果,技術上要攻克的是如何將ZBrush或者CAD進行正確的烘焙,以便匯出相應的低模,法線、視差貼圖、LOD貼圖等等。除此之外,還要做一套良好的記憶體管理和紋理對映,以便能夠在執行時快速定位和生成曲面資訊。


作者:放牛的星星
來源: Unity官方平臺
原地址:https://mp.weixin.qq.com/s/jnOCTpAxUMMHcLGY-FgUgA

相關文章