04
27

3D 정점은 로컬 -> 월드 -> 뷰 -> 투영 -> 화면 좌표로 최종 변환이 된다.

마우스 피킹에서는 화면 좌표 -> 투영 - > 뷰 -> 월드  역순으로 돌아와야 한다.

화면 좌표계에서 있는 마우스 클릭, 그 위치에서 월드 공간에서의 오브젝트를 체킹 하는 것이다.

 

참고로, 투영 정점 -> 화면 좌표(스크린 좌표)로 변환될 때 뷰포트 행렬이 사용된다.

뷰포트 행렬

더보기

뷰포트 행렬은 화면에 벗어나는 선분이나 오브젝트를 클리핑 하는 영역을 의미한다.

뷰포트 행렬은 -1~1 범위를 0~1로 바꾸는 작업과 같음

0~1로 된 것을 이제 화면 좌표계가

0~화면 최대 크기(width)  0~화면 최대 높이(height)

로 되어야 하기 때문에, 뷰포트 가로 및 세로를 크기를 곱하는 작업이다.

뷰포트 행렬의 공식은 다음과 같다.

 

 

화면 좌표를 투영 좌표로 변환

화면 좌표 -> 투영 - > 뷰 -> 월드  역순으로 돌아와야 한다.

우선 화면 좌표에서 투영 좌표로 변환하기 위해서

위에 뷰포트 행렬의 역행렬을 사용하면 되지만, 

공식이 있다. 

vProj.x = (((2.0f * ptCursor.x - 2.0f * g_rtClient.left) / g_rtClient.right) - 1);
vProj.y = -(((2.0f * ptCursor.y - 2.0f * g_rtClient.top) / g_rtClient.bottom) - 1);
vProj.z = 1.0f;

뷰 포트 영역이 윈도우의 전체 클라이언트와 동일하다면 뷰 포트의 시작 시점은 X = Y = 0 이 된다.

 

투영 좌표를 뷰 좌표로 변환

투영 좌표를 구했으면 투영 행렬의 역행렬을 곱해 뷰 정점으로 변환해야 한다.

다만, 투영 행렬은 _11, _22 성분만이 정점에 영향을 미쳐서 

방금 구한 투영 좌표에 카메라의 투영 행렬 _11, _22 성분만 나누면 같은 결과가 나온다.

vView.x = vProj.x / m_pCamera->m_matProj._11;
vView.y = vProj.y / m_pCamera->m_matProj._22;
vView.z = vProj.z;

 

뷰 좌표에서 월드 좌표로 변환

카메라 뷰 행렬의 역행렬을 곱하면 된다.

카메라에서 피킹을 하는 것이기 때문에, 뷰 좌표계에서

레이의 시작은 무조건 (뷰 좌표에서) 원점인 0,0,0이다.

//뷰좌표계에서는 시작은 무조건 0,0,0
KMatrix matInverse;
D3DKMatrixInverse(&matInverse, nullptr, &m_pCamera->m_matView);

//카메라의 월드 좌표의 레이를 만든다.
TRay ray;
ray.direction.x = vView.x * matInverse._11 + vView.y * matInverse._21 + vView.z * matInverse._31;
ray.direction.y = vView.x * matInverse._12 + vView.y * matInverse._22 + vView.z * matInverse._32;
ray.direction.z = vView.x * matInverse._13 + vView.y * matInverse._23 + vView.z * matInverse._33;

ray.position.x = matInverse._41;
ray.position.y = matInverse._42;
ray.position.z = matInverse._43;

위에서 직접 정점을 행렬로 곱한 코드이다. 다이렉트 내장 함수 써도 무방하다.

 

D3DXVec3Normalize(&ray.direction, &ray.direction);

이제 레이는 월드 좌표의 정점이 되었다.

정규화를 하고, 이 레이로 박스 AABB, OBB, 구, 평면 등 교점 계산을 해

충돌 처리, 마우스 피킹을 하면 된다.  

 

레이와 면의 충돌 체크를 하면, 모든 오브젝트를 검출할 수 있지만,

많은 연산양을 요구해서 보통 콜라이더, 바운딩 박스와 같이 박스로 많이

하기도 한다. AABB는 축이 회전이 되지 않는 고정된 박스이고 OBB는 회전되는 박스이다.

 

박스와 레이의 충돌

레이와 AABB박스의 교점을 4개를 구한다.

(Y에 대해서 최소 Y값 최대 Y값, X에 대해서 최소 X값 최대 X값)

위의 사진에서 충돌 났을 때와 안 났을 때와 차이는

왼쪽은 MinX < MaxY 일 때 충돌이 안되고

오른쪽은 (MinY <MaxX && MaxY> MinX)일 때 충돌임

 

 

하지만 효율을 위해 vMax의 xyz값 중 가장 최소 거리 값,

vMin의 xyz값 중 가장 최대 거리 값을 찾아 vMax의 최소 거리 값이 크면 충돌이다.

 

결과

마우스 피킹

바운딩박스(박스 콜라이더)를 사용해서

오브젝트를 검출해서 선택된 오브젝트를 출력하는 모습

 

COMMENT