[轉]基於四叉樹(QuadTree)的LOD地形實現


實現基於四叉樹的LOD地形時,我遇到的主要問題是如何修補地形裂縫。

       本段我將描述使用LOD地形的優勢,我實現LOD地形的思路,實現LOD地形核心模塊的詳細過程,以及修補地形裂縫的思路。

       首先,LOD地形與一般地形不同:一般的地形是這樣實現的:整個地形是一個三角形網格,一個513*513的地形包括513*513個頂點,512*512*2個三角形;在開始渲染地形之前,寫入地形的頂點緩沖(vertexbuffer)和索引緩沖(indices buffer),頂點緩沖和索引緩沖都是數組;頂點緩沖存儲的是地形的頂點,每個頂點的屬性包括其自身的坐標;索引緩沖存儲的是每一幀渲染的頂點的索引,在頂點緩沖中,每個頂點都有自己的索引,索引緩沖的元素便是存儲於頂點緩沖中的元素的索引;每一幀渲染(Render或Draw)地形的時候,只根據索引緩沖便可渲染出地形,比如,一個3*3的地形,頂點緩沖中一共有9個元素,即9個頂點,若索引緩沖中存儲的元素是{0,1,3},則渲染的是頂點緩沖中第0、1、3個頂點,渲染出的形狀是部分地形:一個三角形,類似的,若索引緩沖中存儲的元素是{0,1,3,1,3,4},則渲染出的形狀是部分地形:由兩個三角形拼接而成的正方形,若索引緩沖中存儲的元素是{{0,1,3,}{1,3,4,}{1,2,4,}{2,4,5,}{3,4,6,}{4,6,7,}{4,5,7,}{5,7,8}},則渲染出的是完整的地形。LOD地形是這樣實現的:整個地形也是一個三角形網格,與一般地形不同的是,LOD地形當中,距離攝像頭(攝像頭相當於我們的眼睛)較近的地方,地形的三角網格較多,距離攝像頭較遠的地形,地形的三角網格較少,這樣在距離攝像頭較近的地方,我們看到的地形就更細致更真實,在距離攝像頭較遠的地方,渲染出來的地形比較粗糙,但是我們看到的效果和用一般地形渲染遠處地形呈現的效果一樣,所以,LOD地形每一幀渲染的頂點個數小於一般地形每一幀渲染的頂點個數,其渲染速度比一般地形的渲染速度高。之所以選擇LOD地形,是因為用一般地形的渲染方式渲染大型的地形會導致程序的運行效率比較低,而用LOD地形的渲染方式則會在渲染同樣大小的地形時提高程序的運行效率。比如,只渲染512*512的一般地形時,程序的幀速率(FPS)是100幀/秒,而只渲染1024*1024的一般地形時,程序的幀速率降到20幀/秒。3D游戲不僅包括地形,而且包括其他場景,其他場景的渲染效率有時候較低,比如粒子系統、多層紋理映射等都會使程序的渲染效率降低。包括地形在內的整個場景的幀速率至少要達到30幀/秒,才能保證游戲的流暢度。所以,如果使用大型的地形,必須保證地形的幀速率達到90幀/秒以上,這樣才能提高整個游戲場景的渲染效率。

       其次,我在實現LOD地形時,選擇的地形數據結構是四叉樹(QuadTree),在開始渲染地形之前,首先寫入整個地形的頂點緩沖、索引緩沖,在每一幀開始渲染地形之前,根據攝像頭當前的坐標更新索引緩沖,然后根據索引緩沖渲染出地形,接下來我將詳細介紹如何在每一幀更新索引緩沖。首先介紹四叉樹,地形的四叉樹結構QuadTreeData的核心屬性:int類型的中心頂點的索引m_nCenter,int類型的左上、右上、左下、右下頂點的索引m_nCorner[4],QuadTreeData*類型的左上、右上、左下、右下四個孩子節點m_pChild[4],bool類型的真實葉子變量m_bRealLeaf,bool類型的虛擬葉子變量m_bVirtualLeaf。整個地形是一個四叉樹,以下舉例說明如何建造地形:一個5*5的地形,頭節點的m_nCenter是12,m_nCorner[4]依次是0、4、20、24,m_pChild[0]的m_nCenter是6,m_nCorner[4]依次是0、2、10、12,m_pChild[1]、m_pChild[2]、m_pChild[3]的屬性值可以此類推。以下是我定義的四叉樹結構體:

typedef struct QuadTreeData  
  
float maxHeight;  
float minHeight;  
  
vector<int> mCrackU;  
vector<int> mCrackR;  
vector<int> mCrackB;  
vector<int> mCrackL;  
  
int m_nCenter;  /// 四叉數保存的第一個值,某網格中心點的索引  
int m_nCorner[4];   /// 四叉樹保存的第二個值  
  
bool    m_bVisible;  
bool    m_bVirtualLeaf; ///虛擬的葉子  
bool    m_bRealLeaf;    ///真實的葉子,真實的葉子沒有中心  
  
  
  
int mLevel;  
int mSelfCorner;  
bool    m_bHandled;  
  
vector<byte> mCode;  
vector<byte> mCodeU;  
vector<byte> mCodeB;  
vector<byte> mCodeL;  
vector<byte> mCodeR;  
  
double mGUID;  
  
QuadTreeData* m_pChild[4];  
QuadTreeData* m_pFather;  
QuadTreeData* m_pAncester;  
  
QuadTreeData;  

我們可以設定整個四叉樹的最多層級,比如9,頭結點的層級是0。若某個四叉樹節點的m_nCorner[1]和m_nCorner[0]相差為1,或者該四叉樹節點位於整個四叉樹的第9層,則該四叉樹節點為真實葉子,其m_bRealLeaf值為true,四個孩子節點的值均為空NULL,至此,整個地形的四叉樹建造完成。更新索引緩沖時,首先更新虛擬葉子節點:遍歷四叉樹的每個節點,並評價每個節點:若該節點為真實節點,則該節點不可再分,將virtualLeaf設置為true;若該節點為虛擬節點,若該節點不滿足評價公式(不可再細分),則將該節點的virtualLeaf設置為true。否則將virtualLeaf設置為false(評價公式:g=d/e×r×C1×C2 ,當g<1 ,則該節點可再分,virtualLeaf設置為false。其中,d表示節點和攝像機的距離;e表示節點邊長;r=e/diff ,其中diff表示該節點四個角最大高度與最小高度的差值;C1 、C2 代表分辨率。);其次修補地形裂縫,大致思路是:四叉樹的每個節點都有自己的huffman編碼,通過huffman編碼可以遍歷到自己的位置,如下圖所示:

每個節點同等級的上、下、左、右兄弟節點的huffman編碼可以根據自己處於父節點的位置(左上、右上、左下、右下)推導出來。在第一步更新了四叉樹的虛擬葉子節點之后,遍歷所有的虛擬葉子節,通過每個葉子節點的左上、右上、左下、右下兄弟節點的huffman編碼找到每個葉子節點的兄弟節點,若兄弟節點的virtualLeaf值為false,則在兄弟節點與該節點共享的邊上存在裂縫,進行修補:找出兄弟節點在該邊因細分所添加的三角形網的頂點,將這些點的索引依次壓入該節點該條邊用於修補裂縫的點的鏈表。至此,修補裂縫結束;最后創建新的索引緩沖:遍歷所有虛擬葉子節點,若該節點四個邊的用於修補裂縫的點的列表均為空,則將該節點的索引壓入索引緩沖,否則,將該節點存在裂縫的邊的用於修補裂縫的點的鏈表作為構建地形的三角形網的頂點,壓入該節點的索引。至此,索引緩沖更新完畢。

       效果圖如下:

原文鏈接: 

Unity教程之-基於四叉樹UQuadtree在Unity中實現場景資源的動態管理

最新-基於四叉樹的LOD地形設計

基於四叉樹(QuadTree)的LOD地形實現

面向GPU的多LOD因子的大規模場景可視化策略


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM