射线与AABB盒相交检测算法(Slabs method)

一. 射线与平面求交

设射线的起始点为P0 ,射线方向向量为,则射线的任一一点的方程可表示为

    

设平面的法线向量为,则平面上任一点的坐标P满足

    

意为,坐标原点与平面上任一点的向量上的投影长度为常量d.

由以上射线和平面的方程可知,交点处坐标满足

    

 

解得

    

 

 

二.射线相对包围盒的近面与远面

AABB盒一共有6个面,可将其中三个面分为射线的近面,另三个面视为远面.近面和远面不是按离射线起点或离射线某点的距离来定义的,而是看与盒的6个面的法线向量方向,射线的方向向量方向相同的视为远面,与射线方向向量方向相反的视为近面,即:射线方向向量与近面的法线向量的点乘<0,与远面的法线向量的点乘>0,如果射线与盒的某个面法线向量点乘=0,则射线与盒不会相交。当然,这里不需要会去求射线与各个法线向量的乘积然后再判断哪个为近面哪个为远面,而是直接计算射线与AABB盒同一轴上两个面的交点,然后再利用以下的第3点,来判断哪个是近面交点哪个是远面交点。

如果射线与AABB盒相交,则必会满足如下几点:

  1. 如果射线与AABB盒有交点,那么射线就一定会穿过AABB盒
  2. 射线必定先与三个近面中的一个相交
  3. 根据直线方程和1,2,其在远面交点的t值一定比与近面交点的t值大

对以上三点,1,2都不言自明,对于第三点,想像将AABB盒的每个面进行延展,那射线将分别与AABB盒的三个近面和远面相交,有的相交在盒上,有的相交在盒的延展面上,一共6个交点(如果相交在盒顶点则3个近面(或远面)交点重合),3个近面交点,3个远面交点,3个近面交点中,只有一个会相交在盒上,另外两个相交在盒的延展面上,远面也是一样。而且,在三个近面交点中,在盒上相交的点的t值要比在延展面上将相交的t值最大(考虑t的正负号,而不是绝对值),同时,比在远面上相交的任意点的t值要小。

如果不满足此,则说明所有交点都在延展面上。

 

//sp,sq为射线的起始坐标,amin和amax为AABB盒的最小点和最大点坐标,其点坐标拿数组表示,x,y,z轴的分别对应索引0,1,2
//tmin和tmax分别为射线与远面交点和近面交点的t值
bool isectSegAABB(const float* sp, const float* sq, const float* amin, const float* amax, float& tmin, float& tmax)
{
    static const float EPS = 1e-6f;
    float d[3];
    d[0] = sq[0] - sp[0];
    d[1] = sq[1] - sp[1];
    d[2] = sq[2] - sp[2];
    tmin = 0.0;
    tmax = 1.0f;

    //分别与三个轴的两个分面求交点
    for (int i = 0; i < 3; i++)
    {
        //如果某个轴的分量在两个点间没变,则说明与这个轴垂直,只需要判断是否在AABB盒内部,否则不可能相交
        if (fabsf(d[i]) < EPS)
        {
            if (sp[i] < amin[i] || sp[i] > amax[i])
                return false;
        }
        else{
            //通过上面的求t值公式,求出AABB盒的每个轴上的两个面分别与射线的交点的t值,求出一个大值为远面交点t值,小值为近面交点t值,此时各个面的法向量n在该轴值可取为1,其它两个轴上分值为0
            const float ood = 1.0f / d[i];
            float t1 = (amin[i] - sp[i]) * ood;
            float t2 = (amax[i] - sp[i]) * ood;
            if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; }
            if (t1 > tmin) tmin = t1;
            if (t2 < tmax) tmax = t2;
            if (tmin > tmax) return false;
        }
    }
    return true;
}

posted on 2019-12-14 21:13  凄夜  阅读(4138)  评论(0编辑  收藏  举报

导航