equalizer的0.4版本中,圖中繼資料的管理改用kd-tree實現,而不是原來的鬆散八叉樹。
事實上,kd-tree與八叉樹(octree)都是二叉樹(BSP)的變種。在三維圖形應用,諸如碰撞檢測,遮擋剔除以及光線追蹤等領域,都可以通過對三維情境中的圖中繼資料進行層次化細分,從而加速資料的遍曆與檢測過程。
- 二叉樹指用一個平面對情境的包圍盒進行分割,然後將幾何圖元分別放置在兩個子塊中,由於二叉樹中採用的分割平面可以是任意的,所以分割後的子塊常常是不規則的。
- kd-tree的劃分中將分割平面限定為必須與某個座標軸垂直;
- 而八叉樹則在每次分割時採用三個正交平面對空間進行等分。
二叉樹的劃分過於隨意,在應用中處理比較麻煩,而八叉樹相對規則,處理起來要容易的多,但是由於要對空間進行等分,所以每個子塊對圖元的包圍不夠緊密,在進行檢測的過程中會降低處理的效率。所以有人提出了鬆散八叉樹,也就是仍然採用正交的分割平面,但是允許子塊間不相等且可以相互重疊,這雖然提高了對圖元的包圍,有利於檢測過程的高效執行,但是也增加了結構的複雜性與處理的難度。
相比之下,kd-tree的劃分方法既有二叉樹的結構簡單的特點,又因為不要求對空間進行等分,所以相比八叉樹能夠更緊密的包圍圖元,所以正在被越來越多的人們關注。比如Stanford大學的人這幾年就在研究如何利用kd-tree結構來最佳化運動情境內的三維空間資料的管理,從而在GPU內實現即時的光線追蹤渲染。
回過頭來看equalizer中的eqPly執行個體,關於eqPly應用的結構及其0.3版本時採用的鬆散八叉樹結構,可以參考我前面的文章《針對equalizer(v0.3)中eqPly的分析 》,這裡主要探討0.4版本後的資料結構。
在新的版本中,從Ply格式的模型檔案中載入的頂點和三角形資料都以向量的形式儲存在mesh::VertexData中,用向量管理資料目的有三個,一方面可以供其它對象通過向量索引獲得所需的資料,另一方面向量資料可以簡單的與GPU中的VBO進行映射,當然還有最重要的一點,就是可以利用STL中的演算法對資料進行排序,從而實現kd-tree劃分。
載入模型時,首先由VertexBufferRoot從PLY模型中讀取原始的圖中繼資料,放置在VertexData中,作為VertexBufferBase的子類,VertexBufferRoot調用其setupTree函數開始構造kd-tree。從VertexBufferNode::setupTree中可以知道,對一個節點而言,在對節點內的頂點資料進行排序後,根據垂直於座標軸的一個面對節點內的頂點進行等分,這樣逐級進行劃分,直到節點內的頂點數量少於預先設定的LEAF_SIZE,則該節點為分葉節點,這時按順序將分葉節點內的頂點從VertexData內複製到專門用於儲存kd-tree內頂點資料的VertexBufferData對象內。
VertexBufferData對象是VertexBufferRoot類的成員,也是渲染過程中真正使用的頂點資料來源。 VertexBufferRoot是VertexBufferNode的子類,它與VertexBufferLeaf都是介面類 VertexBufferBase的子類。它們之間的關係如可見:
渲染模型時,首先構建一個用於遍曆kd-tree的向量,然後將kd-tree的根節點(VertexBufferRoot)放入該向量中,通過對節點的包圍盒與視錐體的相交性檢測,可以有三種情況,即完全位於視錐體內,部分位於視錐體內和完全位於視錐體外。不在視錐體內的,根本不必作進一步處理了,完全位於視錐體內的,調用該節點的渲染函數,並沿節點的樹狀結構逐級遍曆到各個頁節點進行幾何圖元的繪製;而對於部分位於視錐體內的節點,則需要對它的子節點分別進行進一步的遍曆檢測。具體的操作過程可以參考eqPly樣本中的eqPly::Channel::_drawModel函數。
當然,上述採用kd-tree的圖形資料加速結構,對於小情境來說價值不大,eqPly中採用kd-tree的目的一方面是由於Equalizer是用於並行渲染架構,它需要將計算負載分配給多個計算節點完成,所以每個節點通常只渲染一個非常複雜的情境中的一小塊,這時kd-tree就可有效裁減掉節點內不渲染的圖形元素,從而可以提高計算效率。此外,就像我前面提到的,kd-tree在當前的運動情境光線追蹤渲染領域很受重視,也許Equalizer的開發人員也在考慮對這些全域光照技術的支援,畢竟這也是充分發揮並行渲染技術優勢的很好的方向。
參考資料
1.Realtime Compute機圖形學,第9章
2.Physically-based image synthesis: From Theory to Implementation, 第四章