当前位置: 首页 > news >正文

AABB(Axis-Aligned Bounding Box)包围盒和OBB(Oriented Bounding Box)有向包围盒

前言

在游戏开发中,AABB(轴对齐包围盒,Axis-Aligned Bounding Box)和 OBB(有向包围盒,Oriented Bounding Box)是两种常用的碰撞检测和物体包围的技术,各自有不同的用途和优缺点。

AABB(Axis-Aligned Bounding Box)包围盒

AABB包围盒概念解释:
AABB是一种最基础也最常用的碰撞检测包围体,它是一个与坐标轴对齐的立方体,用最小和最大的两个点来表示一个完整的包围盒。

主要特点:
1.始终与坐标轴平行
2.不会随物体旋转而旋转
3.计算简单,性能高效
4.内存占用小,仅需存储两个点的坐标

主要应用场景:
1.快速碰撞检测
2.视锥体剔除
3.空间分区
4.物理引擎的粗略检测阶段

原理:
核心逻辑就是存储了minPoint和maxPoint,然后检查不同物体AABB的边在每个轴上的投影是否重叠。

下面是基于Unity实现的包围盒部分的示例代码。

using UnityEngine;public class AABBBoundingBox : MonoBehaviour
{private Vector3 minPoint;private Vector3 maxPoint;private Vector3 center;private Vector3 size;private MeshFilter meshFilter;private Mesh mesh;private Vector3[] originalVertices; // 存储原始顶点数据  void Start(){meshFilter = GetComponent<MeshFilter>();if (meshFilter != null){mesh = meshFilter.mesh;originalVertices = mesh.vertices;}CalculateAABB();}void Update(){// 使用transform.hasChanged检查变换  if (meshFilter != null && transform.hasChanged){CalculateAABB();// 重置hasChanged标记  transform.hasChanged = false;}}void CalculateAABB(){if (mesh == null || originalVertices == null || originalVertices.Length == 0)return;// 初始化最小点和最大点  Vector3 worldVertex = transform.TransformPoint(originalVertices[0]);minPoint = maxPoint = worldVertex;// 遍历所有顶点,更新最小点和最大点  for (int i = 1; i < originalVertices.Length; i++){worldVertex = transform.TransformPoint(originalVertices[i]);// 更新最小点  minPoint.x = Mathf.Min(minPoint.x, worldVertex.x);minPoint.y = Mathf.Min(minPoint.y, worldVertex.y);minPoint.z = Mathf.Min(minPoint.z, worldVertex.z);// 更新最大点  maxPoint.x = Mathf.Max(maxPoint.x, worldVertex.x);maxPoint.y = Mathf.Max(maxPoint.y, worldVertex.y);maxPoint.z = Mathf.Max(maxPoint.z, worldVertex.z);}// 计算包围盒中心点和大小  center = (minPoint + maxPoint) * 0.5f;size = maxPoint - minPoint;}// 考虑缩放的顶点变换  private Vector3 TransformVertexWithScale(Vector3 vertex){// 先应用缩放  Vector3 scaledVertex = new Vector3(vertex.x * transform.localScale.x,vertex.y * transform.localScale.y,vertex.z * transform.localScale.z);// 然后应用旋转和位移  return transform.position + (transform.rotation * scaledVertex);}// 优化的相交检测  public bool IntersectsWithAABB(AABBBoundingBox other){bool noOverlap = other.maxPoint.x < minPoint.x ||other.minPoint.x > maxPoint.x ||other.maxPoint.y < minPoint.y ||other.minPoint.y > maxPoint.y ||other.maxPoint.z < minPoint.z ||other.minPoint.z > maxPoint.z;return !noOverlap;}// 在Scene视图中绘制包围盒  void OnDrawGizmos(){if (!Application.isPlaying) return;// 正常状态下的颜色  Gizmos.color = Color.green;Gizmos.DrawWireCube(center, size);// 绘制包围盒的顶点  Gizmos.color = Color.yellow;float pointSize = 0.1f;Gizmos.DrawCube(minPoint, Vector3.one * pointSize);Gizmos.DrawCube(maxPoint, Vector3.one * pointSize);}// 用于缓存的属性  public Vector3 MinPoint => minPoint;public Vector3 MaxPoint => maxPoint;public Vector3 Center => center;public Vector3 Size => size;// 扩展包围盒  public void Expand(float amount){Vector3 expansion = new Vector3(amount, amount, amount);minPoint -= expansion;maxPoint += expansion;size = maxPoint - minPoint;center = (minPoint + maxPoint) * 0.5f;}// 合并两个AABB  public static AABBBoundingBox Merge(AABBBoundingBox a, AABBBoundingBox b){GameObject go = new GameObject("MergedAABB");AABBBoundingBox merged = go.AddComponent<AABBBoundingBox>();merged.minPoint = Vector3.Min(a.minPoint, b.minPoint);merged.maxPoint = Vector3.Max(a.maxPoint, b.maxPoint);merged.center = (merged.minPoint + merged.maxPoint) * 0.5f;merged.size = merged.maxPoint - merged.minPoint;return merged;}
}

测试代码:

// 检测两个物体的包围盒是否相交  
using UnityEngine;public class CollisionExample : MonoBehaviour
{public GameObject object1;public GameObject object2;void Update(){AABBBoundingBox box1 = object1.GetComponent<AABBBoundingBox>();AABBBoundingBox box2 = object2.GetComponent<AABBBoundingBox>();if (box1.IntersectsWithAABB(box2)){Debug.Log("碰撞发生!");}}
}

在这里插入图片描述

2.OBB(Oriented Bounding Box)有向包围盒

OBB相比AABB更加精确,因为它可以随物体旋转,能更好地适应物体的形状,但性能差于AABB。

OBB概念解释:
1.是一个可以任意旋转的长方体包围盒
2.由中心点、三个轴向量和三个半长度定义
3.可以跟随物体旋转,提供更紧凑的包围
4.碰撞检测计算比AABB复杂,但精确度更高

注意事项:
1.OBB的碰撞检测比AABB更复杂,需要更多计算
2.对于简单物体或不需要精确碰撞的情况,考虑使用AABB
3.可以结合AABB进行多层次碰撞检测。

原理:
OBB通过分离轴定理(Separating Axis Theorem, SAT)进行碰撞检测。
分离轴定理的核心思想是:如果两个凸多面体不相交,那么一定存在一个轴,当两个物体投影到这个轴上时,投影不重叠。

OOB包围盒部分代码示例:

using UnityEngine;public class OBBBoundingBox : MonoBehaviour
{private Vector3 center;        // 包围盒中心点  private Vector3 extents;       // 包围盒半长度  private Vector3[] axes;        // 包围盒的三个轴向量  private Vector3[] vertices;    // 8个顶点  private Matrix4x4 rotation;    // 旋转矩阵  private MeshFilter meshFilter;private Mesh mesh;void Start(){meshFilter = GetComponent<MeshFilter>();if (meshFilter != null){mesh = meshFilter.mesh;axes = new Vector3[3];vertices = new Vector3[8];CalculateOBB();}}void Update(){// 如果物体发生变换,更新OBB  if (transform.hasChanged){CalculateOBB();transform.hasChanged = false;}}void CalculateOBB(){if (mesh == null) return;// 获取物体的顶点  Vector3[] meshVertices = mesh.vertices;if (meshVertices.Length == 0) return;// 计算协方差矩阵  Matrix4x4 covarianceMatrix = CalculateCovarianceMatrix(meshVertices);// 计算主轴(使用特征值分解)  CalculatePrincipalAxes(covarianceMatrix);// 计算包围盒的范围  CalculateExtents(meshVertices);// 更新顶点  UpdateVertices();}private Matrix4x4 CalculateCovarianceMatrix(Vector3[] vertices){// 计算中心点  center = Vector3.zero;for (int i = 0; i < vertices.Length; i++){center += transform.TransformPoint(vertices[i]);}center /= vertices.Length;// 计算协方差矩阵  Matrix4x4 covariance = Matrix4x4.zero;for (int i = 0; i < vertices.Length; i++){Vector3 point = transform.TransformPoint(vertices[i]) - center;covariance[0, 0] += point.x * point.x;covariance[0, 1] += point.x * point.y;covariance[0, 2] += point.x * point.z;covariance[1, 1] += point.y * point.y;covariance[1, 2] += point.y * point.z;covariance[2, 2] += point.z * point.z;}covariance[1, 0] = covariance[0, 1];covariance[2, 0] = covariance[0, 2];covariance[2, 1] = covariance[1, 2];return covariance;}private void CalculatePrincipalAxes(Matrix4x4 covarianceMatrix){// 使用雅可比旋转法计算特征向量  // 这里简化处理,直接使用物体的本地坐标轴  axes[0] = transform.right;axes[1] = transform.up;axes[2] = transform.forward;rotation = Matrix4x4.TRS(Vector3.zero, transform.rotation, Vector3.one);}private void CalculateExtents(Vector3[] meshVertices){// 初始化范围  extents = Vector3.zero;Vector3 min = Vector3.positiveInfinity;Vector3 max = Vector3.negativeInfinity;// 计算在每个轴向上的投影范围  for (int i = 0; i < meshVertices.Length; i++){Vector3 point = transform.TransformPoint(meshVertices[i]) - center;for (int j = 0; j < 3; j++){float projection = Vector3.Dot(point, axes[j]);min[j] = Mathf.Min(min[j], projection);max[j] = Mathf.Max(max[j], projection);}}// 计算半长度  extents = (max - min) * 0.5f;}private void UpdateVertices(){// 计算8个顶点的位置  for (int i = 0; i < 8; i++){Vector3 vertex = new Vector3(((i & 1) == 0 ? -extents.x : extents.x),((i & 2) == 0 ? -extents.y : extents.y),((i & 4) == 0 ? -extents.z : extents.z));vertices[i] = center +axes[0] * vertex.x +axes[1] * vertex.y +axes[2] * vertex.z;}}// 分离轴定理(SAT)检测两个OBB是否相交  public bool IntersectsWithOBB(OBBBoundingBox other){float[,] rotation1 = new float[3, 3];float[,] rotation2 = new float[3, 3];float[,] rotationMatrix = new float[3, 3];// 获取两个OBB的旋转矩阵  for (int i = 0; i < 3; i++){rotation1[i, 0] = axes[i].x;rotation1[i, 1] = axes[i].y;rotation1[i, 2] = axes[i].z;rotation2[i, 0] = other.axes[i].x;rotation2[i, 1] = other.axes[i].y;rotation2[i, 2] = other.axes[i].z;}// 计算旋转矩阵  for (int i = 0; i < 3; i++){for (int j = 0; j < 3; j++){rotationMatrix[i, j] = 0;for (int k = 0; k < 3; k++){rotationMatrix[i, j] += rotation1[i, k] * rotation2[j, k];}}}// 计算平移向量  Vector3 translation = other.center - center;Vector3 translationBox1 = new Vector3(Vector3.Dot(translation, axes[0]),Vector3.Dot(translation, axes[1]),Vector3.Dot(translation, axes[2]));// 15个分离轴检测  return SeparatingAxisTest(translationBox1, extents, other.extents, rotationMatrix);}private bool SeparatingAxisTest(Vector3 translation, Vector3 extents1, Vector3 extents2, float[,] rotation){float[,] absRotation = new float[3, 3];// 计算旋转矩阵的绝对值  for (int i = 0; i < 3; i++){for (int j = 0; j < 3; j++){absRotation[i, j] = Mathf.Abs(rotation[i, j]) + 0.0001f;}}// 测试15个分离轴  for (int i = 0; i < 3; i++){float ra = extents1[i];float rb = extents2[0] * absRotation[i, 0] +extents2[1] * absRotation[i, 1] +extents2[2] * absRotation[i, 2];if (Mathf.Abs(translation[i]) > ra + rb) return false;}for (int i = 0; i < 3; i++){float ra = extents1[0] * absRotation[0, i] +extents1[1] * absRotation[1, i] +extents1[2] * absRotation[2, i];float rb = extents2[i];if (Mathf.Abs(translation[0] * rotation[0, i] +translation[1] * rotation[1, i] +translation[2] * rotation[2, i]) > ra + rb) return false;}return true;}void OnDrawGizmos(){if (!Application.isPlaying || vertices == null) return;Gizmos.color = Color.yellow;// 绘制12条边  int[,] edges = new int[12, 2] {{0,1}, {1,3}, {3,2}, {2,0}, // 底面  {4,5}, {5,7}, {7,6}, {6,4}, // 顶面  {0,4}, {1,5}, {2,6}, {3,7}  // 连接边  };for (int i = 0; i < 12; i++){Gizmos.DrawLine(vertices[edges[i, 0]], vertices[edges[i, 1]]);}}// 属性访问器  public Vector3 Center => center;public Vector3 Extents => extents;public Vector3[] Axes => axes;public Vector3[] Vertices => vertices;
}

测试部分代码:

using UnityEngine;public class OBBTest : MonoBehaviour
{public GameObject object1;public GameObject object2;void Update(){OBBBoundingBox obb1 = object1.GetComponent<OBBBoundingBox>();OBBBoundingBox obb2 = object2.GetComponent<OBBBoundingBox>();if (obb1.IntersectsWithOBB(obb2)){Debug.Log("OBB碰撞发生!");}}
}

在这里插入图片描述

性能优化

当物体发生移动、旋转、缩放后再重新计算。
空间划分优化。通过四叉树或者八叉树将区域划分,只启用组件和计算存在碰撞可能的区域。
大致判断碰撞检测时使用AABB,需要详细计算再使用OBB。


http://www.mrgr.cn/news/91322.html

相关文章:

  • 【医学影像AI】50个眼科影像数据集--1.分类任务
  • 钱从哪来系列:TW某独立游戏团队
  • 【个人开发】deepspeed+Llama-factory 本地数据多卡Lora微调
  • 毕业设计—基于Spring Boot的社区居民健康管理平台的设计与实现
  • 金融交易算法单介绍
  • vscode ESP32配置
  • 宝塔docker 安装oracle11G
  • 赶AI大潮:在VSCode中使用DeepSeek及近百种模型的极简方法
  • 【Linux AnolisOS】关于Docker的一系列问题。尤其是拉取东西时的网络问题,镜像源问题。
  • iOS事件传递和响应
  • WebGPU顶点插槽(Vertex Buffer Slot)使用指南
  • VScode内接入deepseek包过程(本地部署版包会)
  • c/c++蓝桥杯经典编程题100道(19)汉诺塔问题
  • Android车机DIY开发之软件篇(十七) Android模拟器移植Automotive
  • Blazor-父子组件传递任意参数
  • Windows 图形显示驱动开发-CPU 内存调节和64KB 页面支持
  • 【Pandas】pandas Series idxmin
  • java练习(28)
  • 【leetcode】双指针:有效三角形的个数 and 和为s的两个数
  • gsoap实现webservice服务