3D物体AABB碰撞检测算法

AABB包围盒

在游戏中,为了简化物体之间的碰撞检测运算,通常会对物体创建一个规则的几何外形将其包围。
其中,AABB(axis-aligned bounding box)包围盒被称为轴对其包围盒。
二维场景中的AABB包围盒具备特点:(注:下图中的所有坐标系均采用右手直角坐标系)

  1. 表现形式为四边形,即用四边形包围物体。
  2. 四边形的每一条边,都会与坐标系的轴垂直。

如图1-1所示:
图1-1
三维场景中的AABB包围盒特点:

  1. 表现形式为六面体。
  2. 六面体中的每条边都平行于一个坐标平面。

如图1-2所示:
图 1-2
在图1-2中,为了更明显的展示AABB包围盒的特点,在最右侧展示了一个OBB(Oriented Bounding Box)包围盒,也称作有向包围盒。
可以看出,AABB包围盒与OBB包围盒的最直接的区别就是,AABB包围盒是不可以旋转的,而OBB包围盒是可以旋转的,也就是有向的。

二维场景中的AABB碰撞检测原理

首先来看一张二维场景中的物体碰撞图:
图 2-1
在图 2-1中,分别做物体A与物体B在X,Y轴方向的投影,物体A的Y轴方向最大点坐标为Y1,最小点坐标Y2,X轴方向最小点坐标X1,最大点坐标X2,物体B同理。
图中红色区域为物体A与物体B投影的重叠部分。
可以看出,AABB碰撞检测具有如下规则:
物体A与物体B分别沿两个坐标轴做投影,只有在两个坐标轴都发生重叠的情况下,两个物体才意味着发生了碰撞。
所以,在程序中做二维游戏的AABB碰撞检测时,只需验证物体A与物体B是否满足如下条件:

  1. 物体A的Y轴方向最小值大于物体B的Y轴方向最大值;
  2. 物体A的X轴方向最小值大于物体B的X轴方向最大值;
  3. 物体B的Y轴方向最小值大于物体A的Y轴方向最大值;
  4. 物体B的X轴方向最小值大于物体A的X轴方向最大值;

若满足上述条件,则证明物体A与物体B并未发生重合,反之,则证明物体A与物体B重合。

三维场景中的AABB碰撞检测原理

首先,再来看一下图2-1中的二维物体A和物体B的包围盒,可以发现实际上判断物体A与物体B是否发生重合只需要知道两个信息:

  1. 物体A的最小点的信息,即图2-1中A的左下角点;以及物体A的最大点的信息,即图2-1中A的右上角点。
  2. 物体B的最小点的信息,物体B的最大点的信息。

也就是说在二维场景的碰撞检测中,每个物体的顶点坐标信息都可以由两个坐标来确定,即两个坐标就可以标识一个物体了,所以两个物体的碰撞检测只需要获得到四个点坐标就可以了。
之前在图1-2中已经看到,三维场景中物体的AABB包围盒是一个六面体,其坐标系对于二维坐标系来讲只是多了一个Z轴,所以实际上在三维场景中物体的AABB碰撞检测依然可以采用四个点信息的判定来实现。即从物体A的八个顶点与物体B的八个顶点分别选出两个最大与最小的顶点进行对比。三维物体的AABB包围盒的八个顶点依旧可以用两个顶点来标识,如图 3-1 所示:
图3-1
只要确定了图中黑色点部分的坐标,就可以确定八个顶点的全部信息了。

总结

最后,AABB碰见庄策算法虽然计算方法简单,速度快,但是仅适用于精度不搞的游戏中。相对于AABB碰撞检测,还有一种更逼近物体并更为精确的一种算法——OBB碰撞检测。
原文链接:http://www.it165.net/pro/html/201409/22717.html

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,我是一名语言模型AI,无法编写代码。但是我可以为您解释一下AABB的Octree算法AABB的Octree算法是一种用于空间分割的算法,常用于三维游戏中的碰撞检测AABB是指轴对齐的包围,Octree是指八叉树。 该算法的基本思想是将三维空间划分为八个相等的子空间,每个子空间再继续划分为八个子空间,直到每个子空间中只包含一个物体或者没有物体为止。每个子空间都用一个AABB包围来表示。 在进行碰撞检测时,先判断两个物体所在的子空间是否相交,如果相交,则继续递归判断子空间中的物体是否相交。如果两个物体所在的子空间不相交,则可以直接判定它们不相交。 以下是一个简单的AABB的Octree算法的C++代码示例: ``` struct AABB { float minX, minY, minZ; float maxX, maxY, maxZ; }; struct OctreeNode { AABB aabb; std::vector<int> objects; OctreeNode* children[8]; }; class Octree { public: Octree(const AABB& aabb, int maxObjectsPerNode) { root = new OctreeNode(); root->aabb = aabb; root->objects.reserve(maxObjectsPerNode); for (int i = 0; i < 8; i++) { root->children[i] = nullptr; } this->maxObjectsPerNode = maxObjectsPerNode; } void insert(int objectIndex, const AABB& objectAABB) { insert(root, objectIndex, objectAABB); } void query(const AABB& queryAABB, std::vector<int>& result) { query(root, queryAABB, result); } private: OctreeNode* root; int maxObjectsPerNode; void insert(OctreeNode* node, int objectIndex, const AABB& objectAABB) { if (node->children[0] == nullptr) { node->objects.push_back(objectIndex); if (node->objects.size() > maxObjectsPerNode) { splitNode(node); } } else { for (int i = 0; i < 8; i++) { if (isAABBInsideAABB(objectAABB, node->children[i]->aabb)) { insert(node->children[i], objectIndex, objectAABB); break; } } } } void splitNode(OctreeNode* node) { AABB aabb = node->aabb; float centerX = (aabb.minX + aabb.maxX) / 2.0f; float centerY = (aabb.minY + aabb.maxY) / 2.0f; float centerZ = (aabb.minZ + aabb.maxZ) / 2.0f; node->children[0] = new OctreeNode(); node->children[0]->aabb = {aabb.minX, aabb.minY, aabb.minZ, centerX, centerY, centerZ}; node->children[0]->objects.reserve(maxObjectsPerNode); node->children[1] = new OctreeNode(); node->children[1]->aabb = {centerX, aabb.minY, aabb.minZ, aabb.maxX, centerY, centerZ}; node->children[1]->objects.reserve(maxObjectsPerNode); node->children[2] = new OctreeNode(); node->children[2]->aabb = {aabb.minX, centerY, aabb.minZ, centerX, aabb.maxY, centerZ}; node->children[2]->objects.reserve(maxObjectsPerNode); node->children[3] = new OctreeNode(); node->children[3]->aabb = {centerX, centerY, aabb.minZ, aabb.maxX, aabb.maxY, centerZ}; node->children[3]->objects.reserve(maxObjectsPerNode); node->children[4] = new OctreeNode(); node->children[4]->aabb = {aabb.minX, aabb.minY, centerZ, centerX, centerY, aabb.maxZ}; node->children[4]->objects.reserve(maxObjectsPerNode); node->children[5] = new OctreeNode(); node->children[5]->aabb = {centerX, aabb.minY, centerZ, aabb.maxX, centerY, aabb.maxZ}; node->children[5]->objects.reserve(maxObjectsPerNode); node->children[6] = new OctreeNode(); node->children[6]->aabb = {aabb.minX, centerY, centerZ, centerX, aabb.maxY, aabb.maxZ}; node->children[6]->objects.reserve(maxObjectsPerNode); node->children[7] = new OctreeNode(); node->children[7]->aabb = {centerX, centerY, centerZ, aabb.maxX, aabb.maxY, aabb.maxZ}; node->children[7]->objects.reserve(maxObjectsPerNode); for (int i = 0; i < node->objects.size(); i++) { int objectIndex = node->objects[i]; const AABB& objectAABB = getObjectAABB(objectIndex); for (int j = 0; j < 8; j++) { if (isAABBInsideAABB(objectAABB, node->children[j]->aabb)) { insert(node->children[j], objectIndex, objectAABB); break; } } } node->objects.clear(); } void query(OctreeNode* node, const AABB& queryAABB, std::vector<int>& result) { if (!isAABBIntersectAABB(node->aabb, queryAABB)) { return; } if (node->children[0] == nullptr) { for (int i = 0; i < node->objects.size(); i++) { int objectIndex = node->objects[i]; const AABB& objectAABB = getObjectAABB(objectIndex); if (isAABBIntersectAABB(objectAABB, queryAABB)) { result.push_back(objectIndex); } } } else { for (int i = 0; i < 8; i++) { query(node->children[i], queryAABB, result); } } } bool isAABBInsideAABB(const AABB& aabb1, const AABB& aabb2) { return aabb1.minX >= aabb2.minX && aabb1.minY >= aabb2.minY && aabb1.minZ >= aabb2.minZ && aabb1.maxX <= aabb2.maxX && aabb1.maxY <= aabb2.maxY && aabb1.maxZ <= aabb2.maxZ; } bool isAABBIntersectAABB(const AABB& aabb1, const AABB& aabb2) { return aabb1.minX <= aabb2.maxX && aabb1.maxX >= aabb2.minX && aabb1.minY <= aabb2.maxY && aabb1.maxY >= aabb2.minY && aabb1.minZ <= aabb2.maxZ && aabb1.maxZ >= aabb2.minZ; } const AABB& getObjectAABB(int objectIndex) { // TODO: 根据物体索引获取物体AABB包围 } }; ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值