3D模型下的鼠标拣选和碰撞检测-射线与圆的相交算法

在实际应用中,常常使用包围球或者包围盒代替物体与拾取射线进行相交测试,假设拾取射线与包围盒或者包围球相交,就认为拾取射线与物体相交了,其实,在3d世界中有很多的计算都是不精确的,只是简单的模拟,近似的操作。因为确实也不需要必须要完全精确。

 

以包围球为例子,包围球可以用球心o与半径r来表示,假设点p在包围球上,即满足条件:

||p-o|| = r

即点p到球心o的距离等于球体半径r,

射线方程为:

p(t)=po+tu

po表示射线的起点,u表示射线的方向,t表示一个标量。因此,可以用这两个向量来定义射线的结构体,

定义如下结构:

struct ray

{

    D3DXVECTOR3 _origin;//射线起点

    D3DXVECTOR3 _derction;//射线方向   

};

假设射线与球体相交,必然存在一个点pt既在射线上又在球面上,将射线方程带入球面方程即可得到:

||pt-o|| = r  //pt在圆面上 那么pt到圆心o的距离等于r

 

||po+tu||-r=o//如果p(t)=po+tu在圆面上,那么满足||po+tu||=r  用这个方程求到的射线上的点pt满足在圆上

显然,要满足这个条件,就是求解方程中t的值,把公式进行转换:

(po+tu-o)2=r2

 

 

 

将最后的公司转化为一元二次方程:

A=u2

B=2u(p0-0)

C=(po-o)2-r2

At2+Bt+c=0

由于射线的方向u是单位向量,因此A=1,对上面的方程求解可以得到以下两个答案:

t=(-B+(B2-4c)的开平方)/2           

 

 

 

 

 

t=(-B-(B2-4c)的开平方)/2  

 

 


void CGame::HandleMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 switch (message)
 {
 case WM_LBUTTONDOWN:
  {
   POINT pt;
   GetCursorPos(&pt);
   ScreenToClient(hWnd, &pt);
   
   Ray ray = CalculateRay(pt.x, pt.y);
   D3DXMATRIX mat;
   m_pDevice->GetTransform(D3DTS_VIEW, &mat);
   D3DXMatrixInverse(&mat, NULL, &mat);
   ray = TransformRay(ray, &mat);

   for(int i=0; i<2; ++i)
   {
    if( CheckIntersection(&ray, &m_boundSphere[i]) )
     m_bRotate[i] = true;
    else
     m_bRotate[i] = false;
   }
  
  }
  break;
 }
}

CGame::Ray CGame::CalculateRay(int x, int y)
{
 float px=0.0f;
 float py=0.0f;
 //获取视口大小
 D3DVIEWPORT9 vp;
 m_pDevice->GetViewport(&vp);
 //获取投影矩阵
 D3DXMATRIX proj;
 m_pDevice->GetTransform(D3DTS_PROJECTION,&proj);
 
 //计算拾取射线
 px=(((2.0f*x)/vp.Width)-1.0f)/proj._11;
 py=(((-2.0f*y)/vp.Height)+1.0f)/proj._22;
 Ray ray;
 ray._origin = D3DXVECTOR3(0.0f,0.0f,0.0f);
 ray._dirction = D3DXVECTOR3(px,py,1.0f);
 return ray;
}

CGame::Ray CGame::TransformRay(Ray ray, D3DXMATRIX* T)
{
 Ray transRay;
 //转换射线的起点
 D3DXVec3TransformCoord(&transRay._origin,&ray._origin,T);
 //转换射线的方向
 D3DXVec3TransformNormal(&transRay._dirction,&ray._dirction,T);
 D3DXVec3Normalize(&transRay._dirction,&transRay._dirction);
 return transRay;
}

BOOL CGame::CheckIntersection(Ray* ray, BoundSphere* sphere)
{
 //计算t0与t1的值
 D3DXVECTOR3 v = ray->_origin-sphere->_center;
 float b = 2.0f*D3DXVec3Dot(&ray->_dirction,&v);
 float c = D3DXVec3Dot(&v,&v)-(sphere->radius*sphere->radius);

 float n = (b*b)-(4.0f*c);
 if (n<0.0f)
 {
  return false;
 }
 n = sqrtf(n);
 float t0 = (-b+n)/2.0f;
 float t1 = (-b-n)/2.0f;
 //判断是否相交
 if (t0>=0||t1>=0)
 {
  return true;
 }
 return false;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值