射线与AABB相交检测
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相交检测相关推荐
- 射线与OBB相交检测
在上一篇 射线与AABB相交检测 射线与OBB3D 相交检测的原理跟射线与AABB相交检测的原理相同,本篇不再讲解原理 上篇推论出:射线与平面相交点距离射线起点距离t的距离公式为 t = (d - D ...
- 《用一周学习光线追踪》2.BVH树、AABB相交检测
本项目上接<用两天学习光线追踪>,继续学习光线追踪. 项目链接:https://github.com/maijiaquan/ray-tracing-with-imgui 目录: <用 ...
- 射线和三角形的相交检测(ray triangle intersection test)
http://www.cnblogs.com/graphics/archive/2010/08/09/1795348.html 本文以Fast, Minimum Storage Ray Triangl ...
- 射线与圆、球相交检测
射线与圆.球相交检测 本篇讨论2D中射线和圆的相交检测,本方法同样适用于3D中射线和球的相交检测,这是因为可以在包含射线和球心的平面中进行检测,从而将3D问题转化为2D问题.如果射线穿过球心,那么平面 ...
- AABB上距离点最近的距离、AABB与球相交检测
AABB上距离点最近的距离.AABB与球相交检测 AABB 即轴对其矩形边界框(每一条边都平行于坐标轴的矩形边界框),2D中为无旋转的 矩形,3D中为无旋转的六面体,长宽高可以各不相同. AABB内的 ...
- 射线和立方体相交的判断
本文主要介绍如何判定一条线和一个立方体相交 1.Slabs的使用(2D的介绍) 2.将Slabs推广到3D 其中只介绍数学相关,代码实现可自行实现 检测物体碰撞的时候,我们通常在物体表面添加包围盒,其 ...
- 空间射线与三角形相交算法的两种实现
文章目录 1. 概述 2. 常规算法 2.1. 理论推导 2.2. 具体实现 3. 优化算法 3.1. 理论推导 3.2. 具体实现 4. 参考 1. 概述 任何复杂的三维模型都可以视作空间三角面片的 ...
- NX/UG二次开发—3D几何—包围盒相交检测(转载)
一:包围盒介绍 包围盒是指能够包容物体的三维立方体或者二维长方形,是包围体的一种,常常用于模型的碰撞检测.包围体主要包括球体.轴对齐包围盒(AABB).有向包围盒(OBB)和凸包(Convex Hul ...
- 【Unity】图形相交检测
前言 图形相交检测常常用在伤害判定,使用自定义的图形相交检测,可以在一定程度上控制性能. 比如2D格斗游戏中使用的矩形包围盒(AABB),一些动作游戏中常常出现的扇形攻击. 2D的图形相交检测能够满足 ...
最新文章
- 记录一次异常 出现不支持的 SQL92 标记: 70
- Roller5.0.3安装配置部署 step by step
- poj1703Find them, Catch them(并查集以及路径压缩)
- linux安装网卡驱动tgz,Linux安装网卡驱动
- Think in Java第四版 读书笔记2
- 如何下载python3.6版本-python最新版本免费下载-python 3.6.3正式版下载__飞翔下载
- linux的vim怎么配置文件路径,Linux_Linux系统配置VI或VIM的技巧,1、VI或VIM的配置文件的路径 - phpStudy...
- Android开发笔记(一百零一)滑出式菜单
- React的学习曲线
- Ubuntu18.04 下面安装docker
- eclipse导入静态网页模板+搭建springboot环境示例+细节问题解决(详细)
- Python实现回归分析之线性回归
- [剑指Offer]:翻转单词顺序
- .Net与DirectX结合制作泡泡屏保
- 作业2:健身类软件调研
- m4s格式转换mp3_音乐怎么转换mp3格式
- 电脑彻底删除的文件如何恢复?
- 机器学习的可解释性(总结)
- Java面试题(持续日更)
- 遭遇裁员,如何渡过心理危机?