Чтобы определить, нажал ли пользователь на любой из моих 3D-объектов, я пытаюсь превратить экранные координаты щелчка в вектор, который затем я использую для проверки того, любой из моих треугольников попал. Для этого я использую метод XMVector3Unproject, предоставляемый DirectX, и я реализую все в C++/CX.Неожиданный результат при непроектировании координаты экрана в DirectX
Проблема, с которой я сталкиваюсь, заключается в том, что вектор, который возникает из-за непрозрачности экранных координат, совсем не так, как я ожидаю. На следующем рисунке показано следующее:
Позиция курсора в момент, когда происходит щелчок (выделено желтым цветом), видна в изометрическом виде слева. Как только я нажимаю, вектор, полученный в результате непроектирования, появляется за моделью, обозначенной на изображениях, как белая линия, проникающая в модель. Поэтому вместо того, чтобы происходить в месте расположения курсора и идти на экран в изометрическом виде, он появляется в совершенно другом положении.
Когда я перемещаю мышь в изометрическом виде по горизонтали, а затем перемещаю мышь по вертикали и нажимаем на рисунок ниже. Все строки на двух изображениях представляют собой векторы, возникающие при нажатии. Модель была удалена для лучшей видимости.
Как видно из приведенного выше изображения, все векторы, кажется, происходят из того же места. Если я изменяю представление и повторяю процесс, то появляется тот же шаблон, но с другим источником векторов.
Вот фрагменты кода, которые я использую, чтобы придумать с этим. Прежде всего я получаю позицию курсора, используя приведенный ниже код и передать его в мой метод «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;
}
}
В конечном счете вычисленный вектор используется методом Intersects пространства имен DirectX :: TriangleTests, чтобы определить, попал ли треугольник. Я опустил код в приведенном выше фрагменте, потому что он не имеет отношения к этой проблеме.
Для визуализации этих изображений я использую матрицу орфографической проекции и камеру, которая может вращаться вокруг своей локальной оси x и y, которая генерирует матрицу вида. Мировая матрица всегда остается прежней, т. Е. Является просто единичной матрицей.
Матрица вида вычисляются следующим образом (на примере в книге 3D Game Programming Фрэнк Луны):
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;
}
Он находится под влиянием двух методов, которые вращают камеру вокруг оси х-и у-осей камеры:
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));
}
Честно говоря, я еще не понимаю проблему, с которой я столкнулся.Я был бы признателен, если бы кто-нибудь с более глубоким пониманием мог дать мне несколько советов относительно того, где мне нужно применить изменения, чтобы вектор, рассчитанный из непроекции, начинался с позиции курсора и перемещался в экран.
Edit 1:
Я предполагаю, что это связано с тем, что моя камера находится в точке (0, 0, 0) в мировых координатах. Камера вращается вокруг своей локальной оси x и y. Из того, что я понимаю, матрица вида, созданная камерой, строит плоскость, на которой проецируется изображение. Если это так, то это объясняет, почему луч находится в каком-то «неожиданном» месте.
Мое предположение заключается в том, что мне нужно переместить камеру из центра так, чтобы она находилась за пределами объекта. Однако, если просто изменить переменную-член m_cameraPosition
камеры, моя модель полностью искажается.
Любой, кто может помочь и хочет помочь?
Я не знаком с аргументом, но, как я увидел в «XMVectorSet (screenX, screenY, 5.0f, 0.0f)», вы использовали 4D-вектор. Таким образом, вы работаете в 4D-пространстве, и то, что вы видите, является его 2D-проекцией. Я правильно понимаю или нет? –
См. Это возможно: http://stackoverflow.com/a/26662222/1306012 –
Я фактически использую 3D-векторы, четвертая часть (0.0f) координат просто означает, что это вектор. Установка его в 1.0f означает, что это точка. – ackh