지난 강좌
1. OpenGL 윈도우 만들기
2. 삼각형 그리기
3. 알록달록한 삼각형 그리기
4. 텍스쳐 입히기
5. 변환 - 이동, 회전, 스케일 행렬
6. 셰이더 프로그래밍 기초 - 기반코드 작성
7. 버텍스, 프래그먼트 셰이더, 행렬 계산
전체 소스 코드 : https://github.com/sunduk/opengl-3d-examples
공간의 개념
3D 환경을 구성 하는 공간들
3D오브젝트는 여러 공간들을 거쳐서 최종적으로 2D형태의 윈도우 화면에 렌더링 됩니다. 2D게임과 3D게임 개발의 가장 큰 차이점 중 하나가 바로 이 공간에 대한 개념일 것입니다. 2D게임에서는 특별히 공간 이라는 것을 인식하지 않고 윈도우 좌표상에 바로 이미지를 출력해서 개발 하는 경우가 보통입니다. 하지만 3D게임은 카메라의 시점도 자유롭게 변경 할 수 있고 물체의 회전, 확대, 축소가 자유자재로 이루어 지기 때문에 고정된 좌표체계 하나에서만 작업하는 것은 거의 불가능 합니다. 하나의 3D 캐릭터만 하더라도 아티스트가 만들어 놓은 최초 크기가 몇 픽셀이라고 딱 잘라 말할 수는 없습니다. 게임 월드 안에 들어오면 위치도 바뀌고 크기, 방향, 색상등 거의 모든 것이 변환 됩니다.
이것이 3D게임 개발을 처음 접할 때 마주치는 또 하나의 장벽 이라고 생각 합니다. 2D게임이나 3D게임이나 최종 결과물이 평평한 모니터 위에 출력 된다는 것은 동일하지만 내부에 숨겨져 있는 과정은 완전히 다르기 때문이죠. 이 글에서는 3D물체가 어떤 원리로 변환 되고 어떤 경로를 거쳐 화면 픽셀까지 도달 하는지 배워보도록 하겠습니다.
Local(Model) space

3D 물체가 최초로 생성되는 공간 입니다. 우리가 만든 폴리곤의 버텍스들은 모두 이 로컬 공간(또는 모델 공간)에서 생성됩니다. 또한 아티스트가 3D 툴을 이용해서 캐릭터나 물체들을 만들면 이것 또한 로컬 공간에서 작업하는 것입니다. 로컬 공간을 포함한 모든 공간들은 각자 자신만의 좌표계를 갖고 있습니다. 좌표계란 3D물체의 위치를 나타내기 위한 좌표 시스템으로 이해할 수 있습니다. 3D 캐릭터를 예로 들면 발 밑이나 무게중심이 원점이 되어 각 버텍스들의 위치가 결정 되고 회전, 확대/축소 등도 이 원점을 기준으로 이루어 집니다.
World space

각각 개별 파일로 작업되어 있는 3D 물체들은 게임 안으로 로딩되어 적절한 위치에 재배치하는 과정을 거치게 됩니다. 그렇지 않으면 모두 화면 가운데에 겹쳐서 출력될 것이기 때문이죠. 3D게임에서는 하나의 씬을 구성하는 과정 이라고 할 수 있는데 이 때 사용되는 공간을 월드 공간이라고 부릅니다. 로컬 좌표계에서 작업되어 있는 각 버텍스 들은 이 월드 공간으로 옮겨져서 화면 여기 저기에 자리를 잡게 됩니다. 이 과정에서 크기가 바뀌기도 하고 바라보는 방향이 변경되기도 합니다. 물체를 구성 하는 각 버텍스들은 월드 공간을 이루는 좌표계를 기준으로 그 위치가 바뀌게 됩니다. 이제 개별적인 로컬 공간에서 벗어나 다른 물체들과 상호작용 하는 공간으로 재배치 되는 것입니다. 이 과정은 로컬 좌표계 기준으로 구성되어 있는 각 버텍스들에 월드 행렬을 곱해 이루어지게 됩니다. 앞서 예제 프로그램에서 TRS행렬을 작성했던 것을 기억하실텐데 바로 이 TRS행렬을 월드 행렬(World matrix) 또는 모델 행렬(Model matrix) 이라고 부릅니다.
View(Camera) space

월드 행렬을 적용한 물체들은 다 적절한 위치로 재배치가 되었으나 아직 렌더링 할 준비가 끝난 것은 아닙니다. 3D게임에서는 시점을 자유롭게 바꿀 수 있고 이것에 따라 화면에 출력되는 물체들의 위치가 바뀌게 됩니다. 마치 카메라를 들고 동영상을 찍는 것에 비유할 수 있습니다. 게임의 매 프레임 마다 어떤 물체들을 화면에 렌더링 할지 결정하는 작업이 필요한데 이것이 이루어지는 공간이 뷰 공간 입니다. 로컬 공간에 있던 버텍스들이 월드 행렬을 만나 월드 공간으로 온 것 처럼, 월드 공간에 있는 버텍스들은 또 다시 뷰 행렬(View matrix)을 만나 뷰 공간으로 변환 됩니다. 뷰 공간은 3D 환경에서 가상의 카메라가 존재한다고 가정한 뒤 이 카메라의 위치를 원점으로 잡고 모든 버텍스들을 다시 재배치 하는 개념입니다. 3D게임에서 카메라를 움직이고 회전하는 것은 사실 카메라는 가만히 있고 다른 물체들이 카메라의 반대 방향으로 움직이고 회전 하는 것입니다. 현실이 아닌 가상 세계에서는 카메라 라는 것이 실제로 존재할 수 없고 단지 작동 방식만 흉내내어 보여주는 것이죠.
Clip space

최초에 로컬 공간에서 배치 되어 있던 버텍스들은 월드, 뷰 행렬을 적용 받아 뷰 공간 까지 도달 했습니다. 이제 화면에 어디까지 보여주고 원근감은 어느정도로 할지 결정하는 단계가 남았습니다. 이 과정에서는 각 버텍스에 프로젝션 행렬(Projection matrix)을 적용하여 보다 현실감 있는 장면을 만들게 됩니다. 우리 눈으로 보는 것 처럼 물체에 원근감을 적용할 수 도 있고, 얼마나 멀리 까지 볼 것인지 또는 얼마나 가까운 물체까지 렌더링 할 것인지를 정하는 단계입니다. 이 과정에서 우리가 정한 범위 밖에 있는 버텍스들은 모두 제거 됩니다.
Screen space
긴 여행을 거쳐 클립 공간 까지 도착한 버텍스들은 스크린 공간으로 넘어가기 전에 -1 ~ +1사이의 범위를 갖고 있는 NDC좌표계로 다시 한번 이동하게 됩니다. 이후 최종 게임 화면이 될 윈도우 화면으로 들어가게 되는데 이것을 스크린 스페이스(화면 공간)라고 하며 각 버텍스에 뷰포트 행렬(Viewport matrix)을 곱해서 이 공간으로 재배치 됩니다. 이 공간에 들어온 3D 물체들은 이제 윈도우에 바로 출력 할 수 있는 2D형태로 변환된 상태이며 각 폴리곤 마다 렌더링 할 픽셀 숫자도 결정이 됩니다. 이 픽셀들에 적절한 색상을 입혀주는 작업이 앞서 셰이더 기초 시간에 잠깐 다루었던 프래그먼트(픽셀) 셰이더입니다.

NDC(Normalized Device Coordinates)

Screen space
이번시간에는 3D공간의 개념에 대해서 이론적으로 알아봤습니다. 다음번에는 실제로 공간 변환 로직을 통해 3D물체를 2D화면에 출력하는 코드를 구현해 보도록 하겠습니다.
감사합니다.