http://blog.csdn.net/i_dovelemon/article/details/38342739

引言

在上一节中,我讲述了如何实现射线与三角形的交叉检测算法。 但是,我们应该知道,在游戏开发中,一个模型有很多的三角形构成,如果要对所有的物体,所有的三角形进行这种检测,就算现在的计算机运算能力,也是无法高 效的完成。所以,我们需要通过其他的手段来提早剔除一些不可能发生交叉的物体,这种早退的思想,大量的运用在3D游戏技术中。在本篇文章中,我将像大家讲 述如何实现射线与轴向包围盒AABB的交叉检测。如果读者不明白什么是轴向包围盒,请看这篇文章

Ray-AABB交叉检测算法

现如今,有很多的Ray-AABB交叉检测算法,这里主要讲述一种称之为"Slabs method"的交叉检测算法。

在3D空间中,我们先确定正对着射线的三个面,也就是说,我们可以通过某种方式将AABB相对于射线Ray的背面给忽略掉,从而确定三个候选的面。这三个
候选的面,就是有可能和射线Ray发生交叉的最近的面。同时,我们还需要知道一个事实:当射线与这三个候选面中的一个发生交叉之后,射线Ray的原点到这
个面的距离要比到其他几个面的距离要长。

如果对于上面的事实不是很清楚的话,我们来看下图:

在上图中,我们的射线在右下角,向左上角发射。所以,候选面就是y1面,和x2面(这里由于讲述的方便,使用2D图形来表示)。ty是射线到y1面的距
离,tx是射线到x2的距离。从上图中可以看出tx > ty, 而射线与x2平面相交叉。

通过上图,大家就应该明白了,上面那个事实的含义了。这个算法也就是基于此等事实来实现的。

我们知道射线的公式为R(t) = O + t *
D,上图中所说的射线到某个面的距离,实际上就是射线原点到射线与该平面的交叉点的距离,而这个距离,刚好就是我们将平面方程带入R(t) = O + t
* D中,计算出来的t的值。所以,我们现在要做的就是确定哪几个面试候选面,并且将候选面的方程带入到R(t) = O + t *
D中,求出t的值,同时我们还要确保求出的交叉点在AABB盒上,才能够算作发生了交叉,如果仅仅是求出最大的t值,而不保证交点是否在AABB盒上,算
法也是无效的。

在上面,讲述了算法的大概流程,但是,还有几个问题需要解决。

首先:

1.如何确定候选面

2.如何确定候选面的方程

3.如何对交叉点是否在AABB盒上进行判断

下面我们来依次解决这几个问题。

对于问题1,很简单,如上图中的y1平面和y2平面来说,只要将平面方程带入射线Ray的方程,求出这两个平面的t值,然后t值较小的那个自然先与射线交
叉,那么就表示它是一个候选面了。可以看出,解决这个问题的方法还是要确定平面的方程。这个问题刚好是问题2。

我们可以使用如下的方程来表示某一个候选面的方程:

X * n = d ;
其中X表示的就是该候选面上的点,n表示的是该面的法线,注意一个面有两个法线,我们使用的法线并不是像3D图形学中某个顶点法线那样的定义,在这里,我
们一致使用某个分量为1的法线,也就是说,如果上面的方程表示的是AABB盒的左面的面,那么公式中的n表示的就是(1,0,0),但上面的公式表示的是
AABB盒的右边的面的时候,n表示的值依然是(1,0,0)。读者看到这里,可能要骂娘了,为毛这么搞,干嘛要搞一样的法线,为什么不像DirectX
里面定义顶点法线那样来定义了?额,这样表示其实是一个编程技巧,当后面看代码的时候,就能够明白这样做的好处了。在明白了n怎么样表示之后,还剩下d。
这里的d表示的就是这个AABB盒的这个面在垂直于该面的那个轴上的分量。也就是说,如果上面表示的是AABB盒的左边的面的时候,d表示的就是这个面上
所有点的x坐标(由于是轴向包围盒AABB,所以左边的面的x坐标都是一样的)。注意,这里表示的是坐标值,也就是表示带有方向的。这样说,读者可能又不
理解了,平面上的点和平面的法线点积怎么可能会是负值了。额,的确,如果使用的是DirectX中顶点的法线定义方式的时候,从原点到平面上的点所构成的
向量与DirectX法线的点积肯定是一个正数,但是亲爱的读者啊,我这里使用的并不是那种法线定义方式,所以还请原谅,这里的d值有可能就是负值了。

好了,费了很大的力气讲解这个公式的来历,希望读者可以好好的明白下上面那些话的意义。通过上面的公式,我们就能够将这个公式带入到射线方程中,从而得到如下的结果:

t = (d - O * n) / (D * n) 。对于AABB盒来说,它的面的法线总是有两个分量是0,而另外一个分量由于我上面讲述的那种技巧,使得那个分量总是为1,所以就能够得到如下的统一的t值公式:

当候选面垂直与x轴的两个面时,t = (d - Ox) / Dx

当候选面垂直与y轴的两个面时,t = (d - Oy) / Dy

当候选面垂直与z轴的两个面时,t = (d - Oz) / Dz

有了方程之后,我们就可以求出t值了。

现在就剩下最后一个问题了,如何确保求出的t值所表示的那个交叉点是在AABB盒上的了??

为了求出这个问题,我们需要在求t值的过程中,保留另外三个并不是候选面t值。然后,通过这些t值来确定是否在AABB盒上。这个过程将在代码中展示出来,因为只是简单的比较操作,所以不再赘述。

Ray-AABB交叉算法实现

下面是我实现的一个版本的代码:

  1. <span style="font-family:Microsoft YaHei;">bool Ray::intersectWithAABB(AABB* a, VECTOR3* vcHit)
  2. {
  3. float tmin = 0.0f ;
  4. float tmax = FLT_MAX ;
  5. //The plane perpendicular to x-axie
  6. if(abs(dir.x) < 0.000001f) //If the ray parallel to the plane
  7. {
  8. //If the ray is not within AABB box, then not intersecting
  9. if(origin.x < a->min.x || origin.x > a->max.x)
  10. return false ;
  11. }
  12. else
  13. {
  14. //Compute the distance of ray to the near plane and far plane
  15. float ood = 1.0f / dir.x ;
  16. float t1 = (a->min.x - origin.x) * ood ;
  17. float t2 = (a->max.x - origin.x) * ood ;
  18. //Make t1 be intersecting with the near plane, t2 with the far plane
  19. if(t1 > t2)
  20. {
  21. float temp = t1 ;
  22. t1 = t2 ;
  23. t2 = temp ;
  24. }
  25. //Compute the intersection of slab intersection intervals
  26. if(t1 > tmin) tmin = t1 ;
  27. if(t2 < tmax) tmax = t2 ;
  28. //Exit with no collision as soon as slab intersection becomes empty
  29. if(tmin > tmax) return false ;
  30. }// end for perpendicular to x-axie
  31. //The plane perpendicular to y-axie
  32. if(abs(dir.y) < 0.000001f) //If the ray parallel to the plane
  33. {
  34. //If the ray is not within AABB box, then not intersecting
  35. if(origin.y < a->min.y || origin.y > a->max.y)
  36. return false ;
  37. }
  38. else
  39. {
  40. //Compute the distance of ray to the near plane and far plane
  41. float ood = 1.0f / dir.y ;
  42. float t1 = (a->min.y - origin.y) * ood ;
  43. float t2 = (a->max.y - origin.y) * ood ;
  44. //Make t1 be intersecting with the near plane, t2 with the far plane
  45. if(t1 > t2)
  46. {
  47. float temp = t1 ;
  48. t1 = t2 ;
  49. t2 = temp ;
  50. }
  51. //Compute the intersection of slab intersection intervals
  52. if(t1 > tmin) tmin = t1 ;
  53. if(t2 < tmax) tmax = t2 ;
  54. //Exit with no collision as soon as slab intersection becomes empty
  55. if(tmin > tmax) return false ;
  56. }// end for perpendicular to y-axie
  57. //The plane perpendicular to z-axie
  58. if(abs(dir.z) < 0.000001f) //If the ray parallel to the plane
  59. {
  60. //If the ray is not within AABB box, then not intersecting
  61. if(origin.z < a->min.z || origin.z > a->max.z)
  62. return false ;
  63. }
  64. else
  65. {
  66. //Compute the distance of ray to the near plane and far plane
  67. float ood = 1.0f / dir.z ;
  68. float t1 = (a->min.z - origin.z) * ood ;
  69. float t2 = (a->max.z - origin.z) * ood ;
  70. //Make t1 be intersecting with the near plane, t2 with the far plane
  71. if(t1 > t2)
  72. {
  73. float temp = t1 ;
  74. t1 = t2 ;
  75. t2 = temp ;
  76. }
  77. //Compute the intersection of slab intersection intervals
  78. if(t1 > tmin) tmin = t1 ;
  79. if(t2 < tmax) tmax = t2 ;
  80. //Exit with no collision as soon as slab intersection becomes empty
  81. if(tmin > tmax) return false ;
  82. }// end for perpendicular to z-axie
  83. vcHit->x = origin.x + tmin * dir.x ;
  84. vcHit->y = origin.y + tmin * dir.y ;
  85. vcHit->z = origin.z + tmin * dir.z ;
  86. return true ;
  87. }// end for intersectWithAABB</span>

这个代码我大概的解释下,为了保证,在求交点的时候不发生除0异常,该算法先判断此时的方向向量D的某个分量是否为0,如果是的话,只需要简单的判断就能够知道是否与AABB交叉,因为此时射线是与某个轴平行的。

在上面的代码中,除了一直求三个候选平面的最大t值之外,该算法还一直计算了另外三个非候选平面的最小t值,并且自每次计算之后,判断下此时的交叉点的t
值,也就是tmin,是否大于tmax,如果是的话,就说明,该轴上求出的t值并不在AABB盒上(只要简单的在草稿纸上画个图,就应该明白这个),而一
旦有一个轴上发生了分离,那么我们就不需要进行下面的步骤了,一个轴上发生了分离,则表示射线与AABB盒不可能发生交叉。

程序实例

没有交叉前的截图:

交叉之后的图:

好了,今天的文章到此结束。

参考书: Real time collision detection

Graphics Gems I

3D空间中射线与轴向包围盒AABB的交叉检测算法 【转】的更多相关文章

  1. 3D空间中射线与轴向包围盒AABB的交叉检测算法【转】

    引言 在上一节中,我讲述了如何实现射线与三角形的交叉检测算法.但是,我们应该知道,在游戏开发中,一个模型有很多的三角形构成,如果要对所有的物体,所有的三角形进行这种检测,就算现在的计算机运算能力,也是 ...

  2. 3D空间中射线与三角形的交叉检测算法【转】

    引言 射线Ray,在3D图形学中有很多重要的应用.比如,pick操作就是使用射线Ray来实现的,还有诸如子弹射线的碰撞检测等等都可以使用射线Ray来完成.所以,在本次博客中,将会简单的像大家介绍下,如 ...

  3. 3D空间中射线与三角形的交叉检測算法

    引言 射线Ray,在3D图形学中有非常多重要的应用.比方,pick操作就是使用射线Ray来实现的,还有诸如子弹射线的碰撞检測等等都能够使用射线Ray来完毕. 所以,在本次博客中,将会简单的像大家介绍下 ...

  4. WebGL和ThreeJs学习6--射线法确定3D空间中所选物体

    一.在 threejs 中如何确定下图3D空间中鼠标点击位置的 object 对象? 二.射线法确定步骤及代码 //Three.js提供一个射线类Raycaster来拾取场景里面的物体.更方便的使用鼠 ...

  5. 2D和3D空间中计算两点之间的距离

    自己在做游戏的忘记了Unity帮我们提供计算两点之间的距离,在百度搜索了下. 原来有一个公式自己就写了一个方法O(∩_∩)O~,到僵尸到达某一个点之后就向另一个奔跑过去 /// <summary ...

  6. OpenGL ES 3D空间中自定义显示空间

    在Android中,我们所熟知的是在ES管线中,其在图元装配时,会进行图元组装与图元分配,这样就回剪裁出来视景体中的物体.但是如果我想在3D场景中规定一个区域,凡是在这个区域中的物体就能显示出来,非这 ...

  7. CSS3之3D效果中的transform运用

    css3中添加了很多新的标签 属性 描述 css transform 向元素应用 2D 或 3D 转换. 3 transform-origin 允许你改变被转换元素的位置. 3 transform-s ...

  8. Direct3D 11 Tutorial 4: 3D Spaces_Direct3D 11 教程4:3D空间

    概述 在上一个教程中,我们在应用程序窗口的中心成功渲染了一个三角形. 我们没有太注意我们在顶点缓冲区中拾取的顶点位置. 在本教程中,我们将深入研究3D位置和转换的细节. 本教程的结果将是渲染到屏幕的3 ...

  9. 视觉SLAM的数学基础 第一篇 3D空间的位置表示

    视觉SLAM中的数学基础 第一篇 3D空间的位置表示 前言 转眼间一个学期又将过去,距离我上次写<一起做RGBD SLAM>已经半年之久.<一起做>系列反响很不错,主要由于它为 ...

随机推荐

  1. UVa 1452 递推 Jump

    约瑟夫变形,先计算出3个数时,最后三个数字的编号. 然后以这三个数为起点,就可以递推出n个数对应的最后三个数字的编号. 递推公式都是一样的. #include <iostream> #in ...

  2. 计算n的阶乘(n!)末尾0的个数

    题目: 给定一个正整数n,请计算n的阶乘n!末尾所含有“0”的个数. 举例: 5!=120,其末尾所含有的“0”的个数为1: 10!= 3628800,其末尾所含有的“0”的个数为2: 20!= 24 ...

  3. Android SDK Manager 报错:Connection to https://dl-ssl.google.com refused

    Connection to https://dl-ssl.google.com refused. OR Failed to fectch URl https://dl-ssl.google.com/a ...

  4. npm link & run npm script

    npm link & run npm script https://blog.csdn.net/juhaotian/article/details/78672390 npm link命令可以将 ...

  5. 设计模式(八)组合模式 Composite

    组合模式: 允许你将对象组合成树形结构来表现“整体/部分”层次结构.组合能让客户以一致的方式处理个别对象以及对象组合. 组合模式适用于创建复杂的对象,这个对象包含某些个别的对象以及这些对象的组合. 从 ...

  6. 硅谷和国内的 iOS 开发到底有何不同?

    前段时间在国内各大互联网公司转了一圈.与各位 iOS 业界大佬交流了之后,深感国内变化之大,敬佩诸位国内开发者的实力和韧劲.除此之外,我还发现硅谷和国内的 iOS 开发还是差别很大,且听我慢慢道来. ...

  7. Welcome-to-Swift-20扩展(Extensions)

    扩展就是向一个已有的类.结构体或枚举类型添加新功能(functionality).这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模).扩展和 Objective-C 中的分类(cate ...

  8. 【bzoj3261】最大异或和 可持久化Trie树

    题目描述 给定一个非负整数序列 {a},初始长度为 N.       有M个操作,有以下两种操作类型:1.A x:添加操作,表示在序列末尾添加一个数 x,序列的长度 N+1.2.Q l r x:询问操 ...

  9. NYOJ——301递推求值(矩阵快速幂)

    递推求值 时间限制:1000 ms | 内存限制:65535 KB 难度:4 描述 给你一个递推公式: f(x)=a*f(x-2)+b*f(x-1)+c 并给你f(1),f(2)的值,请求出f(n)的 ...

  10. spring分布式事务控制

    应用场景问题描述解决方法多数据源配置单元测试第一种方法:最大努力一次提交模式第二种方法:最大努力一次提交模式 但使用ChainedTransactionManagerChainedTransactio ...