第三章 演算法設計與討論
3.2 K-D Tree
光線追蹤需跟場景做求交測試,假如場景都由三角片所組成,那畫面上每 一個像素值都需跟場景中的所有三角片做求交測試,其結果在渲染一張畫面所 需的時間要相當的久。所以演化出許多加速結構來減少場景中的求交測試。本
研究採用 K-D Tree 演算法來實作,雖然建置樹狀結構的時間會較久,但走訪時 間是相對於其他加速結構中較快的。
3.2.1 K-D Tree Construction
本研究演算法首先在 OpenCL 主機端上建立樹狀結構,先計算每個三角片三 個點的平均值作為空間描述的分類依據,並找出所有三角片的最大和最小值當作 根結點走訪的第一個包圍盒(Bounding Box)。其後以座標軸對稱(Axis-Aligned
Bounding Box)的格式切開場景,以由上往下的方式進行,將場景已 X.Y.Z 三軸輪 流做切割如此遞迴下去。之後將切割好的樹狀結構以前序走訪(pre-order)存入一維 陣列中,存入的三角片是以編號方式存入,此陣列作法是避免如果從主機端以指 標結構的方式傳入裝置端會因為兩者所指向的記憶體區塊位置不同而造成資料 傳送錯誤。最後把主機端陣列資訊傳入裝置端。下圖為場景切割建構圖。
圖 3: K-D Tree 樹狀建構圖
3.2.2 K-D Tree Traversal
K-D Tree 的走訪是實作在裝置端上,光線會先從根節點(root)出發,先對根節 點的包圍盒進行求交點測試,當無交點產生時即不需要走訪下去。當與根節點有 交點時會根據此節點的邊界值(Bounding Value)來判定是該往右還是往左走,如此 走訪下去直到走到葉節點(leaf)或是堆疊內的節點為空。當與葉節點的包圍盒有交 點時,會再將光線與此節點內的所有三角片進行求交點測試,如此一來變可減少 光線與場景中的所有三角片的求交點測試,並減少渲染時間,提高效能。在走訪 的過程中我們採用固定大小的堆疊(stack)來記錄等待走訪的節點,在這我們以迴 圈的方式走訪,因為 OpenCL 並不支援程式以遞迴的方式寫成。當光線與節點包 圍盒產生交點時,會再根據此節點的邊界值來判定是左右子樹皆需走訪,還是只 與一邊有交集。最後再根據光線位置來判定當大於邊界值則往右子樹走訪,小於 邊界值則往左子樹走訪,當相等時會先把待走訪的節點放入堆疊中,等待一邊子 樹走訪完後,再取出堆疊內的節點繼續走訪。堆疊方式紀錄等待走訪節點的方法 預計在全域記憶體存取頻率較高時,會更加吃重裝置的記憶體頻寬(memory
bandwidth)效能和區域記憶體(local memory)。附圖為傳入裝置端的一維陣列結構,
當從主機端傳入裝置端的三角片會給予編號,objId 即為每個節點中所存的三角片 編號。axis 為該節點是用 x, y, z 哪一軸做平分左右子樹。bound 為該節點切割標 準值。objNum 為該節點內物體數量。leftChild 和 rightChild 則為該節點所記錄的 左右子樹。num[3]為記憶體對齊所配置的額外空間,因 GPU 在做資料搬移時是一
次抓四個 byte 的資料,為了湊齊四的倍數,額外產生的配置空間。boundMax 和
boundMin 為此節點的最大最小值。
struct clTnode {
int objId[maxInnode];
int axis;
float bound;
int objNum;
int leftChild;
int rightChild;
int num[3];
float4 boundMax;
float4 boundMin;
};
圖 4: 自訂傳入裝置端的 K-D Tree 陣列