(DirectX : Basic) 17.(수학) World, View 변환 행렬

Posted by : at

Category : DirectX


좌표계 변환

(y)
 | 
 |     M(x, y)
 |
 |
 |
 |------------------- (x)
 A

원점을 A라 가정하고 x, y축의 단위 백터를 u1, v1이라 가정하자
AM의 벡터를 표현 하는 방법은 AM = x*u1 + y*v1 이라 표현할 수 있다.

여기서 문제는 원점을 다시 표현하고 싶을때 (가령 카메라의 좌표, 각도가 변경되었을 경우)
상대적으로 M의 좌표를 어떻게 표현할꺼냐에 대한 문제이다.
(우리가 갖고있는 값은 M의 좌표 및 새로운 원점의 각도 정도이다.)

좀 정리하자면 우린 AM = x*u1 + y*v1을 알고 있고
원점이 변경된 BM을 구하고 싶은데 x, u1, y, v1을 통해 표현해야한다.

(y)
 | 
 |     M(x, y)\   /
 |             \ /
 |              B
 |
 |------------------- (x)
 A

B가 새로운 원점좌표이고 B를 기준으로 M을 표현(BM)하고자 한다면??
BM = x2*u2 + y2*v2 여기서 (x2, y2)는 B에서 M을 봤을때 상대적 위치 u2, v2는 B원점좌표의 단위 벡터이다.

// u1, v1 단위 벡터를 u2, v2로 표현이 가능하다
// ux, uy, vx, vy는 임의의 값이라 생각하자
u1 = ux*u2 + uy*v2  // u1을 u2, v2 성분의 합으로 표현이 가능
v1 = vx*u2 + vy*v2
// 일단 하려는게 u1, v1으로 표현된 AM을 u2, v2로 표현을 변경하려는게 목적이다.
AM = x*u1 + y*v1
AM = x*(ux*u2 + uy*v2) + y*(vx*u2 + vy*v2)
AM = u2*(x*ux+y*vx) + v2*(x*uy+y*vy)
// 여기까지하면 AM을 u2, v2로 표현이 가능해진다.
// 벡터상으로 볼때
AM = AB + BM  // 이거는 벡터 공식

// 우리가 알고 싶은 것은 BM이기에
BM = BA + AM
BM = AM + BA

// 라고 표현이 가능해진다.
// AM = u2*(x*ux+y*vx) + v2*(x*uy+y*vy) 라고 위에서 증명했기에
BM = u2*(x*ux+y*vx) + v2*(x*uy+y*vy) + BA
// 라고 도출이 된다.

BA또한 B를 기준으로 A를 바라보는 벡터이기에

// 여기서 ux, uy, vx, vy, qx, qy는 알수없음.

BA = qx*u2 + qy*v2
// 라고 표현해보자면
// 우선 BM = u2*x2 + v2*y2라고 정의한걸 잊지말자

BM = u2*(x*ux+y*vx) + v2*(x*uy+y*vy) + qx*u2 + qy*v2
// 좀 정리하자면
BM = u2*(x*ux + y*vx + qx) + v2*(x*uy + y*vy + qy)

// x2 = x*ux + y*vx + qx
// y2 = x*uy + y*vy + qy

// 라고 도출되며, ux, uy, vx, yv, qx, qy만 알면
// 기존 좌표 (x, y)에서 새로운 원점의 좌표 (x2, y2)를 Matrix를 곱하는 형태로 구할 수 있다.

3차원으로 정리하면 아래와 같은 Matrix가 도출된다.

ux uy uz 0
vx vy vz 0
wx wy wz 0
qx qy qz 0

즉, 내 좌표계 [x, y, z, 1]에 Matrix를 곱할경우 변경된 좌표계에서 내 위치(x, y, z)를 구할 수 있다.
그럼 이제 Matrix구하는 방법만 알면되겠군?? (맞다. 사실 Matrix구하는 방법만 알면되고 위의 설명은 이런식으로 좌표계 변환이 이루어진다고 알려주는 것이다.)

추가적으로 [x, y, z, 0] 마지막 항을 0으로 넣을시
qx, qy, qz Translation(위치) 정보가 모두 무시되기에 방향성만 표현하는 Vector가 될수 있음


3D Game Engine에서 사용되는 좌표계 종류

  • Local : 각 3D 오브젝트 하나하나의 좌표계
    • 예를 들어 사람모양을 한 오브젝트가 있다면 발끝을 기준으로 손은 (x, y, z) 머리는 … 이런식
  • World : 전체 월드에 오브젝트를 두고 그리는 좌표계(각 오브젝트에 월드행렬을 곱하면 자신의 위치가 나타난다)
  • View : 카메라의 위치를 기준으로 찍히는 화면의 좌표계
  • Projection(투영) : 카메라에 의해 나타날 부분/나타나지 않을 부분, 작게/크게 표현할 부분을 담당하는 좌표계
  • Screen : 실제로 모니터(해상도)에 그려질 부분의 좌표계

3D Game Engine은 보통 위의 5개의 좌표계를 쓰며 서로서로간 좌표계 변환이 필요하다
사실 이게 설명으로 보면 이해가 어려움. 오히려 수식이 쉽다


일단 좌표계의 각 축 이름을 좀 더 직관적으로 변경하겠다.

(up)
 |     (look)
 |     /
 |    /
 |   /
 |  /
 | /
 |------------------------- (right)

Local <-> World

  • 참고로 헷갈리수 있어 다시 정리하지만, World 좌표는 각 오브젝트마다 있다.
  • 왜냐하면 World행렬이란게 World라는 좌표계에서 해당 오브젝트의 위치를 의미하기 때문이다.

Local 좌표계에서 Rotation만 up축의 a도 돌리고 World 좌표계에서 (x, y, z)만큼 이동한 물체의 World 좌표계 변환을 구해보자

(up)
 |     (look)
 |     /
 |    /
 |   /         O(x, y, z)
 |  /
 | /
 |------------------------- (right)

Local 좌표계 x, y, z에 있던 물체를 World 좌표계로 옮기고 싶다면?

right.x right.y right.z 0
up.x    up.y    up.z    0
look.x  look.y  look.z  0
pos.x   pos.y   pos.z   1
  • right.x, y, z / up.x, y, z / look.x, y, z : 각 방향의 단위 벡터이다.
  • pos.x, y, z : 오브젝트가 이동할 좌표이다.

위 Matrix에서 우리가 하는 부분은 pos(x, y, z)뿐이고 right, up, look은 어떻게 구해야할까?

앞에서 배운 SRT(Scale, Rotation, Translation)가 Matrix의 right, up, look이게 된다.
Scale -> Rotation -> Translation을 하게 되면 자연스럽게 World Matrix가 나오게 된다.

(x, y, z, 1)*[Scale Matrix]*[Rotation Matrix]*[Translation Matrix] 이렇게 하면됨
각 Matrix를 어떻게 구하는지 헷갈린다면 앞의 강의를 참조.

# Scale Matrix
a 0 0 0 
0 b 0 0
0 0 c 0
0 0 0 1
# Rotation Matrix
// z축 회전
cos(b) -sin(b) 0 0
sin(b) cos(b)  0 0
0      0       1 0
0      0       0 1

// x축 회전
1 0      0       0
0 cos(b) -sin(b) 0
0 sin(b) cos(b)  0
0 0      0       1

// y축 회전
cos(b)  0 sin(b) 0
0       1 0      0
-sin(b) 0 cos(b) 0
0       0 0      1
# Translation Matrix
1 0 0 0 
0 1 0 0
0 0 1 0
a b c 1

World <-> View

  • View(카메라)역시 World행렬에 하나의 오브젝트로 있고,
  • View가 직는 영역을 View 좌표계가 된다.
\              /
 \  O(물체)   /
  \          /
    (카메라)

위 그림처럼 보자면 카메라가 오른쪽으로 돌면 물체는 왼쪽으로 간다고 생각
왼쪽으로 돌면 물체는 오른쪽으로 간다고 생각 할 수 있다.

이게 View 좌표계를 구하는 힌트이다.

카메라의 World Matrix는 아래와 같고

right.x right.y right.z 0
up.x    up.y    up.z    0
look.x  look.y  look.z  0
x       y       z       1

사실상 반대로 동작해야하니 역행렬을 구하면 된다.
SRT가 카메라의 World Matrix이고 카메라는 S(Scale)이 없기에 RT가 World Matrix가 된다.
결국 RT의 역행렬을 구하면 되고 (RT)^-1 = T^-1*R^-1
right, up, look은 각 축이기에 직교성이 역행렬이 단순하다

  • 이렇게 생각하면 오히려 쉽다.
  • 카메라가 기준(원점)이 됐다고 생각하고, 카메라의 좌표계에 각 오브젝트를 위치시킨다면?
  • 즉, World좌표계를 Local좌표계(카메라 기준)로 변환해야한다.
// T^-1
1  0  0  0 
0  1  0  0
0  0  1  0
-x -y -z 1

// 이렇게도 표현이 가능
1    0    0    0 
0    1    0    0
0    0    1    0
-c.x -c.y -c.z 1

c(camera) vector이고 c.x는 c의 x축을 의미함 기존의 x와 완전동일

// R^-1
// 이게 가능한게 right, up, look은 orthogonal하기에 가능함
// 일반 Matrix의 Transpos는 그냥 뒤집는다고 되지는 않음(그냥 알고만 있자..)
right.x up.x look.x 0
right.y up.y look.y 0
right.z up.z look.z 0
0      0     0      1
// T^-1*R^-1
right.x  up.x  look.x  0
right.y  up.y  look.y  0
right.z  up.z  look.z  0
-c*right -c*up -c*look 1

이걸 어떻게 구현하지???

걱정하지말자 MS가 미리준비했다.

// View 변환 행렬을 리턴해준다.
XMMATRIX XM_CALLCONV  noexcept XMMatrixLookAtLH(
  FXMVECTOR EyePosition,    // 오브젝트의 현 위치
  FXMVECTOR FocusPosition,  // 카메라가 주시하고 있는 점
  FXMVECTOR UpDirection     // 대략적 up벡터 보통 (0, 1, 0)
);
 (up)
  |    
  |     
  |   
  |           
  |  
  | 
(Eye)-------------------------> (Focus)

간략하게 동작원리를 설명하자면
EyePosition에서 FocusPosition으로 선을 그으면 Look vector가 생성된다.
Up과 Look으로 평면을 만들고 그 평면의 수직선으 Right vector를 만든다.
Look과 Right를 외적해 다시 실질적 Up vector를 생한다.

이러한 방법으로 View Matrix를 생성한다.


About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links