2018년 7월 1일 일요일

투영 행렬 ( Persfective projection matrix )

■ 동차 좌표

동차좌표란 n+1 차원의 특정한 좌표들이 사영된 n차원에서는 같다(동차)라는 것을 나타내는 좌표계이다..라고 하면 뭔소리인가 싶을것다. 그래서 예를들어


이런식으로 3차원의 점 P를 2차원의 평면( ndc ) 으로 사영시켜보자. 그 결과 P는 P'이 될것이다. 그런데 이것을 자세히보면 사실 P뿐만 아니라 P를 포함한 P' 까지에 있는 모든 임의의 점 R 또한 P'로 사영되는 것을 확인할수가 있다. 즉 사영한다면 n차원의 사영된 임의의 점은 n+1 차원의 특정한 점들과 같는 것이다. 그렇다면 n차원의 사영된 좌표가 n+1의 특정한 좌표들과 같다는 사실을 n+1의 특정한 좌표로 어떻게 나타낼수 있을까 있을까? 그림을 잘보기 바란다. S부터 P까지 이어진 선이 θ 각을 가진 삼각형과 같다. y 값만 고려한다면, 즉 삼각형의 특성에 따라 임의의 점 R의 y/z 는 P의 y'/d 와 같다는 것을 확인할수가있다. 그렇기에, 다른 값들도 고려하여, n+1 의 좌표가 ( r1, r2, r3, r4, ... rn+1 ) 이라 할때 이 좌표를 ( r1/rn+1, r2/rn+1, r3/rn+1, r4/rn+1 ...rn/rn+1) 로 나타낼수가 있고 이를 동차좌표라한다.


■ 동차 좌표와 DirectX

OpenGL 이나 기타 3D API 도 그렇겠지만, DirectX 의 경우는 '이동'이란 아핀 변환을 선형변환으로 이용하기위해 약간의 꼼수(?) 로 3차원을 다루지만 4차원의 동촤 좌표를 쓰고있는 상태이다. 그에따라 DirectX 에서는 (x, y, z, w) 에서 z 가 아닌 마지막 성분인 w를 나머지 성분에 나눠 동차좌표를 나타낸다. 하지만 2차원인 NDC에 투영할시에는 w가 아닌 z값으로 나누어야 된다. 그렇기에 조금있다 보여줄 테지만, 투영 행렬을 만들때 우리는 z값을 w 값으로 보내는 약간의 꼼수를 부릴것이다.

NDC  : normalized device coordinates 의 약자로 (-1, -1)~(1, 1)의 종횡비 1:1 비율을 가진 2차원 좌표계를 의미한다. 뷰 스페이스의 모든 3차원 좌표는 이 2차원 좌표계로 사영된다.

■ 뷰 포트의 종횡비

NDC 란 위에서 설명했듯, 1:1의 종횡비를 가진 정규화된 좌표계를 의미한다. 하지만 결론적으로 이 NDC는 그대로 뷰포트로 나타낼것이 아니라, 뷰포트의 해상도에 맞춰 변환하여 나타낼것이다. 왜냐하면 일반적으로 뷰 포트의 해상도는 정수형태로 NDC 보다 훨씬 큰값이며, 더 중요한것은 종횡비가 NDC 처럼 1:1 이 아닌경우가 더 많다. 해상도에 맞춰 스케일링을 해주는것은 나중문제라고 쳐도, 종횡비는 NDC에 사영시에 꼭 고려를 해줘야한다. 예를들어, 뷰포트가 2:1의 종횡비를 가졌다고 할때, 단순히 NDC의 종횡비에 맞춰 NDC에 물체를 사영시켰다면 x축이 2배로 늘어난 모습으로 뷰 포트에 나타날것이다. 그렇기에 NDC에 뷰 스페이스의 3차원 물체들을 사영시킬시에 종횡비를 고려하여 x축의 경우는 축소하여 사영시킨다. ( 물론 x축을 기준으로 y축 스케일링 해도된다. 하지만 일반적으로 y축을 기준으로 하여 x축을 스케일링한다 )

■ 투영 행렬 만들기

위에서는 NDC로 사영된다 라는 표현을 썻는데, 사실 3D에서 사영이란 3D인 뷰 스페이스에서 근 평면( near plane )으로의 사영을 뜻한다. 하지만 편의를 위해 근평면을 일반적으로 NDC로 놓고 쓰기에  NDC로 사영된다 란 표현을 쓴것이다. 그리고 여기서도 마찬가지로 근 평면을 NDC로 놓을것이다.

P = (x, y z)
P' = (x_ndc, y_ndc, z_ndc)

이 그림을 봐보자. 우리의 목표는 임의의 점 P가 사영된 근평면( ndc )의 P'을 P로 부터 구하는 것이다. 위의 동차좌표에서 설명했듯, 점 P의 y/z 는 P'의 y_ndc/d 와 같고 점 P의 x/z는 P'의 x_ndc/d 와 같다. 그렇기에



( y_ndc/d = y/z 에서 단순이 d를 오른쪽으로 옮긴것 뿐이다 )

위의 식을 도출할수가있고. 더 나아가 종횡비 ( a = width / height ) 까지 고려하면



위의 식을 얻을수가있고 그래서




A와 같은 행렬을 얻을수있다.

아마 여기서 3행 4열의 1값을 왜 넣었는가 궁금해 하는 사람이있을 것인데, 그것은 위에서 설명했다 시피 DirectX 는 4차원 동차좌표 기반이기에 기본적으로 w값을 나누기 때문이다. 즉 w' 에 z값을 넣어놀수 밖에 없다.

( 위의 식에서 w라고 적었는데, 사실 w는 그냥 1이다 )

■ Z값 구하는 식 세우기

단순히 수학적으로 생각했을때, 2차원에 투영된다 함은 z값이 남아있지 않아야 된다는 것이 맞다. 하지만 DirextX에서는 z값을 딴곳에 쓰일일이 존재하므로( z 축 정렬같은 것 ) z값을 ndc와 비슷하게 0~1 사이의 정규화된 값으로 z_ndc에 보존해놓는다. 그렇다면 z값을 어떻게 0~1의 정규화된 값으로 사상시킬수있을까 를 한번 생각해보자.
( 근평면은 0으로 원거리평면은 1로 사상된다 )



우선 시작은 행렬에 z'을 구하는 임의의 식 A 와 B가 있고, p의 z값에는 근평면의 z값인 n을 넣은 상태라고 하자. 여기서 우리의 목표는 n으로부터 B에 대한 식을 구하는 것이고, 그 다음에는 원거리 평면 f로부터 A의 식을 구하는것이다. 그럼 우선 n으로부터 B의 식을 구해보자.



위 행렬의 곱으로부터 z_ndc가 위와 같은 형태가 되는것을 확인할수있을것이다. 그런데 n값은 0으로 사상될것을 우리는 알고있기 때문에,



B는 위와 같다는  것을 알수있다. 그렇다면 이번에는 B를 구했기 때문에



행렬을 이런식으로 바꾸고 점 p의 z값을 원거리 평면의 z값인 f 로 놔보도록하자.



그렇다면 우선 이런식을 구할수있을것이고, 마찬가지로 f는 1로 사상될것을 알기 때문에



A에 대한 식을 위와같이 쓸수있고, 아까 구한 B에 대해서도



이렇게 구할수가있다. 그래서 최종행렬 A는



이렇게 되고 이것이 DirectX 에서 투영행렬이다.


댓글 없음:

댓글 쓰기