C++自研游戏引擎-碰撞检测组件-八叉树AABB检测算法实现
八叉树碰撞检测是一种在三维空间中高效处理物体碰撞检测的算法,其原理可以类比为一个管理三维空间物体的智能系统。这个示例包含两个部分:八叉树部分用于宏观检测,AABB用于微观检测。AABB可以更换为均值或节点检测来提高检测精度。
八叉树的构建
- 确定根节点范围
首先要为整个碰撞检测系统确定一个初始范围,这就像是为所有参与碰撞检测的物体划定一个 “活动区域”。这个范围是一个能够完全容纳所有待检测物体的三维立方体空间,它构成了八叉树的根节点。 - 递归分割空间
为了更高效地管理和查找物体,八叉树会对这个初始的大立方体空间进行递归分割。具体做法是沿着三个坐标轴的中点,将大立方体分割成八个小立方体,每个小立方体对应根节点的一个子节点。之后,系统会检查每个子节点所包含的物体数量:
若某个子节点中的物体数量小于预设的阈值,就认为该区域内的物体分布较为稀疏,无需再进行分割,这些物体就存储在该节点中。
若物体数量超过阈值,说明该区域物体较为密集,需要进一步细分。于是会将这个节点的空间继续分割成八个更小的子空间,并对每个子空间重复上述检查过程,直到满足停止条件。
碰撞检测过程 - 插入物体
在将物体的轴对齐包围盒(AABB)插入八叉树时,系统会从根节点开始判断物体的 AABB 与当前节点的空间是否相交:
如果不相交,表明该物体不在当前节点所管理的空间范围内,无需在此节点存储该物体。
如果相交,则将物体插入当前节点。若当前节点已经被分割成子节点,系统会进一步判断物体的 AABB 与哪个子节点的空间相交,并将物体插入对应的子节点中。 - 查询碰撞
当需要检测某个物体(用其 AABB 表示)是否与其他物体发生碰撞时,系统会从八叉树的根节点开始查询:
若该物体的 AABB 与当前节点的空间不相交,说明该节点及其子节点中的物体都不可能与该物体发生碰撞,无需继续检查该节点及其子树。
若相交,则检查当前节点中存储的物体的 AABB 与该物体的 AABB 是否相交。
若当前节点有子节点,系统会递归地对每个子节点进行相同的查询操作,直到遍历完所有可能发生碰撞的节点。
八叉树碰撞检测的优缺点
优点
高效性:通过对三维空间进行递归分割,八叉树将碰撞检测的范围缩小到可能发生碰撞的区域,避免了对所有物体进行两两比较,从而显著减少了不必要的计算,提高了碰撞检测的效率。在处理大量物体的场景中,这种优势更为明显。
适应性:八叉树能够根据物体在空间中的实际分布情况自适应地进行空间划分,对于物体分布不均匀的场景也能有效地组织和管理物体。
缺点
构建和维护成本较高:构建八叉树需要对空间进行递归分割,并将物体分配到相应的节点中,这需要一定的时间和空间开销。特别是在物体频繁移动或新增、删除物体的场景中,需要不断更新八叉树的结构,增加了维护成本。
存在精度问题:使用 AABB 来近似表示物体可能会导致一定的精度损失,尤其是对于形状复杂的物体,AABB 可能无法精确地描述其外形,从而产生误判。
C++代码
#include <iostream>
#include <vector>
#include <memory>// 定义三维向量结构体
struct Vec3 {float x, y, z;Vec3(float x = 0, float y = 0, float z = 0) : x(x), y(y), z(z) {}
};// 定义 AABB 结构体
struct AABB {Vec3 min;Vec3 max;AABB(const Vec3& min, const Vec3& max) : min(min), max(max) {}// 判断两个 AABB 是否相交bool intersects(const AABB& other) const {return (min.x <= other.max.x && max.x >= other.min.x) &&(min.y <= other.max.y && max.y >= other.min.y) &&(min.z <= other.max.z && max.z >= other.min.z);}
};// 定义八叉树节点类
class OctreeNode {
public:AABB bounds;std::vector<AABB> objects;std::vector<std::unique_ptr<OctreeNode>> children;OctreeNode(const AABB& bounds) : bounds(bounds) {}// 插入 AABB 到节点中void insert(const AABB& object) {if (children.empty()) {if (objects.size() < 8) {objects.push_back(object);} else {split();insert(object);}} else {for (auto& child : children) {if (child->bounds.intersects(object)) {child->insert(object);}}}}// 分割节点void split() {Vec3 center((bounds.min.x + bounds.max.x) / 2, (bounds.min.y + bounds.max.y) / 2, (bounds.min.z + bounds.max.z) / 2);children.resize(8);children[0] = std::make_unique<OctreeNode>(AABB(bounds.min, center));children[1] = std::make_unique<OctreeNode>(AABB(Vec3(center.x, bounds.min.y, bounds.min.z), Vec3(bounds.max.x, center.y, center.z)));children[2] = std::make_unique<OctreeNode>(AABB(Vec3(bounds.min.x, center.y, bounds.min.z), Vec3(center.x, bounds.max.y, center.z)));children[3] = std::make_unique<OctreeNode>(AABB(Vec3(center.x, center.y, bounds.min.z), Vec3(bounds.max.x, bounds.max.y, center.z)));children[4] = std::make_unique<OctreeNode>(AABB(Vec3(bounds.min.x, bounds.min.y, center.z), Vec3(center.x, center.y, bounds.max.z)));children[5] = std::make_unique<OctreeNode>(AABB(Vec3(center.x, bounds.min.y, center.z), Vec3(bounds.max.x, center.y, bounds.max.z)));children[6] = std::make_unique<OctreeNode>(AABB(Vec3(bounds.min.x, center.y, center.z), Vec3(center.x, bounds.max.y, bounds.max.z)));children[7] = std::make_unique<OctreeNode>(AABB(center, bounds.max));for (const auto& object : objects) {for (auto& child : children) {if (child->bounds.intersects(object)) {child->insert(object);}}}objects.clear();}// 检测与指定 AABB 的碰撞void query(const AABB& object, std::vector<AABB>& result) const {if (bounds.intersects(object)) {for (const auto& obj : objects) {if (obj.intersects(object)) {result.push_back(obj);}}for (const auto& child : children) {child->query(object, result);}}}
};// 定义八叉树类
class Octree {
public:std::unique_ptr<OctreeNode> root;Octree(const AABB& bounds) : root(std::make_unique<OctreeNode>(bounds)) {}// 插入 AABB 到八叉树中void insert(const AABB& object) {root->insert(object);}// 检测与指定 AABB 的碰撞std::vector<AABB> query(const AABB& object) const {std::vector<AABB> result;root->query(object, result);return result;}
};// 示例使用
int main() {// 定义八叉树的边界AABB octreeBounds(Vec3(0, 0, 0), Vec3(100, 100, 100));Octree octree(octreeBounds);// 插入一些 AABBoctree.insert(AABB(Vec3(10, 10, 10), Vec3(20, 20, 20)));octree.insert(AABB(Vec3(30, 30, 30), Vec3(40, 40, 40)));// 定义一个查询的 AABBAABB queryAABB(Vec3(15, 15, 15), Vec3(25, 25, 25));// 进行碰撞检测std::vector<AABB> collisions = octree.query(queryAABB);// 输出碰撞结果std::cout << "Collisions found: " << collisions.size() << std::endl;for (const auto& collision : collisions) {std::cout << "Collision: min(" << collision.min.x << ", " << collision.min.y << ", " << collision.min.z << "), max("<< collision.max.x << ", " << collision.max.y << ", " << collision.max.z << ")" << std::endl;}return 0;
}
相关文章:
C++自研游戏引擎-碰撞检测组件-八叉树AABB检测算法实现
八叉树碰撞检测是一种在三维空间中高效处理物体碰撞检测的算法,其原理可以类比为一个管理三维空间物体的智能系统。这个示例包含两个部分:八叉树部分用于宏观检测,AABB用于微观检测。AABB可以更换为均值或节点检测来提高检测精度。 八叉树的…...
haproxy详解笔记
一、概述 HAProxy(High Availability Proxy)是一款开源的高性能 TCP/HTTP 负载均衡器和代理服务器,用于将大量并发连接分发到多个服务器上,从而提高系统的可用性和负载能力。它支持多种负载均衡算法,能够根据服务器的…...
windows 通过docker 安装mysql
参考:Docker安装并使用Mysql(可用详细)_docker 安装mysql-CSDN博客 1. 拉取镜像:docker pull mysql:5.7 2. 查看镜像:docker image 3. 创建mysql 容器实例,并将data 目录挂载到本地d盘上 docker run --n…...
【STM32】通过L496的HAL库Flash建立FatFS文件系统(CubeMX自动配置R0.12C版本)
【STM32】通过L496的HAL库Flash建立FatFS文件系统(CubeMX自动配置R0.12C版本) 文章目录 FlashFlash地址写Flash地址读 FatFS文件系统配置FatFS移植驱动函数时间戳函数 文件操作函数工作区缓存文件挂载和格式化测试文件读写测试其他文件操作函数 测试附录…...
QTreeView笔记
1.定义TreeModel类 我们需要继承自QAbstractItemModel,让我们来看看它有哪些接口。 QAbstractItemModel类中定义如下: Q_INVOKABLE virtual QModelIndex index(int row, int column, const QModelIndex &parent QModelIndex()) const 0;Q_INVOK…...
传感器篇(一)——深度相机
目录 一 概要 二 原理 三 对比 四 产品 五 结论 一 概要 深度相机是一种能够获取物体深度信息的设备,相较于普通相机只能记录物体的二维图像信息,深度相机可以感知物体与相机之间的距离,从而提供三维空间信息。在你正在阅读的报告中提到…...
Qt 控件整理 —— 按钮类
一、PushButton 1. 介绍 在Qt中最常见的就是按钮,它的继承关系如下: 2. 常用属性 3. 例子 我们之前写过一个例子,根据上下左右的按钮去操控一个按钮,当时只是做了一些比较粗糙的去演示信号和槽是这么连接的,这次我们…...
校园网绕过认证上网很简单
校园网绕过认证就是不用通过校园WiFi的WEB页面登录,这个WEB登录页面就是认证页面. 所谓绕过认证,就是不通过校园WiFi WEB登录页面直接上网,校园WiFi没有密码,直接就能连接上,我们连上这个WiFi的时候,它会给…...
蓝桥杯篇---温度传感器 DS18B20
文章目录 前言DS18B201. DS18B20 引脚说明2. 单总线通信协议3. DS18B20 操作流程初始化写操作读操作 4. 示例代码5. 代码说明6. 注意事项总结 前言 以上就是今天要讲的内容,本文简单介绍了IAP15F2K61S2中温度传感器模块DS18B20的使用。 DS18B20 DS18B20 是一款数字…...
WPS或word接入智能AI
DeepSeek接入WPS 配置WPS (1)下载 OfficeAl助手插件: 插件下载地址:https://www.office-ai.cn/。 安装插件后,打开WPS,菜单栏会新增"OfficeAl助手”选项卡。 如果没有出现, 左上找到文件菜单 -> 选项 ,在…...
vue3:template中v-for循环遍历这个centrerTopdata,我希望自循环前面三个就可以了怎么写?
问: template中v-for循环遍历这个centrerTopdata,我希望自循环前面三个就可以了怎么写? 回答: 问: <div v-for"(item, index) in centrerTopdata.slice(0, 3)" :key"index"> div cl…...
Java练习(20)
ps:练习来自力扣 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。 class Solution {pu…...
MySQL | MySQL安装教程
MySQL | MySQL安装教程(压缩包(ZIP)安装-详细版) 🪄个人博客:https://vite.xingji.fun MySQL概述 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,MySQL AB公司被Sun公司收购,Sun公…...
.NET 9.0 的 Blazor Web App 项目,进度条 <progress> 组件使用注意事项
一、执行过程中,要刷新 进度条 的显示,需要 延时、释放,否则进度条不 实时 更新,最后一下到 100% // 延时,释放给前端:【必须】,否则进度条不 实时 更新,最后一下到 100await Task.D…...
李超线段树 树链剖分 学习笔记
今天学习了李超线段树。 [洛谷 P4097] 【模板】李超线段树 & [HEOI2013] Segment 刚开始学李超线段树,觉得挺简单的。其实它跟吉司机线段树有点像,只是维护的东西要少一些,并且代码更好写。 对于每个节点,考虑维护在它中点处的…...
【SpringBoot3.x+】slf4j-log4j12依赖引入打印日志报错的两种解决方法
最开始引入了1.7.5版本的slf4j-log4j依赖包,但是控制台不报错也不显示日志 在https://mvnrepository.com/找到最新的2.0.16版本之后出现报错: 进入提示的slf4j网站中可以找到从2.0.0版本开始,slf4j-log4j已经被slf4j-reload4j取代࿱…...
安装 Ollama 需要哪些步骤?(windows+mac+linux+二进制+Docker)
安装 Ollama 的步骤根据操作系统不同会有所差异,以下是针对不同操作系统的详细安装指南: Windows 系统 下载安装包:访问 Ollama 官方下载页面,下载适用于 Windows 的安装程序 OllamaSetup.exe。运行安装程序:双击下载的安装包,按照提示完成安装。默认安装路径为 C:\User…...
算法学习笔记之贪心算法
导引(硕鼠的交易) 硕鼠准备了M磅猫粮与看守仓库的猫交易奶酪。 仓库有N个房间,第i个房间有 J[i] 磅奶酪并需要 F[i] 磅猫粮交换,硕鼠可以按比例来交换,不必交换所有的奶酪 计算硕鼠最多能得到多少磅奶酪。 输入M和…...
探索DeepSeek:开源大模型领域的中国力量
在人工智能技术迅猛发展的今天,大语言模型(LLM)已成为全球科技竞争的焦点。来自中国的深度求索(DeepSeek)团队凭借其开源模型系列,正在为这一领域注入新的活力。本文将带您了解DeepSeek的技术突破、开源生态…...
372_C++_当有多个通道,开启不同告警的同一种的开关时,限制该开关的打开数量(比如视频上传开关)
GetCloudUploadNum函数 GetCloudUploadNum 函数主要用于统计和控制云端视频上传的通道数量,其主要功能如下: 功能目的// 检查每个通道的云端视频上传配置,并统计启用云端上传的通道总数 int CloudUploadNum = 0; bool InValidCloudUploadChn[MAX_CHN_NUMPARA] = {};...
【视频总结】Deep Dive into LLMs like ChatGPT 深入探索像ChatGPT这样的大语言模型|Andrej Karpathy
【视频总结】Deep Dive into LLMs like ChatGPT 深入探索像ChatGPT这样的大语言模型|Andrej Karpathy 大型语言模型(LLM)工作原理与使用指南核心观点模型训练三阶段1. 预训练阶段2. 后训练阶段(Post-training)3. 强化学…...
SQL自学,mysql从入门到精通 --- 第 5 天,对函数的处理
对函数的处理 新建一个成绩表 rootmysqldb 09:39: [d1]> create table score (-> name varchar(30),-> chinese int,-> math int,-> music int,-> team int,-> magic int,-> computer int-> ); Query OK, 0 rows affected (0.01 sec)rootmysqldb…...
DeepSeek R1 “顿悟时刻”(Aha Moment) 的重现与探索:基于 GRPO 的倒计时游戏训练
本文翻译整合转载于: Deepseek R1 是如何训练的Mini-R1:重现 Deepseek R1 的 “顿悟时刻” RL 教程 Deepseek R1 的发布震惊了整个行业。为什么?DeepSeek-R1 是一个开放模型,在复杂推理任务中可与 OpenAI 的 o1 相媲美,…...
【JavaScript爬虫记录】记录一下使用JavaScript爬取m4s流视频过程(内含ffmpeg合并)
前言 前段时间发现了一个很喜欢的视频,可惜网站不让下载,简单看了一下视频是被切片成m4s格式的流文件,初步想法是将所有的流文件下载下来然后使用ffmpeg合并成一个完整的mp4,于是写了一段脚本来实现一下,电脑没有配python环境,所以使用JavaScript实现,合并功能需要安装ffmpeg,…...
【线性代数】1行列式
1. 行列式的概念 行列式的符号表示: 行列式的计算结果:一个数 计算模型1:二阶行列式 二阶行列式: 三阶行列式: n阶行列式: 🍎计算行列式 计算模型2:上三角形行列式 上三角形行列式特征:主对角线下皆为0。 上三角形行列式: 化上三角形通用方法:主对角线下,…...
数据结构(考研)
线性表 顺序表 顺序表的静态分配 //线性表的元素类型为 ElemType//顺序表的静态分配 #define MaxSize10 typedef int ElemType; typedef struct{ElemType data[MaxSize];int length; }SqList;顺序表的动态分配 //顺序表的动态分配 #define InitSize 10 typedef struct{El…...
安装WPS后,导致python调用Excel.Application异常,解决办法
在使用xlwings编辑excel文件时,默认调用的是“Excel.Application”,如果安装过wps,会导致该注册表为WPS,会导致xlwings执行异常 因为安装过WPS,导致与Excel不兼容的问题,想必大家都听说过。有些问题及时删…...
【transformers.Trainer填坑】在自定义compute_metrics时logits和labels数据维度不一致问题
问题描述 我在使用 transformers.Trainer 训练我的模型时,我自定义了 compute_loss 函数和compute_metrics函数,我的模型是一个简单的二分类模型。 在自定义 compute_loss 时这样写的: def compute_loss(self, model, inputs, return_outp…...
Django创建超管用户
在 Django 中创建超级用户(superuser)可以通过命令行工具 createsuperuser 完成。以下是具体步骤: 1. 确保已进行数据库迁移 在创建超级用户前,确保已执行数据库迁移: python manage.py migrate 2. 创建超级用户 …...
vue3实战-----集成sass
vue3实战-----集成sass 1.安装2.使用3.全局样式文件中不能使用变量 1.安装 在使用scss之前需要安装sass和sass-loader两个插件。 2.使用 安装好之后就可以在组件中使用scss了。需要加上lang“scss”。 注意:scss中变量用$,less中变量用。 3.全局样式文件中不能使用变量 …...
