我正在尝试在 3D 世界中实现框选择。 基本上,单击,按住鼠标,然后取消按下鼠标,得到一个框,然后选择框。 首先,我试图弄清楚如何获取 3d 点击的坐标。
我有光线选择,那就是没有得到正确的坐标(获取原点和方向)。 无论屏幕的 X/Y 是什么,它都会返回相同的原点(尽管方向不同)。
我也试过:
D3DXVECTOR3 ori = D3DXVECTOR3(sx, sy, 0.0f);
D3DXVECTOR3 out;
D3DXVec3Unproject(&out, &ori, &viewPort, &projectionMat, &viewMat, &worldMat);
它得到同样的东西,无论坐标如何(并且是错误的),坐标都非常接近。这几乎就像归还眼睛,而不是实际的世界坐标。
如何使用 directx 9c 将 2d 屏幕坐标转换为 3d?
这在 Direct3D 中称为拾取,要在 3D 空间中选择模型,主要需要 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 ;
}
事实证明,我以错误/相反的方式处理了这个问题。 最终将2D转换为3D没有意义。 但事实证明,将顶点从 3D 转换为 2D,然后查看 2D 框内是否是正确的答案!