空间数据结构(四叉树、八叉树、BVH树、BSP树、k-d树)

2024-01-18 16:20

本文主要是介绍空间数据结构(四叉树、八叉树、BVH树、BSP树、k-d树),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一. 前言

在游戏程序中,利用空间数据结构加速计算往往是非常重要的优化思想,空间数据结构可以应用于场景管理、渲染、物理、游戏逻辑等方面。

二、多叉树

在这里插入图片描述

2.1 四叉树

四叉树是很常见的一种 2D 碰撞检测方法,实现手段也五花八门。不过在具体实现中要注意优化细节,控制建树时间消耗与建树空间大小,特别是在 JS 语言环境下。但四叉树的射线检测、区域检测效率比较高,树更新很快,会产生物体多次划分,空间占用大。

在这里插入图片描述
四叉树的结构在空间数据对象分布比较均匀时,具有比较高的空间数据插入和查询效率(复杂度O(logN))。

//示例:一个四叉树节点的简单结构
struct QuadtreeNode {Data data;QuadtreeNode* children[2][2];int divide;  //表示这个区域的划分长度
};//示例:找到x,y位置对应的四叉树节点
QuadTreeNode* findNode(int x,int y,QuadtreeNode * root){if(!root)return;QuadtreeNode* node = root;for(int i = 0; i < N && n; ++i){//通过diliver来将x,y归纳为0或1的值,从而索引到对应的子节点。int divide = node->divide;int divideX = x / divide;int divideY = y / divide;QuadtreeNode* temp = node->children[divideX][divideY];if(!temp){break;}node = temp;//如果归纳为1的值,还需要减去该划分长度,以便进一步划分x -= (divideX == 1 ? divide : 0);y -= (divideY == 1 ? divide : 0);}return node;
}

2.2 八叉树
在这里插入图片描述

八叉树虽然包围精确性没 BVH 高(可用状态压缩改善)、占用空间较大(过度划分),但是建树和增删非常快,很适合用作物体的筛选。目前 98K 使用了八叉树对模型包围盒进行空间划分,简单高效的建树比精确计算建树(比如 BVH 建树会有大量计算消耗)更加划算。缺点和四叉树一样,射线检测、区域检测较快,树更新很快, 会产生物体多次划分,空间占用大。

2.3 应用

相比网格,四叉树/八叉树主要是多了层次,它们可以进行区域较大的划分,然后可以对各种检测算法进行分区域的剪枝/过滤。
下面提几个应用(实际应用面很广):

  • 场景管理
    特别适合大规模的广阔室外场景管理。一般来说如果游戏场景是基于地形的(甚至没有高度)(如城市、平原、2D场景),那么适合用四叉树来管理。而如果游戏场景在高度轴上也有大量物体需要管理(如太空、高山),那么适合用八叉树来管理。
  • 碰撞检测
    类似上面感知检测。不同划分区域保证不会碰撞的情况下,就能快速过滤与本物体不同区域的其他潜在物体碰撞。
  • 光线追踪(Ray Tracing)过滤
    光线追踪渲染,可使用八叉树来划分3D空间区域,从而过滤掉大量不必要的区域。

三、二叉树

在这里插入图片描述
3.1 BVH树

四叉树和八叉树是以平均空间来划分物体,划分算法简单,而 BVH 是对当前物体集合进行空间的划分,追求左右空间大小相对均衡且无相交。BVH 构建的一般是二叉树,划分算法复杂。

主流物理引擎都有采用 BVH(层次包围盒树 (Bounding Volume Hierarchy Based On Tree)),因为其功能支持完备、查找精确性高、性能不俗。但是其在建树和增删改时要维护平衡树,消耗很大。针对这个问题,有一些时序性的空间优化方法,通过减少增删改达到优化目的,感兴趣的朋友可以参考各大物理引擎中的实现方法。

在这里插入图片描述
3.2 BSP树

BSP(Binary Space Partitioning Tree),二维空间分割树,非常经典,1993年在知名游戏 DOOM 里第一次被应用,早期 CS 也是用 BSP 来做地形碰撞。BSP 通常通过计算得到一个合理的任意角度片面或者法线,然后对空间进行划分。标准的 BSP 虽然高效,但树构建非常消耗时间,通常都是编辑器预处理,比较适合静态模型或者静态场景使用。

在这里插入图片描述

3D空间下要构造一棵较平衡的BSP树,则需要尽可能每次划分出一个节点时,让其左子树节点数和右子树节点数相差不多:

  • 在一个平面形状集合里,用其中一个平面构造一个BSP树节点时,需满足它前方的平面形状数和后方的平面形状数之差 小于
    一定阈值;若超过阈值则尝试用下一个形状来构造。
  • 一个麻烦的问题是当2个平面形状是相交时,即出现平面形状既可以在前方也可以在后方的情况。这时候就需要一个将该形状切割成两个子形状,从而可以一个添加在前方,一个添加在后方,避免冲突。
  • 构造完一个节点则移除对应的平面,该节点前面的平面形状和后面的平面形状则作为两个子平面形状集合。
  • 对这两个子集合以重复步骤1、2继续构造出两个子节点,并作为本节点的左右儿子。
  • 最后所有平面形状都被用于构造节点,组成了一棵BSP树。
//BSP tree节点结构示例
class BSPTreeNode {Plane plane;				  //平面BSPTreeNode* front;           //前向的节点BSPTreeNode* back;            //后向的节点//Data data;                  //数据
};

由于需要进行N次划分,每次划分后,要在子集合里一个个挑选合适的平面(需要logN次遍历),为了评定合适又需要与子集合里所有其它形状比较前后位置(需要logN次比较),因此可以知道BSP树构造的平均时间复杂度为 O(Nlog²N)

判断点在平面前后的算法:平面的法向量(A,B,C),则平面的方程为:Ax + By + Cz + D = 0;
将点(x0,y0,z0)代入方程得到 distance = Ax0 + By0 + Cz0 + D;
若 distance < 0 则在平面背后
若 distance = 0 则在平面中
若 distance > 0 则在平面前方

3.3 k-d树

k-d树((k-dimensional tree))是一棵二叉树,其每个节点都代表一个 k维坐标点:

树的每层都是对应一个划分维度(取决于你定义第i层是哪个维度)
树的每个节点代表一个超平面,该超平面垂直于当前划分维度的坐标轴,并在该维度上将空间划分为两部分,一部分在其左子树,另一部分在其右子树
实际上,k-d树就是一种特殊形式的BSP树(轴对齐的BSP树)。

//一种实现方式示例:二维k-d树节点
class KdTreeNode{Vector2 position;         //位置int dimension;            //当前所属层的维度KdTreeNode* children[2];  //两个子树//Data data;              //数据
};

k-d 树是一种特殊的 BSP 树,它基于动态计算的三个轴进行划分。k-d 树相比 BSP 可能精确性没那么高,但是建树时间大大减少,因为对轴划分算法简单,所以很适合使用

举例,一棵k-d树(k=2)的结构如图:
在这里插入图片描述

根据第一层划分维度为X,第二层为Y,第三层为X,
所以该k-d树(k=2)对应代表划分的空间,看起来应该是这样的:
在这里插入图片描述

这篇关于空间数据结构(四叉树、八叉树、BVH树、BSP树、k-d树)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/619567

相关文章

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

MySQL启动报错:InnoDB表空间丢失问题及解决方法

《MySQL启动报错:InnoDB表空间丢失问题及解决方法》在启动MySQL时,遇到了InnoDB:Tablespace5975wasnotfound,该错误表明MySQL在启动过程中无法找到指定的s... 目录mysql 启动报错:InnoDB 表空间丢失问题及解决方法错误分析解决方案1. 启用 inno

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

《在Java中基于Geotools对PostGIS数据库的空间查询实践教程》本文将深入探讨这一实践,从连接配置到复杂空间查询操作,包括点查询、区域范围查询以及空间关系判断等,全方位展示如何在Java环... 目录前言一、相关技术背景介绍1、评价对象AOI2、数据处理流程二、对AOI空间范围查询实践1、空间查

MySQL表空间结构详解表空间到段页操作

《MySQL表空间结构详解表空间到段页操作》在MySQL架构和存储引擎专题中介绍了使用不同存储引擎创建表时生成的表空间数据文件,在本章节主要介绍使用InnoDB存储引擎创建表时生成的表空间数据文件,对... 目录️‍一、什么是表空间结构1.1 表空间与表空间文件的关系是什么?️‍二、用户数据在表空间中是怎么

CentOS7增加Swap空间的两种方法

《CentOS7增加Swap空间的两种方法》当服务器物理内存不足时,增加Swap空间可以作为虚拟内存使用,帮助系统处理内存压力,本文给大家介绍了CentOS7增加Swap空间的两种方法:创建新的Swa... 目录在Centos 7上增加Swap空间的方法方法一:创建新的Swap文件(推荐)方法二:调整Sww

C#数据结构之字符串(string)详解

《C#数据结构之字符串(string)详解》:本文主要介绍C#数据结构之字符串(string),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录转义字符序列字符串的创建字符串的声明null字符串与空字符串重复单字符字符串的构造字符串的属性和常用方法属性常用方法总结摘

查看Oracle数据库中UNDO表空间的使用情况(最新推荐)

《查看Oracle数据库中UNDO表空间的使用情况(最新推荐)》Oracle数据库中查看UNDO表空间使用情况的4种方法:DBA_TABLESPACES和DBA_DATA_FILES提供基本信息,V$... 目录1. 通过 DBjavascriptA_TABLESPACES 和 DBA_DATA_FILES

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

Linux环境变量&&进程地址空间详解

《Linux环境变量&&进程地址空间详解》本文介绍了Linux环境变量、命令行参数、进程地址空间以及Linux内核进程调度队列的相关知识,环境变量是系统运行环境的参数,命令行参数用于传递给程序的参数,... 目录一、初步认识环境变量1.1常见的环境变量1.2环境变量的基本概念二、命令行参数2.1通过命令编程

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig