Чтобы определить, щелкнул ли пользователь какой-либо из моих 3D-объектов, я пытаюсь превратить координаты экрана щелчка в вектор, который затем я использую, чтобы проверить, попал ли какой-либо из моих треугольников. Для этого я использую метод XMVector3Unproject, предоставляемый DirectX и Im, реализующий все в С++/CX.
Проблема, с которой сталкивается Im, заключается в том, что вектор, являющийся результатом непроектирования экранных координат, вовсе не таков, как я ожидаю. На следующем рисунке показано следующее:
Позиция курсора во время щелчка (выделено желтым цветом) видна в изометрическом виде слева. Как только я нажимаю, вектор, полученный в результате непроектирования, появляется за моделью, обозначенной на изображениях, как белая линия, проникающая в модель. Поэтому вместо того, чтобы происходить в месте расположения курсора и идти в экран в изометрическом виде, он появляется в совершенно другом положении.
Когда я перемещаю мышь в изометрическом представлении по горизонтали, пока нажимаем, а затем перемещаем мышь по вертикали и нажимаем на рисунок ниже. Все строки на двух изображениях представляют собой векторы, возникающие при нажатии. Модель была удалена для лучшей видимости.
Таким образом, как видно из приведенного выше изображения, все векторы, по-видимому, происходят из одного и того же местоположения. Если я изменяю представление и повторяю процесс, то появляется тот же шаблон, но с другим источником векторов.
Вот фрагменты кода, которые я использую, чтобы придумать это. Прежде всего, я получаю позицию курсора, используя приведенный ниже код, и передаю его методу "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, чтобы определить, попал ли треугольник. Ive опустил код в приведенном выше фрагменте, потому что он не имеет отношения к этой проблеме.
Для рендеринга этих изображений я использую матрицу ортографических проекций и камеру, которая может вращаться вокруг своей локальной оси x и y, которая генерирует матрицу вида. Мировая матрица всегда остается прежней, т.е. Является просто единичной матрицей.
Матрица представления вычисляется следующим образом (на основе примера в программе программирования 3D Фрэнк Лунас):
void Camera::SetViewMatrix()
{
XMFLOAT3 cameraPosition;
XMFLOAT3 cameraXAxis;
XMFLOAT3 cameraYAxis;
XMFLOAT3 cameraZAxis;
XMFLOAT4X4 viewMatrix;
// Keep camera 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));
}
Честно говоря, я еще не понимаю проблему, с которой сталкивается Im. Id быть благодарным, если кто-либо с более глубоким пониманием может дать мне некоторые подсказки относительно того, где мне нужно применить изменения, чтобы вектор, вычисленный из непроектирования, начинался с позиции курсора и перемещался в экран.
Изменить 1:
Я предполагаю, что это связано с тем, что моя камера находится в (0, 0, 0) в мировых координатах. Камера вращается вокруг своей локальной оси x и y. Из того, что я понимаю, матрица вида, созданная камерой, строит плоскость, на которой проецируется изображение. Если это так, то это объясняет, почему луч находится в каком-то "неожиданном" месте.
Мое предположение заключается в том, что мне нужно переместить камеру из центра так, чтобы она находилась вне объекта. Однако, если просто изменить переменную-член m_cameraPosition
камеры, моя модель полностью искажается.
Кто-нибудь может и хочет помочь?