간단한 렌더링 파이프라인
그림은 간단한 형태의 렌더링 파이프라인을 설명하고 있다. 한 정점이 입력되면 이 정점은 렌더링 파이프라인 과정을 거쳐서 출력되고, 우리는 출력된 정보를 디스플레이 장치를 통해 확인할 수 있다. 먼저 정점이 입력되면 이 정점은 월드 변환(World Transformation), 뷰 변환(Viewing Transformation), 투영 변환(Projection Transformation)의 과정을 거치게 된다. 그리고 클리핑(Clipping)과 같은 추가적인 과정을 더 거치게 된다. 추가적인 과정은 위의 그림에서는 표현하지 않았다. 중요한 사실은 우리가 디스플레이로 확인할 수 있는 영상은 결국 2D로 변환된 영상이다. 하지만 우리는 DirectX9와 같은 프레임워크를 사용하여 3D 좌표 공간을 대상으로 프로그래밍한다. 여기서 알 수 있는 사실은 3D 정보가 렌더링 파이프라인을 거쳐서 2D 정보로 변환된다는 점이다. 실제로 렌더링 파이프라인을 거치면 3D 정보는 2D 정보로 변환된다.
아래 그림은 렌더링 파이프라인의 전체적인 모습을 보여준다.
그렇다면 월드 변환, 뷰 변환, 투영 변환은 각각 무엇을 의미하며, 어떻게 이뤄지는지 알아보자. 그리고 코드에서는 어떻게 구현되는지도 살펴보자.
월드 변환
우리가 월드 공간에서 나타내고자 하는 기하물체들은 정점들의 모임으로 표현된다. 직육면체의 경우 8개의 정점으로 이뤄져있다. 그렇다면 직육면체를 모델링할때 기준이 되는 좌표계는 무엇인가? 로컬 좌표계 혹은 모델 좌표계이다.
모델 좌표계를 기준으로 물체를 구성하는 정점이 정의되고, 이 정점들의 모임으로 물체를 나타낼 수 있다. 그러면 이런 모델들이 3개가 모여있는 공간 즉, 월드를 나타내고자 할 경우는 어떻게 해야할까? 각각의 모델은 자신의 기준 좌표계가 있다. 이 기준 좌표계의 (0, 0, 0)을 기준으로 3개의 모델을 나타내면 어떻게 될까? 아마 3개의 물체가 겹쳐져서 그려질 것이다. 원래 월드 공간에서 3개의 모델을 겹치게 나타내고 싶다면 아무런 문제가 없지만, 3개의 모델이 겹쳐지지 않게 그리고 싶으면 모델 좌표계를 월드 좌표계로 그대로 사용할 수 없다. 모델 좌표계가 월드 좌표계의 원점을 기준으로 얼마나 떨어져있는지를 나타낸다면 해당 모델은 다른 모델과 구분될 수 있을 것이다. 이런 생각이 바로 월드 변환의 아이디어이다.
직육면체, 삼각뿔, 원기둥 모두 각자의 모델 좌표계를 기준으로 물체가 정의되어 있다. 이를 월드 공간에 배치하기 위해서 각각의 모델 좌표계를 월드 좌표계를 기준으로 이동시키면 된다. 이동 행렬(Translate Matrix)를 모델 좌표계에 곱해주면 모델을 구성하는 각 정점은 월드 좌표계를 기준으로 하는 정점으로 변환된다.
뷰 변환, 카메라 변환
월드 공간 상에 물체를 배치했다. 배치된 물체를 보기 위한 카메라가 필요하다. 월드 공간 상의 물체가 이제 카메라의 위치를 원점으로 하는 좌표계에 배치된다. 이렇게 3차원 월드 좌표계를 카메라의 위치를 기준으로 한 카메라 좌표계로 변환하는 것을 뷰 변환 혹은 카메라 변환이라고 한다.
모델 좌표계, 월드 좌표계, 카메라 좌표계로 변환하는 과정을 다시 살펴보자.
세 물체가 각각 모델 좌표 공간에 위치한다.
세 물체가 월드 좌표 공간에 위치한다.
두 물체가 카메라 좌표 공간에 위치한다.
월드 공간에 위치하는 물체를 카메라 좌표 공간으로 변환하기 위해서는 변환 행렬이 필요하다. DirectX9에서는 카메라 변환 행렬을 쉽게 계산할 수 있는 함수를 제공하고 있다. D3DXMatrixLookAtLH(), D3DXMatrixLookAtRH()가 카메라 변환 행렬을 계산하는 함수이다.
함수의 인자를 전달하면 카메라 변환 행렬이 계산된다.- AT: 카메라가 바라보는 지점
- EYE: 카메라의 위치
- UP: 카메라의 상향 벡터
투영 변환
월드 좌표계와 카메라 좌표계는 모두 3차원 좌표계이다. 우리가 디스플레이로 확인하게 되는 렌더링 결과들은 모두 2차원의 화면이다. 이는 3차원 좌표계를 2차원 좌표계로 바꾸는 변환이 필요하다는 것을 알 수 있다.
3차원 좌표계를 어떻게 2차원 좌표계로 변환할 것인가? 가장 쉬운 방법은 3차원 좌표계의 한 축을 없애버리면 된다. 예를 들어 x, y, z 축 중에서 z 축을 제거하면, 즉 z 값을 모두 0으로 만들면 자동적으로 x, y 축으로 구성된 2차원 좌표계로 변환된다. 하지만 이런 방식으로 투영 변환하게 되면 z 값으로 구별할 수 있었던 물체 간의 앞뒤 정보 즉, 깊이 정보가 투영 변환을 거치게 되면 소실된다. 참고로 이런 문제를 해결 위해 z 버퍼(z-buffer)가 필요하다.
먼저 직교 투영(Orthographic Projection)와 원근 투영(Perspective Projection)에 대해서 살펴보자.
위의 그림은 직교 투영과 원근 투영의 방법과 결과에 대해 보여주고 있다. 직교 투영과 원근 투영의 가장 큰 차이는 투영 결과이다. 위의 그림에서 확인할 수 있듯이 직교 투영은 물체가 뒤에 있더라도 앞에 있는 물체와 크기 차이가 없다. 원근 투영은 이와 달리 앞에 있는 물체는 크게, 뒤에 있는 물체는 작게 나타낸다.
우리는 특별한 경우가 아니면 원근 투영을 사용하게 될 것이다. 투영 변환을 하면 3차원 좌표 공간에은 2차원 좌표 공간으로 변환된다. 카메라 변환 행렬처럼 투영 변환 행렬을 계산해주는 함수가 있다.
함수 인자를 전달하면 투영 변환 행렬이 계산된다.
세 가지 중요한 변환들
- 월드 변환(World Transform)
- 뷰 변환(View Transform)
- 투영 변환(Projection Transform)
y축을 기준으로 회전하는 삼각형
정리하면
- 정점은 월드 변환, 뷰 변환, 투영 변환을 거친다.
- 해당 변환 행렬을 계산하여 D3D 디바이스에 설정하면 된다.
다음 주제: [DirectX9]광원(Lights)
감사합니다. 좋은 공부가 되었습니다. ^^
답글삭제오래된 게시물에 댓글 감사합니다~
삭제와.. 정말 잘 정리하셨네요. 정말정말 감사합니다 !
답글삭제