介绍
四叉树(Quadtree)和八叉树(Octree)是一种常用的空间分割数据结构,用于将多维空间划分为更小的子空间。它们在计算机图形学、计算机视觉、物理模拟和空间索引等领域得到广泛应用。
四叉树是一种二维空间划分树结构,将一个矩形区域递归地划分为四个子区域,每个子区域可以继续划分为四个更小的子区域。每个节点代表一个区域,节点可以有四个子节点或者为空。四叉树的每个节点可以用来存储空间中的对象或者特定的数据。
八叉树是四叉树的三维版本,将一片空间递归分为八个部分。
这两种树结构的使用范围包括但不限于以下情况:
- 空间索引:四叉树和八叉树可用于对空间中的对象进行索引和快速查找。通过树结构的划分,可以有效地确定对象所在的区域,从而加速搜索和查询操作。
- 空间分割:四叉树和八叉树可以将空间划分为更小的子空间,用于处理和分析空间数据。例如,在计算机图形学中,可以使用四叉树或八叉树来进行空间剖分和渲染优化。
- 碰撞检测:在物理模拟和游戏开发中,四叉树和八叉树可以用于快速检测物体之间的碰撞。通过树结构的划分和查询,可以有效地排除无关的对象,并减少碰撞检测的计算量。
- 图像压缩:四叉树和八叉树可以用于图像压缩算法,如基于树的压缩算法。通过将图像划分为不同的区域,并使用树结构来表示和存储图像数据,可以实现图像的高效压缩和重构。
- 网格剖分:在计算机辅助设计和有限元分析中,四叉树和八叉树可以用于将复杂的几何体分解成简单的网格,从而简化计算和分析过程。
总而言之,四叉树和八叉树是一种常用的空间分割数据结构,适用于许多领域的空间数据处理和分析。它们提供了一种有效的方法来处理大规模的空间数据,并减少计算和查询的时间复杂度。
构造节点:
对于每个节点,我们定义它的包围盒,定义它的孩子节点数组,孩子节点包围盒数组,同时可以定义在递归中会用到的一些判断条件,比如最大深度,包围盒最小大小,当前深度等。
public class OctreeNode
{
int maxDeep;
float minSize;
int curDeep;
Bounds spaceBounds;
OctreeNode[] childrenNodes;
Bounds[] childrenBounds;
public OctreeNode(int _maxDeep,float _minSize,int _curDeep,Bounds _spaceBounds)
{
maxDeep = _maxDeep;
minSize = _minSize;
curDeep = _curDeep;
spaceBounds = _spaceBounds;
CreateChildrenBounds(_spaceBounds);
}
}
构造子节点包围盒:
根据父节点的包围盒的中心点和大小,构建八个子节点的包围盒。
public void CreateChildrenBounds(Bounds _spaceBounds)
{
Vector3 center = _spaceBounds.center;
float childLen = _spaceBounds.size.y / 2;
float quarter = _spaceBounds.size.y / 4;
Vector3 childSize = new Vector3(childLen, childLen, childLen);
childrenBounds = new Bounds[8];
int count = 0;
for(int x = -1; x <= 1; x += 2)
{
for (int y = -1; y <= 1; y += 2)
{
for (int z = -1; z <= 1; z += 2)
{
childrenBounds[count++] = new Bounds(center + new Vector3(x*quarter,y*quarter,z*quarter),childSize);
}
}
}
}
递归构造树:
递归的退出条件是当节点的包围盒大小小于允许的最小值或者当前深度大于最大深度;
传入参数是GameObject对象,这是为了获得对象的Bounds;
函数逻辑是,判断子节点的包围盒是否与游戏对象的包围盒相交,相交的话就可以递归继续划分空间。
public void DivideAndAdd(GameObject go)
{
if (spaceBounds.size.y < minSize || curDeep > maxDeep) return;
if (childrenNodes == null) childrenNodes = new OctreeNode[8];
bool ifDivided = false;
for(int i = 0; i < 8; i++)
{
if (childrenNodes[i] == null)
childrenNodes[i] = new OctreeNode(maxDeep, minSize, curDeep + 1, childrenBounds[i]);
if (childrenBounds[i].Intersects(go.GetComponent<Collider>().bounds))
{
ifDivided = true;
childrenNodes[i].DivideAndAdd(go);
}
}
if (!ifDivided) childrenNodes = null;
}
八叉树的尝试