使用射线矢量对三角形图元求交 D3DXIntersect()函数说明

  使用射线矢量对场景中的所有三角形图元求交,获得三角形索引值和重心坐标。1 D3D扩展函数实现求交

这种方法很简单也很好用,对于应用来说应尽力是用这种方式来实现,毕竟效率比自己写得要高得多。

实际上其实没什么好讲的,大概讲一下函数D3DXIntersect吧

D3D SDK该函数声明如下

HRESULT D3DXIntersect(              LPD3DXBASEMESH pMesh,
      CONST D3DXVECTOR3 *pRayPos,
      CONST D3DXVECTOR3 *pRayDir,
      BOOL *pHit,
      DWORD *pFaceIndex,
      FLOAT *pU,
      FLOAT *pV,
      FLOAT *pDist,
      LPD3DXBUFFER *ppAllHits,
      DWORD *pCountOfHits
);
l            pMesh指向一个ID3DXBaseMesh的对象,最简单的方式是从.x文件获得,描述了要进行相交检测的三角面元集合的信息,具体规范参阅direct9 SDK
l            pRayPos 指向射线发出点
l            pRayDir 指向前面我们辛辛苦苦求出的射线方向的向量
l            pHit 当检测到相交图元时,指向一个true,不与任何图元相交则为假
l            pU 用于返回重心坐标U分量
l            pV返回重心坐标V分量
l            pDist 返回射线发出点到相交点的长度
注意:以上红色字体部分均指最近的一个返回结果(即*pDist最小)
l            ppAllHits用于如果存在多个相交三角面返回相交的所有结果
l            pCountOfHits 返回共有多少个三角形与该射线相交

补充:重心坐标的概念

其中pU和pV用到了重心坐标的概念,下面稍作描述

一个三角形有三个顶点,在迪卡尔坐标系中假设表示为V1(x1,y1,z1),V2(x2,y2,z2),V3(x3,y3,z3),则三角形内任意一点的坐标可以表示为 pV = V1 + U(V2-V1) + V(V3-V1),所以已知三个顶点坐标的情况下,任意一点可用坐标(U,V)来表示,其中 参数U控制V2在结果中占多大的权值,参数V控制V3占多大权值,最终1-U-V控制V1占多大权值,这种坐标定义方式就叫重心坐标。

2射线三角面相交的数学算法

     使用d3d扩展函数,毕竟有时不能满足具体需求,掌握了该方法,我们才能够获得最大的控制自由度,任意修改算法。

    

已知条件: 射线源点orginPoint,三角形三个顶点 v1,v2,v3,射线方向 Dir

(均以三维坐标向量形式表示)

算法目的: 判断射线与三角形是否相交,如果相交求出交点的重心坐标(U,V)和射线原点到交点的距离T。

我们可先假设射线与三角形相交则交点(注以下均为向量运算,*数乘,dot(X,Y) X,Y 点乘,cross(X,Y)X,Y叉乘;U,V,T为标量)

则:

IntersectPoint = V1 + U*(V2-V1) + V*(V3-V1) ;

IntersectPoint = originPoint + T*Dir;

所以

orginPoint + T*Dir = V1 + U*(V2-V1) + V*(V3-V1);

整理得:

这是一个简单的线性方程组,若有解则行列式 不为0。

根据T,U,V的含义当T>0, 0<U<1,0<V<1,0<U+V<1时该交点在三角形内部,

解此方程组即可获得我们关心的值,具体解法不再赘述,克莱姆法则就够了(详细见线性代数):射线原点到相交点的距离T,和交点的中心坐标(U,V)。

下面给出Direct 9 SDK示例程序中的实现代码

IntersectTriangle( const D3DXVECTOR3& orig,

                   const D3DXVECTOR3& dir, D3DXVECTOR3& v0,

                    D3DXVECTOR3& v1, D3DXVECTOR3& v2,

                    FLOAT* t, FLOAT* u, FLOAT* v )

{

    // 算出两个边的向量

    D3DXVECTOR3 edge1 = v1 - v0;

    D3DXVECTOR3 edge2 = v2 - v0;

    D3DXVECTOR3 pvec;

    D3DXVec3Cross( &pvec, &dir, &edge2 );

    // 如果det为0,或接近于零则射线与三角面共面或平行,不相交

//此处det就相当于上面的 

    FLOAT det = D3DXVec3Dot( &edge1, &pvec );

    D3DXVECTOR3 tvec;

    if( det > 0 )

    {

        tvec = orig - v0;

    }

    else

    {

        tvec = v0 - orig;

        det = -det;

    }

    if( det < 0.0001f )

        return FALSE;

    // 计算u并测试是否合法(在三角形内)

    *u = D3DXVec3Dot( &tvec, &pvec );

    if( *u < 0.0f || *u > det )

        return FALSE;

    // Prepare to test V parameter

    D3DXVECTOR3 qvec;

    D3DXVec3Cross( &qvec, &tvec, &edge1 );

    //计算u并测试是否合法(在三角形内)

    *v = D3DXVec3Dot( &dir, &qvec );

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

        return FALSE;

    /*计算t,并把t,u,v放缩为合法值(注意前面的t,v,u不同于算法描述中的相应量,乘了一个系数det),注意:由于该步运算需要使用除法,所以放到最后来进行,避免不必要的运算,提高算法效率*/

    *t = D3DXVec3Dot( &edge2, &qvec );

    FLOAT fInvDet = 1.0f / det;

    *t *= fInvDet;

    *u *= fInvDet;

    *v *= fInvDet;

    return TRUE;

}

3 拾取完成根据获得的中心坐标计算我们关心的常见量,。

根据重心坐标(U,V),我们可以很容易的算出各种相关量比如纹理坐标和交点的差值颜色,假设以纹理坐标为例设V1,V2,V3的纹理坐标分别为T1(tu1,tv1),T2(tu2,tv2),T3(tu3,tv3)则交点的坐标为

IntersectPointTexture = T1 + U(T2-T1) + V(T3-T1)

 

Reference:http://hi.baidu.com/meiruidong/blog/item/e1552dfd10b0328bb801a042.html

你可能感兴趣的:(算法,测试,扩展,float,reference)