본문 바로가기

게임 프로그래밍

DX - 투영 행렬

왜 투영 행렬이 필요한가?

  • 우리가 다루는 3D 공간의 정보는 모니터라는 2D 평면에 시각적으로 표시되어야 합니다.
  • 이때 3D 공간의 점(Vertex) 들을 2D 화면으로 변환(투영) 해주는 작업이 필요합니다.
  • 이 변환을 수행하는 수학적 도구가 바로 투영 행렬(Projection Matrix) 입니다.

 

투영 행렬의 구조

[ 1/(aspect) * scale      0               0             0 ]
[ 0                      scale            0             0 ]
[ 0                       0           f/(f - n)         1 ]
[ 0                       0         -n*f/(f - n)        0 ]
  • 이 행렬은 3D 공간의 점을 카메라 시점(View Space) 에서 NDC 공간(정규화된 장치 좌표) 으로 변환합니다.
  • 여기서 aspect, scale, n, f는 각각 다음을 의미합니다:

 

투영 행렬 요소 설명

 

NDC 공간(정규화된 장치 좌표) 

  • GPU가 화면에 그리기 직전 사용하는 좌표계로, 모든 좌표가 -1에서 +1 사이로 정규화되어 있습니다.
  • X축: 왼쪽 -1 ~ 오른쪽 +1
  • Y축: 아래 -1 ~ 위 +1
  • Z축: 0 ~ 1 (깊이값)

 

Aspect (종횡비)

  • 화면의 가로/세로 비율입니다:
  • WINCX / WINCY
  • x축 비율 조절에 영향을 주며, 수평 시야의 폭을 결정합니다.

 

Scale (시야각에 따른 스케일)

  • 카메라의 시야각(FOV, Field of View)에 따라 결정됩니다:
  • 1 / tan(FOV * 0.5)
  • y축 비율을 직접 조절하며, 결과적으로 x축도 영향을 받습니다.

 

n, f (Near, Far Plane)

  • n: 카메라에서 가장 가까운 깊이
  • f: 카메라에서 가장 먼 깊이
  • 이 두 값은 z축의 깊이값을 정규화(NDC로 보정) 하는 데 사용됩니다.

 

M11 (1행 1열): x축 크기 결정

 

행렬 예시

  • WINCX = 800, WINCY = 600 → Aspect = 4 / 3
  • FOV = 90도 = π / 2 → Scale = 1 / tan(π / 4) = 1
  • 결과: M11 = (3 / 4) * 1 = 0.75

x축 크기 비율은 0.75배

 

M22 (2행 2열): y축 크기 결정

예시

  • FOV = 90도 = π / 2 → tan(π / 4) = 1
  • 결과: M22 = 1 / 1 = 1

y축 크기 비율은 1배

 

 

M34 (3행 4열): 동차 좌표 w 생성

  • 값: 1
  • 이 요소는 투영 과정 후 동차 좌표의 w 값을 설정해줍니다.
  • 이로 인해 최종적으로 (x, y, z) 좌표들이 w로 나뉘며, 원근감(Perspective) 이 표현됩니다.

 

동차 좌표(Homogeneous Coordinates)와 투영

  • 3D 공간의 점: (x, y, z, w=1)
  • 투영 행렬 적용 후: (x’, y’, z’, w’ = z)
    • x’ = x * (1 / Aspect * Scale), y’ = y * Scale, z’ = z(f/(f - n) + -n*f/(f - n)), w’ = z
  • 최종 2D 화면 좌표로의 변환:

 

왜 나누기(w')를 해야 할까?

  • w'는 View 공간 상에서의 깊이(z값)입니다.
  • 이 값을 통해 가까운 객체는 크게, 먼 객체는 작게 보이도록 원근감을 적용합니다.
  • 결과적으로 (x'/z, y'/z, z'/z)가 되어 NDC 공간의 2D 투영 좌표가 완성됩니다.

 

Q1. 왜 FOV가 90도일 때 크기 비율이 1인 이유.

  • Scale = 1 / tan(FOV / 2)
  • FOV = 90도 → tan(45도) = 1
  • 따라서 y축 비율 = 1배가 됩니다.

y 크기 지정

  • x축에는 종횡비를 나눠주는 이유는, 현재 모니터는 대부분 가로가 더 긴 직사각형입니다.
  • 때문에 x축 스케일은 다음과 같이 조정되어야 합니다.

x 크기 지정

  • y축은 정사각형 기준으로 맞췄으므로, 가로 방향 스케일을 줄여주어야 정사각형이 찌그러지지 않습니다.

 

Q2. 왜 z 나누기를 해야 하나요?

  • DX에서 (Vector와 Vertex를 구별할 때 4번째 좌표인 w 값을 이용 하여 구별합니다.
    • 이때 w≠0 이면 Vertex, w=0이면 방향 벡터로 취급됩니다 (DX의 규칙).  
  • 원근 투영 행렬을 곱한 결과는 동차 좌표계로 표현된 4D 벡터 (x’,y’,z’,w’) 가 나옵니다.
  • 화면에 그리기 위해서는 정규화된 3D 좌표로 바꾸어야 합니다. 이를 위해 동차 좌표를 비동차화 합니다.

비동차화

  • 이 나눗셈은 2D 평면에서의 원근 투영을 완성하는 마지막 단계입니다.

 

끝으로..

부족하지만 제가 이해한 부분을 정리한거라. 
특정 부분이 틀렸거나, 조금 더 깔끔한 설명이 가능하시다면 피드백 댓글로 남겨주시면 감사하겠습니다.

'게임 프로그래밍' 카테고리의 다른 글

동차 좌표  (0) 2025.04.20
몬스터 행동 패턴 구현 정리 (2D)  (0) 2025.04.03