您的位置:首页 > 其它

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

2016-09-23 20:17 288 查看
http://blog.csdn.net/i_dovelemon/article/details/38332499

引言

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

Ray-Triangle交叉检测算法

             在Tomas Moller的MT97论文中,提出了一种新的算法。这种算法能够减少以前进行Ray-Triangle交叉检测所需要的内存消耗。在以前,进行Ray-Triangle交叉检测,主要是计算射线与三角形所构成的平面的交点,然后重新判断交点是否在三角形上,从而来判断是否发生了交叉。这种方法很直观,符合我们一直以来所学的数学知识。但是,这种检测方法进行的计算较多,而且还需要根据三角形来求它所在的平面,这样又需要进行计算,同时也需要另外开辟空间来保存计算出来的平面。

            数学之美,就在于能够找到其他的方法来代替这种显而易见的方式,从而将问题简化到一定的程度。这种简化的过程,不需要在代码中实现,只需要我们事先根据条件,然后在草稿纸上计算出最后的结论,我们只需要在我们的代码中直接使用最终得到的结论即可。

            在Tomas Moller的论文中,它提到了这样的一个概念:

            如果一个点在三角形V0, V1, V2上,那么这个点就可以用如下的方式来表示:

            T(u, v) = (1 - u - v) * V0 + u * V1 + v * V2 ;

            这里u+v <= 1, u >= 0 ,  v >=0

            而对于射线,我们一般使用如下的方程来表示它:

             R (t)= O + t * D ; (O为射线的起始点,D为射线的方向)

            所以,既然他们要有交点,我们就能够直接使用如下的方法来得出:

             O + t * D = (1 - u - v) * V0 + u * V1 + v * V2

            然后在进行一系列的变换,最终得到结果。感兴趣的读者可以自行阅读Tomas
Moller的论文,论文中详细的解释了推导过程。这里不再赘述。

Ray-Triangle交叉检测算法实现

            以下是Ray-Triangle交叉检测算法的Moller算法实现,基本上就是Tomas Moller论文中代码的拷贝,如下所示:

[cpp] view
plain copy

<span style="font-family:Microsoft YaHei;">bool Ray::intersectWithTriangle(VECTOR3 v0,VECTOR3 v1, VECTOR3 v2,  

            bool bCull,   

            float *t)  

{  

    VECTOR3 edge1, edge2, tvec, pvec, qvec ;  

    float det, inv_det ;  

    float u,v ;  

  

    //Find vectors for two edges sharing vert0  

    Vec3Sub(edge1, v1, v0);  

    Vec3Sub(edge2, v2, v0);  

  

    //Begin calculating determinant - also used to calculate U parameter  

    Vec3Cross(pvec, dir, edge2);  

  

    //If the determinant is near zero, ray lies in plane of triangle  

    Vec3Dot(det, edge1, pvec);  

  

    //If bCull is true  

    if(bCull)  

    {  

        if(det < 0.00001f)  

            return false ;  

  

        //Calculate distance from vert0 to ray origin  

        Vec3Sub(tvec, origin, v0);  

  

        //Calculate U parameter and test bounds  

        Vec3Dot(u, tvec, pvec);  

        if(u < 0.0 || u > det)  

            return false ;  

  

        //Prepare to test v parameter  

        Vec3Cross(qvec, tvec, edge1);  

  

        //Calculate V parameter and test bounds  

        Vec3Dot(v, dir, qvec);  

        if(v < 0.0f || u + v > det)  

            return false ;  

  

        //Calculate t , scale paramter, ray intersect triangle  

        Vec3Dot(*t, edge2, qvec);  

        inv_det = 1.0f / det ;  

        *t *= inv_det ;  

        u *= inv_det ;  

        v *= inv_det ;  

    }  

    else  

    {  

        if(det > -0.00001f && det < 0.00001)  

            return false ;  

        inv_det = 1.0f / det ;  

  

        //calculate distance from v0 to ray origin  

        Vec3Sub(tvec, origin, v0);  

  

        //Calculate u parameter  and test bounds  

        Vec3Dot(u, tvec, pvec);  

        u *= inv_det ;  

        if(u < 0.0 || u > 1.0)  

            return false ;  

  

        //prepare to test v parameter  

        Vec3Cross(qvec, tvec, edge1);  

  

        //Calculate v parameter and test bounds  

        Vec3Dot(v, dir, qvec);  

        v *= inv_det ;  

        if(v < 0.0 || u + v > 1.0)  

            return false ;  

  

        //calculate t, ray intersect triangle  

        Vec3Dot(*t, edge2, qvec);  

        *t *= inv_det ;  

    }  

  

    return true ;  

}// end for intersectWithTriangle</span>  

示例程序截图

                 这个图是在没有发生交叉的时候的情况,



                    下图是在发生了交叉之后的截图:



             今天的笔记就此结束。以后会陆续出现这种文章,请大家关注吧!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: