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

    技术2022-06-25  50

    在实际应用中,常常使用包围球或者包围盒代替物体与拾取射线进行相交测试,假设拾取射线与包围盒或者包围球相交,就认为拾取射线与物体相交了,其实,在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;}


    最新回复(0)