八叉树 Octree
?
http://hi.baidu.com/onlywater/blog/item/905c5e162ed18f4021a4e9c1.html
(一)基本原理
????用八叉樹來表示三維形體,并研究在這種表示下的各種操作及應用是在進入80年代后才比較全面地開展起來的。這種方法,既可以看成是四叉樹方法在三維空間的推廣,也可以認為是用三維體素陣列表示形體方法的一種改進。
????八叉樹的邏輯結構如下:
????假設要表示的形體V可以放在一個充分大的正方體C內,C的邊長為2 n,形體V C,它的八叉樹可以用以下的遞歸方法來定義:
????八叉樹的每個節點與C的一個子立方體對應,樹根與C本身相對應,如果V=C,那么V的八叉樹僅有樹根,如果V≠C,則將C等分為八個子立方體,每個子立方體與樹根的一個子節點相對應。只要某個子立方體不是完全空白或完全為V所占據,就要被八等分(圖2-5-1),從而對應的節點也就有了八個子節點。這樣的遞歸判斷、分割一直要進行到節點所對應的立方體或是完全空白,或是完全為V占據,或是其大小已是預先定義的體素大小,并且對它與V之交作一定的“舍入”,使體素或認為是空白的,或認為是V占據的。
????如此所生成的八叉樹上的節點可分為三類:
????灰節點,它對應的立方體部分地為V所占據;
????白節點,它所對應的立方體中無V的內容;
????黑節點,它所對應的立方體全為V所占據。
????后兩類又稱為葉結點。形體V關于C的八叉樹的邏輯結構是這樣的:它是一顆樹,其上的節點要么是葉節點,要么就是有八個子節點的灰節點。根節點與C相對應,其它節點與C的某個子立方體相對應。
????因為八叉樹的結構與四叉樹的結構是如此的相似,所以八叉樹的存貯結構方式可以完全沿用四叉樹的有關方法。因而,根據不同的存貯方式,八叉樹也可以分別稱為常規的、線性的、一對八的八叉樹等等。
????另外,由于這種方法充分利用了形體在空上的相關性,因此,一般來說,它所占用的存貯空間要比三維體素陣列的少。但是實際上它還是使用了相當多的存貯,這并不是八叉樹的主要優點。這一方法的主要優點在于可以非常方便地實現有廣泛用途的集合運算(例如可以求兩個物體的并、交、差等運算),而這些恰是其它表示方法比較難以處理或者需要耗費許多計算資源的地方。不僅如此,由于這種方法的有序性及分層性,因而對顯示精度和速度的平衡、隱線和隱面的消除等,帶來了很大的方便,特別有用。
(二)八叉樹的存貯結構
????八叉樹有三種不同的存貯結構,分別是規則方式、線性方式以及一對八方式。相應的八叉樹也分別稱為規則八叉樹、線性八叉樹以及一對八式八叉樹。不同的存貯結構的空間利用率及運算操作的方便性是不同的。分析表明,一對八式八叉樹優點更多一些。
1、規則八叉樹
????規則八叉樹的存貯結構用一個有九個字段的記錄來表示樹中的每個結點。其中一個字段用來描述該結點的特性(在目前假定下,只要描述它是灰、白、黑三類結點中哪一類即可),其余的八個字段用來作為存放指向其八個子結點的指針。這是最普遍使用的表示樹形數據的存貯結構方式。
????規則八叉樹缺陷較多,最大的問題是指針占用了大量的空間。假定每個指針要用兩個字節表示,而結點的描述用一個字節,那么存放指針要占總的存貯量的94%。因此,這種方法雖然十分自然,容易掌握,但在存貯空間的使用率方面不很理想。
2、線性八叉樹
????線性八叉樹注重考慮如何提高空間利用率。用某一預先確定的次序遍歷八叉樹(例如以深度第一的方式),將八叉樹轉換成一個線性表(圖2-5-2),表的每個元素與一個結點相對應。對于結點的描述可以豐富一點,例如用適當的方式來說明它是否為葉結點,如果不是葉結點時還可用其八個子結點值的平均值作為非葉結點的值等等。這樣,可以在內存中以緊湊的方式來表示線性表,可以不用指針或者僅用一個指針表示即可。
????線性八叉樹不僅節省存貯空間,對某些運算也較為方便。但是為此付出的代價是喪失了一定的靈活性。例如為了存取屬于原圖形右下角的子圖形對應的結點,那么必須先遍歷了其余七個子圖形對應的所有結點后才能進行;不能方便地以其它遍歷方式對樹的結點進行存取,導致了許多與此相關的運算效率變低。因此盡管不少文章討論了這種八叉樹的應用,但是仍很難令人滿意
????一個非葉結點有八個子結點,為了確定起見,將它們分別標記為0,1,2,3,4,5,6,7。從上面的介紹可以看到,如果一個記錄與一個結點相對應,那么在這個記錄中描述的是這個結點的八個子結點的特性值。而指針給出的則是該八個子結點所對應記錄的存放處,而且還隱含地假定了這些子結點記錄存放的次序。也就是說,即使某個記錄是不必要的(例如,該結點已是葉結點),那么相應的存貯位置也必須空閑在那里(圖2-5-3),以保證不會錯誤地存取到其它同輩結點的記錄。這樣當然會有一定的浪費,除非它是完全的八叉樹,即所有的葉結點均在同一層次出現,而在該層次之上的所有層中的結點均為非葉結點。
????為了克服這種缺陷,有兩條途徑可以采納。一是增加計算量,在記錄中增加一定的信息,使計算工作適當減少或者更方便。
?
?
?
================================================================================
http://www.cppblog.com/d3d/archive/2009/01/19/72321.html
?
Octree的定義是:若不為空樹的話,樹中任一節點的子節點恰好只會有八個,或
零個,也就是子節點不會有0與8以外的數目。那么,這要用來做什么?想象一個
立方體,我們最少可以切成多少個相同等分的小立方體?答案就是8個。再想象
我們有一個房間,房間里某個角落藏著一枚金幣,我們想很快的把金幣找出來,
聰明的你會怎么做?我們可以把房間當成一個立方體,先切成八個小立方體,
然后排除掉沒有放任何東西的小立方體,再把有可能藏金幣的小立方體繼續切八
等份….如此下去,平均在Log8(房間內的所有物品數)的時間內就可找到金幣。
因此,Octree就是用在3D空間中的場景管理,可以很快地知道物體在3D場景中
的位置,或偵測與其它物體是否有碰撞以及是否在可視范圍內。
2、實現Octree的原理
(1). 設定最大遞歸深度
(2). 找出場景的最大尺寸,并以此尺寸建立第一個立方體
(3). 依序將單位元元素丟入能被包含且沒有子節點的立方體
(4). 若沒有達到最大遞歸深度,就進行細分八等份,再將該立方體所裝的單位元元素全部分擔給八
個子立方體
(5). 若發現子立方體所分配到的單位元元素數量不為零且跟父立方體是一樣的,則該子立方體停止
細分,因為跟據空間分割理論,細分的空間所得到的分配必定較少,若是一樣數目,則再怎么切數目
還是一樣,會造成無窮切割的情形。
(6). 重復3,直到達到最大遞歸深度。
4、BSP Tree和Octree對比
a) BSP Tree將場景分割為1個面,而Octree分割為3個面。
b) BSP Tree每個節點最多有2個子結點,而Octree最多有8個子結點
因此BSP Tree可以用在不論幾唯的場景中,而Octree則常用于三維場景
#include? < iostream >
using namespace std;
// 定義八叉樹節點類
template < class T >
struct OctreeNode
{
????T?data;? // 節點數據
T?xmin,xmax;? // 節點坐標,即六面體個頂點的坐標
T?ymin,ymax;
????T?zmin,zmax;
????OctreeNode? < T > * top_left_front, * top_left_back;? // 該節點的個子結點
OctreeNode? < T > * top_right_front, * top_right_back;
????OctreeNode? < T > * bottom_left_front, * bottom_left_back;
????OctreeNode? < T > * bottom_right_front, * bottom_right_back;
????OctreeNode? // 節點類
(T?nodeValue? = T(),
????????T?xminValue? = T(),T?xmaxValue? = T(),
????????T?yminValue? = T(),T?ymaxValue? = T(),
????????T?zminValue? = T(),T?zmaxValue? = T(),
????????OctreeNode < T >* top_left_front_Node? = NULL,
????????OctreeNode < T >* top_left_back_Node? = NULL,
????????OctreeNode < T >* top_right_front_Node? = NULL,
????????OctreeNode < T >* top_right_back_Node? = NULL,
????????OctreeNode < T >* bottom_left_front_Node? = NULL,
????????OctreeNode < T >* bottom_left_back_Node? = NULL,
????????OctreeNode < T >* bottom_right_front_Node? = NULL,
????????OctreeNode < T >* bottom_right_back_Node? = NULL?)
????????:data(nodeValue),
????????xmin(xminValue),xmax(xmaxValue),
????????ymin(yminValue),ymax(ymaxValue),
????????zmin(zminValue),zmax(zmaxValue),
????????top_left_front(top_left_front_Node),
????????top_left_back(top_left_back_Node),
????????top_right_front(top_right_front_Node),
????????top_right_back(top_right_back_Node),
????????bottom_left_front(bottom_left_front_Node),
????????bottom_left_back(bottom_left_back_Node),
????????bottom_right_front(bottom_right_front_Node),
????????bottom_right_back(bottom_right_back_Node){}
};
// 創建八叉樹
template? < class T >
void createOctree(OctreeNode < T > * & root, int maxdepth, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax)
{
????cout << " 處理中,請稍候…… " << endl;
????maxdepth = maxdepth - 1 ;? // 每遞歸一次就將最大遞歸深度-1
if (maxdepth >= 0 )
????{
????????root = new OctreeNode < T > ();
????????root -> data? = 9 ;? // 為節點賦值,可以存儲節點信息,如物體可見性。由于是簡單實現八叉樹功能,簡單賦值為。
root -> xmin = xmin;? // 為節點坐標賦值
root -> xmax = xmax;
????????root -> ymin = ymin;
????????root -> ymax = ymax;
????????root -> zmin = zmin;
????????root -> zmax = zmax;
???????? double xm = (xmax - xmin) / 2 ; // 計算節點個維度上的半邊長
double ym = (ymax - ymin) / 2 ;
???????? double zm = (ymax - ymin) / 2 ;
???????? // 遞歸創建子樹,根據每一個節點所處(是幾號節點)的位置決定其子結點的坐標。
createOctree(root -> top_left_front,maxdepth,xmin,xmax - xm,ymax - ym,ymax,zmax - zm,zmax);
????????createOctree(root -> top_left_back,maxdepth,xmin,xmax - xm,ymin,ymax - ym,zmax - zm,zmax);
????????createOctree(root -> top_right_front,maxdepth,xmax - xm,xmax,ymax - ym,ymax,zmax - zm,zmax);
????????createOctree(root -> top_right_back,maxdepth,xmax - xm,xmax,ymin,ymax - ym,zmax - zm,zmax);
????????createOctree(root -> bottom_left_front,maxdepth,xmin,xmax - xm,ymax - ym,ymax,zmin,zmax - zm);
????????createOctree(root -> bottom_left_back,maxdepth,xmin,xmax - xm,ymin,ymax - ym,zmin,zmax - zm);
????????createOctree(root -> bottom_right_front,maxdepth,xmax - xm,xmax,ymax - ym,ymax,zmin,zmax - zm);
????????createOctree(root -> bottom_right_back,maxdepth,xmax - xm,xmax,ymin,ymax - ym,zmin,zmax - zm);
????}
}
int i = 1 ;
// 先序遍歷八叉樹
template? < class T >
void preOrder(?OctreeNode < T > * & p)
{
???? if (p)
????{
????????cout << i << " .當前節點的值為: " << p -> data << " /n坐標為: " ;
????????cout << " xmin:? " << p -> xmin << " xmax:? " << p -> xmax;
????????cout << " ymin:? " << p -> ymin << " ymax:? " << p -> ymax;
????????cout << " zmin:? " << p -> zmin << " zmax:? " << p -> zmax;
????????i += 1 ;
????????cout << endl;
????????preOrder(p -> top_left_front);
????????preOrder(p -> top_left_back);
????????preOrder(p -> top_right_front);
????????preOrder(p -> top_right_back);
????????preOrder(p -> bottom_left_front);
????????preOrder(p -> bottom_left_back);
????????preOrder(p -> bottom_right_front);
????????preOrder(p -> bottom_right_back);
????????cout << endl;
????}
}
// 求八叉樹的深度
template < class T >
int depth(OctreeNode < T > *& p)
{
???? if (p? == NULL)
???????? return - 1 ;
???? int h? = depth(p -> top_left_front);
???? return h + 1 ;
}
// 計算單位長度,為查找點做準備
int cal( int num)
{
???? int result = 1 ;
???? if ( 1 == num)
????????result = 1 ;
???? else
????{
???????? for ( int i = 1 ;i < num;i ++ )
????????????result = 2 * result;
????}
???? return result;
}
// 查找點
int maxdepth = 0 ;
int times = 0 ;
static double xmin = 0 ,xmax = 0 ,ymin = 0 ,ymax = 0 ,zmin = 0 ,zmax = 0 ;
int tmaxdepth = 0 ;
double txm = 1 ,tym = 1 ,tzm = 1 ;
template < class T >
void find(OctreeNode < T > *& p, double x, double y, double z)
{
???? double xm = (p -> xmax - p -> xmin) / 2 ;
???? double ym = (p -> ymax - p -> ymin) / 2 ;
???? double zm = (p -> ymax - p -> ymin) / 2 ;
????times ++ ;
???? if (x > xmax? || x < xmin? || y > ymax? || y < ymin? || z > zmax? || z < zmin)
????{
????????cout << " 該點不在場景中! " << endl;
???????? return ;
????}
???? if (x <= p -> xmin + txm? && x >= p -> xmax - txm? && y <= p -> ymin + tym? && y >= p -> ymax - tym? && z <= p -> zmin + tzm? && z >= p -> zmax - tzm?)
????{
????????cout << endl << " 找到該點! " << " 該點位于 " << endl;
????????cout << " xmin:? " << p -> xmin << " xmax:? " << p -> xmax;
????????cout << " ymin:? " << p -> ymin << " ymax:? " << p -> ymax;
????????cout << " zmin:? " << p -> zmin << " zmax:? " << p -> zmax;
????????cout << " 節點內! " << endl;
????????cout << " 共經過 " << times << " 次遞歸! " << endl;
????}
???? else if (x < (p -> xmax - xm)? && y < (p -> ymax - ym)? && z < (p -> zmax - zm))
????{
????????cout << " 當前經過節點坐標: " << endl;
????????cout << " xmin:? " << p -> xmin << " xmax:? " << p -> xmax;
????????cout << " ymin:? " << p -> ymin << " ymax:? " << p -> ymax;
????????cout << " zmin:? " << p -> zmin << " zmax:? " << p -> zmax;
????????cout << endl;
????????find(p -> bottom_left_back,x,y,z);
????}
???? else if (x < (p -> xmax - xm)? && y < (p -> ymax - ym)? && z > (p -> zmax - zm))
????{
????????cout << " 當前經過節點坐標: " << endl;
????????cout << " xmin:? " << p -> xmin << " xmax:? " << p -> xmax;
????????cout << " ymin:? " << p -> ymin << " ymax:? " << p -> ymax;
????????cout << " zmin:? " << p -> zmin << " zmax:? " << p -> zmax;
????????cout << endl;
????????find(p -> top_left_back,x,y,z);
????}
???? else if (x > (p -> xmax - xm)? && y < (p -> ymax - ym)? && z < (p -> zmax - zm))
????{
????????cout << " 當前經過節點坐標: " << endl;
????????cout << " xmin:? " << p -> xmin << " xmax:? " << p -> xmax;
????????cout << " ymin:? " << p -> ymin << " ymax:? " << p -> ymax;
????????cout << " zmin:? " << p -> zmin << " zmax:? " << p -> zmax;
????????cout << endl;
????????find(p -> bottom_right_back,x,y,z);
????}
???? else if (x > (p -> xmax - xm)? && y < (p -> ymax - ym)? && z > (p -> zmax - zm))
????{
????????cout << " 當前經過節點坐標: " << endl;
????????cout << " xmin:? " << p -> xmin << " xmax:? " << p -> xmax;
????????cout << " ymin:? " << p -> ymin << " ymax:? " << p -> ymax;
????????cout << " zmin:? " << p -> zmin << " zmax:? " << p -> zmax;
????????cout << endl;
????????find(p -> top_right_back,x,y,z);
????}
???? else if (x < (p -> xmax - xm)? && y > (p -> ymax - ym)? && z < (p -> zmax - zm))
????{
????????cout << " 當前經過節點坐標: " << endl;
????????cout << " xmin:? " << p -> xmin << " xmax:? " << p -> xmax;
????????cout << " ymin:? " << p -> ymin << " ymax:? " << p -> ymax;
????????cout << " zmin:? " << p -> zmin << " zmax:? " << p -> zmax;
????????cout << endl;
????????find(p -> bottom_left_front,x,y,z);
????}
???? else if (x < (p -> xmax - xm)? && y > (p -> ymax - ym)? && z > (p -> zmax - zm))
????{
????????cout << " 當前經過節點坐標: " << endl;
????????cout << " xmin:? " << p -> xmin << " xmax:? " << p -> xmax;
????????cout << " ymin:? " << p -> ymin << " ymax:? " << p -> ymax;
????????cout << " zmin:? " << p -> zmin << " zmax:? " << p -> zmax;
????????cout << endl;
????????find(p -> top_left_front,x,y,z);
????}
???? else if (x > (p -> xmax - xm)? && y > (p -> ymax - ym)? && z < (p -> zmax - zm))
????{
????????cout << " 當前經過節點坐標: " << endl;
????????cout << " xmin:? " << p -> xmin << " xmax:? " << p -> xmax;
????????cout << " ymin:? " << p -> ymin << " ymax:? " << p -> ymax;
????????cout << " zmin:? " << p -> zmin << " zmax:? " << p -> zmax;
????????cout << endl;
????????find(p -> bottom_right_front,x,y,z);
????}
???? else if (x > (p -> xmax - xm)? && y > (p -> ymax - ym)? && z > (p -> zmax - zm))
????{
????????cout << " 當前經過節點坐標: " << endl;
????????cout << " xmin:? " << p -> xmin << " xmax:? " << p -> xmax;
????????cout << " ymin:? " << p -> ymin << " ymax:? " << p -> ymax;
????????cout << " zmin:? " << p -> zmin << " zmax:? " << p -> zmax;
????????cout << endl;
????????find(p -> top_right_front,x,y,z);
????}
}
// main函數
int main?()
{
????OctreeNode < double > * rootNode? = NULL;
???? int choiced? = 0 ;
???? while ( true )
????{
????????system( " cls " );
????????cout << " 請選擇操作:/n " ;
????????cout << " 1.創建八叉樹?2.先序遍歷八叉樹/n " ;
????????cout << " 3.查看樹深度?4.查找節點???/n " ;
????????cout << " 0.退出/n/n " ;
????????cin >> choiced;
???????? if (choiced? == 0 )
???????????? return 0 ;
???????? else if (choiced? == 1 )
????????{
????????????system( " cls " );
????????????cout << " 請輸入最大遞歸深度: " << endl;
????????????cin >> maxdepth;
????????????cout << " 請輸入外包盒坐標,順序如下:xmin,xmax,ymin,ymax,zmin,zmax " << endl;
????????????cin >> xmin >> xmax >> ymin >> ymax >> zmin >> zmax;
???????????? if (maxdepth >= 0 || xmax > xmin? || ymax > ymin? || zmax > zmin? || xmin > 0 || ymin > 0 || zmin > 0 )
????????????{
????????????????tmaxdepth = cal(maxdepth);
????????????????txm = (xmax - xmin) / tmaxdepth;
????????????????tym = (ymax - ymin) / tmaxdepth;
????????????????tzm = (zmax - zmin) / tmaxdepth;
????????????????createOctree(rootNode,maxdepth,xmin,xmax,ymin,ymax,zmin,zmax);
????????????}
???????????? else
????????????{
????????????????cout << " 輸入錯誤! " ;
???????????????? return 0 ;
????????????}
????????}
???????? else if (choiced? == 2 )
????????{
????????????system( " cls " );
????????????cout << " 先序遍歷八叉樹結果:/n " ;
????????????i = 1 ;
????????????preOrder(rootNode);
????????????cout << endl;
????????????system( " pause " );
????????}
???????? else if (choiced? == 3 )
????????{
????????????system( " cls " );
???????????? int dep? = depth(rootNode);
????????????cout << " 此八叉樹的深度為 " << dep + 1 << endl;
????????????system( " pause " );
????????}
???????? else if (choiced? == 4 )
????????{
????????????system( " cls " );
????????????cout << " 請輸入您希望查找的點的坐標,順序如下:x,y,z/n " ;
???????????? double x,y,z;
????????????cin >> x >> y >> z;
????????????times = 0 ;
????????????cout << endl << " 開始搜尋該點…… " << endl;
????????????find(rootNode,x,y,z);
????????????system( " pause " );
????????}
???????? else
????????{
????????????system( " cls " );
????????????cout << " /n/n錯誤選擇!/n " ;
????????????system( " pause " );
????????}
????}
}
?
總結
以上是生活随笔為你收集整理的八叉树 Octree的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Levenshtein Distance
- 下一篇: 论文阅读(Improving neura