Выбор с помощью DirectX12 Tool Kit - PullRequest
0 голосов
/ 23 апреля 2020

Я сталкиваюсь с проблемами при попытке сделать трехмерные объекты кликабельными с помощью мыши. Для проверки пересечения я использую лучевую отливку. Код I найден , перенесен для моего решения:

Точный выбор

bool RaySphereIntersect(Vector3, Vector3, float);

bool TestIntersection(Matrix projectionMatrix, Matrix viewMatrix, Matrix worldMatrix, Vector3 origin, float radius, int m_screenWidth, int m_screenHeight, int mouseX, int mouseY)
{
float pointX, pointY;
Matrix inverseViewMatrix, translateMatrix, inverseWorldMatrix;
Vector3 direction, rayOrigin, rayDirection;

bool intersect, result;

// Move the mouse cursor coordinates into the -1 to +1 range.
pointX = ((2.0f * (float)mouseX) / (float)m_screenWidth) - 1.0f;
pointY = (((2.0f * (float)mouseY) / (float)m_screenHeight) - 1.0f) * -1.0f;

// Adjust the points using the projection matrix to account for the aspect ratio of the viewport.
pointX = pointX / projectionMatrix._11;
pointY = pointY / projectionMatrix._22;

// Get the inverse of the view matrix.
inverseViewMatrix=XMMatrixInverse(NULL, viewMatrix);

// Calculate the direction of the picking ray in view space.
direction.x = (pointX * inverseViewMatrix._11) + (pointY * inverseViewMatrix._21) + inverseViewMatrix._31;
direction.y = (pointX * inverseViewMatrix._12) + (pointY * inverseViewMatrix._22) + inverseViewMatrix._32;
direction.z = (pointX * inverseViewMatrix._13) + (pointY * inverseViewMatrix._23) + inverseViewMatrix._33;

// Get the origin of the picking ray which is the position of the camera.
// Get the world matrix and translate to the location of the sphere.
// Now get the inverse of the translated world matrix.
inverseWorldMatrix= XMMatrixInverse(NULL, worldMatrix);

// Now transform the ray origin and the ray direction from view space to world space.
rayOrigin=XMVector3TransformCoord(origin, inverseWorldMatrix);
rayDirection=XMVector3TransformNormal(direction, inverseWorldMatrix);

// Normalize the ray direction.
rayDirection=XMVector3Normalize(rayDirection);



// Now perform the ray-sphere intersection test.
intersect = RaySphereIntersect(rayOrigin, rayDirection, radius);

if (intersect == true)
 return true;
else
 return false;

}



 bool RaySphereIntersect(Vector3 rayOrigin, Vector3 rayDirection, float radius)
 {
 float a, b, c, discriminant;
 // Calculate the a, b, and c coefficients.
 a = (rayDirection.x * rayDirection.x) + (rayDirection.y * rayDirection.y) + (rayDirection.z * rayDirection.z);
 b = ((rayDirection.x * rayOrigin.x) + (rayDirection.y * rayOrigin.y) + (rayDirection.z * rayOrigin.z)) * 2.0f;
 c = ((rayOrigin.x * rayOrigin.x) + (rayOrigin.y * rayOrigin.y) + (rayOrigin.z * rayOrigin.z)) - (radius * radius);

// Find the discriminant.
discriminant = (b * b) - (4 * a * c);

// if discriminant is negative the picking ray missed the sphere, otherwise it intersected the sphere.
 if (discriminant < 0.0f)
 return false;
 else
 return true;

 }

Как мне создать сферу

D3DSphere(float x, float y, float z, float radius, float r, float g, float b, float a)  
{
this->x = x;
this->y = y;
this->z = z;

this->radius = radius;

this->shape = GeometricPrimitive::CreateSphere(radius*2.0f);

this->world = Matrix::Identity;
this->world = XMMatrixMultiply(this->world, Matrix::CreateTranslation(x, y, z));
this->index = vsphere;

d3dsphere[vsphere] = this;
vsphere++;
}

Как позвонить raycaster

void Game::LButtonUp(int x, int y)
{
Vector3 eye(camx, camy, camz);
Vector3 at(Vector3::Zero);
m_view = Matrix::CreateLookAt(eye, at, Vector3::UnitY);
for (int i = 0; i < vsphere; i++)
{
if (TestIntersection(m_projection, m_view, d3dsphere[i]->world, eye, d3dsphere[i]->radius, 800, 600, x, y))
{
MessageBoxW(NULL, L"LOL", L"It works", MB_OK);

break;
}
}

}

Ничего не происходит, если щелкнуть, но если я поворачиваю камеру перпендикулярно XOY, иногда, щелкая возле сферы, появляется окно сообщения.


Обновление

MessageBox отображается независимо от угла камеры, и кажется, что он правильно определяет пересечение, но отражается относительно центра окна. Например, если сфера находится в точке (0, window.bottom-20), тогда я получу MessageBox, если щелкнуть в точке (0, 20).

Что если вычисление направления луча выбора неверно, если оно было записано для левой стороны? система, а я использую правша?

1 Ответ

2 голосов
/ 23 апреля 2020

Возможно, из-за системы для правой руки, которая по умолчанию используется в наборе инструментов DirectX, следующий раздел от заклинателя

pointX = ((2.0f * (float)mouseX) / (float)m_screenWidth) - 1.0f;
pointY = (((2.0f * (float)mouseY) / (float)m_screenHeight) - 1.0f) * -1.0f; 

должен быть изменен на

pointX = (((2.0f * (float)mouseX) / (float)m_screenWidth) - 1.0f) * -1.0f;
pointY = (((2.0f * (float)mouseY) / (float)m_screenHeight) - 1.0f); 

Важно

Этот код также будет работать неправильно из-за независимости от глубины, то есть вы можете выбрать сферу, которая находится позади сферы, которую вы щелкаете. Для решения этого я изменил код:

float distance3(float x1, float y1, float z1, float x2, float y2, float z2)
{
float dx=x1-x2;
float dy=y1-y2;
float dz=z1-z2;
return sqrt(dx*dx+dy*dy+dz*dz);
}

void Game::LButtonUp(int x, int y)
{
Vector3 eye(camx, camy, camz);
Vector3 at(Vector3::Zero);
m_view = Matrix::CreateLookAt(eye, at, Vector3::UnitY);
int last_index=-1;
float last_distance=99999.0f;//set the obviously highest value, may happen in your scene

for (int i = 0; i < vsphere; i++)
{
if (TestIntersection(m_projection, m_view, d3dsphere[i]->world, eye, d3dsphere[i]->radius, 800, 600, x, y))
{
float distance=distance3(camx,camy,camz, d3dsphere[i]->x, d3dsphere[i]->y, d3dsphere[i]->z);
if(distance<last_distance)
{
last_distance=distance;
last_index=i;
}
}
}

d3dsphere[last_index];//picked sphere 
}
...