1. Coordinate Systems

  • DirectX는 왼손 좌표계 사용

    • 왼손 좌표계

      1. 왼손 엄지, 검지, 중지를 폈을 때, 각각 X, Y, Z축을 할당한 양의 방향

      2. 또는 왼손으로 엄지 척👍할 때, 엄지를 제외한 네 손가락을 X축 -> Y축 순으로 거머쥔 후 엄지의 방향이 Z축 양의 방향

    • Unity(Y is up), Unreal(Z is up) 또한 왼손 좌표계를 사용

2. 공간 좌표 변환

$$ v' = v \cdot MVP $$
  1. Object Space(Local Space)

    • 모델 자체의 좌표계로, 메시가 정의된 기준 좌표

    • 원점((0, 0, 0))은 보통 모델의 중심

  2. World Space

    • 게임 세계 기준의 좌표계

    • Object Space 좌표에 World Matrix(Model Matrix)를 곱하여 변환(VS 책임)

  3. View Space(Camera Space)

    • 카메라를 기준으로 한 좌표계

    • 카메라의 위치가 원점

    • DirextX 기준으로 카메라가 바라보는 방향은 +Z 방향(‘Y is up’ 기준)

    • World Space 좌표에 View Matrix를 곱하여 변환(VS 책임)

  4. Clip Space

    • 카메라에서 보이는 영역을 정규화된 볼륨(-w ~ w) 안으로 구겨넣은 좌표

    • 부등식(\(-w \le x \le w\) 등)을 통해 Clipping을 쉽게 수행할 수 있도록 하기 위함

    • 아직 Clipping 자체는 일어나지 않고, 원근감도 살아있지 않은 상태(이것들은 Rasterizer 책임)

    • View Space 좌표에 Projection Matrix를 곱하여 변환(VS 책임)

  5. NDC(Normalized Device Coordinates)

    • 화면 크기(해상도, 화면 비율)와는 독립적인 정규화 좌표계

    • 좌표의 각 축이 (-1, 1) 범위의 정육면체 범위로 좁혀짐

      • 단! DirextX에서는 NDC의 Z 범위가 -1 ~ 1이 아니라 0 ~ 1 범위임에 유의!
    • 아직 픽셀 좌표는 아니며, 이 상태로 Rasterizer에서 처리

    • 시각적으로는 오브젝트들이 정사각형 공간에 짜부라진 형태로 보임

    • Clip Space 좌표에 Perspective Division을 적용하여 변환

      $$ (x_{\text{ndc}}, y_{\text{ndc}}, z_{\text{ndc}}) = (x / w, y / w, z / w) $$
    • 원근감이 실제로 적용되는 순간으로, 카메라에 가까우면 \(w\)가 작아 큰 값이 되고, 카메라에서 멀면 \(w\)가 커 작은 값으로 변환됨

    • Perspective Division은 Rasterizer의 고정 기능 단계

  6. Viewport Space(Screen Space)

    • 실제 픽셀의 좌표

    • NDC를 Viewport 설정에 따라 늘리는 Viewport Transform을 수행(Rasterizer 책임)

    • 화면 자체는 2D지만, Depth Test 등을 위해 깊이 범위를 0 ~ 1로 유지

3. World Matrix

  • Object 좌표를 World(Model) 좌표로 변환하는 행렬

  • 사용자가 물체를 어떻게 배치하고 싶은지에 따라 행렬의 형태가 크게 달라짐

  • 크게 스케일, 회전, 이동 변환(아핀 변환)을 수행하며, 순서에 따라 \(M_{\mathrm{affine}} = S R T\) 순서(행 벡터 기준)로 곱하여 상수 버퍼로 Vertex Shader에 넘겨줌

    • 전단 변환 또한 아핀 변환의 일종이지만, 이 경우 SRT 변환이 크게 영향을 받을 수 있어 많은 엔진들은 전단 변환을 허용하지 않음

4. View Matrix

  • World 좌표를 View(Camera) 좌표로 변환하는 행렬

  • 게임 내에 배치된 정점들의 좌표들이 카메라를 원점으로 한 기준으로 바뀜

  • 아래 설명에서는 카메라의 위치를 \(\mathrm{POS} = (t_x, t_y, t_z)\), 카메라가 바라보고 있는 방향을 \(\mathrm{EYE} = (f_x, f_y, f_z)\)로 가정

  • View Matrix를 구성하는 방법

    • 카메라 이동(\(T\)): \(\begin{bmatrix} 1 & 0 & 0 & 0 \\\\ 0 & 1 & 0 & 0 \\\\ 0 & 0 & 1 & 0 \\\\ -t_x & -t_y & -t_z & 1 \end{bmatrix}\)

      • 카메라 기준에서는 월드 내 모든 오브젝트들이 카메라 위치만큼 역으로 이동한 것과 동일
    • 카메라 회전(\(R\)): \(\begin{bmatrix} r_x & u_x & f_x & 0 \\\\ r_y & u_y & f_y & 0 \\\\ r_z & u_z & f_z & 0 \\\\ 0 & 0 & 0 & 1 \end{bmatrix}\)

      • 카메라의 기저를 기반으로 생성

        • 카메라의 방향 벡터를 기반으로 카메라의 기저를 생성하는 방법은 여기를 참조

          • 단, DirectX에서는 행 기준으로 연산함에 유의
    • 최종 View Matrix(\(V = RT\)): \(\begin{bmatrix} r_x & u_x & f_x & 0 \\\\ r_y & u_y & f_y & 0 \\\\ r_z & u_z & f_z & 0 \\\\ -r \cdot \mathrm{EYE} & -u \cdot \mathrm{EYE} & -f \cdot \mathrm{EYE} & 1 \end{bmatrix}\)

5. Projection Matrix

  • View(Camera) 좌표를 Clip 좌표로 변환하는 행렬

  • 원근 효과 생성, View Frustum(Clipping할 범위) 정의의 역할

  • Projection Matrix(\(P\)): \(\begin{bmatrix} \frac{1}{\tan{(\mathrm{FOV_y})} \cdot \mathrm{aspect}} & 0 & 0 & 0 \\\\ 0 & \frac{1}{\tan{(\mathrm{FOV_y}})} & 0 & 0 \\\\ 0 & 0 & \frac{f}{f - n} & 1 \\\\ 0 & 0 & \frac{-n \cdot f}{f - n} & 0 \end{bmatrix}\)

    • \(FOV_y\)는 보통 사용자가 직접 지정(\(FOV_x\)는 \(FOV_y\)와 화면비로 결정됨)

    • \(\mathrm{aspect}\)는 화면비, \(f\), \(n\)은 카메라의 로컬 z 기저를 기준으로 각각 far plane, near plane까지의 거리

  • 다음 글: ❎ Direct3D 11 Graphics Pipeline - 9. 프레임과 게임 루프