[OpenGL ES를 이용한 3차원 컴퓨터 그래픽스 입문] 5. 정점 처리
이 글은 제 개인적인 공부를 위해 작성한 글입니다. 틀린 내용이 있을 수 있고, 피드백은 환영합니다.
개요
GPU는 폴리곤 메시를 입력으로 받아서, 각각의 3차원 폴리곤을 스크린에 그려질 2차원 형태로 바꾸고, 이 2차원 폴리곤 내부를 차지하는 픽셀(pixel)들의 색상을 결정한다. 이 픽셀들은 컬러 버퍼(color buffer)에 기록되는데, 이는 스크린에 나타날 픽셀 전체를 저장하는 메모리 공간을 말한다. 컬러 버퍼는 주기적으로 스크린으로 복사된다.
GPU 렌더링은 파이프라인(pipeline) 구조로 구현된다(한 단계의 결과물이 다음 단계의 입력으로 사용되는 일련의 데이터 처리 단계를 파이프라인이라고 한다). 그림 5.1은 이 같은 흐름을 따라 3차원 모델이 스크린에 렌더링되는 과정을 보여주고 있다. 이 그림에서 쉐이더(shader)는 프로그램과 동의어이다. 즉, 우리는 GPU 렌더링을 위해서 정점 쉐이더(vertex shader)와 프래그먼트 쉐이더(fragment shader)라는 두 가지 프로그램을 작성해야 한다. 반면, 래스터라이저와 출력 병합기는 하드웨어로 고정된(hardwired) 단계로, 정해진 연산을 수행한다.
정점 쉐이더는 정점 배열에 저장된 모든 정점에 대해서 변환(transform)을 비롯한 다양한 연산을 수행한다.
래스터라이저는 변환된 정점들로 삼각형들을 조립한 후, 각 삼각형 내부를 차지하는 프래그먼트(fragment)를 생성한다. 한 프래그먼트는 컬러 버퍼의 한 픽셀을 갱신하는 데 필요한 데이터를 총칭하는 것이다. (지금은 정확하게 이해하기 어렵겠지만, 7장부터 프래그먼트에 대해 자세히 설명하기 시작한다) 예를 들어, 스크린에서 100개의 픽셀을 차지하는 삼각형이 있다면, 이 삼각형으로부터 총 100개의 프래그먼트가 만들어진다. 래스터라이저가 출력한 프래그먼트는 하나하나 프래그먼트 쉐이더로 입력되어, 라이팅과 텍스처링 등의 작업을 거쳐 색상이 결정된다. 출력 병합기는 이러한 프래그먼트와 현재 컬러 버퍼에 저장된 픽셀 중 하나를 선택하거나 혹은 이들의 색을 결합하여 컬러 버퍼를 갱신한다.
그림 5.2. 정점 쉐이더가 수행하는 변환 세 가지는 5.1절, 5.2절, 5.4절에서 차례로 설명한다.
지금부터 1부 끝까지 그림 5.1의 각 단계를 차례차례 설명할 것인데, 이 장과 다음 장은 첫 단계인 정점 쉐이더를 다룬다. 정점 쉐이더가 수행하는 연산 중 필수불가결한 것은 그림 5.2에 보인 변환들이다. 4.4절에서 다룬 월드 변환이 우선 수행되는데, 5.1절은 월드 변환에 대해 조금 더 논의하고, 그 뒤에 그림 5.2의 나머지 변환을 다룰 것이다.
5.1 노멀의 월드 변환
각자의 오브젝트 공간에서 만들어진 물체를 단일한 월드 공간으로 모으는 것이 월드 변환의 역할이다. 4.4절의 월드 변환은 정점 배열에 저장된 데이터 중 ‘위치’에 적용되었다. 정점 배열의 중요한 데이터 또 하나는 ‘노멀’인데, 여기에도 월드 변환을 그대로 적용할 수 있을까?
4.4절에서 기술한 것처럼, 월드 변환이 아핀 변환들로만 구성되었다면 [L|t]로 표기할 수 있다. L은 ‘누적된 선형 변환’을, t는 ‘누적된 이동’을 표현하는데, 노멀 변환에 있어 t는 무시해도 된다. 벡터는 이동에 의해 영향 받지 않기 때문이다.
그림 5.3.a는 3차원 오브젝트 공간의 단면인 xy평면을 보여주는데, (4,1)과 (1,4) 점을 잇는 선분은 xy평면에 수직인 삼각형의 단면을, n은 그 삼각형의 노멀을 나타낸다(물론 n은 xy평면에 나란하다). 간단한 논의를 위해, L을 2차원 축소확대 행렬이라 하고, $s_x$ = 0.5, $s_y$ = 1이라고 설정하자. 그림 5.3.a는 L에 의해 변환된 삼각형과 삼각형 노멀은 서로 수직하지 않다는 것을 보여준다. 따라서 노멀 변환을 위해 L을 사용할 수는 없다.
[노트: 노멀 변환]에 따르면 $(L^{-1})^T$에 의해 변환된 노멀은 L에 의해 변환된 삼각형과 항상 수직 관계를 유지한다. 즉, 노멀 변환을 위해 L 대신 $(L^{-1})^T$를 사용해야 한다는 것이다. $(L^{-1})^T$는 L의 역전치행렬(inverse transpose)인데, 간단히 $L^{-T}$로 표기한다. 그림 5.3.b는 삼각형 노멀에 $L^{-T}$를 적용한 결과를 보여준다.
그림 5.3.a. 노멀 변환. 정점과 노멀 모두 동일한 행렬 L에 의해 변환되었다. L은 축소확대 변환이므로, L에 의해 변환된 노멀을 단위 벡터로 만들기 위해 정규화를 수행했다.
그림 5.3.b. 정점은 L에 의해 변환되지만, 노멀은 $L^{-T}$에 의해 변환되어야 한다.
그림 5.3.a에서 L을 노멀에 적용했을 때 문제가 발생했던 것은 L이 비균등 축소확대였기 때문이다. 만약 L이 비균등 축소확대를 포함하지 않는다면, 즉 L이 회전, 이동, 균등 축소확대 중 하나이거나 이들이 결합된 경우라면, 노멀에 L을 적용해도 된다. 하지만, 알고리즘을 단순화하기 위해, L이 비균등 축소확대를 포함하건 말건 노멀 변환에는 무조건 $L^{-T}$를 사용할 것이다. 물론, 변환된 노멀의 길이를 1로 만들기 위해 정규화를 거쳐야 한다.
이해를 돕기 위하여 그림 5.3에서는 삼각형 노멀을 사용했지만, 폴리곤 메시 렌더링에 실제로 사용되는 것은 정점 노멀이다. 월드 공간 정점 노멀 계산에도 $L^{-T}$가 그대로 쓰인다.
5.2 뷰 변환
월드 변환이 완료되어 모든 물체가 월드 공간에 모아졌다고 가정하자. 월드 공간의 특정 영역을 스크린에 렌더링하기 위해서는 가상 카메라의 위치와 방향을 설정하여야 한다.
5.2.1 카메라 공간
그림 5.5. EYE, AT, UP으로부터 카메라 공간이 정의된다. 카메라 공간의 원점은 EYE이고 기저는 {u,v,n}이다. 원점에 놓인 카메라는 -n축 방향을 바라보고 있다.
그림 5.5에 보인 것처럼, 카메라의 위치와 방향은 EYE, AT, UP 파라미터를 통해 정의된다.
- EYE는 월드 공간에서의 카메라의 위치이다.
- AT은 월드 공간에서 카메라가 바라보는 기준점이다. 이는 스크린에 담고자하는 물체들이 모여 있는 공간의 가운데 방향에 설정되는데, 그림 5.5에서 AT은 주전자 주둥이 끝에 놓여져 있다.
- UP은 카메라의 상단이 가리키는 방향을 대략적으로 묘사하는 벡터이다. 대부분의 경우, UP은 월드 공간의 수직 방향으로 즉 y축으로 설정된다.
이들 파라미터를 이용해 카메라 공간(camera space)이 정의되는데, 그 원점은 EYE에 놓여진다. 한편, 카메라 공간의 기저를 {u,v,n}으로 표기하자. 그림 5.5의 가운데 상자는 EYE, AT, UP을 사용해 어떻게 {u,v,n}이 계산되는지 보여준다. 먼저 AT과 EYE를 잇는 벡터를 정규화하여 단위 벡터 n을 얻는다. 다음, UP과 n의 벡터곱을 정규화하여 또 다른 단위 벡터 u를 얻는다. 마지막으로 n과 u의 벡터곱을 v로 취한다. 이렇게 얻어진 {u,v,n}은 직교정규 기저이다. 우리는 카메라 공간을 {u,v,n,EYE}로 표기한다. 월드 공간은 {e1,e2,e3,O}로 표기한다. 여기에서 O는 원점을 의미한다.
5.2.2 공간 이전과 뷰 행렬
우리는 앞 절에서 월드 공간 말고도 카메라 공간을 새로 만들었는데, 하나의 정점은 이 두 공간에서 다른 좌표를 가진다. 그림 5.6의 첫 번째 상자를 보자. EYE의 월드 공간 좌표는 (18,8,0)이고, AT이 놓여진 주전자 주둥이 끝의 월드 공간 좌표는 (10,2,0)이다. 주둥이 끝은 카메라 공간의 -n축 위에 존재하므로, 이것의 u와 v좌표는 0이 된다. 한편, 주둥이 끝과 EYE 사이의 거리는 10이다. 따라서, 카메라 공간에서 주둥이 끝의 좌표는 (0,0,-10)이다. 월드 공간 좌표 (18,8,0)과 다르다.
이와 같은 방식으로 월드 공간의 모든 정점들이 카메라 공간으로 재정의되면 렌더링 알고리즘을 설계 구현하는 것이 매우 쉬워진다. 이처럼 하나의 공간에서 정의된 물체를 다른 공간으로 옮기는 것을 일반적으로 공간 이전(space change)이라고 부르는데, 월드 공간에서 카메라 공간으로의 이전은 카메라 좌표계 {u,v,n,EYE}를 월드 좌표계 {e1,e2,e3,O}에 포개 놓는 과정으로 묘사될 수 있다. 이를 위해서는, 그림 5.6에 보인 것처럼 EYE를 먼저 O로 ‘이동’시킨 뒤 {u,v,n}을 {e1,e2,e3}와 일치하도록 ‘회전’시키면 된다. 이 과정에서 모든 물체는 카메라 좌표계를 따라 같이 움직인다. 마치 물체와 카메라 좌표계가 보이지 않는 철사로 단단히 묶여져 있다고 상상하면 도움이 될 것이다. 그림 5.6에서 월드 공간 좌표가 (10,2,0)이던 주둥이 끝 정점도 이동과 회전을 거쳐 (0,0,-10)의 좌표를 가지게 되었는데, 이제 월드 공간과 카메라 공간이 동일해졌으므로, 이 월드 공간의 좌표 (0,0,-10)을 그대로 카메라 공간 좌표로 취하면 된다.
그림 5.6. 카메라 좌표계 {u,v,n,EYE}를 월드 좌표계 {e1,e2,e3,O}에 포개 놓기 위해서는 이동과 회전이 필요하다. 이 두 변환이 결합된 것이 뷰 변환인데, 이는 월드 공간의 물체를 카메라 공간으로 옮긴다.
월드 공간에서 카메라 공간으로의 이전을 뷰 변환(view transform) 혹은 카메라 변환(camera transform)이라 부른다. 뷰 변환의 첫 단계인 이동은 변위 벡터 O-EYE로 정의된다. O는 (0,0,0)이므로 변위 벡터는 ($-EYE_x$, $-EYE_y$, $-EYE_z$)가 된다.
\[T = \begin{pmatrix} 1 & 0 & 0 & -EYE_x \\ 0 & 1 & 0 & -EYE_y \\ 0 & 0 & 1 & -EYE_z \\ 0 & 0 & 0 & 1 \\ \end{pmatrix} \tag{5.7}\]그림 5.6의 예에서 EYE의 월드 공간 좌표는 (18,8,0)이다. 따라서 (10,2,0)에 위치한 주둥이의 끝은 위 행렬 T에 의해 (-8,-6,0)으로 이동된다.
\[T \begin{pmatrix} 10 \\ 2 \\ 0 \\ 1 \\ \end{pmatrix} = \begin{pmatrix} 1 & 0 & 0 & -18 \\ 0 & 1 & 0 & -8 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix} \begin{pmatrix} 10 \\ 2 \\ 0 \\ 1 \\ \end{pmatrix} = \begin{pmatrix} -8 \\ -6 \\ 0 \\ 1 \\ \end{pmatrix} \tag{5.8}\]그림 5.6의 두 번째 상자에서 볼 수 있듯, 이동에 의하여 월드 공간과 카메라 공간은 원점을 공유하게 된다. 따라서 기저 {u,v,n}은 회전을 통하여 {e1,e2,e3}에 포개질 수 있다. 4장 그림 4.12에 보인 것처럼 이 회전은 카메라 공간의 기저 {u,v,n}을 행으로 가지는 행렬로 정의된다.
\[R = \begin{pmatrix} u_x & u_y & u_z & 0 \\ v_x & v_y & v_z & 0 \\ n_x & n_y & n_z & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix} \tag{5.9}\]그림 5.6의 마지막 상자에 보인 것처럼 물체들은 R에 의해 회전되어 공간 이전이 완료된다. 공간 이전을 구성하는 이러한 회전은 기저 이전(basis change)이라고 부른다. 그림 5.6의 뷰 변환을 구성하는 기저 이전은 {e1,e2,e3} 기준의 좌표 (-8,-6,0)을 {u,v,n} 기준의 좌표인 (0,0,-10)으로 옮긴다.
우리는 뷰 변환을 $M_{view}$로 표기하는데, 이는 식 (5.7)의 이동 행렬 T와 식 (5.9)의 회전 행렬 R을 결합한 4X4 행렬로 정의된다.
\[M_{view} = RT = \begin{pmatrix} u_x & u_y & u_z & 0 \\ v_x & v_y & v_z & 0 \\ n_x & n_y & n_z & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix} \begin{pmatrix} 1 & 0 & 0 & -EYE_x \\ 0 & 1 & 0 & -EYE_y \\ 0 & 0 & 1 & -EYE_z \\ 0 & 0 & 0 & 1 \\ \end{pmatrix} = \begin{pmatrix} u_x & u_y & u_z & -u \cdot EYE \\ v_x & v_y & v_z & -v \cdot EYE \\ n_x & n_y & n_z & -n \cdot EYE \\ 0 & 0 & 0 & 1 \\ \end{pmatrix} \tag{5.10}\]$M_{view}$는 월드 공간의 모든 물체에 공히 적용된다.
공간 이전은 앞으로 이 책에서 여러 번 언급될 것이니 완벽하게 이해해야 한다. 또한 공간 이전은 컴퓨터 그래픽스는 물론, 로봇, 컴퓨터 비전, 증강 현실 등과 같은 분야에서도 핵심적인 요소이다.
5.3 오른손 좌표계와 왼손 좌표계
3차원 카테시안 좌표계는 오른손 좌표계와 왼손 좌표계로 나뉜다. 그림 5.7을 보자. 오른손 좌표계의 경우, 오른손 네 손가락이 x축에서 y축으로 움직일 때 엄지손가락이 z축을 가리키게 된다. 왼손 좌표계의 경우 동일한 규칙이 왼손에 적용된다. Direct3D는 왼손 좌표계를 사용하지만, OpenGL과 OpenGL ES는 오른손 좌표계를 사용한다. 따라서, 이 책도 기본적으로 오른손 좌표계를 사용한다.
그림 5.8.a. 오른손 좌표계에서 왼손 좌표계로의 포팅.
그림 5.8.a는 삼각형과 사각형으로 구성된 간단한 물체를 보여준다. 이는 오른손 좌표계에서 -z축 방향에 놓여있다. 카메라의 위치(EYE)가 원점에 있고, 시선(AT-EYE)은 (0,0,-1) 방향이라고 가정하자. 그러면 우리는 그림 5.8.a의 우측 상자에 보인 영상을 얻을 것이다.
그림 5.8.a의 물체를 왼손 좌표계로 포팅해 보자. 예를 들어, OpenGL에서 모델링한 물체를 Direct3D로 포팅하는 경우가 이에 해당한다. 그림 5.8.b는 그 결과를 보여준다. 물체 정점 및 카메라 시선의 좌표들을 그대로 사용했다. 물체의 경우, 왼손 좌표계의 -z축 방향에 놓이게 된다. 그 결과 얻게 되는 영상은 그림 5.8.b의 우측 상자에 나와 있다. 그림 5.8.a의 영상과 비교해 보면 좌우가 뒤바뀌어졌음을 알 수 있다.
OpenGL 기반 게임을 Direct3D로 포팅할 경우 우리는 동일한 게임 경험을 기대할텐데, 이처럼 영상 좌우가 바뀌면 동일한 경험을 얻을 수 없다. 그런데 이 문제를 해결하는 방법은 간단하다. 포팅 시 물체와 카메라 파라미터의 z좌표의 부호를 바꾸면 된다. 그림 5.8.c는 이를 통해 5.8.a와 동일한 영상을 얻을 수 있음을 보여준다. 이렇게 z좌표의 부호를 바꾸는 것은 그림 5.8.a에서 그림 5.8.c로 넘어오는 과정이 직관적으로 보여주는 것처럼, z축을 반대 방향으로 돌리는 것을 생각하면 이해가 쉬울 것이다.
5.4 투영 변환
월드 공간에 정의된 EYE, AT, UP을 카메라의 ‘외부’ 파라미터로 본다면, 이제 카메라의 렌즈를 선택하고 줌인/줌아웃을 조절하는 것에 해당하는 ‘내부’ 파라미터를 정의할 차례이다. 한편, 뷰 변환에 의해 월드 공간의 모든 물체가 카메라 공간 {u, v, n, EYE}로 변환되었다면 이제 더 이상 월드 공간은 고려하지 않아도 되므로, 이제부터는 카메라 공간 기저 {u,v,n}을 버리고 카메라 공간의 축을 x,y,z로 표기할 것이다. 이는 단지 우리가 x,y,z축에 더욱 익숙하기 때문이다.
5.4.1 뷰 프러스텀
그림 5.9. 피라미드 모양의 뷰 프러스텀 바깥의 폴리곤은 최종 스크린에 나타나지 못한다.
일반적으로 카메라의 시야(field of view : fov)는 제한되어 있기 때문에 카메라 공간의 모든 물체를 스크린에 담아낼 수 없다. 카메라의 가시 영역을 뷰 볼륨(view volume)이라 부르는데, 이는 fovy, aspect, n, f 네 가지 파라미터를 사용해 결정된다. 그림 5.9에 보인 바와 같이, fovy는 y축 기준의 시야각을 말하는데, 주전자 위의 원통은 시야각 바깥에 놓여 있으므로 카메라에 보이지 않게 된다. 한편, aspect에 의해 정의된 뷰 볼륨은 꼭지점을 원점에 두고 -z축을 중심축으로 가진 무한한 크기의 피라미드가 된다.
뷰 볼륨을 정의하는 나머지 파라미터 n과 f는 각각 원점으로부터 전방 평면(near plane)과 후방 평면(far plane)까지의 거리를 나타낸다. 그림 5.9에 보인 것처럼, fovy와 aspect에 의해 정의된 무한한 크기의 뷰 볼륨은 z축에 수직인 전방 평면 z=-n과 후방 평면 z=-f에 의해 절단되어 유한한 크기의 뷰 볼륨으로 바뀐다. 이를 뷰 프러스텀(view frustum) 혹은 절두체라고 부른다.
뷰 프러스텀 바깥에 놓인 물체는 보이지 않는 것으로 처리되므로, 그림 5.9에서는 원통뿐 아니라 구 역시 보이지 않는다. 전후방 평면은 실세계의 카메라 작동 원리와 모순되지만, 계산 효율성 제고를 위해 도입된 개념이다. 그림 5.9의 원통 및 구와 같이 뷰 프러스텀 바깥에 놓인 물체는 대개 미리 걸러져서 GPU 파이프라인에 들어가지 못하게 한다. 이러한 전처리를 뷰 프러스텀 컬링(view frustum culling)이라 부른다. 컴퓨터 그래픽스에서 컬링이라 함은, 카메라에 보이지 않아서 최종 영상에 나타날 수 없는 영역을 제거하는 과정을 말한다.
그림 5.10. 폴리곤이 뷰 프러스텀의 경계에 걸치는 경우 뷰 프러스텀 바깥에 있는 부분, 예를 들면 빨간색으로 표시된 부분은 잘라져 버려진다. 이를 클리핑이라 한다.
그림 5.9에서 카메라에 보이는 유일한 물체는 주전자이다. 그런데 주전자의 손잡이는 뷰 프러스텀의 후방 평면과 교차한다. 하나의 폴리곤이 뷰 프러스텀과 교차할 경우, 그림 5.10에 보인 것처럼 이 폴리곤은 잘라져서 프러스텀 안쪽에 놓인 부분만 GPU 파이프라인의 다음 단계로 넘어가게 된다. 이렇게 폴리곤을 자르는 작업을 클리핑(clipping)이라 부르는데, 이는 그림 5.1에 보인 래스터라이저(GPU 파이프라인의 두 번째 단계)에 의해 수행된다.
5.4.2 투영 행렬
그림 5.10은 클리핑의 개념을 명확하게 보여준다. 하지만 실제 클리핑은 카메라 공간에서 뷰 프러스텀을 이용해 수행되지 않는다. 그림 5.11.a에 보인 것처럼, 피라미드 모양의 뷰 프러스텀을 좌표계의 주축에 나란한 2X2X2 크기의 정육면체 뷰 볼륨으로 변형하는 것을 생각해 보자. 이를 투영 변환 (projection transform)이라고 한다. 카메라 공간 물체는 이렇게 투영 변환을 거친 뒤, 2X2X2 크기의 정육면체에 대해 클리핑된다. 이런 점에서, 투영 변환 이후에 물체가 놓이는 공간을 클립 공간(clip space)이라고 따로 부른다.
그림 5.11.a. 투영 변환. 뷰 프러스텀을 정육면체로 변형하는 투영 변환이 카메라 공간의 물체에 적용되었다. 주전자가 어떻게 변형되는지 보자.
그림 5.11.b. 이 단면도는 투영 변환이 원근법을 구현함을 보여준다.
그림 5.11.b의 왼쪽에 그려진 뷰 프러스텀의 단면들을 보자. 뷰 프러스텀은 원점에 위치한 카메라로 수렴하는 투영선(projection line)들의 집합으로 이해할 수 있다. 뷰 프러스텀과 원점 사이에 놓여 있으면서 z축에 수직인 가상의 투영 평면(projection plane)을 생각해 보자. 뷰 프러스텀의 투영선은 투영 평면에 영상을 형성할 것이다.
하나의 투영선 위에 존재하는 모든 3차원 점은 투영 평면의 한 점에 맺히게 된다. 그림 5.11.b의 두 선분 $l_1$과 $l_2$를 생각해 보자. 3차원 공간에서는 $l_1$이 $l_2$보다 길지만 투영 평면에서 이들은 동일한 길이를 가지게 된다. 즉 멀리 있는 물체가 작게 보이게 되는데 이것이 바로 원근법이다.
이제 그림 5.11.b의 오른쪽에 그려진 단면을 보자. 투영 변환의 결과, 모든 투영선들은 서로 평행해져 z축에 나란해졌다. 즉 우리는 단일한 투영 방향을 가지게 되었다. 투영 변환에 의해 변형된 주전자를 관찰해 보면, 뷰 프러스텀 앞부분에 있는 폴리곤은 상대적으로 커진 반면 뷰 프러스텀 뒷부분에 있는 폴리곤은 상대적으로 작아졌음을 발견할 수 있다. 이 결과, 변형된 선분 $l_1’$와 $l_2’$는 동일한 길이를 가지게 되고, 단일한 투영 방향을 따라 이들은 동일한 길이로 보이게 된다. 주의할 점은, 우리의 투영 변환은 3차원 공간 물체를 2차원 투영 평면에 실제로 투영하는 것이 아니라, 3차원 공간 내에서 원근법을 구현한다는 것이다.
투영 변환은 정점 쉐이더가 수행하는 마지막 연산이다. 투영 변환된 물체는 이제 하드웨어로 고정된 래스터라이저로 들어갈 것이다. 앞으로 7장에서 기술할 것처럼, 래스터라이저는 2X2X2 크기의 정육면체 뷰 볼륨을 확대하여, 그림 5.11.a 오른쪽에 음영 표시된 면이 스크린에 딱 맞도록 확대할 것이다. 이런 확대는 실제로 물체에 적용되는데, 확대된 물체는 z축을 따라 스크린에 투영된다. 따라서, $l_1’$와 $l_2’$는 스크린에서 동일한 길이를 가지게 된다.
뷰 프러스텀을 2X2X2 크기의 정육면체로 변형하는 투영 행렬은 다음과 같다.
\[M = \begin{pmatrix} \frac{cot\frac{fovy}{2}}{aspect} & 0 & 0 & 0 \\ 0 & cot \frac{fovy}{2} & 0 & 0 \\ 0 & 0 & \frac{f + n}{f - n} & \frac{2nf}{f - n} \\ 0 & 0 & -1 & 0 \\ \end{pmatrix} \tag{5.11}\]위 행렬은 뷰 프러스텀 파라미터인 fovy, aspect, n, f에 의해서 정의되었다. 투영 행렬의 중요한 특징 중 하나는 아핀 변환을 위한 행렬과 달리 마지막 행이 (0 0 0 1)이 아니라는 것이다. 이것이 가지는 의미에 대해서는 7.2절에서 논의할 것이다.
클립 공간의 물체는 래스터라이저로 입력된다. 그림 5.11에서 확인할 수 있는 것처럼, 클립 공간은 ‘오른손’ 좌표계이다. 그런데 래스터라이저는 모든 물체가 ‘왼손’ 클립 공간에서 정의되어 있다는 가정에서 설계되어 있다. 따라서 우리는 래스터라이저로 진입하기 위하여 오른손 좌표계를 왼손 좌표계로 변환해야 한다. 이를 위해서는 정점 z좌표의 부호를 변경하면 된다. 그림 5.12를 보자.
그림 5.12. 오른손 좌표계에서 왼손 좌표계로의 변환을 위해 모든 정점의 z좌표 부호가 바뀌었다. 개념적으로 이는 물체를 고정한 채 좌표계의 z축을 반전시킨 것과 동일하다.
카메라 공간의 정점 v는 식 (5.11)의 행렬 M에 의해 v’로 변환되는데, v’의 z좌표는 M의 세 번째 행이 결정한다. 따라서 v’의 z좌표 부호를 바꾸기 위해서는 M의 세 번째 행의 모든 원소의 부호를 바꾸면 된다.
\[M_{proj} = \begin{pmatrix} \frac{cot\frac{fovy}{2}}{aspect} & 0 & 0 & 0 \\ 0 & cot \frac{fovy}{2} & 0 & 0 \\ 0 & 0 & -\frac{f + n}{f - n} & -\frac{2nf}{f - n} \\ 0 & 0 & -1 & 0 \\ \end{pmatrix} \tag{5.12}\]그림 5.13은 식 (5.12)의 $M_{proj}$에 의해 오른손 카메라 공간의 물체가 왼손 클립 공간으로 변환된 것을 보여준다.
그림 5.13. 정점 쉐이더의 마지막 변환인 $M{proj}$는 오른손 카메라 공간에서 정의된 정점을 왼손 클립 공간으로 옮긴다. 이 그림은 5.11.a와 그림 5.12를 통합한 것이다._
참고



