这种方法很简单也很好用,对于应用来说应尽力是用这种方式来实现,毕竟效率比自己写得要高得多。
实际上其实没什么好讲的,大概讲一下函数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