0

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

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

我也试过:

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

它得到了同样的结果,无论坐标是什么,坐标都非常接近(并且是错误的)。这几乎就像返回眼睛,而不是实际的世界坐标。

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

4

2 回答 2

5

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

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

生成拾取射线

当我们在屏幕上点击鼠标时(假设屏幕上的点是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 ;
}
于 2013-10-26T05:26:58.777 回答
0

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

于 2015-07-24T18:05:17.317 回答