Octree は、3 次元空間内のオブジェクトの効率的な保存とクエリに使用される空間セグメンテーション データ構造です。
3 次元空間を 8 つの同じサイズのサブキューブ (8 つの子ノード) に分割し、それぞれをさらに 8 つの子ノードに細分化することができます。
オクツリーの構築と操作は次のとおりです。
オクツリーの構築: 最初に、3 次元空間全体がルート ノードとして大きな立方体に分割されます。次に、ルート ノードを 8 つのサブノードに再帰的に分割し、各サブノードがサブキューブを表します。このプロセスは、何らかの終了条件 (最大深度に達するか、サブキューブ内のオブジェクトの数がしきい値に達するなど) が満たされるまで再帰的に続行されます。
オブジェクトの挿入: オクツリーにオブジェクトを挿入するには、オブジェクトが配置されているサブキューブを見つける必要があります。ルート ノードから開始して、適切な子ノードが見つかるまで、オブジェクトの位置に従って子ノードがトラバースされます。子ノードが存在しない場合は、新しい子ノードを作成します。オブジェクトをこの子ノードに挿入します。子ノードが終了条件に達した場合は、特定の状況に応じて処理(オブジェクトをノードに格納したり、子ノードをさらに細分化するなど)します。
オブジェクトのクエリ: オクツリー内のオブジェクトをクエリするには、ルート ノードから開始し、クエリ条件に従って子ノードをトラバースする必要があるかどうかを判断できます。クエリ条件が子ノードの範囲と重複する場合、子ノードは再帰的に走査されます。クエリ条件が子ノードと重複しない場合、その子ノードはスキップできます。
オクツリーの各ノードは立方体の体積要素を表し、各ノードには 8 つの子ノードがあり、これら 8 つの子ノードによって表される体積要素の合計が親ノードの体積となります。
オクツリーのノードは 3 つのカテゴリに分類できます:
灰色のノード: 対応する立方体が検索要素によって占有されている;
白いノード: 対応する立方体に検索要素のコンテンツがない;
黒のノード: 対応する立方体が検索要素によって完全に占有されている。 。
八分木アルゴリズムのアイデア
(1). 最大再帰深さを設定します。
(2). シーンの最大サイズを見つけて、このサイズで最初の立方体を構築します。
(3). 含めることができ、子ノードを持たないキューブに ID 要素を順番にドロップします。
(4). 最大再帰深さに達しない場合は、それを 8 つの等しい部分に分割し、立方体に含まれるすべての単位要素を 8 つのサブ立方体に割り当てます。
(5). サブ立方体に割り当てられた単位要素の数がゼロではなく、親立方体と同じであることが判明した場合、空間分割理論によれば、サブ立方体は再分割されないため、サブ立方体は再分割を停止します。スペースが割り当てられています 少なくなければなりません 同じ数であれば、どのように切り取っても同じ数になり、無限に切り取ることになります。
(6). 最大再帰深さに達するまで 3 を繰り返します。
最大再帰深度を決定する方法。
最大再帰深度は、解像度を設定することで計算できます。解像度は、最下位レベルのオクツリーの最小ボクセルのサイズを表します。したがって、オクツリーの深さは解像度とポイントの関数です。クラウドデータ。
オクツリーに基づくボクセル最近傍検索 ボクセル
最近
傍検索では、クエリ点が位置するボクセル内の他の点のインデックスが返されるため、検索点と検索結果の間の距離はオクツリー パラメーターの解像度に依存します
C++ での簡単な実装を見てみましょう。
#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=(zmax-zmin)/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->zmax-p->zmin)/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");
}
}
}
このコードはインターネットから来たものですが、よくわかりません。一見すると、バイナリ ツリーとは明らかに異なります。バイナリ ツリーのノード構造には 2 つのノード構造ポインタが含まれていますが、これには 8 つのノード構造ポインタが含まれています;
走って見てください。
長さ、幅、高さが 2 の外側の境界ボックスを入力します。
(1, 1, 1) を見つけて直接終了します。
さらにいくつか検索すると、出力は次のようになります。
トラバーサルの場合、出力は非常に大きくなります。
まだよく理解できていません。コードを実行して理解を深め、次回に続けます。