Terrain Reandering, Again !
作者:clayman
僅供個人學習使用,轉載請保留作者以及原文鏈接,勿用於任何商業用途。
前段時間修改了一個基於roam的古老地形系統,於是有了此文。
首先,關於roam,雖然早已經忘了具體算法,也從來沒有自己實現過基於roam的地形,但和以前blog里說的一樣,roam已經完完全全過時了,原因有以下幾點:
1.roam需要在CPU端實現,需要使用dynamic buffer,bad to gpu :(
2.roam對視點位置太敏感,微小的移動都會導致重新鑲嵌,而重新鑲嵌出的模型常常只在局部有微小變化,不但對性能提升無益,還導致視點連續改變時,popping現象嚴重:(
3.roam計算量太大。本人沒有做具體性能分析,但同事告知,如果鑲嵌密度提高一倍,即使i5的cpu算起來也很吃力,況且我們游戲的視野並不算遠:(
由於以上幾點原因,我希望新的地形1,提高性能,對CPU和GPU都相對友好;2,算法簡單;3,減少popping現象。最終,我們有了一個chunk lod + interlocking tile的方案。有趣的是完成新系統前,我都沒有看過<<Simplified Terrain Using Interlocking Tiles>>,而是從wow的實現得到了很多靈感。有沒有搞錯Chunk lod + interlocking tile?? 高端前沿開發者在鄙視我了,確實,這並不是什么新技術。但下面我就告訴你為什么它已經足夠好了!!
首先,geo clipmap,dynamic tessellation之類的方案一開始就被否決了,原因很簡單,對GPU不友好。我希望新系統在dx9 sm2.0級別的硬件上也能跑。此外,一直以來我都認為通過動態計算來實現地形這類相對“靜態”的物體太不值得。即使目標平台是DX11,也不值得通過tessellator計算地形,這兩種技術雖然只需要非常少的vertex buffer,但計算過程所消耗的帶寬和計算量遠遠超過小buffer帶來的好處。
Chunk lod對地形來說是非常理想的算法,對整個地形塊進行lod改變,避免了roam過度敏感的問題,此外,算法非常簡單,甚至可以說不需要算法:每個chunk使用固定的vertex buffer,預先計算好幾個不同LOD下的索引數據,選擇不同的index就完成了lod變換,static vertex buffer,static index buffer,沒有運行時計算,並且index可以被所有相同lod的chunk共享。最經典的例子莫過於wow的地形(不熟悉的趕緊google)。Chunk lod的問題在於如何與相鄰不同lod的chunk相連接,避免出現T-junctions。這個問題初看起來很復雜,但如果我們限制第n層lod的chunk只能與n+1層的chunk相連接,那問題就變得非常簡單,我們只需要為每層LOD創建9種不同的索引,保證邊緣的頂點一致就可以,如下圖所示:

對於有n層lod的地形,需要n*10種不同索引,聽起來似乎有些多,但考慮到同層的所有chunk都能夠共享這10塊buffer,其實數據量並沒有多少。有什么沒考慮到的嗎?
要對地形打洞怎么辦?
很常見的需求。如何從頂點級的層面來考慮,似乎完全無法和目前的方法兼容;但如果從像素級的層面考慮,地形上的洞其實就是透明像素而已,完全可以使用貼圖或者添加頂點數據實現特定部分的透明:)
精度最高的層是每個頂點之間2個單位的距離,大部分情況下很好,但在某些山尖的地方稍微有些粗糙,不把最高lod增加到1個單位間距的情況下,有什么方法可以改進?
注意觀察上圖,interlocking tile的精髓就在於只要邊緣保持和鄰接層一致,內部可以任意鑲嵌。如何你覺得0級的lod在某些情況下精度還不夠,可以再創建0+級,邊緣和0級的保持一致,內部精度可以再提高n倍,對於有需要的特定chunk使用0+級索引,其余大部分chunk仍使用0級:)
Chunk lod改變時還是會出現popping現象,有什么解決方案嗎?
方案1,讓你的第0級chunk范圍足夠大,當遠處的chunk lod改變時,不太容易發現popping :)
方案2,所有地面都覆蓋上草地,就算有popping你也看不到了:)
方案3,使用Geo-Morphing,我們可以得到完美的lod轉變。比如每個頂點都保存了在不同LOD層下的高端,當LOD變化時,插值變換到正確的高度。
方案4,把以上3種方案結合起來,讓0級范圍足夠大,對0到1級之間的轉變使用morphing,植樹種草,你基本看不到popping了:)
那我的頂點數據不是要多很多?
壓縮,壓縮,壓縮。地形數據並不需要32位float精度。通常16位就足夠了,嘗試使用short,ubyte,udec3之類的格式保存位置,法線和紋理坐標。
如何管理組織地形?
每個chunk不管處於任何lod狀態,大小都是固定的,基本上二位數組就夠了,對,完全沒有必要使用四叉樹!
如何實現無限大地形?
你大概需要把剛才的二位數組改為環形二維數組,當視線移動時,刪除看不到的chunk,並且把新chunk加載到之前刪除的空間,類似這樣

對於wow類型的游戲,多少級lod才夠,每個chunk多大,多少三角形合適?
如果你看過介紹wow地形的文章,大都會告訴你wow使用的了2級lod。下圖的場景中,大部分地形都處於0級,從有霧的區域開始,非常遠的地形才會使用1級。0級Lod每個chunk512個三角形,1級256個。下圖的場景大概有250~300個chunk,每個chunk一次DP!三個,不要超過三個LOD,不能再多了:)

地形渲染的瓶頸在什么地方?multipass紋理性能如何。
根據上一個問題,chunk/DP數量其實都不是問題,頂點/三角形數量對性能的影響非常小。曾經在perfhud的測試中發現不同lod但覆蓋近似大小屏幕區域的chunk渲染費時幾乎一樣,主要計算都花費在pixel shader,因此,shader lod也很重要。比如對只對最近的n個tile使用normal map,對遠端的chunk直接用一張預計算的紋理或者霧的顏色,盡量避免multipass。
關於texture blend有什么要說的嗎?
1, 最大四層紋理對於大多數游戲都足夠了。我們的古老地形系統使用multipass理論上可以支持無限層紋理,但最終很少有一個chunk會使用4層以上,並且multipass最終成了最大瓶頸。2,如果你覺得普通線性混合出的紋理效果不好,那么可以參考泰坦之旅的方法。3. 通常把4層紋理的權重保存在一個ubyte4中,如果保證權重總和為一,那么可以通過x-rgb計算第四層紋理的權重,在r通道中保存一些其他東西:)
好吧,還有什么更高級的技術嗎?
類wow游戲中,基於height map的技術已經非常成熟,由於硬件的發展,lod已經變的越來越不太重要。現有技術就可以完全滿足需要。如果想有突破,那么09年GDC《HALO WARS: The Terrain of Next-Gen》中基於vector field的地形解決了height map不能出現高度重疊的問題,非常值得嘗試(一直沒明白他們如何解決碰撞檢測,求高人指點)
