[Direct3D] 투영변환행렬(Projection Matrix)의 유도과정 - 픽킹

3D공간을 나타내는 스크린상에 어느 한 점을 클릭하여,
물체를 선택하는등의 상호작용을 위해서는 '픽킹'(picking)이라는 기술이 필요하다.
픽킹은, 스크린좌표상의 2D좌표를 3D공간상의 좌표(또는 반직선)로 변환하는 작업을 필요로한다.

스크린좌표상의 2D좌표를 3D공간상의 좌표계로 변환하기 위해서는,
Direct3D에서 물체를 이루는 버텍스가 어떤과정을 거쳐서
스크린좌표계(2D좌표)로 변환되는가를 알아볼 필요가있다.

이 번 포스팅에서는 픽킹의 기본지식인 투영변환행렬의 유도과정을 알아보자.



처음으로, 물체는 로컬스페이스에 정의되어진다.
로컬스페이스에 정의되어진 물체는 월드변환에 의해 월드스페이스로 이동한다.
(기본적인 내용이지만, 로컬스페이스나 월드스페이스가 무엇인지 모르는 분은 먼저 이부분에 대해서 공부하길 당부한다.)
월드스페이스에는 렌더링할 대상의 물체들과 카메라, 광원등이 배치된다.
다음으로, 카메라행렬(뷰행렬)에 의해 뷰스페이스로 버텍스들이 변환되어 이동된다.
뷰스페이스로의 변환을 다시한번 복습하자면,
카메라가 원점으로 이동되어지고, 카메라가 원점으로 이동한만큼 모든 물체도 같은 방향과 크기만큼 이동한다.
(따라서, 카메라 기준에서 물체들의 위치는 변함이 없다.)
그 다음, 카메라가 바라보는 방향(+z축방향)과 윗쪽방향, 그리고 카메라의 오른쪽방향의 벡터를 각각 직각이 되게끔하여
카메라의 바라보는방향축, 윗방향축, 오른쪽방향축을 새로운 축으로하는 회전변환을 하게된다.
정리하자면, 카메라를 원점으로 이동시키는 이동변환, 카메라의 각 방향벡터를 새로운 축으로 하도록 회전변환이 이루어진다.
이해를 돕기위해 아래의 그림을 참고하자. ( 출처 : Direct3D9 2008 Nov Document)



카메라변환 다음으로, 투영변환이 이루어진다. 이 투영변환의 부분부터 차근차근 살펴보도록하자.
(우리의 목적은 버텍스들이 변환과정거쳐 최종스크린좌표로 어떻게 표시되는지 살펴보고, 그 역순으로 계산을 하기 위함을 잊지말자)


우선 위의 그림을 보면서 투영변환에 대해서 생각해보자.
위의 그림에서 왼쪽은 카메라좌표계의 xz 평면이고, 이 상태에서 투영변환된것이 오른쪽의 xz 평면이다.
왼쪽그림에서 원점에 카메라가 있고, 카메라가 비출수있는 만큼의 공간이 빨간선이 둘러진 부분이다.
카메라가 무한히 먼곳까지 바라볼 수 없기때문에, 카메라가 비출수있는 공간은 유한하다.
그림에서는, z=near 에서 z=far 까지의 공간이 카메라에 비춰진다.
또한, z=near 은 좁고, z=far 부분은 넓은데, 그 이유는 원근을 나타내기 위함이다.
왼쪽그림에서 빨간선으로 둘러진 부분을 절두체공간이라고 한다.
투영변환은 이 절두체공간을 오른쪽그림과 같은 공간으로 변환하는것을 말한다.
절두체공간의 이해를 돕기위해 아래의 그림을 참고하자.
(출처 : 자작)


Direct3D 에서는 변환과정을 하나의 4x4 행렬로 나타내는데, 투영변환행렬은 아래와 같다.


행렬에 사용된 변수는 아래와 같다.
n : 카메라에서 가장 가까운 절두체 면까지의 거리
f : 카메라에서 가장 먼 절두체 면까지의 거리
Width : 카메라에서 가장 가까운 절두체 면의 너비
Height :
카메라에서 가장 가까운 절두체 면의 높이

이제 구체적으로 위와같은 투영변환행렬의 유도과정을 살펴보자.
투영변환행렬에의해 뷰스페이스의 점 P(x,y,z) 를
(-1,-1,0) ~ (1,1,1) 로 표현되는 공간상의 점 Q(X, Y, Z) 로 변환한다고 하자.
(이 때, 정점의 위치벡터는 4차원 벡터로 표현되는 이유는 생략)


먼저, 점P의 x가 점Q의 X로 변환되는 과정을 살펴보자.



절두체공간의 xz 평면위에 정점P가 위의 그림처럼 위치한다고 할 때,
점P를 z=n 상에 사영시키자. 사영된 점T의 x좌표가 투영변환된 점Q의 X이다.
(다만, 사영된 점T의 z좌표는 조정하여줘야 한다.)

사영된 점 T의 X 좌표는 닮음비를 이용해서 구하자. 계산과정은 아래와 같다.


투영변환에 의해 정점이 이동되는 공간은 (-1,-1,0)~(1,1,1)의 공간이므로,

[-1,1]로 범위를 조정하는 계산을 추가로 해준다. 과정은 아래와 같다.



정리하자면, X 는 아래와 같다.


위의 수식중, 중간의 식이 최종변환된 X의 수식이고,
마지막의 식이 나중에 행렬을 구할 때 계산편의상 X에 z를 곱해둔식이다.
마찬가지로 y에서 Y로의 변환은 아래와 같다. ( 논리는 같으므로 계산은 각자 해보시길.. )


마지막으로, z에서 Z로의 변환을 살펴보자.
z의 변환의 경우 x,y의 변환과는 계산과정이 다르다. 하지만, 어렵지 않다.


위와같은 형식으로 Z를 표기할 때, 



z가 n 일경우 변환된 Z는 0 이고,

z가 f 일 경우 변환된 Z는 1 이 될것이다.

따라서, A와 B를 구하여 대입하면,



위와같은 결과를 얻을 수 있다.


길었다...
정리하자면,


이제, (x, y, z, 1) 이 (zX, zY, zZ, z) 로 변환되도록 행렬을 구성하면 된다.
(X, Y, Z, 1) 를 (zX, zY, zZ, z) 로 편의상 바꿔놓고있음에 유의하자.


이 때, right = - left 이고, top = - bottom 이므로,
행렬의 (3,1)성분과 (3,2)성분이 0 이 된다.
또, right-left = Width,  top - bottom = Height 로 정의하면,
최종 투영변환행렬이 아래와 같이 얻어진다.