光线与包围盒(AABB)的相交检测算法

来源:互联网 发布:国外域名注册平台 编辑:程序博客网 时间:2024/04/27 18:47

转自:http://blog.csdn.net/u012325397/article/details/50807880  侵删

这里介绍两种算法,第一种比较容易理解


下面是Cocos2dx中实现Ray-AABB相交(碰撞)检测的算法,说明看注释


[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. bool Ray::intersects(const AABB& aabb) const  
  2. {  
  3.   Vec3 ptOnPlane; //射线与包围盒某面的交点  
  4.   Vec3 min = aabb._min; //aabb包围盒最小点坐标  
  5.   Vec3 max = aabb._max; //aabb包围盒最大点坐标  
  6.     
  7.   const Vec3& origin = _origin; //射线起始点  
  8.   const Vec3& dir = _direction; //方向矢量  
  9.     
  10.   float t;  
  11.     
  12.   //分别判断射线与各面的相交情况  
  13.     
  14.   //判断射线与包围盒x轴方向的面是否有交点  
  15.   if (dir.x != 0.f) //射线x轴方向分量不为0 若射线方向矢量的x轴分量为0,射线不可能经过包围盒朝x轴方向的两个面  
  16.   {  
  17.     /* 
  18.       使用射线与平面相交的公式求交点 
  19.      */  
  20.     if (dir.x > 0)//若射线沿x轴正方向偏移  
  21.       t = (min.x - origin.x) / dir.x;  
  22.     else  //射线沿x轴负方向偏移  
  23.       t = (max.x - origin.x) / dir.x;  
  24.       
  25.     if (t > 0.f) //t>0时则射线与平面相交  
  26.     {  
  27.       ptOnPlane = origin + t * dir; //计算交点坐标  
  28.       //判断交点是否在当前面内  
  29.       if (min.y < ptOnPlane.y && ptOnPlane.y < max.y && min.z < ptOnPlane.z && ptOnPlane.z < max.z)  
  30.       {  
  31.         return true//射线与包围盒有交点  
  32.       }  
  33.     }  
  34.   }  
  35.     
  36.   //若射线沿y轴方向有分量 判断是否与包围盒y轴方向有交点  
  37.   if (dir.y != 0.f)  
  38.   {  
  39.     if (dir.y > 0)  
  40.       t = (min.y - origin.y) / dir.y;  
  41.     else  
  42.       t = (max.y - origin.y) / dir.y;  
  43.       
  44.     if (t > 0.f)  
  45.     {  
  46.       ptOnPlane = origin + t * dir;  
  47.   
  48.       if (min.z < ptOnPlane.z && ptOnPlane.z < max.z && min.x < ptOnPlane.x && ptOnPlane.x < max.x)  
  49.       {  
  50.         return true;  
  51.       }  
  52.     }  
  53.   }  
  54.     
  55.   //若射线沿z轴方向有分量 判断是否与包围盒y轴方向有交点  
  56.   if (dir.z != 0.f)  
  57.   {  
  58.     if (dir.z > 0)  
  59.       t = (min.z - origin.z) / dir.z;  
  60.     else  
  61.       t = (max.z - origin.z) / dir.z;  
  62.       
  63.     if (t > 0.f)  
  64.     {  
  65.       ptOnPlane = origin + t * dir;  
  66.         
  67.       if (min.x < ptOnPlane.x && ptOnPlane.x < max.x && min.y < ptOnPlane.y && ptOnPlane.y < max.y)  
  68.       {  
  69.         return true;  
  70.       }  
  71.     }  
  72.   }  
  73.     
  74.   return false;  
  75. }  


下面是另外一种Ray-AABB检测算法,称为"Slabs method"



观察上述三幅图可以得出,只要发生区间交叠,光线与平面就能相交,

那么区间交叠出现的条件便是:光线进入平面处的最大t值小于光线离开平面处的最小t值


那么问题就变成了如何求 光线进入平面处的最大t值 以及 光线离开平面处的最小t值

这个问题很简单,通过光线与平面相交的参数方程求解就可以了,

光线的参数方程为R(t) = O + t * Dir

一般平面方程为aX+bY+cZ+d=0,因为AABB的六个面分别平行于XY、XZ、YZ平面,所以平面的方程为X=d,Y=d,Z=d

光线与垂直于x轴的两个面相交时,t = (d - O.x) / Dir.x
光线与垂直于y轴的两个面相交时,t = (d - O.y) / Dir.y
光线与垂直于z轴的两个面相交时,t = (d - O.z) / Dir.z


注意到t<0时,交点位于光线的起点之后,则光线(射线)并未与盒体发生相交


[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. bool BBox::hit(const Ray& ray) const  
  2. {  
  3.     double ox = ray.o.x;double oy = ray.o.y;double oz = ray.o.z;  
  4.     double dx = ray.d.x;double dy = ray.d.y;double dz = ray.d.z;  
  5.     double tx_min,ty_min,tz_min;  
  6.     double tx_max,ty_max,tz_max;  
  7.   
  8.     //x0,y0,z0为包围体的最小顶点  
  9.     //x1,y1,z1为包围体的最大顶点  
  10.     if(abs(dx) < 0.000001f)   
  11.     {  
  12.         //若射线方向矢量的x轴分量为0且原点不在盒体内  
  13.         if(ox < x1 || ox > x0)  
  14.             return false ;  
  15.     }  
  16.     else  
  17.     {  
  18.         if(dx>=0)  
  19.         {  
  20.             tx_min = (x0-ox)/dx;  
  21.             tx_max = (x1-ox)/dx;  
  22.         }  
  23.         else  
  24.         {  
  25.             tx_min = (x1-ox)/dx;  
  26.             tx_max = (x0-ox)/dx;  
  27.         }  
  28.   
  29.     }  
  30.   
  31.       
  32.     if(abs(dy) < 0.000001f)   
  33.     {  
  34.         //若射线方向矢量的x轴分量为0且原点不在盒体内  
  35.         if(oy < y1 || oy > y0)  
  36.             return false ;  
  37.     }  
  38.     else  
  39.     {  
  40.         if(dy>=0)  
  41.         {  
  42.             ty_min = (y0-oy)/dy;  
  43.             ty_max = (y1-oy)/dy;  
  44.         }  
  45.         else  
  46.         {  
  47.             ty_min = (y1-oy)/dy;  
  48.             ty_max = (y0-oy)/dy;  
  49.         }  
  50.   
  51.     }  
  52.   
  53.       
  54.     if(abs(dz) < 0.000001f)   
  55.     {  
  56.         //若射线方向矢量的x轴分量为0且原点不在盒体内  
  57.         if(oz < z1 || oz > z0)  
  58.             return false ;  
  59.     }  
  60.     else  
  61.     {  
  62.         if(dz>=0)  
  63.         {  
  64.             tz_min = (z0-oz)/dz;  
  65.             tz_max = (z1-oz)/dz;  
  66.         }  
  67.         else  
  68.         {  
  69.             tz_min = (z1-oz)/dz;  
  70.             tz_max = (z0-oz)/dz;  
  71.         }  
  72.   
  73.     }  
  74.   
  75.     double t0,t1;  
  76.       
  77.     //光线进入平面处(最靠近的平面)的最大t值   
  78.     t0=max(tz_min,max(tx_min,ty_min));  
  79.       
  80.     //光线离开平面处(最远离的平面)的最小t值  
  81.     t1=min(tz_max,min(tx_max,ty_max));  
  82.       
  83.     return t0<t1;  
  84. }  
0 0