Box2D使用了一个叫做 slab 的碰撞检测算法。

在2D中AABB是一个矩形边界框,slab 指的是矩形一组平行线之间的范围,所以在2D中矩形边界框四条边,两两一组,可以组成两个 slab。

如下图:

平行于Y轴的两条边(紫色线)之间的范围是 x-slab,范围无限长,宽度限制在两条紫色线之间

平行于X轴的两条边(红色线)之间的范围是 y-slab ,范围无限长,宽度限制在两条红色线之间

中间灰色比较深的部分就是矩形框,它是两个slab的重合部分,即属于x-slab 也属于 y-slab

则有下面几条规则:

2D 中

1.如果一个点在 AABB内,则该点必定同时在 x-slab、y-slab

2.如果一条射线和AABB相交,则这条射线和 两个 slab 相交部分必定有重合部分

3.同理在3D中 1、2 依然满足,因为3D AABB是六个面,有三对相互平行的面,每一对平行的面之间的范围就是一个 slab。因为 3D 不好绘制,下面依然用 2D讲述

射线与AABB边界框相交情况如下图

射线R1

在 t1 位置进 y-slab

在 t2 位置出 y-slab

在 t3 位置进 x-slab

在 t4 位置出  x-slab

注:t1、t2、t3、t4 为从射线起点到各个交点的距离

但是 区间 (t1, t2) 和 (t3, t4) 没有重叠部分,则判定R1与AABB不相交

射线R2

在 t1 位置进 x-slab

在 t3 位置进 y-slab

在 t2 位置出 x-slab

在 t4 位置出  y-slab

区间 (t1, t2) 和 (t3, t4) 有重叠部分(图中绿色线条) t3 到 t2 部分,绿色线条部分既属于 x-slab,又属于 y-slab 部分,则判定射线 R2 与 AABB相交

由上得出结论,如果射线与AABB各个面相交的 t 区间 有重叠部分,则射线与AABB相交

还有一种情况就是射线的反向延长线与 slab 相交,判定为相交无效

下面计算射线与AABB各个面的相交结果

射线与x-slab相交的t值为 (t1,t2),t1 < t2

射线与y-slab相交的t值为 (t3,t4),t3 < t4

射线与z-slab 相交的 t值为 (t5,t6),t5 < t6

只要满足区间 (t1,t2) ,(t3,t4),(t5,t6),在一维数轴上有重叠部分就说明射线与AABB相交,到此可以发现我们将 3D、2D 问题降维到了 1维 数轴上数值比较的问题了。

方法就是判断 t1、t3、t5中最大值 < t2、t4、t6中最小值

相交条件为:Max(t1, t3, t5 ) < Min(t2, t4,t6)

射线方程: p + t * rayDir

其中 p为 射线起点,rayDir 为射线的方向向量,t 为射线长度系数。

平面方程为 S * n = d

注:上边两个方程中 * 的作用

当用在  点 * 向量 或 向量 * 向量 时就是 向量点乘(Dot)

当用在 数值 * 向量 就是 数值乘 (X)

其中 S 为平面上的点,n为平面法向量,d为原点(0, 0, 0)到平面的距离。

如果射线与平面相交,令交点为 V,则点 V 即在射线上,也在平面上

代入射线方程中:V = p + t * rayDir

代入平面方程中:V * n = d,即 Dot( V , n ) = d

注:下面使用 Dot 表示点乘

将 V 展开为(p + t * rayDir)得到

Dot(( p + t * rayDir) , n) = d

Dot(p, n) + Dot(t * rayDir, n) = d

Dot(t * rayDir, n) = d - Dot(p, n)

t * Dot(rayDir, n) = d - Dot(p, n)

t = (d - Dot(p, n)) / Dot(rayDir, n)

射线与平面相交点距离射线起点距离t的距离公式为 t = (d - Dot(p, n)) / Dot(rayDir, n)

注意:Dot(rayDir, n), 射线向量与AABB某个面法向量点乘,当这两个向量垂直时,也就是射线平行于平面时,点乘结果为 0

AABB 以最小点 Min(x, y, z)、最大点Max(x, y, z) 表示

AABB 的六个面的方程满足

Dot(Min, normal) = d

Dot(Max, normal) = d

其中 normal 可分别表示为 AABB的三个轴向 (1, 0, 0),(0, 1, 0),(0, 0, 1)

求射线与 x-slab 相交值为 t1,t2

令t1< t2,如果 t1 > t2 ,则 t1、t2值互换

求射线与 y-slab相交值为 t3,t4

令t3< t4,如果 t3 > t4 ,则t3、t4值互换

求射线与 z-slab相交值为 t5,t6

令t5< t6,如果 t5 > t6 ,则t5、t6值互换

(t1,t2) ,(t3,t4),(t5,t6) 如果有一组数据两个值都小于 0,如 (t1 = -1、t2 = -3) ,则射线与AABB不相交,

下一步:如果 t1、t2 、t3 、t4、 t5 、t6 有 小于 0 的,令其值 = 0

代码逻辑如下,关于图3的情况我还没有验证

AABB定义

public class AABB
{public Vector3 min;public Vector3 max;public AABB(Vector3 min, Vector3 max){this.min = min;this.max = max;}
}

逻辑代码

/// <summary>
/// 射线与AABB相交检测
/// 下面方法是 射线与 3D AABB 相交的计算
/// 如果想计算 射线与 2D AABB 相交,则将下方关于 z 坐标的部分删除即可
/// </summary>
public class RayAABBCollision
{/// <summary>/// 判断射线与AABB是否相交/// </summary>/// <param name="source">射线起点</param>/// <param name="rayDir">射线方向</param>/// <param name="aabb">AABB</param>/// <returns></returns>public bool IsCollision(Vector3 source, Vector3 rayDir, AABB aabb){float length = 0;return IsCollision(source, rayDir, aabb, ref length);}/// <summary>/// 判断射线与AABB是否相交/// </summary>/// <param name="raySource">射线起点</param>/// <param name="rayDir">射线方向</param>/// <param name="aabb">AABB</param>/// <param name="point">射线与AABB交点坐标</param>/// <returns></returns>public bool IsCollision(Vector3 raySource, Vector3 rayDir, AABB aabb, ref Vector3 point){float length = 0;bool collision = IsCollision(raySource, rayDir, aabb, ref length);point = raySource + rayDir * length;return collision;}/// <summary>/// 判断射线与AABB是否相交/// </summary>/// <param name="raySource">射线起点</param>/// <param name="rayDir">射线方向向量</param>/// <param name="aabb">AABB</param>/// <param name="length">射线起点到相交点距离</param>/// <returns></returns>public bool IsCollision(Vector3 raySource, Vector3 rayDir, AABB aabb, ref float length){float t1 = 0;float t2 = 0;bool collision = Calculate(raySource, rayDir, aabb, new Vector3(1, 0, 0), ref t1, ref t2);if (!collision || !CheckValue(ref t1, ref t2)){return false;}float t3 = 0;float t4 = 0;collision = Calculate(raySource, rayDir, aabb, new Vector3(0, 1, 0), ref t3, ref t4);if (!collision || !CheckValue(ref t3, ref t4)){return false;}float t5 = 0;float t6 = 0;collision = Calculate(raySource, rayDir, aabb, new Vector3(0, 0, 1), ref t5, ref t6);if (!collision || !CheckValue(ref t5, ref t6)){return false;}float entryT1 = Math.Max(Math.Max(t1, t3), t5);float entryT2 = Math.Min(Math.Min(t2, t4), t6);length = entryT1;return (entryT1 < entryT2);}/// <summary>/// 射线与平面相交计算/// </summary>/// <param name="raySource">射线起点</param>/// <param name="rayDir">射线方向向量</param>/// <param name="aabb">aabb</param>/// <param name="normal">平面法向量</param>/// t = (d - Dot(raySource, normal)) / Dot(rayDir, normal)/// d = Dot(planePoint, normal)/// t = (Dot(planePoint, normal)  - Dot(raySource, normal)) / Dot(rayDir, normal)/// t = Dot((planePoint - raySource), normal) / Dot(rayDir, normal)private bool Calculate(Vector3 raySource, Vector3 rayDir, AABB aabb, Vector3 normal, ref float t1, ref float t2){float p_sub_r_dot_n1 = Vector3.Dot(aabb.min - raySource, normal);float p_sub_r_dot_n2 = Vector3.Dot(aabb.max - raySource, normal);float r_dot_n = Vector3.Dot(rayDir, normal);if (Math.Abs(r_dot_n) <= float.Epsilon)  // 射线垂直于平面法向量,所以射线与平面平行{float dot1 = Vector3.Dot(aabb.min - raySource, normal);float dot2 = Vector3.Dot(aabb.max - raySource, normal);if (dot1 * dot2 > 0){return false;}}t1 = p_sub_r_dot_n1 / r_dot_n;t2 = p_sub_r_dot_n2 / r_dot_n;return true;}private bool CheckValue(ref float value1, ref float value2){if (value1 < 0 && value2 < 0){return false;}value1 = Mathf.Clamp(value1, 0, value1);value2 = Mathf.Clamp(value2, 0, value2);if (value1 > value2){float temp = value1;value1 = value2;value2 = temp;}return true;}
}

测试结果,测试动画总AABB边红色为相交

射线与AABB相交检测相关推荐

  1. 射线与OBB相交检测

    在上一篇 射线与AABB相交检测 射线与OBB3D 相交检测的原理跟射线与AABB相交检测的原理相同,本篇不再讲解原理 上篇推论出:射线与平面相交点距离射线起点距离t的距离公式为 t = (d - D ...

  2. 《用一周学习光线追踪》2.BVH树、AABB相交检测

    本项目上接<用两天学习光线追踪>,继续学习光线追踪. 项目链接:https://github.com/maijiaquan/ray-tracing-with-imgui 目录: <用 ...

  3. 射线和三角形的相交检测(ray triangle intersection test)

    http://www.cnblogs.com/graphics/archive/2010/08/09/1795348.html 本文以Fast, Minimum Storage Ray Triangl ...

  4. 射线与圆、球相交检测

    射线与圆.球相交检测 本篇讨论2D中射线和圆的相交检测,本方法同样适用于3D中射线和球的相交检测,这是因为可以在包含射线和球心的平面中进行检测,从而将3D问题转化为2D问题.如果射线穿过球心,那么平面 ...

  5. AABB上距离点最近的距离、AABB与球相交检测

    AABB上距离点最近的距离.AABB与球相交检测 AABB 即轴对其矩形边界框(每一条边都平行于坐标轴的矩形边界框),2D中为无旋转的 矩形,3D中为无旋转的六面体,长宽高可以各不相同. AABB内的 ...

  6. 射线和立方体相交的判断

    本文主要介绍如何判定一条线和一个立方体相交 1.Slabs的使用(2D的介绍) 2.将Slabs推广到3D 其中只介绍数学相关,代码实现可自行实现 检测物体碰撞的时候,我们通常在物体表面添加包围盒,其 ...

  7. 空间射线与三角形相交算法的两种实现

    文章目录 1. 概述 2. 常规算法 2.1. 理论推导 2.2. 具体实现 3. 优化算法 3.1. 理论推导 3.2. 具体实现 4. 参考 1. 概述 任何复杂的三维模型都可以视作空间三角面片的 ...

  8. NX/UG二次开发—3D几何—包围盒相交检测(转载)

    一:包围盒介绍 包围盒是指能够包容物体的三维立方体或者二维长方形,是包围体的一种,常常用于模型的碰撞检测.包围体主要包括球体.轴对齐包围盒(AABB).有向包围盒(OBB)和凸包(Convex Hul ...

  9. 【Unity】图形相交检测

    前言 图形相交检测常常用在伤害判定,使用自定义的图形相交检测,可以在一定程度上控制性能. 比如2D格斗游戏中使用的矩形包围盒(AABB),一些动作游戏中常常出现的扇形攻击. 2D的图形相交检测能够满足 ...

最新文章

  1. 记录一次异常 出现不支持的 SQL92 标记: 70
  2. Roller5.0.3安装配置部署 step by step
  3. poj1703Find them, Catch them(并查集以及路径压缩)
  4. linux安装网卡驱动tgz,Linux安装网卡驱动
  5. Think in Java第四版 读书笔记2
  6. 如何下载python3.6版本-python最新版本免费下载-python 3.6.3正式版下载__飞翔下载
  7. linux的vim怎么配置文件路径,Linux_Linux系统配置VI或VIM的技巧,1、VI或VIM的配置文件的路径 - phpStudy...
  8. Android开发笔记(一百零一)滑出式菜单
  9. React的学习曲线
  10. Ubuntu18.04 下面安装docker
  11. eclipse导入静态网页模板+搭建springboot环境示例+细节问题解决(详细)
  12. Python实现回归分析之线性回归
  13. [剑指Offer]:翻转单词顺序
  14. .Net与DirectX结合制作泡泡屏保
  15. 作业2:健身类软件调研
  16. m4s格式转换mp3_音乐怎么转换mp3格式
  17. 电脑彻底删除的文件如何恢复?
  18. 机器学习的可解释性(总结)
  19. Java面试题(持续日更)
  20. 遭遇裁员,如何渡过心理危机?

热门文章

  1. 安卓手机开不了机_vivo手机开不了机怎么办
  2. 咖说 | 富达:企业财资为何该考虑数字货币
  3. 论相对性原理1-狭义相对论命题的证明
  4. 第八届蓝桥杯-第四题方格分割
  5. 陌陌的付费用户猛增,背后的运营增长逻辑是什么?
  6. 1972年重大考古发现:出土《孙子兵法》和失传千年的《孙膑兵法》
  7. ipad / iphone 固件恢复安装
  8. PHP正则匹配数字,字母,中文
  9. 苹果手机耗电快_苹果手机耗电太快?关闭这几大功能,从此告别一天三充
  10. 如何求C语言字符串长度(strlen函数和sizeof关键字)