c++ - 在 DirectX 中取消投影屏幕坐标时出现意外结果

标签 c++ 3d directx projection raycasting

为了能够确定用户是否点击了我的任何 3D 对象,我试图将点击的屏幕坐标转换为一个 vector ,然后我用它来检查是否有任何三角形被点击。为此,我使用了 XMVector3Unproject DirectX 提供的方法,我正在用 C++/CX 实现所有内容。

我面临的问题是,取消投影屏幕坐标所产生的 vector 与我预期的完全不同。下图说明了这一点:

Vector resulting from unprojection 单击发生时的光标位置(以黄色突出显示)在左侧的等距 View 中可见。我一单击,取消投影产生的 vector 就会出现在图像中指示的模型后面,作为穿透模型的白线。因此,它不是从光标位置开始并进入等距 View 中的屏幕,而是出现在一个完全不同的位置。

当我在等距 View 中水平移动鼠标并单击时,然后垂直移动鼠标并单击下面的图案。两张图片中的所有线条都代表点击产生的 vector 。为了更好的可见性,模型已被删除。

Vector resulting from unprojection originate in same location 从上图可以看出,所有 vector 似乎都来自同一位置。如果我更改 View 并重复该过程,则会出现相同的图案,但 vector 的原点不同。

Different perspective, different origin of the vectors

以下是我用来提出这个问题的代码片段。首先,我使用以下代码接收光标位置,并将其与绘图区域的宽度和高度一起传递给我的“SelectObject”方法:

void Demo::OnPointerPressed(Object^ sender, PointerEventArgs^ e)
{
  Point currentPosition = e->CurrentPoint->Position;

  if(m_model->SelectObject(currentPosition.X, currentPosition.Y, m_renderTargetWidth, m_renderTargetHeight))
  {
    m_RefreshImage = true;
  }
}

“SelectObject”方法如下所示:

bool Model::SelectObject(float screenX, float screenY, float screenWidth, float screenHeight)
{
  XMMATRIX projectionMatrix = XMLoadFloat4x4(&m_modelViewProjectionConstantBufferData->projection);
  XMMATRIX viewMatrix       = XMLoadFloat4x4(&m_modelViewProjectionConstantBufferData->view);
  XMMATRIX modelMatrix      = XMLoadFloat4x4(&m_modelViewProjectionConstantBufferData->model);

  XMVECTOR v = XMVector3Unproject(XMVectorSet(screenX, screenY, 5.0f, 0.0f),
                                  0.0f,
                                  0.0f,
                                  screenWidth,
                                  screenHeight,
                                  0.0f,
                                  1.0f,
                                  projectionMatrix,
                                  viewMatrix,
                                  modelMatrix);

  XMVECTOR rayOrigin = XMVector3Unproject(XMVectorSet(screenX, screenY, 0.0f, 0.0f),
                                          0.0f,
                                          0.0f,
                                          screenWidth,
                                          screenHeight,
                                          0.0f,
                                          1.0f,
                                          projectionMatrix,
                                          viewMatrix,
                                          modelMatrix);

  // Code to retrieve v0, v1 and v2 is omitted

  if(Intersects(rayOrigin, XMVector3Normalize(v - rayOrigin), v0, v1, v2, depth))
  {
    return true;
  }
}

最终计算出的 vector 被 Intersects 使用DirectX::TriangleTests 命名空间的方法来检测三角形是否被击中。我省略了上面片段中的代码,因为它与这个问题无关。

为了渲染这些图像,我使用了一个正交投影矩阵和一个可以围绕其生成 View 矩阵的局部 x 轴和 y 轴旋转的相机。世界矩阵始终保持不变,即它只是一个单位矩阵。

View 矩阵计算如下(基于 Frank Luna 的书 3D 游戏编程中的示例):

void Camera::SetViewMatrix()
{
  XMFLOAT3 cameraPosition;
  XMFLOAT3 cameraXAxis;
  XMFLOAT3 cameraYAxis;
  XMFLOAT3 cameraZAxis;

  XMFLOAT4X4 viewMatrix;

  // Keep camera's axes orthogonal to each other and of unit length.
  m_cameraZAxis = XMVector3Normalize(m_cameraZAxis);
  m_cameraYAxis = XMVector3Normalize(XMVector3Cross(m_cameraZAxis, m_cameraXAxis));

  // m_cameraYAxis and m_cameraZAxis are already normalized, so there is no need
  // to normalize the below cross product of the two.
  m_cameraXAxis = XMVector3Cross(m_cameraYAxis, m_cameraZAxis);

  // Fill in the view matrix entries.
  float x = -XMVectorGetX(XMVector3Dot(m_cameraPosition, m_cameraXAxis));
  float y = -XMVectorGetX(XMVector3Dot(m_cameraPosition, m_cameraYAxis));
  float z = -XMVectorGetX(XMVector3Dot(m_cameraPosition, m_cameraZAxis));

  XMStoreFloat3(&cameraPosition, m_cameraPosition);
  XMStoreFloat3(&cameraXAxis   , m_cameraXAxis);
  XMStoreFloat3(&cameraYAxis   , m_cameraYAxis);
  XMStoreFloat3(&cameraZAxis   , m_cameraZAxis);

  viewMatrix(0, 0) = cameraXAxis.x;
  viewMatrix(1, 0) = cameraXAxis.y;
  viewMatrix(2, 0) = cameraXAxis.z;
  viewMatrix(3, 0) = x;

  viewMatrix(0, 1) = cameraYAxis.x;
  viewMatrix(1, 1) = cameraYAxis.y;
  viewMatrix(2, 1) = cameraYAxis.z;
  viewMatrix(3, 1) = y;

  viewMatrix(0, 2) = cameraZAxis.x;
  viewMatrix(1, 2) = cameraZAxis.y;
  viewMatrix(2, 2) = cameraZAxis.z;
  viewMatrix(3, 2) = z;

  viewMatrix(0, 3) = 0.0f;
  viewMatrix(1, 3) = 0.0f;
  viewMatrix(2, 3) = 0.0f;
  viewMatrix(3, 3) = 1.0f;

  m_modelViewProjectionConstantBufferData->view = viewMatrix;
}

它受两种围绕相机的 x 轴和 y 轴旋转相机的方法的影响:

void Camera::ChangeCameraPitch(float angle)
{
  XMMATRIX rotationMatrix = XMMatrixRotationAxis(m_cameraXAxis, angle);

  m_cameraYAxis = XMVector3TransformNormal(m_cameraYAxis, rotationMatrix);
  m_cameraZAxis = XMVector3TransformNormal(m_cameraZAxis, rotationMatrix);
}

void Camera::ChangeCameraYaw(float angle)
{
  XMMATRIX rotationMatrix = XMMatrixRotationAxis(m_cameraYAxis, angle);

  m_cameraXAxis = XMVector3TransformNormal(m_cameraXAxis, rotationMatrix);
  m_cameraZAxis = XMVector3TransformNormal(m_cameraZAxis, rotationMatrix);
}

世界/模型矩阵和投影矩阵计算如下:

void Model::SetProjectionMatrix(float width, float height, float nearZ, float farZ)
{
  XMMATRIX orthographicProjectionMatrix = XMMatrixOrthographicRH(width, height, nearZ, farZ);

  XMFLOAT4X4 orientation = XMFLOAT4X4
  (
    1.0f, 0.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 1.0f
  );

  XMMATRIX orientationMatrix = XMLoadFloat4x4(&orientation);

  XMStoreFloat4x4(&m_modelViewProjectionConstantBufferData->projection, XMMatrixTranspose(orthographicProjectionMatrix * orientationMatrix));
}

void Model::SetModelMatrix()
{
  XMFLOAT4X4 orientation = XMFLOAT4X4
  (
    1.0f, 0.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 1.0f
  );

  XMMATRIX orientationMatrix = XMLoadFloat4x4(&orientation);

  XMStoreFloat4x4(&m_modelViewProjectionConstantBufferData->model, XMMatrixTranspose(orientationMatrix));
}

坦率地说,我还不明白我面临的问题。如果任何有更深入见解的人能给我一些提示,告诉我需要在哪里应用更改,以便从非投影计算的 vector 从光标位置开始并移动到屏幕中,我将不胜感激。

编辑 1:

我认为这与我的相机位于世界坐标中的 (0, 0, 0) 这一事实有关。相机围绕其局部 x 轴和 y 轴旋转。据我了解,相机创建的 View 矩阵构建了图像投影到的平面。如果真是这样,就可以解释为什么射线位于某种“意外”位置。

我的假设是我需要将相机移出中心,以便它位于对象的外部。但是,如果简单地修改相机的成员变量 m_cameraPosition,我的模型就会完全变形。

有没有人能够并愿意提供帮助?

最佳答案

感谢您的提示,卡 PIL 。我尝试了 XMMatrixLookAtRH 方法,但无法使用该方法更改相机的俯仰/偏航,因此我放弃了该方法并想出了自己生成矩阵。

解决我问题的方法是在将模型、 View 和投影矩阵传递给 XMVector3Unproject 之前使用 XMMatrixTranspose 对其进行转置。因此,而不是让代码如下

  XMMATRIX projectionMatrix = XMLoadFloat4x4(&m_modelViewProjectionConstantBufferData->projection);
  XMMATRIX viewMatrix       = XMLoadFloat4x4(&m_modelViewProjectionConstantBufferData->view);
  XMMATRIX modelMatrix      = XMLoadFloat4x4(&m_modelViewProjectionConstantBufferData->model);

  XMVECTOR rayBegin = XMVector3Unproject(XMVectorSet(screenX, screenY, -m_boundingSphereRadius, 0.0f),
                                         0.0f,
                                         0.0f,
                                         screenWidth,
                                         screenHeight,
                                         0.0f,
                                         1.0f,
                                         projectionMatrix,
                                         viewMatrix,
                                         modelMatrix);

应该是

  XMMATRIX projectionMatrix = XMMatrixTranspose(XMLoadFloat4x4(&m_modelViewProjectionConstantBufferData->projection));
  XMMATRIX viewMatrix       = XMMatrixTranspose(XMLoadFloat4x4(&m_modelViewProjectionConstantBufferData->view));
  XMMATRIX modelMatrix      = XMMatrixTranspose(XMLoadFloat4x4(&m_modelViewProjectionConstantBufferData->model));

  XMVECTOR rayBegin = XMVector3Unproject(XMVectorSet(screenX, screenY, -m_boundingSphereRadius, 0.0f),
                                         0.0f,
                                         0.0f,
                                         screenWidth,
                                         screenHeight,
                                         0.0f,
                                         1.0f,
                                         projectionMatrix,
                                         viewMatrix,
                                         modelMatrix);

我不完全清楚为什么我需要在将矩阵传递给 unproject 方法之前转置它们。但是,我怀疑这与我移动相机时遇到的问题有关。 this 在 StackOverflow 上已经描述了这个问题。发帖。

我还没有设法解决那个问题。简单地转置 View 矩阵并不能解决它。不过,我的主要问题已经解决,我的模型终于可以点击了。

如果有人要补充说明为什么需要转置矩阵或为什么移动相机会扭曲模型,请继续发表评论或回答。

关于c++ - 在 DirectX 中取消投影屏幕坐标时出现意外结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37844196/

相关文章:

c++ - 删除在另一个对象的构造函数中创建的 "new object"?

Java3D 形状只能从一个角度看到

c++ - 制作模型加载器 : What to do after reading the vertices and texture?

node.js - 将 DirectX Hwnd 嵌入到 NodeJS 中?

c++ - 简单的运算符重载不起作用

.net - 如何为 MSHTML v9 编译 .dll。我目前收到 100 多个 "MIDL 2035: constant expression expected"错误

c++ - 可以将套接字从非阻塞更改为阻塞然后再次非阻塞吗?

c++ - 制作 3d 视频游戏需要哪些关键工具和步骤?

java - Android OpenGL ES 2.0 : Cube model is not only distorted (perspective is wrong?),但面部加载不正确(顶点不正确?)

visual-c++ - 未解析的外部符号_IID_IDXGIAdapter4