2D屏幕坐标到3D位置Directx 9 / Box选择

时间:2013-10-21 01:46:41

标签: graphics 3d directx raytracing projection-matrix

我正在尝试在3d世界中实现框选择。基本上,单击,按住鼠标,然后取消鼠标,获取一个框,然后选择框。首先,我想弄清楚如何获得3d中点击的坐标。

我有raypicking,而且没有获得正确的坐标(得到原点和方向)。无论屏幕的X / Y是什么(尽管方向不同),它都会保持返回相同的原点。

我也试过了:

D3DXVECTOR3 ori = D3DXVECTOR3(sx, sy, 0.0f);
D3DXVECTOR3 out;
D3DXVec3Unproject(&out, &ori, &viewPort, &projectionMat, &viewMat, &worldMat);

它得到同样的东西,坐标彼此非常接近,无论坐标是什么(并且是错误的)。它几乎就像回归眼睛,而不是现实世界坐标。

如何使用directx 9c将2d屏幕坐标转换为3d?

2 个答案:

答案 0 :(得分:6)

这在Direct3D中称为选择,要在3D空间中选择模型,您主要需要3个步骤:

  1. 生成拣选光线
  2. 在同一空间中变换拾取光线和要拾取的模型
  3. 对拾取光线和模型进行交叉测试
  4. 生成拾取光线

    当我们在屏幕上单击鼠标时(比如屏幕上的点是s),当区域上的方框项目围绕投影窗口上的点s时,将选择模型。 因此,为了生成具有给定屏幕坐标(x,y)的拾取光线,首先我们需要将(x,y)变换到投影窗口,这可以通过视口变换的反转过程来完成。另一件事是投影窗口上的点是由项目矩阵缩放的,所以我们应该用比例因子来划分它。 在DirectX中,摄像机始终位于原点,因此拾取光线从原点开始,投影窗口是近剪裁平面(z = 1)。这是代码在下面所做的。

    Ray CalcPickingRay(LPDIRECT3DDEVICE9 Device, int screen_x, int screen_y) 
    {
         float px = 0.0f;
         float py = 0.0f;
    
         // Get viewport
         D3DVIEWPORT9 vp;
         Device->GetViewport(&vp);
    
         // Get Projection matrix
         D3DXMATRIX proj;
         Device->GetTransform(D3DTS_PROJECTION, &proj);
    
         px = ((( 2.0f * screen_x) / vp.Width)  - 1.0f) / proj(0, 0);
         py = (((-2.0f * screen_y) / vp.Height) + 1.0f) / proj(1, 1);
    
         Ray ray;
         ray._origin    = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
         ray._direction = D3DXVECTOR3(px, py, 1.0f);
    
         return ray;
    }
    

    将拾取光线和模型转换为相同的空间。

    我们总是通过将拾取光线转换为世界空间来获得此效果,只需获取视图矩阵的反转,然后将反转矩阵应用于拾取光线。 //将光线从视图空间转换为世界空间

    void TransformRay(Ray* ray, D3DXMATRIX* invertViewMatrix)
    {
        // transform the ray's origin, w = 1.
        D3DXVec3TransformCoord(
            &ray->_origin,
            &ray->_origin,
            invertViewMatrix);
    
        // transform the ray's direction, w = 0.
        D3DXVec3TransformNormal(
            &ray->_direction,
            &ray->_direction,
            invertViewMatrix);
    
        // normalize the direction
        D3DXVec3Normalize(&ray->_direction, &ray->_direction);
    }
    

    做交叉测试

    如果以上所有内容都很好,您现在可以进行相交测试。这是一个光线盒交叉点,因此您可以使用D3DXboxBoundProbe函数。您可以更改框的可视模式以查看拾取是否真的有效,例如,如果D3DXboxBoundProbe返回TRUE,则将填充模式设置为实线或线框。

    您可以响应WM_LBUTTONDOWN执行拾取。

    case WM_LBUTTONDOWN:
    {
        // Get screen point
        int iMouseX = (short)LOWORD(lParam) ;
        int iMouseY = (short)HIWORD(lParam) ;
    
        // Calculate the picking ray
        Ray ray = CalcPickingRay(g_pd3dDevice, iMouseX, iMouseY) ;
    
        // transform the ray from view space to world space
        // get view matrix
        D3DXMATRIX view;
        g_pd3dDevice->GetTransform(D3DTS_VIEW, &view);
    
        // inverse it
        D3DXMATRIX viewInverse;
        D3DXMatrixInverse(&viewInverse, 0, &view);
    
        // apply on the ray
        TransformRay(&ray, &viewInverse) ;
    
        // collision detection
        D3DXVECTOR3 v(0.0f, 0.0f, 0.0f);
        if(D3DXSphereBoundProbe(box.minPoint, box.maxPoint &ray._origin, &ray._direction))
        {
            g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
        }
        break ;
    }
    

答案 1 :(得分:0)

事实证明,我正在以错误/相反的方式处理问题。将2D转换为3D最终没有意义。但事实证明,将顶点从3D转换为2D,然后查看2D框内是否是正确的答案!