3D 그래픽스에서 Vertex Transformations

WikiBooks/Cg Programming 항목에서 Vertex Transformations에 관한 좋은 내용이 있어서 발 번역 번역해 보았다. Vertex가 로컬 좌표계에서 여행을 시작하여 여러 좌표계들을 지나 Screen좌표계에 도달하기까지의 여행에 대해서 대략적이나마 밑단 변환 연산들에 대한 기초 이론을 설명한다. Unreal, Unity 등 좋은 엔진들을 만지다보면 이런 기본에 허기질 때가 많다. ㅎ

Original : http://en.wikibooks.org/wiki/Cg_Programming/Vertex_Transformations

Cg Programming/Vertex Transformations

 Vertex Shader의 가장 중요한 작업 중에 하나는 모델링의 Vertex들을 원래 좌표계(3D 모델링 툴에서 정의된)에서 스크린 좌표계로 변환하는 일이다. Programmable vertex shader는 vertex 변환에 대한 많은 방법들을 제공하지만, 몇몇 변환들은 버텍스 쉐이더 이후의 Fixed-Function Stage들에서 일어난다. 그래서 Vertex Shader를 만들 때 Vertex Shader에서 어떠한 변환을 수행해야 하는 가를 알고 있는 것이 매우 중요하다. 이러한 변환들은 대게 uniform 파라미터로 정의 및 구현이 되고, 쉐이더에 주어지는 Vertex와 Normal Vector들과 Matrix-Vector 곱 연산에 의해서 적용이 된다. 이 계산은 점들과 방향에 대해서는 간단하지만 Normal Vector에 대해서는 그리 간단하지 않다. (챕터에서 설명: Section “Applying Matrix Transformations”.)

 먼저 좌표계와 그들 사이의 변환에 대해서 알아보고 후에 개별 변환에 대해서 이야기 해보자.

Overview: The Camera Analogy

 Vertex 변환 프로세스를 그림과 같이 카메라의 관점에서 유추해본다면 좀 더 쉽게 이해할 수 있다. Vertex 변환의 단계와 관련 변환들은 아래와 같다.

The camera analogy: 1. positioning the model, 2. positioning the camera, 3. adjusting the zoom, 4. cropping the image

1. 모델을 위치 시킴         – Modeling 변환
2. 카메라를 놓음         – Viewing 변환
3. 줌을 셋팅함                – Projection 변환
4. 이미지를 잘라냄        – Viewing 변환

1 ~ 3번 변환들이 Vertex Shader에서 수행되는 변환이다. Vertex Shader 후에 Fixed-Function 단계에서 Perspective Division(Normalized Device 좌표를 얻기 위해서 x,y,z를 w로 나누는 작업, 흔히 Projection 변환의 일부로 간주)이 자동으로 적용 된다. Viewport 변환 또한 Fixed-Function 단계에서 자동 적용 된다. 이런 Fixed-Function 단계의 변환들은 수정을 가할 수 없는 반면에, 다른 변환들은 여기에 기술된 것과든 또다른 다른 형태의 변환들로 대체가 가능하다. 다만, 고전적인 변환을 아는 것은 Cliping을 잘 활용하고 varying 변수들을 정확히 보간하는 것에 매우 유용하다.

아래는 여러 종류의 좌표 시스템 사이의 Vertex 변환을 대략적으로 도식화 한 것이다. 변환시 적용되는 Matrix도 같이 표현했다.

object/model coordinates

 

vertex input parameters with semantics (in particular the semantic POSITION

modeling transformation: model matrix \mathrm{M}_{\text{object}\to \text{world}}

world coordinates

viewing transformation: view matrix \mathrm{M}_{\text{world}\to \text{view}}

view/eye coordinates

 

projection transformation: projection matrix \mathrm{M}_\text{projection}

clip coordinates

vertex output parameter with semantic POSITION

perspective division (by the w coordinate)

normalized device coordinates

viewport transformation

screen/window coordinates

Modeling, Viewing 그리고 Projection 변환은 Vertex Shader에서 적용된다. Perspective Division과 Viewport 변환은 Vertex Shader 이후에 Fixed-Function 단계에서 적용된다. 이 변환들에 대해 좀 더 자세히 알아보자.

Modeling Transformation

Modeling 변환은 Object 좌표계(로컬 혹은 모델링 좌표계)에서 월드 좌표계로의 변환이다. Object 좌표는 3D 모델링 툴에서 일반적으로 만들어져 온다. 반면 월드 좌표계는 모든 물체들이 공통으로 사용하는 Scene의 좌표이다. 각각의 Object들은 각각 다른 Object 좌표들을 가지고 있으므로 Modeling 변환도 다르다. 그래서 각각의 Modeling 변환이 각 물체에 따로 적용되어야 한다.

Model Matrix의 구조

Modeling 변환은 4×4 행렬로 표현될 수 있다.

\mathrm{M}_{\text{object}\to \text{world}} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{1,1} & a_{1,2} & a_{1,3} & t_1 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{2,1} & a_{2,2} & a_{2,3} & t_2 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{3,1} & a_{3,2} & a_{3,3} & t_3 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & 0 & 0 & 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]   \text{ with } \mathrm{A} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{1,1} & a_{1,2} & a_{1,3} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{2,1} & a_{2,2} & a_{2,3} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{3,1} & a_{3,2} & a_{3,3}<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]   \text{ and } \mathbf{t} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> t_1\\<br /><br /><br /><br /><br /><br /><br /><br /><br /> t_2\\<br /><br /><br /><br /><br /><br /><br /><br /><br /> t_3<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

 A는 3 x 3 행렬이고, 3D 공간 상에서 선형 변환을 나타낸다. 이 행렬은 회전, 크기 변환을 포함하는 행렬이다. t 는 3D 벡터이고 위치 변환값을 의미한다.  \mathrm{M}_{\text{object}\to \text{world}} 행렬은 A와 t가 합쳐진 4 x 4 행렬이다. 수학적으로 말하면 Model Matrix는 Affine 변환이다. (아핀 변환). 위치 변환이 포함된 선형 변환. 이를 위해 모든 3차원 공간의 점들은 네번째 좌표값이 1을 가지는 4차원 벡터로 표현된다.

P = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> p_1\\<br /><br /><br /><br /><br /><br /><br /><br /><br /> p_2\\<br /><br /><br /><br /><br /><br /><br /><br /><br /> p_3\\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

특정 점 P를 행렬과 곱할 때, 3차원 선형 변환과 위치 변환이 합쳐면 아래와 같은 결과를 나타낸다.

\mathrm{M}_{\text{object}\to \text{world}}\;P = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{1,1} & a_{1,2} & a_{1,3} & t_1 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{2,1} & a_{2,2} & a_{2,3} & t_2 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{3,1} & a_{3,2} & a_{3,3} & t_3 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & 0 & 0 & 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]<br /><br /><br /><br /><br /><br /><br /><br /><br /> \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> p_1 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> p_2 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> p_3 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]<br /><br /><br /><br /><br /><br /><br /><br /><br />    = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{1,1} p_1 + a_{1,2} p_2 + a_{1,3} p_3 + t_1 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{2,1} p_1 + a_{2,2} p_2 + a_{2,3} p_3 + t_2 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> a_{3,1} p_1 + a_{3,2} p_2 + a_{3,3} p_3 + t_3 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

4번째 좌표값을 따로 떼어 내면 결과값은 아래와 같다.

\mathrm{A} \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> p_1\\<br /><br /><br /><br /><br /><br /><br /><br /><br /> p_2\\<br /><br /><br /><br /><br /><br /><br /><br /><br /> p_3<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right] +<br /><br /><br /><br /><br /><br /><br /><br /><br /> \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> t_1\\<br /><br /><br /><br /><br /><br /><br /><br /><br /> t_2\\<br /><br /><br /><br /><br /><br /><br /><br /><br /> t_3<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]


Vertex Shader에서의 Model Matrix 접근 (Accessing the Model Matrix in a Vertex Shader)

Model Matrix, \mathrm{M}_{\text{object}\to \text{world}}는 Vertex Shader에서 이용할 수 있도록 Uniform 변수로 정의될 수 있다. 그러나, 일반적으로 Viewing 변환 행렬과 함께 묶여져서 ModelView Matrix의 형태로 Uniform 파라미터에 할당되어 사용된다. 몇몇 API에서는 Built-in(내장) Uniform 파라미터로 이용가능하다. (참고  Section “Applying Matrix Transformations”.)

Model 행렬의 계산(Computing the Model Matrix)

엄격하게 말하면 Cg 프로그래머는 Model Matrix의 계산에 관해서는 걱정할 필요가 없다. Vertex Shader 에서 Uniform 파라미터의 형태로 제공되기 때문이다. 실제로 렌더 엔진, Scene Graph, 게임 엔진들은 대게 Model Matrix를 제공한다. 그래서 Vertex Shader를 개발하는 개발자들은 Model Matrix의 계산에 대해서는 고민하지 안하도 된다. 다면 몇몇의 경우엔 Graphics Application을 개발하는 과정에서 계산을 해야하는 상황이 발생한다.

 Model Matrix는 오브젝트의 변환값의 요소들을 조합하여 4 x 4 행렬로 구해낼 수 있다. 특별히 위치값, 회전값, 크기값으로 말이다. 계층 관계가 있는 Scene Graph 상에서는 오브젝트의 부모들의 모든 변환들이 Model Matrix의 형태로 계산되어 있다. 그 행렬들의 가장 기초적인 변환에 대해서 살펴보자.

벡터  t = (t_1, t_2, t_3)에 대한 위치 변환을 4 x 4 행렬로 표현하면 아래와 같다.
\mathrm{M}_{\text{translation}} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> 1 & 0 & 0 & t_1 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & 1 & 0 & t_2 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & 0 & 1 & t_3 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & 0 & 0 & 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

x, y, z 축에 대한 Scale 값에 대한 변환을 4 x 4 행렬로 표현하면 아래와 같다.

\mathrm{M}_{\text{scaling}} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> s_x & 0 & 0 & 0 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & s_y & 0 & 0 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & 0 & s_z & 0 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & 0 & 0 & 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

아래는 x, y, z축으로의 회전 값에 대한 표현이다.

\mathrm{M}_{\text{rotation}} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> (1-\cos\alpha) x\,x + \cos\alpha    & (1-\cos\alpha) x\,y - z \sin\alpha   & (1-\cos\alpha) z\,x + y \sin\alpha  & 0 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> (1-\cos\alpha) x\,y + z \sin\alpha  & (1-\cos\alpha) y\,y + \cos\alpha     & (1-\cos\alpha) y\,z - x \sin\alpha  & 0 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> (1-\cos\alpha) z\,x - y \sin\alpha  & (1-\cos\alpha) y\,z + x \sin\alpha   & (1-\cos\alpha) z\,z + \cos\alpha    & 0 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0                                   & 0                                    & 0                                   & 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

특정 회전 축에 의한 회전을 구하는 것은 쉽게 유추가 가능하다. 이런 부분은 Euler Angle을 위한 회전을 구현하고자 할 때 필요하다. 그러나, 여러 개의 축에 의한 Euler Angle 회전 값을 구하는 것은 여기서 논하지 않을 것이다.

Normalized 쿼터니언  (x_q, y_q, z_q, w_q) 은 각도 2 \arccos(w_q)의 회전과 일치한다. 회전축의 방향은 Normalized 된 3D 벡터(x_q, y_q, z_q)에 의해서 결정된다.

 추가적인 기본 변환들이 존재하지만, Model Matrix의 계산에서는 중요치 않다. 이 4 x 4 행렬들은 행렬의 곱으로 묶어진다.  \mathrm{M}_1, \mathrm{M}_2, and \mathrm{M}_3 행렬 이 존재한다고 하면 순서대로 적용이 된다. (\mathrm{M}_1은 Object 좌표계에서 부모 좌표계로 변환,  \mathrm{M}_2는 부모 좌표계에서 부모의 부모 좌표계로 변환, \mathrm{M}_3는 부모의 부모 좌표계에서 월드 좌표계로의 변환). 그래서 묶여진 행렬은 아래와 같이 표현된다.

\mathrm{M}_\text{combined} = \mathrm{M}_3 \mathrm{M}_2 \mathrm{M}_1\,\!

Matrix의 연산 순서가 중요한데, 오른쪽에서 왼쪽으로 읽으면 되고 \mathrm{M}_1이 최초로 적용되고 연속하여, \mathrm{M}_2, \mathrm{M}_3순서로 적용이 된다.

 

Viewing Transformation

Viewing 변환은 카메라를 위치시키는 것과 동일한 변환이다. 다시 말해, Viewing 변환은 월드 좌표계를 좌표계의 원점에 놓여있는 카메라의 View 좌표계로 변환하는 변환이다.

Illustration of the view coordinate system.

xz 평면상에서 OpenGL의 경우에는 -z 방향으로 물체를 놓는 일이고, DirectX의 경우 +z 방향에 물체를 위치시키는 변환이다. 이 때 Up 향향은 y이다.

Vertex Shader에서의 View Matrix 접근(Accessing the View Matrix in a Vertex Shader)

Modeling 변환과 마찬가지로 Viewing 변환도 4 x 4 행렬로 표시될 수 있고 View Matrix (\mathrm{M}_{\text{world}\to \text{view}})로 불리워 진다. Vertex Shader의 Uniform 변수로 정의 될 수도 있지만, 대게는 Model Matrix( \mathrm{M}_{\text{object}\to \text{world}})와 묶여저서 Model View Matrix ( \mathrm{M}_{\text{object}\to \text{view}})형태로 존재한다. Model 행렬의 적용이 먼저이기 때문에 정확한 순서는 아래와 같다.

\mathrm{M}_{\text{object}\to \text{view}} = \mathrm{M}_{\text{world}\to \text{view}} \mathrm{M}_{\text{object}\to \text{world}}\,\!

(See also Section “Applying Matrix Transformations”.)

View 행렬의 계산(Computing the View Matrix)

Model Matrix와 비슷하게 Cg 프로그래머들은 View Matrix를 구하는 방법을 고민하지 않아도 된다. 대게는 Vertex Shader에서 Uniform 파라미터로 제공이 된다. 다만 Graphics Application을 개발할 때는 가끔 View Matrix를 구하는 작업이 필요하다.

 위치값 t에 대한  카메라의 View Matrix \mathrm{M}_{\text{world}\to \text{view}}를 구하는 방법을 간단히 알아보자. 카메라가 -z 방향을 바라보고 있는 OpenGL의 오른손 좌표계로 한정한다. 단계는 매우 간단하다.

1. View 좌표계 z 축의 World 좌표에서의 z 방향을 계산한다. Negative Normalized d 벡터를 이용한다.
\mathbf{z} = -\frac{\mathbf{d}}{|\mathbf{d}|}

2. View 좌표계 x 축의 World 좌표에서의 x 방향을 계산한다.

\mathbf{x} = \frac{\mathbf{d} \times \mathbf{k}}{|\mathbf{d} \times \mathbf{k}|}

3. View 좌표계 y 축의 World 좌표에서의 y 방향을 계산한다.

\mathbf{y} = \mathbf{z} \times \mathbf{x}

x, y, z와 t를 이용하여 Inverse View Matrix  \mathrm{M}_{\text{view}\to \text{world}}를 쉽게 구해낼 수 있다. 왜냐하면 이 행렬은 원점 (0, 0, 0)이 t 로, (1, 0, 0), (0, 1, 0), (0, 0, 1) Unit Vector는 각각 x, y, z이기 때문이다. 벡터의 값을 행렬의 Colomn값으로 적용하면 아래와 같다.
\mathrm{M}_{\text{view}\to \text{world}}:

\mathrm{M}_{\text{view}\to \text{world}} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> x_1 & y_1 & z_1 & t_1 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> x_2 & y_2 & z_2 & t_2 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> x_3 & y_3 & z_3 & t_3 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0            & 0            & 0            & 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

그런데, 우리는 \mathrm{M}_{\text{world}\to \text{view}} 매트릭스가 필요하므로  \mathrm{M}_{\text{view}\to \text{world}} 행렬의 역행렬을 구해야 한다.  \mathrm{M}_\text{view→world} 행렬은 아래와 같이 구성된다.

\mathrm{M}_{\text{view}\to \text{world}} =\left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> \mathrm{R} & \mathbf{t} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> \mathbf{0}^T & 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

3 x 3 행렬 R과 3D Vector t. 이러한 행렬의 역행렬은 아래와 같다.

\mathrm{M}_{\text{view}\to \text{world}}^{-1} = \mathrm{M}_{\text{world}\to \text{view}} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> \mathrm{R}^{-1} & -\mathrm{R}^{-1}\mathbf{t} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> \mathbf{0}^T & 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

위의 경우에 R 행렬은 Orthogonal 하기 때문에(각각의 칼럼 벡터들이 Normalized되었고, 서로 수직임), R의 역행렬은 간단하게 R 전치 행렬이 된다.

\mathrm{M}_{\text{world}\to \text{view}} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> \mathrm{R}^T & -\mathrm{R}^T\mathbf{t} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> \mathbf{0}^T & 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]   \text{with }\mathrm{R} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> x_1 & y_1 & z_1 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> x_2 & y_2 & z_2 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> x_3 & y_3 & z_3<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

이 과정을 유추해 내려면 약간의 선형 대수학의 지식이 필요하지만 결과 값을 계산하는 과정은 기초적인 벡터와 행렬에 대한 연산이고 어떠한 언어로든 쉽게 프로그래밍 할 수 있다.

Projection 변환과 Perspective Division (Projection Transformation and Perspective Division)

 먼저, Projection 변환은 Projection의 종류에 대해서 결정한다. Perspective인지 Orthographic인지. Perspective Projection은 원근으로 인한 축소가 있는 선형 시점이고, Orthographic은 원근 축소가 없는 Projection이다. 원근으로 인한 축소는 Perspective Division 시점에 결정된다. 그러나, 모든 Perspective Projection의 제어 값은 Projection 변환 안에 셋팅이 되어 있다.

 기술적으로 이야기하자면, Projection 변환은 View 좌표계에서 Clip 좌표계로 변환하는 변환이다. Clip 좌표계 영역 밖에 있는 보이지 않는 모든 물체들은 잘려진다. 이 변환은 Vertex가 semantic POSITION과 함께 리턴되기 이전에 Vertex에 행해지는 마지막 변환이다. Clip 좌표계는 그 후에 Perspective Division 과정으로 Normalized Device 좌표계로 변환이 된다. 이 때 4번째 좌표값으로 모든 좌표값들을 나누는 연산이 수행된다. Normalized Device 좌표계는 보이는 모든 영역의 점들이 -1 에서 1 사이의 값을 가지기 때문에 그렇게 불림)

Vertex Shader에서의 Projection 행렬 접근(Accessing the Projection Matrix in a Vertex Shader)

Modeling, Viewing 변환과 마찬가지로 Projection 변환도 4 x 4 행렬로 표현이 되고,  \mathrm{M}_\text{projection} 대게는 Vertex Shader의 Uniform Parameter에 정의되어 있다.

Projection Matrix 계산 (Computing the Projection Matrix)

ModelView Matrix와 비슷하게 Cg 프로그래머는 Projection Matrix 계산에 대해서는 특별히 고민할 필요가 없다. 마찬가지로 Graphics Application을 개발할 때는 필요할 수 있다.

 여기서는 3가지의 경우에 대해서 Projection Matrix를 구하는 것을 알아보자. (OpenGL 기준)

– 표준 Perspective Projection (OpenGL 2.x 의 gluPerspective function)
– Oblique Perspecive Projection (OpenGL 2.x 의  glFrustum function)
– Orthographic Projection (OpenGL 2.x 의 glOrtho)

표준 Perspective Projection은 다음과 같은 속성이 있다.

– 각도  \theta_\text{fovy} 는 그림에서 y 방향의 Field Of View를 나타낸다.
– 거리 n 은 Near Clipping, f는 Far Clipping을 나타낸다.

– Aspect Ratio a는 Near Clipping 으로 생성되는 사각형의 높이에 대한 폭의 비율이다.

View 위치와 Clipping 면들로 인해서 가운데 있는 사각형이 View Frustum을 정의하게 된다. Frustum 바깥 영역의 물체들은 모두 잘려나가게 된다. 또한 Near와 Far Clipping이 필요한 이유는 깊이 정보를 저장하는 버퍼의 값들이 정확도에 한계가 있기 때문에 무한대로 큰 Frustum을 표현하는 것은 불가능하다.


\theta_\text{fovy}, a, n, 그리고 f파라미터를 통해서, Perspecive Projection의 Projection 행렬은 아래와 같이 정의된다.

\mathrm{M}_{\text{projection}} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> \frac{d}{a} & 0 & 0 & 0 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & d & 0 & 0 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & 0 & \frac{n+f}{n-f} & \frac{2 n f}{n-f} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0            & 0            & -1            & 0<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]   \text{ with } d = \frac{1}{\tan(\theta_{\text{fovy}}/2)}

기울어진(Oblique) Perspective Projection은 다음과 같은 속성을 가진다.

– n, f는 표준 Perspective Projection과 동일하다.

 r (right), l (left), t (top), and b (bottom) 좌표들이 각각의 모서리들을 정의한다. 이 좌표들은 View Frustum 의 앞쪽 사각형을 정의한다. 그래서 단순히 Aspect Ratio a와 Fov만을 가지고 면들을 정의하는 것보다 더 구체적으로 Frustum을 정의할 수 있다.

n, f, r, l, t, 그리고 b파라미터를 통해서 , Oblique Perspective Projection의 행렬, \mathrm{M}_\text{projection} 은 아래와 같이 정의 된다.

\mathrm{M}_{\text{projection}} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> \frac{2 n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & \frac{2 n}{t-b} & \frac{t+b}{t-b} & 0 \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & 0 & \frac{n+f}{n-f} & \frac{2 n f}{n-f} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0            & 0            & -1            & 0<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]


축소(foreshortening)가 없는 Orthographic Projection은 그림과 같다.

파라미터들은 Oblique Perspective Projection과 같지만 View Frustum이 잘려진 피라미드가 아닌 간단한 박스 형태이다.

n, f, r, l, t, 그리고 b파라미터를 통해서 , Orthographic Projection의 행렬, \mathrm{M}_\text{projection} 은 아래와 같이 정의 된다.

\mathrm{M}_{\text{projection}} = \left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> \frac{2 }{r-l} & 0 & 0 & -\frac{r+l}{r-l} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & \frac{2 }{t-b} & 0 & -\frac{t+b}{t-b} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & 0 & \frac{-2}{f-n} & -\frac{f+n}{f-n} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0            & 0            & 0            & 1<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

Viewport Transformation

Projection 변환은 View 좌표계에서 Clip 좌표계로의 변환이다. 이 변환은 다시 Clip 좌표계의 4번째 요소값으로 나누게 되는 Perspective Division 과정에 의해서 Normalized Device 좌표계로 변환이 된다. Normalized Device 좌표계(NDC)에서 View 볼륨은 -1과 1사이의 중앙에 Origin값이 있는 박스 형태이다. 이 박스는 다시 Viewport 변환에 의해서 스크린 좌표계(혹은 윈도우 좌표계라고도)로 변환된다. (아래 그림)

이 변환의 파라미터들은  좌표 s_x, s_y 의 왼쪽 하단과 그 사각형의 넓이 w_s 그리고 높이 h_s, Near와 Far Cliping Planes의 깊이 n_s and f_s 로 구성된다. (이 깊이 값은 0과 1 사이값이다.). OpenGL과 OpenGL ES에서 이 파라미터들은 두개의 함수로 셋팅이 된다.

glViewport(GLint s_x, GLint s_y, GLsizei w_s, GLsizei h_s);
glDepthRangef(GLclampf n_s, GLclampf f_s);

 

Viewport 변환의 행렬은 Fixed-Function 단계에서 자동으로 적용되기 때문에 그리 중요하지 않다. 다만 완벽하게 하는 측면에서 아래와 같다.

\left[ \begin{matrix}<br /><br /><br /><br /><br /><br /><br /><br /><br /> \frac{w_s}{2} & 0 & 0 & s_x + \frac{w_s}{2}  \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & \frac{h_s}{2} & 0 & s_y + \frac{h_s}{2} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0 & 0 & \frac{f_s - n_s}{2} & \frac{n_s+f_s}{2} \\<br /><br /><br /><br /><br /><br /><br /><br /><br /> 0            & 0            & 0            & 0<br /><br /><br /><br /><br /><br /><br /><br /><br /> \end{matrix} \right]

추가로, Chapter 4 of Nvidia’s Cg Tutorial 을 살펴보면 고전적인 Vertex 변환들이 덜 디테일하게 설명되어 있다.