八叉树

来源:互联网 发布:g20对杭州的影响 知乎 编辑:程序博客网 时间:2024/05/21 21:34

在写Ambient Occlusion时需要进行射线与物体的各个三角形的碰撞检测

但是逐个三角形进行判断需要进行大量的无意义计算

可以通过一个八叉树管理物体的包围盒空间

通过八叉树对物体的包围盒进行空间管理

这样判断射线是否与物体相交时可以省去大量的计算

当射线与某一个空间有相交的话,则进一步判断子空间与射线的相交性



算法描述:

判断当前节点空间中的三角形数是否小于我们设置的一个阀值,若小于则该节点设置为叶子节点(即无子节点)

若大于该阀值,则将该空间分为8份,并判断出在各子空间中的三角形,然后再对其子节点进行判断


 将节点所在空间为分8份比较简单,假设当前节点所表示空间的包围盒为Bound

则Bound.Center为该包围盒的中心,Bound.Extents为该包围盒中心到各边的距离

八个子空间的长宽高都为父空间的一半,

八个子空间中心的坐标分别为Bound.Center向八个方向移动0.5f * Bound.Extents的距离



首先我们先分析下节点的结构体:

我们要通过八叉树管理空间,则所有的节点都需要维护一个包围盒,表示节点所表示的空间

且每个叶子空间中都应该维护一份索引列表,以便我们通过八叉树对物体进行碰撞检测等操作


结构体如下:

struct OctreeNode{    #pragma region Properties     DirectX::BoundingBox Bounds;     std::vector<UINT> indices;                OctreeNode* Children[8];             //子节点          bool Isleaf;                                   //是否为叶子节点     #pragma endregion     //......};




并且定义一个函数将空间分为8份:

struct OctreeNode{    //.....        void Subdivide( DirectX::BoundingBox box[8] )    {        // the half lenght of width , height , depth        DirectX::XMFLOAT3 HalfExtent( 0.5f * Bounds.Extents.x ,                                      0.5f * Bounds.Extents.y ,                                      0.5f * Bounds.Extents.z );                //        // cacluate eight children node        //        // Top four quadrants        box[0].Center = DirectX::XMFLOAT3( Bounds.Center.x + HalfExtent.x ,                                           Bounds.Center.y + HalfExtent.y ,                                           Bounds.Center.z + HalfExtent.z );        box[0].Extents = HalfExtent;        box[1].Center = DirectX::XMFLOAT3( Bounds.Center.x - HalfExtent.x ,                                           Bounds.Center.y + HalfExtent.y ,                                           Bounds.Center.z + HalfExtent.z );        box[1].Extents = HalfExtent;        box[2].Center = DirectX::XMFLOAT3( Bounds.Center.x - HalfExtent.x ,                                           Bounds.Center.y + HalfExtent.y ,                                           Bounds.Center.z - HalfExtent.z );        box[2].Extents = HalfExtent;        box[3].Center = DirectX::XMFLOAT3( Bounds.Center.x + HalfExtent.x ,                                           Bounds.Center.y + HalfExtent.y ,                                           Bounds.Center.z - HalfExtent.z );        box[3].Extents = HalfExtent;        // bottom for quadrants        box[4].Center = DirectX::XMFLOAT3( Bounds.Center.x + HalfExtent.x ,                                           Bounds.Center.y - HalfExtent.y ,                                           Bounds.Center.z + HalfExtent.z );        box[4].Extents = HalfExtent;        box[5].Center = DirectX::XMFLOAT3( Bounds.Center.x - HalfExtent.x ,                                           Bounds.Center.y - HalfExtent.y ,                                           Bounds.Center.z + HalfExtent.z );        box[5].Extents = HalfExtent;        box[6].Center = DirectX::XMFLOAT3( Bounds.Center.x - HalfExtent.x ,                                           Bounds.Center.y - HalfExtent.y ,                                           Bounds.Center.z - HalfExtent.z );        box[6].Extents = HalfExtent;        box[7].Center = DirectX::XMFLOAT3( Bounds.Center.x + HalfExtent.x ,                                           Bounds.Center.y - HalfExtent.y ,                                           Bounds.Center.z - HalfExtent.z );        box[7].Extents = HalfExtent;    }};



然后我们需要定义一个八叉树类为物体生成一棵八叉树,并且添加一些碰撞检测函数

class Octress{public:Octress();~Octress();public:void Build(std::vector<DirectX::XMFLOAT3> vertices , std::vector<UINT> indices);void RayIntersect(DirectX::XMFLOAT3 RayPos , DirectX::XMFLOAT3 RayDir);private:// 以该节点一棵生成八叉树void BuildOctree(OctreeNode* parent , std::vector<UINT> indices);// 判断光线是否与当前节点所表示空间相交bool RayOctreeIntersect(OctreeNode* parent , DirectX::XMFLOAT3 RayPos , DirectX::XMFLOAT3 RayDir);private:// 根节点OctreeNode* mRoot;// 维护一份顶点拷贝std::vector<DirectX::XMFLOAT3> mVertices;}


void Octree::Build(std::vector<DirectX::XMFLOAT3> vertices , std::vector<UINT> indices){// 保存一份顶点拷贝mVertices = vertices;// 首先根据顶点列表计算出该物体的包围盒...// 创建并填充root节点...// 通过根节点生成一棵八叉树BuildOctree(mRoot , indices);}bool BuildOctree(OctreeNode* parent , std::vector<UINT> indices){// 判断当前节点的三角形数是否小于设定的阀值// 若小于,则设置该节点为叶子节点,不再分解if((indices.size() / 3) < threshold){parent->indices = indices;parent->Isleaf = true;}else{// 继续分解空间DirectX::BoundingBox box[8];parent->Subdivide(box);// 将父空间的索引分配到相应的子空间中for(int i = 0; i < 8; i++){parent->Children[i] = new OctreeNode;parent->Children[i]->Bounds = box[i];// 遍历各个三角形,若与子空间相交// 则认为该三角形属于该子空间std::vector<UINT> IntersectIndices;for(int j = 0; j < indices.size() / 3; j++){// 得到当前三角形的三个顶点...// 判断该三角形是否与该子空间相交...//若相交,则将索引入栈IntersectIndices.push_back(...);...//递归分解该子节点BuildOctree(parent->Children[i] , IntersectIndices);}}}}


0 0
原创粉丝点击