热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

RayAABB交叉检测算法

[toc]  最近在解决三维问题时,需要判断线段是否与立方体交叉,这个问题可以引申为:射线是否穿过立方体AABB。  在3D游戏开发中碰撞检测普遍采用的算法是轴对齐矩形边界框(Ax

[toc]
  最近在解决三维问题时,需要判断线段是否与立方体交叉,这个问题可以引申为:射线是否穿过立方体AABB。
  在3D游戏开发中碰撞检测普遍采用的算法是轴对齐矩形边界框(Axially Aligned Bounding Box, AABB)包装盒方法,其基本思想是用一个立方体或者球体完全包裹住3D物体对象,然后根据包装盒的距离、位置等相关信息来计算是否发生碰撞。

slab的碰撞检测算法

  本文接下来主要讨论射线与AABB的关系,主要对box2d碰撞检测使用的slab的碰撞检测算法(==Slabs method==)进行介绍,然后使用python语言实现slab碰撞检测方法,该方法可以用于3D物体拾取等应用场景。
  Slab英文翻译是“平板”,本文是指两个平行平面/直线之间的空间。在2D空间中slab可以理解为平行于坐标轴的两条直线间的区域,3D空间中为平行于xy平面(或者yz面,xz面)的两个平面之间的区域。由此,我们可以把3D空间中的AABB盒子看做是由AABB的3组平行面形成的3个方向的slab的交集。
  另外,引入候选面的概念:在3D空间中,我们先确定正对着射线的三个面,也就是说,我们可以通过某种方式将AABB相对于射线Ray的背面给忽略掉,从而确定三个候选的面。==这三个候选的面,就是有可能和射线Ray发生交叉的最近的面==。
---
  根据这个定义,我们可以得到以下三个结论:

  1. 性质一:如果一个点在AABB中,那么这个点必定同时在这3个slab中。
  2. 性质二:如果一条射线和AABB相交,那么这条射线和3个slab的相交部分必定有重合部分。
  3. 性质三:当射线与这三个候选面中的一个发生交叉之后,射线Ray的原点到这个面的距离要比到其他几个面的距离要长。

      性质一和性质二比较容易理解,如果射线和3个slab的相交线段没有重合,那么这些线段就不可能同时存在于3个slab中,也就不可能在AABB盒子中。
      为了方便理解性质三,使用2D图形来讲解:
图片名称

  在上图中,我们的射线在右下角,向左上角发射,射线经过一个A点,其中候选面是y1面和x2面。
  根据上述性质,可以看到A点同时在2D空间中的2个slab中;此外,根据性质二,因为射线与平面相交,那么这条射线与slab的相交部分必有重合部分,因为A点在射线上,且在平面中,那么可以得到max(t1,t2)<=tA<=min(t3,t4);根据性质三:当交叉后,可以看出t2>t1。
  同理,我们可以把上述的验证过程推广到三维中。在三维空间中,假设射线到3个候选面的距离分别是t1、t2、t3,到候选面对应的面的距离分别为t4、t5、t6,那么根据性质二,射线与AABB碰撞的条件是max(t1,t2,t3)<=min(t4,t5,t6);如果发生交叉,那么根据性质三,射线到最近的交叉面的距离是是max(t1,t2,t3)。
---
  在上述性质基础上,确定射线与AABB是否交叉需要三步骤:

  1. 如何确定候选面:只要将平面方程带入射线Ray的方程,求出这两个平面的t值,然后t值较小的那个自然先与射线交叉,那么就表示它是一个候选面。射线可以用参数方程表示为R(t) = P0 + t·d, (其中P0为射线起点,d为射线的方向向量)
  2. 如何确定候选面的方程。平面由隐式定义方程X·n=D, (其中X为平面上的点,n为平面法向量,D为原点到平面的距离)给出。由于AABB的slab平面都分别和两个坐标轴平行,它的面的法线总是有两个分量是0,而另外一个分量总是为1,所以我们一致使用某个轴分量为1的法线。如果上面的方程表示的是AABB盒的左面的面,那么公式中的n表示的就是(1,0,0),但上面的公式表示的是AABB盒的右边的面的时候,n表示的值依然是(1,0,0)。
  3. 如何对交叉点是否在AABB盒上进行判断。根据性质二判断,即射线与AABB碰撞的条件是max(t1,t2,t3)<=min(t4,t5,t6)。

    碰撞检测算法公式推导

  求取t值的公式推导如下:

图片名称


碰撞检测算法Python源代码

最后,附上我的Python代码片段,代码实时更新于GitHub

# Ray-AABB方法 相交返回True,否则返回False
# TDPoint = collections.namedtuple("TDPoint", ["x", "y","z"])
# AABB有最大和最小点组成,数据结构为{max=TDPoint,min=TDPoint}
# Ray由原点和方向组成,其中方向矢量为1,数据结构为{TDPoint,TDPoint}
def intersectWithAABB(AABB,Ray):
    tmin=0
    tmax=10000

    # 
    if(math.fabs(Ray[1].x)<0.000001):
        if (Ray[0].x<AABB.min.x) or (Ray[0].x>AABB.max.x):
            return  False
    else:
        ood=1.0/Ray[1].x
        t1=(AABB.min.x-Ray[0].x)*ood
        t2=(AABB.max.x-Ray[0].x)*ood
        # t1做候选平面,t2做远平面
        if (t1>t2):
            temp=t1
            t1=t2
            t2=temp
        if t1>tmin:
            tmin=t1
        if t2<tmax:
            tmax=t2
        if tmin>tmax:
            return False
    # 

    # 
    if(math.fabs(Ray[1].y)<0.000001):
        if (Ray[0].y<AABB.min.y) or (Ray[0].y>AABB.max.y):
            return  False
    else:
        ood=1.0/Ray[1].y
        t1=(AABB.min.y-Ray[0].y)*ood
        t2=(AABB.max.y-Ray[0].y)*ood
        # t1做候选平面,t2做远平面
        if (t1>t2):
            temp=t1
            t1=t2
            t2=temp
        if t1>tmin:
            tmin=t1
        if t2<tmax:
            tmax=t2
        if tmin>tmax:
            return False
    # 

    # 
    if(math.fabs(Ray[1].z)<0.000001):
        if (Ray[0].z<AABB.min.z) or (Ray[0].z>AABB.max.z):
            return  False
    else:
        ood=1.0/Ray[1].z
        t1=(AABB.min.z-Ray[0].z)*ood
        t2=(AABB.max.z-Ray[0].z)*ood
        # t1做候选平面,t2做远平面
        if (t1>t2):
            temp=t1
            t1=t2
            t2=temp
        if t1>tmin:
            tmin=t1
        if t2<tmax:
            tmax=t2
        if tmin>tmax:
            return False
    # 
    return True

from 3D空间中射线与轴向包围盒AABB的交叉检测算法
from Box2D 射线和AABB的碰撞检测


推荐阅读
  • 探讨一个显示数字的故障计算器,它支持两种操作:将当前数字乘以2或减去1。本文将详细介绍如何用最少的操作次数将初始值X转换为目标值Y。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文介绍如何使用 Python 将一个字符串按照指定的行和元素分隔符进行两次拆分,最终将字符串转换为矩阵形式。通过两种不同的方法实现这一功能:一种是使用循环与 split() 方法,另一种是利用列表推导式。 ... [详细]
  • Python 异步编程:深入理解 asyncio 库(上)
    本文介绍了 Python 3.4 版本引入的标准库 asyncio,该库为异步 IO 提供了强大的支持。我们将探讨为什么需要 asyncio,以及它如何简化并发编程的复杂性,并详细介绍其核心概念和使用方法。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • 本文详细介绍了 GWT 中 PopupPanel 类的 onKeyDownPreview 方法,提供了多个代码示例及应用场景,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • python的交互模式怎么输出名文汉字[python常见问题]
    在命令行模式下敲命令python,就看到类似如下的一堆文本输出,然后就进入到Python交互模式,它的提示符是>>>,此时我们可以使用print() ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文详细介绍了Java中org.eclipse.ui.forms.widgets.ExpandableComposite类的addExpansionListener()方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。这些示例来源于多个知名开源项目,具有很高的参考价值。 ... [详细]
  • 本文介绍如何使用 Python 编写程序,检查给定列表中的元素是否形成交替峰值模式。我们将探讨两种不同的方法来实现这一目标,并提供详细的代码示例。 ... [详细]
  • Python自动化处理:从Word文档提取内容并生成带水印的PDF
    本文介绍如何利用Python实现从特定网站下载Word文档,去除水印并添加自定义水印,最终将文档转换为PDF格式。该方法适用于批量处理和自动化需求。 ... [详细]
  • 本文详细解析了Python中的os和sys模块,介绍了它们的功能、常用方法及其在实际编程中的应用。 ... [详细]
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
author-avatar
mobiledu2502887593
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有