ack*_*ckh 11 c++ directx 3d projection raycasting
为了能够确定用户是否点击了我的任何3D对象,我试图将点击的屏幕坐标转换为矢量,然后我用它来检查我的三角形是否被击中.为此,我使用DirectX提供的XMVector3Unproject方法,并且我在C++/CX中实现了所有内容.
我面临的问题是,未预测屏幕坐标导致的矢量根本不像我预期的那样.下图说明了这一点:
点击发生时的光标位置(以黄色突出显示)在左侧的等轴测视图中可见.一旦我点击,由于未投影而产生的矢量出现在图像中指示的模型后面,因为白线穿透模型.因此,它不是始于光标位置而是在等轴测视图中进入屏幕,而是出现在完全不同的位置.
当我在等轴测视图中水平移动鼠标时,单击并在此之后垂直移动鼠标并单击下面的图案.两个图像中的所有线代表由点击产生的矢量.该模型已被删除,以获得更好的可见性.
从上面的图像可以看出,所有矢量似乎都来自相同的位置.如果我更改视图并重复该过程,则会出现相同的模式但具有不同的矢量原点.
以下是我用来提出这个问题的代码片段.首先,我使用下面的代码接收光标位置,并将其与绘图区域的宽度和高度一起传递给我的"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;
}
}
Run Code Online (Sandbox Code Playgroud)
"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;
}
}
Run Code Online (Sandbox Code Playgroud)
最终,计算的向量由DirectX :: TriangleTests命名空间的Intersects方法使用,以检测三角形是否被击中.我省略了上面剪辑中的代码,因为它与此问题无关.
为了渲染这些图像,我使用正交投影矩阵和可以围绕其局部x轴和y轴旋转的相机,其产生视图矩阵.世界矩阵总是保持不变,即它只是一个单位矩阵.
视图矩阵计算如下(基于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;
}
Run Code Online (Sandbox Code Playgroud)
它受两种方法的影响,这些方法围绕摄像机的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);
}
Run Code Online (Sandbox Code Playgroud)
世界/模型矩阵和投影矩阵计算如下:
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));
}
Run Code Online (Sandbox Code Playgroud)
坦率地说,我还没有理解我面临的问题.如果有更深入见解的人能给我一些关于我需要应用更改的提示,以便从非投影计算的向量从光标位置开始并移动到屏幕中,我将不胜感激.
编辑1:
我认为这与我的相机在世界坐标中位于(0,0,0)的事实有关.相机围绕其局部x轴和y轴旋转.根据我的理解,相机创建的视图矩阵构建了投影图像的平面.如果是这种情况,它将解释为什么光线处于某种"意外"位置.
我的假设是我需要将相机移出中心,使其位于物体外.但是,如果只是修改m_cameraPosition相机的成员变量,我的模型会完全失真.
那里的任何人都能够并愿意提供帮助吗?
谢谢你的提示,卡皮尔.我尝试了这种XMMatrixLookAtRH方法,但无法使用这种方法改变相机的俯仰/偏航,所以我放弃了这种方法,并想出了自己生成矩阵.
解决了我的问题是XMMatrixTranspose在传递模型,视图和投影矩阵之前使用它们XMVector3Unproject.因此,而不是如下代码
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);
Run Code Online (Sandbox Code Playgroud)
它需要
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);
Run Code Online (Sandbox Code Playgroud)
我不清楚为什么在将矩阵传递给unproject方法之前需要转置矩阵.但是,我怀疑它与我移动相机时遇到的问题有关.这个帖子已经在StackOverflow上描述了这个问题.
我还没有设法解决这个问题.简单地转置视图矩阵并不能解决它.但是,我的主要问题已经解决,我的模型终于可以点击了.
如果有人有任何需要添加的东西,并阐明为什么矩阵需要转置或为什么移动相机扭曲模型,请继续发表评论或答案.
如前所述,即使单击“立即”有效,问题也尚未完全解决。移动相机时模型变形的问题(我怀疑与此相关)仍然存在。我所说的“模型扭曲”的意思可以从下图中看出:
左图显示了当相机位于世界中心(即 (0, 0, 0))时模型的外观,而右图显示了当我沿负 y 轴方向移动相机时会发生什么。可以看出,模型底部变宽,顶部变小,这与我上面提供的链接中描述的行为相同。
我最终为解决这两个问题所做的是:
SetViewMatrix(代码见下文)该SetViewMatrix方法现在如下所示:
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(0, 1) = cameraXAxis.y;
viewMatrix(0, 2) = cameraXAxis.z;
viewMatrix(0, 3) = x;
viewMatrix(1, 0) = cameraYAxis.x;
viewMatrix(1, 1) = cameraYAxis.y;
viewMatrix(1, 2) = cameraYAxis.z;
viewMatrix(1, 3) = y;
viewMatrix(2, 0) = cameraZAxis.x;
viewMatrix(2, 1) = cameraZAxis.y;
viewMatrix(2, 2) = cameraZAxis.z;
viewMatrix(2, 3) = z;
viewMatrix(3, 0) = 0.0f;
viewMatrix(3, 1) = 0.0f;
viewMatrix(3, 2) = 0.0f;
viewMatrix(3, 3) = 1.0f;
m_modelViewProjectionConstantBufferData->view = viewMatrix;
}
Run Code Online (Sandbox Code Playgroud)
所以我只是交换了行和列坐标。请注意,我必须确保在我的ChangeCameraYaw方法之前调用我的方法ChangeCameraPitch。这是必要的,因为否则模型的方向不是我想要的。
还有另一种方法可以使用。row_major我可以将顶点着色器中的关键字与视图矩阵一起使用,而不是通过交换行和列坐标并在将其传递给 XMVector3Unproject 之前转置来转置视图矩阵:
cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
matrix model;
row_major matrix view;
matrix projection;
};
Run Code Online (Sandbox Code Playgroud)
我在这篇博文中想到了这个想法。关键字row_major影响着色器编译器如何解释内存中的矩阵。也可以通过改变顶点着色器中向量*矩阵乘法的顺序来实现相同的效果,即使用pos = mul(view, pos);而不是pos = mul(pos, view);
差不多就这样了。这两个问题确实是相互关联的,但是使用我在这个问题中发布的内容,我能够解决这两个问题,所以我接受我自己的回复作为这个问题的答案。希望它对将来的人有所帮助。