뷰 스페이스란?
'뷰 행렬은 카메라의 위치와 방향을 기반으로 3D공간을 카메라의 시점으로 변환하는 행렬'이다..
이 행렬이 필요한 이유는 월드 공간은 매우 넓은 영역인데 이 영역을 다 보여준다면 프레임이 쭉쭉 떨어질 것이다.
그래서 가상의 카메라 개념을 넣어서 카메라를 중심으로 화면이 보이도록 하겠다라는 것이다.
이렇게 하기 위해서는 '월드 공간'에 있던 여러 오브젝트들을 카메라를 중심으로하는 좌표계로 변환 시켜야 하는데
이 공간을 view space라 한다. (view space로 변환 시켜주는 행렬이 View Matrix이다)
이 과정을 짧게 정리하면 카메라를 월드 시스템의 원점으로 변환하고, 카메라가 양의 Z축을 보도록 회전 시켜야 한다는 것이다. 이때 월드에 대한 관점이 바뀌지 않도록 하기 위해서는 카메라에 맞추어 월드내의 모든 기하물체를 변환해야한다.
View Matrix 구현
그럼이제 실제 구현을 해보자.
DX9에서 D3DXMatrixLookAtLH라는 함수를 제공한다. 이 함수를 통해서 뷰 스페이스 변환 행렬을 만들 수도 있지만 직접 만들어보자.
아까 '카메라를 월드 시스템의 원점으로 변환하고 카메라가 양의 Z축을 보도록 회전'시켜야 한다고 했다.
이해하기 쉽게 생각하면 카메라 좌표를 월드 좌표와 포개지도록 조정하는 것이다.
만약 (18, 8, 0)의 위치에 Eye(카메라 위치)가 있다면 이것을 (0, 0, 0)으로 보내야 하니 (-18, -8, 0)이 이동 행렬의 역행렬이 될 것이다.
표현하면 아래와 같다. (Tx(18), Ty(8), Tz(0))
카메라가 Tx, Ty, Tz만큼 이동한것의 역은 당연히 -Tx, -Ty, -Tz일 것이다. (이렇게 원점의 위치로 되돌림)
이 것을 월드에 위치한 모든 오브젝트에 적용 시킨다.
만약 (10, 2, 0)의 위치의 어떤 정점에 위의 행렬을 적용시키면 (-8, -6, 0, 1)인 점을 가지게 될 것이다.
그리고 카메라 회전행렬의 역행렬을 보도록 하자.
회전행렬의 역행렬은 전치행렬과 같다. 따라서 위의 카메라의 회전 행렬의 역행렬은 아래 처럼 바뀐다.
월드 변환행렬은 SRT로 진행을 하는데 카메라의 경우 크기는 아무 의미가 없기 때문에 항등행렬이라 가정하여 구해보도록하자.
먼저 이동을 시키고 회전을 시켜야 하기때문에 아래처럼 구성이 된다.
그런데 위의 View Space변환 행렬은 SRT 행렬의 역행렬 이기도 하다.
즉, 카메라의 월드 변환행렬의 역행렬은 뷰 스페이스 변환 행렬이 되는 것이고 뷰 스페이스 변환 행렬의 역행렬은 카메라 월드 변환 행렬이 되는 것이다.
위의 수식을 써보면 아래와 같이 전개가 된다.
마지막 T^-1의 3행 성분과 R^-1의 0열 성분을 곱하는 것을 좀더 우아하게 표현한것이
-dot(T, X)가 된다.
이제 코드를 보도록 하자.
enum
{
RIGHT,
UP,
LOOK,
};
void MyViewMarix(D3DXMATRIX* pOut, const D3DXVECTOR3* pEye, const D3DXVECTOR3* pAt, const D3DXVECTOR3* pUp)
{
// 항등 행렬로 초기화
D3DXMatrixIdentity(pOut);
D3DXVECTOR3 vAxis[4];
// z축 방향 방향벡터를 구한다.
vAxis[LOOK] = *pAt - *pEye;
D3DXVec3Normalize(&vAxis[LOOK], &vAxis[LOOK]);
// x축 방향 벡터를 구한다.
D3DXVec3Cross(&vAxis[RIGHT], pUp, &vAxis[LOOK]);
D3DXVec3Normalize(&vAxis[RIGHT], &vAxis[RIGHT]);
// y축 방향 벡터를 구한다.
D3DXVec3Cross(&vAxis[UP], &vAxis[LOOK], &vAxis[RIGHT]);
D3DXVec3Normalize(&vAxis[UP], &vAxis[UP]);
// 위에서 만든 기저벡터로 Matrix로 재구성
for (int i = 0; i < LOOK + 1; ++i) memcpy(&(pOut->m[i][0]), &vAxis[i], sizeof(_vec3));
// 직교 행렬(A)의 전치행렬(B)과 직교행렬의 역행렬(C)는 동일하다.
D3DXMatrixTranspose(pOut, pOut);
/*
카메라 좌표(Eye)를 새로구한 축기준으로 이동시킨다.
원점 이동이기 떄문에 -1를 곱해주고 전치행렬을 곱한다.
즉 내적를 진행한다.
*/
for (int i = 0; i < LOOK + 1; ++i) pOut->m[3][i] = -D3DXVec3Dot(pEye, &vAxis[i]);
}
위처럼 구하는 방법도 있고 아래의 방법도 있다. (둘다 똑같은 코드이다)
void MakeViewMatrix(_matrix* pOut, const _vec3* pEye, const _vec3* pAt, const _vec3* pUp)
{
D3DXMatrixIdentity(pOut);
_vec3 vRight = {1.f, 0.f, 0.f};
_vec3 vUp = {0.f, 1.f, 0.f};
_vec3 vLook = {0.f, 0.f, 1.f};
_vec3 vPos = {0.f, 0.f, 0.f};
// Look
vLook = *pAt - *pEye;
D3DXVec3Normalize(&vLook, &vLook);
// Right
D3DXVec3Cross(&vRight, pUp, &vLook);
D3DXVec3Normalize(&vRight, &vRight);
// Up
D3DXVec3Cross(&vUp, &vLook, &vRight);
D3DXVec3Normalize(&vUp, &vUp);
// pos
vPos = *pEye;
MakeTransformMatrix(pOut, &vRight, &vUp, &vLook, &vPos);
D3DXMatrixInverse(pOut, NULL, pOut);
}
결국 이렇게 만든 View Space 변환 행렬(매개 변수로 받은 pOut)을 모든 물체에 적용하면 View Space 변환이 이루어 지는 것이다
'컴퓨터 그래픽스 > DirectX' 카테고리의 다른 글
NavMesh 만들기 (2) | 2024.12.07 |
---|---|
[Animation] 애니매이션을 만들어보기 (정리글) (1) | 2024.12.06 |
[Animation] 스키닝 애니매이션이란? (Skinning Animation) (1) | 2024.11.10 |
[DX] 렌더링 파이프 라인 (2) | 2023.11.30 |
[DX] Multi Sampling (0) | 2023.08.23 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!