View Matrix (뷰 행렬)
카메라와 좌표계
- 월드 좌표계와 카메라 좌표계는 서로 반대 방향
- 서로 반대되는 좌표계로 인해 x와 y값이 반대로 뒤집힘
- 원래 좌표계를 사용하고 싶다면 카메라를 180도 회전시켜 +z축이 카메라 뒤편을 향하도록 설정
- 카메라는 크기 변환이 없으므로 이동(Translation)과 회전(Rotation)만 고려
뷰 행렬 변환
- 카메라 좌표를 t(t1, t2, t3)로 가정
- 이동 행렬 T와 회전 행렬 R은 아래와 같음

- 회전 행렬 R은 직교행렬이므로 회전행렬 R의 전치는 역행렬과 동일

- 이동 행렬 T도 마찬가지로 전치를 통해 역행렬 계산

- 위의 두 행렬을 곱한 최종 뷰 행렬 변환

뷰 행렬의 계산 순서
- 기존 변환과 반대로 이동의 역행렬 적용 후 회전의 역행렬 적용
- 모든 좌표는 카메라 중심으로 이동한 후 회전되어야 함
- 최종적으로 3D 장면을 2D 평면에 표현
최종적으로 2차원 평면에서 3차원 공간감을 느낄 수 있도록 변환
원근 투영 변환 : 눈이 세상을 보는 방식으로 공간 변환
투시 원근법과 시야각
- 투시 원근법 : 모든 점이 한 소실점을 향해 수렴하는 방식
- 3차원 공간에 투시 원근법을 적용하기 위해 공간의 모든 점이 한 점을 향해 모이는 형태로 변환

- 화각 (Field of View) : 원근 투영 변환을 설계하기 위해 시야 범위 설정
- 카메라에 화각 생성시, 상하좌우가 균등한 사각뿔 영역 생성
- 원근 투영 변환 : 뷰 공간을 카메라의 한 점으로 모이는 사각뿔 형태를 가진 공간으로 변환하는 작업
투영 평면과 초점 거리
- 투영 평면 : 물체의 상이 맺히는 가상의 평면
- 투영 평면의 크기는 카메라와 멀어질수록 더 커짐
- 초점 거리 (Focal Length) : 카메라에서 투영 평면까지의 거리

- 화각 : theta, 초점 거리 : d
- 원근 투영을 계산하기 위해 투영 평면의 위치를 먼저 지정해야 함
- 일반적으로 계산의 편의를 위해 위, 아래 크기가 1이 되는 지점으로 결정
- 상하좌우의 화각이 동일하므로 투영 평면은 [-1, 1]의 범위를 갖는 정사각형

- NDC (Normalized Device Coordinate)
- 2차원 투영 평면의 좌표 시스템
- 화면에 출력되기 직전 단계의 좌표계
- 위에서 설명한 [-1, 1]의 범위로 정규화된 3차원 공간
- 클립 좌표 단계에서 투영 변환을 거친 후 동차 좌표를 w값으로 나누어 생성
NDC와 투영 평면의 관계
- 투영 평면에 대응하는 NDC가 일정한 값을 가져야 한다면 카메라의 화각이 변할때 초점 거리 또한 달라짐
- 화각이 커질수록 초점 거리는 가까워짐
- 화각과 초점 거리의 관계는 tan함수로 설정

- tan함수로 초점 거리를 구한 후, 뷰 공간의 점을 투영 평면 위의 점으로 대응시키는 원근 투영 변환 설계
투영 변환의 기하학적 해석
- 유클리드 공간 : 3차원 공간에서 세 축이 직교하는 공간 (로컬, 월드, 카메라 공간)
- 정육면체의 유클리드 공간은 원근 투영 변환에 따라 사각뿔 형태로 전환 (사영 공간, Projection Space)
- 사영 공간의 x,y축은 여전히 직교, z축은 x축과 y축에 모두 영향
- 초점 거리에 따라 x축과 y축이 만들어내는 투영 평면의 면적이 달라지는 이유
- 비례식을 통한 투영 좌표 유도
- 유클리드 공간이 사영 공간으로 변환되며 축이 직교하는 체계가 달라지기 때문에 표준 기저 벡터의 변화를 관찰하는 방법으로는 생성 불가
- x축을 배제한 y축과 z축으로 공간 설정, 삼각형의 닮은 꼴 성질로부터 비례식 생성

- 위의 비례식으로 아래의 식 성립

- x축과 y축은 서로 같은 시야각, 서로 직교하므로 x축도 동일

- 투영된 최종 좌표

- 좌표를 계산했다면 모니터의 최종 화면 구성, NDC 좌표를 화면 해상도만큼 가로와 세로를 늘려줌

- 대부분의 경우 최종 화면이 1:1 비율이 아닌 가로가 더 긴 비율 사용 => 물체가 찌그러짐
- 이를 방지하기 위해 종횡비(가로 세로의 비율)를 미리 파악해 NDC 영역에서 미리 찌그러트린 후에 펼침
Aspect Ratio (종횡비)
- 좌우로 찌그러트리려면 x축 값 변경, 종횡비의 역수를 곱하면 가능 (1/a)

- 최종 NDC 값을 계산하는 원근 투영 행렬

- 원근 투영 행렬을 사용할때 변환할 점의 z값이 행렬에 사용되기 때문에 변환할 점마다 항상 새롭게 계산해야 함
- 모든 변환의 행렬을 미리 곱해둔 행렬을 적용해 반복되는 연산량 감소의 이점 사라짐
- 따라서 원근 투영 행렬을 구성할 때 점의 값을 사용하지 않고 카메라 설정만으로 행렬 제작
- 위의 투영 행렬의 구성 요소는 모두 -v(z)값을 분모로 가지므로 행렬에서 값 제거 후 결과값에 -v(z)를 나누어줌
- 최적화를 적용한 NDC 값을 만드는 과정
- 행렬 곱에서 생성된 벡터는 최종 NDC 좌표와는 다르지만 원근 투영 행렬 P점과 무관하게 카메라 설정 값으로만 구성

- 클립 좌표 : 원근 투영 행렬 P로 변환되는 좌표계

- 클립 좌표 점 P의 요소를 P의 세번째 값인 -v(z)값으로 나눔

Depth vs Distance (깊이 vs 거리)
깊이값과 거리
- 원근 투영에서는 한 점(x,y,z)이 Near Clipping Plane 위의 점(x',y',z')으로 투영
- 3차원 공간상에 있는 물체에 의해 (x',y',z') 위치에 있는 픽셀의 색 결정
- 픽셀의 깊이 값 : z-near
- 카메라와 물체 사이의 거리 : sqrt(x^2 + y^2 + z^2), 카메라의 좌표가 (0,0,0)일 경우
- 깊이로부터 거리 계산 가능

깊이값 (행렬 유도)
- 3차원 공간에서 다수의 물체를 그릴 때에는 어떤 물체가 앞에 있는지 확인하기 위해 그리는 순서 고려
- 물체가 카메라로부터 얼마나 떨어져 있는지에 대한 정보 기록
- 앞에 있는 물체를 나중에 그리도록 설정
- 카메라에 부여한 시야각은 깊이값과 무관하기 때문에 카메라에 추가 속성 부여 (근평면, 원평면)
- 근평면 (Near Plane), 원평면 (Far Plane)

- 절두체 : 카메라 시야를 구성하는 사영 공간을 근평면과 원평면으로 잘라준 형태
- NDC 깊이값은 멀어질수록 증가, NDC 공간은 왼손 좌표계
- DirectX에서는 0~1 사용
- 기존 행렬에서 쓸모 없던 한 행을 사용해 깊이 값까지 구할 수 있는 최종 행렬 도출

- 깊이 값을 활용하는 3행은 4개의 미지수 i, j, k, l
- 깊이 값은 뷰 좌표계의 x축과 y축에 각각 직교하므로 영향 없음
- 따라서 i, j = 0

- k와 l을 구하기 위해서는 2개의 샘플 데이터 필요

- 카메라로부터 근평면까지의 거리 n, 원평면까지의 거리 f로 가정
- 근평면의 점을 p1, 원평면의 점을 p2라고 할때 다음과 같이 계산

- 클립 좌표의 네번째 요소로 모든 요소를 나눈 값은 NDC 좌표, NDC 좌표의 z값은 각각 -1, 1
- 따라서 근평면의 클립 좌표는 (0, 0, -n, n), 원평면의 클립 좌표는 (0, 0, f, f)

- 이로부터 연립 방정식 도출 가능
- 최종 투영 행렬

참고 및 출처:
게임 개발을 위한 수학
oh.