WonHada.com으로 이전

렌더링 파이프라인을 따라 3D 세계로 가자 본문

공부X3

렌더링 파이프라인을 따라 3D 세계로 가자

반주부 2008. 6. 25. 07:30
반응형


[Flash] http://labs.apollocation.co.kr/applications/publicMP3Player/assets/PublicMP3Player.swf


플래시 플레이어에서 3D 지원이 현실화 되었으니 이제 우리도 3D에 대한 지식을 넓혀 가야할 것 입니다..아래 좋은 글이 있어 가져 왔습니다..

===================================================================================

과거의 PC에서 3D 그래픽을 하기 위해서는 특별한 하드웨어의 도움없이 모든 걸 소프트웨어적으로만 처리해야 했다. 그래서 3D 엔진의 개발은 최종적으로 화면에 표시되는 조그만픽셀 하나에 이르기까지 세부적인 3차원 렌더링의 모든 과정을 충분히 이해해야 가능한 일이었다. 하지만 요즘의 PC에는 3D 그래픽을 고속으로 처리해주는 3차원 가속 카드가 기본으로 장착됐고, OpenGL과 다이렉트3D 같은 표준적인 3D API의 활용으로 인해 과거처럼 3D 렌더링 파이프라인에 대한 깊은 지식 없이도 비교적 손쉽게 3차원 영상을 표현할 수 있다.

그러나 과거에 비해 3D 그래픽 프로그래밍에 손쉽게 접근할 수 있게 된 반면 3D 렌더링 파이프라인 상의 처리과정을 충분히 이해해야 가능한 렌더링 속도의 최적화에 대한 지식이 부족한 3D 프로그래머를 양산하는 결과를 가져오고 말았다. 국내에도 최근에 3D 온라인 게임의 붐과 함께 개발중이거나 베타 테스트 중인 3D 온라인 게임중 일부는 높은 수준의 3D 영상이 아님에도 불구하고 비교적 저사양의 3D 가속 카드에서 제대로 된 처리 성능을 내지 못하고 있거나, 심지어 비교적 고사양의 3D 가속 카드에서도 매끄럽게 실행되지 못하는 경우가 있다.

이러한 문제의 해결은 단순히 3D API의 사용을 능숙하게 하는 것으로는 부족하고 3차원 렌더링의 전과정을 충분히 이해한 바탕에서 다양한 렌더링 최적화 기법들을 적용해야 해결되는 문제이다. 그래서 이번호에서는 우선 3차원 데이터들이 어떤 과정을 거쳐서 최종적인 영상으로 만들어지는지, 렌더링 파이프라인을 따라가면서 주로 저 수준의 최적화 방법을 알아보고 다음호에서는 렌더링 파이프라인의 작동 과정을 이해한 바탕에서 렌더링 파이프라인에 공급되는 데이터를 최소화하고 처리 속도를 올릴 수 있는 고수준의 3차원의 데이터 구조와 다양한 최적화 기법들을 알아보기로 한다.

[Flash] http://labs.apollocation.co.kr/applications/publicMP3Player/assets/PublicMP3Player.swf



3차원 물체의 렌더링 방법과 데이터적 표현

3차원 물체를 표현하는 방법은 여러 가지가 있지만 PC에서 표현되는 3차원 물체들은 대부분 몇 개의 꼭지점(Vertex)로 구성된 다각형을 기본 단위로 해서 처리하는 폴리곤 렌더링이 기본이다. 반면 의료분야에서 X선 CT나 MRI 같은 의료 장비에 사용되는 렌더링 방법은 볼륨 렌더링(Volume Rendering)이라 불리우고, 3차원 물체의 체적을 구성하는 복셀(Voxel)을 기본단위로 해서 영상 처리가 이뤄진다.

불륨 렌더링의 기본 단위인 복셀은 체적(Volume)을 구성하는 최소단위로서‘Volume Cell’의 합성어이다(2D 화면상의 영상을 구성하는 최소단위를 일컫는 픽셀(Pixel)도 Picture Cell의 합성어이다).

볼륨 렌더링

볼륨 렌더링은 게임 분야의 경우 노바로직사의‘코만치’라는 헬기시물레이션 게임에서 Voxel Space라는 이름으로 처음 사용됐는데, 부드러운 자연지형을 묘사하는 데 아주 뛰어나서 당시 주목을 받았었다. 그 이후 최근까지도 노바로직의 여러 게임들과 일부 다른 게임들에서 볼륨 렌더링 기법이 사용되기는 했지만 3D 가속 카드의 보편화와 함께 게임 분야에서는 이제 완전히 과거로 사라진 렌더링방법이 됐다. 불륨 렌더링 방식을 지원하는 3D 가속 카드가 없기때문이다.

폴리곤 렌더링

PC에 장착되는 3D 가속 카드의 렌더링 방식은 소위 폴리곤 렌더링이라고 불린다. 폴리곤, 즉 다각형을 이용해 3차원 물체를 묘사하는 방법을 채용하고 있다. 폴리곤 렌더링처럼 다각형을 기본 단위로 해서 3차원 물체를 처리하는 방법은 다각형을 구성하는 꼭지점에 대해 필요한 처리를 하는 것만으로도 전체 3차원 물체를 다룰 수 있기 때문에 처리량을 상당히 줄일 수 있는 이점이 있다.

곡면 렌더링

패치(patch)라고 불리는 조그만 곡면 조각들의 조합으로 3차원 물체를 구성할 수도 있다. 이런 방식은 게임의 경우 퀘이크3에서 부드러운 곡면을 묘사하기 위해 처음으로 이용된 방법인데(퀘이크3에 사용된 곡면은 가장 간단한 베지어 곡면이다), 3차식 이상의 고차 매개변수 방정식을 이용해 곡면을 기술해야 하기 때문에 그 동안 속도를 요하는 게임 분야에서는 거의 사용되지 않았다. 그래서 일찍이 고성능의 워크스테이션을 무대로 탄생한 OpenGL API의 경우에나 초기버전부터 GLU라는 유틸리티 라이브러리를 통해 이러한 곡면을 처리하는 API를 지원했지만, 다이렉트3D의 경우 최근의 8.0버전에 와서야 다이렉트3DX라는 유틸리티 라이브러리를 통해 처음으로 곡면을 지원하게 됐다. 물론 근본적으로 현재 PC의 3D 가속 카드의 경우 그러한 곡면을 직접적으로 묘사하는 기능이 없지만, 곡면은 삼각형들을 조합하면 근사적으로 표현할 수 있다.

[Flash] http://labs.apollocation.co.kr/applications/publicMP3Player/assets/PublicMP3Player.swf



표준 방식이 된 폴리곤 렌더링

앞에서 알아본 세가지 방식의 렌더링 방법 중에서 폴리곤 렌더링이 가장 적은 비용으로 효율적인 3차원 영상의 묘사가 가능하기 때문에PC에 장착되는 3D 가속 카드의 렌더링 방식으로 채용됐고, 지금은 사실상의 표준적인 방식이 됐다. 물론 의료 분야에서도 고속의 CT영상 처리가 필요해지고, 가정마다 일상의 가전제품(?)처럼 CT 장비가 보급된다면 볼륨 렌더링을 지원하는 저가의 하드웨어가 등장할 수도 있을 것이다. 하지만 CT 영상에 게임처럼 고속의 처리가 필요할 지는 의문이다. 사실 고속의 처리를 요구하는 게임 소프트웨어가 아니었다면 3D 가속 카드가 PC에 장착되는 기본적인 하드웨어가 될 수 없었을 것이다. 아마도 전문적인 3D 영상을 다루는 고가의 워크스테이션 장비에나 장착되는 하드웨어로 머물렀을 것이다.

메시, 삼각형, 꼭지점

앞에서 알아본 것처럼 보통 PC에서 구동되는 3D 엔진은 폴리곤 렌더링 방식을 채용하기 때문에 3D 엔진에서 처리되는 3차원 물체의 데이터는 세 개의 꼭지점으로 구성되는 삼각형을 기본 단위로 한 다각형들의 조합으로 이뤄져 있다. 이러한 삼각형들의 조합으로 이뤄진 3차원 물체를 보통 메시(mesh)라 하고, PC에서 운용되는 3D 스튜디오 맥스, 마야 등의 3D 그래픽 툴도 기본적으로는 삼각형을 단위로 3차원 물체를 다루고 있다. 그리고 이러한 삼각형과 삼각형들의 조합인 메시는 근본적으로 꼭지점을 가장 기본적인 구성 단위로 하고 있다.
이러한 꼭지점들은 3차원 처리의 가장 기본이 되는 좌표변환의 대상이고 조명계산의 기본 단위가 된다. 다음은 꼭지점의 데이터를 담는 기본적인 구조체이다.

struct VERTEX {
         float x, y, z; // 위치
         float nx, ny, nz; // 법선 벡터
         float tu, tv; // 텍스처 좌표
};

[Flash] http://labs.apollocation.co.kr/applications/publicMP3Player/assets/PublicMP3Player.swf



폴리곤 렌더링 방식은 대부분의 처리를 꼭지점을 대상으로 하고 영상을 2차원 화면상에 수많은 픽셀의 조합으로 표시하는 최종적인 단계에 가서나 꼭지점을 단위로 처리된 데이터를 바탕으로 삼각형의 내부를 여러 가지 색의 픽셀들로 채우게 된다. 그렇기 때문에 3차원 물체를 표현하는 데 사용하는 데이터 중에서 가장 중요한 데이터는 바로 꼭지점 데이터라고 할 수 있다. 간혹 3D 엔진을 개발하는 이들 중에 꼭지점이 아닌 삼각형을 기본 단위로 데이터 구조를 잡았다 낭패를 보는 경우가 있다. 폴리곤 렌더링이라는 말에 현혹돼서 그런 듯한데, 왜 삼각형이 아니라 꼭지점을 기본 단위로 데이터를 처리해야하는지 알아보자.

[Flash] http://labs.apollocation.co.kr/applications/publicMP3Player/assets/PublicMP3Player.swf



꼭지점이 중요한 이유

한 조각의 메시가 여러 개의 삼각형들의 조합으로 이뤄져 있고, 또한 이러한 개별 삼각형이나 전체 메시는 꼭지점들의 조합으로 이뤄져 있음을 확인할 수 있다. 가운데 a라고 표시된 꼭지점에 주목해보자. 이 꼭지점은 주변을 둘러싸고 있는 여러 개의 삼각형이 서로 공유하는 꼭지점으로 1번부터 6번까지 모두 6개의 삼각형이 a라는 꼭지점을 공유하고 있다. 만약 삼각형을 단위로 해서 모든 처리가 이뤄진다면 a라는 꼭지점의 데이터는 각 삼각형마다 한번씩 무려 6번의 처리를 반복해 받게 된다. 그런데 그 처리가 만약 좌표변환과 조명계산이라면 반복할 필요가 없는 완전히 동일한 처리가 된다. 별 생각 없이 삼각형 단위로 루프를 돌면서 삼각형을 구성하는 세 개의 꼭지점에 대해서 처리를 한다면 일반적인 형태의 메시의 경우 6번의 동일한 처리를 반복하게 되는 것이다.
물론 삼각형이 많지 않은 간단한 물체들을 처리한다면 큰 문제는없겠지만, 온갖 잡다한 물체들이 난무하는 게임에서 저렇게 처리되고 있다면 렌더링 성능에 지대한 악영향을 미칠 것이다. 실제로 초보프로그래머들이 이런 실수를 종종 저지른다. 이런 일이 발생하는 이유는 대개 3D API의 사용법을 설명하기 위한 기본 예제를 기초삼아 프로그래밍을 하기 때문이다. 그러나 그런 예제들은 간단한 예로서 API의 기능 설명이 우선이므로 간단한 3차원 물체들만 처리하는 게 보통이다. 제대로 최적화된 코드로 작성된 것이 아니더라도 별로 문제가 드러나지 않다가, 본격적인 3D 애플리케이션을 위해 다량의 데이터를 사용하면 그제서야 겉으로 문제가 드러나는 것이다. 사실 공유되는 꼭지점의 데이터를 반복해 처리하지 않도록 하는 방법은 보통 3D API에서 제공된다. 구체적인 API에 대해서는 뒤에서 다루기로 한다.

[Flash] http://labs.apollocation.co.kr/applications/publicMP3Player/assets/PublicMP3Player.swf



파이프라인이란

렌더링 파이프라인에서‘파이프라인’이라는 표현은 3차원 물체를 구성하는 꼭지점의 데이터들이 최종적인 화면상의 픽셀들로 바뀔 때까지 여러 가지 필요한 처리를 단계적으로 거치면서 순차적으로 다음 단계로 전해지기 때문이다. 이는 마치 화합물의 재료가 화학공정의 파이프라인을 따라 흐르는 것과 유사하기 때문에 붙여진 이름이다. 결국 렌더링의 속도를 높이는 것은 이러한 파이프라인 상에서 데이터들이 어떤 과정을 거쳐서 처리되는지를 잘 이해해야 가능한 일이다. 그럼 폴리곤 렌더링 방식에서 렌더링 파이프라인이 어떻게 3차원 데이터들을 처리하는지 알아보자.

렌더링 파이프라인을 따라서 3차원 물체를 2차원 화면에 최종적으로 묘사하기까지 매우 많은 과정을 거쳐야 한다. 특히 고속의 화면 처리를 위해 실시간으로 렌더링을 수행해야 하는 게임 애플리케이션의 경우 여러 가지 묘법(?)들이 있기 마련이다. 하지만 필자는 여기서 어떤 묘법을 얘기하려는 게 아니라 기본적인 원리를 통해 그야말로 기본적인 최적화 기법을 설명하려는 것이다. 정말 아쉬운 것은 아직까지는 국내에 3D 프로그래머 인력이 많지 않을 뿐더러 설사 있다 하더라도 단순히 3D API 사용법만 익힌 수준에서 3D 프로그래밍을 하는 이들이 많기 때문에 기본적인 최적화 지식 없이 만들어진 게임들이 나오고 있는 것이다.

그러면 과연 폴리곤 렌더링 방식의 경우 렌더링 파이프라인이 어떻게 구성돼 있는지 살펴보자. 과거 3D 가속 카드가 없던 시절에는 모든 걸 소프트웨어로만 처리해야 했기 때문에 표준적인 파이프라인이 딱히 있지도 않았고, 느린 시스템에서 조금이라도 속도를 높이기 위해 온갖 묘법이 난무했다. 하지만 근래들어 3D 가속 카드가 기본이 되고 이런 하드웨어의 기능을 인터페이스해주는 3D API인 OpenGL과 다이렉트3D가 사실상 양대 표준으로 자리잡으면서 과거와는 달리 렌더링 파이프라인도 상당히 표준화(?)됐다고 볼 수 있다.

[Flash] http://labs.apollocation.co.kr/applications/publicMP3Player/assets/PublicMP3Player.swf



렌더링 파이프라인의 구성

3D 렌더링 파이프라인은 여러 단계의 처리 모듈들로 구성돼 있다. 이 모듈들은 장면 그래프 모듈(Scene Graph module), 좌표변환 모듈(Transform module), 조명 모듈(Lighting module), 삼각형 설정 모듈(Triangle Setup module), 주사 변환 모듈(Rasterizer) 등 크게 5가지 모듈로 구분할 수 있다. 이중에서 장면 그래프 모듈을 제외한 좌표변환 모듈에서 주사변환 모듈까지 네 가지 모듈은 양대 3D API인 OpenGL과 다이렉트3D에 구현돼 있고, 특히 엔비디아의 지포스 계열과 ATI의 라데온 계열의 그래픽 카드들은 이 네 가지 모듈의 기능이 모두 하드웨어에 구현 돼 있다. 3D 프로그래머의 입장에서는 모든 사용자가 적어도 이런 3D 가속 카드만 장착하고 있으면 좋을 것이다. 그러나 국내의 일부 상용 3D 게임들처럼 이 카드들만 지원하겠다고 하는 건 몹시 곤란한 일이다.

장면 그래프 모듈

장면 그래프 모듈이 담당하는 역할은 3차원 화면을 구성하는 수많은 3차원 물체들의 데이터를 통합적으로 관리하고 다음 단계의 렌더링 단계로 유효적절하게 공급하는 것이다. 아직 이런 기능은 애플리케이션이 직접 담당하고 있고, 애플리케이션마다 워낙 다양한 기법들이 존재하기 때문에 특정 API 형태로 표준화하기 힘들다. 더구나 하드웨어에서 처리되도록 하는 것은 아직 요원한 일이다. 렌더링 파이프라인을 언급할 때는 장면 그래프 모듈을 제외한 나머지 네 가지 모듈만을 지칭하는 경우가 보통이다.

좌표변환 모듈

좌표변환 모듈이 담당하는 역할은 3차원 데이터를 한 좌표계에서 다른 좌표계로 변환해 다음 단계에서 필요한 형태로 변환하는 것이다.
여기서는 행렬연산을 이용한 좌표변환 계산이 이뤄지며, 그동안 주로 소프트웨어적으로만 처리되던 기능이다. 이 때 이 모듈에 공급되는 데이터는 바로 꼭지점의 데이터로 꼭지점의 위치를 나타내는 3차원 좌표 값이 주 처리 대상이 된다. 이와 더불어 조명 모듈이 조명 계산을 하는 데 필요한 법선 벡터의 변환도 담당한다

[Flash] http://labs.apollocation.co.kr/applications/publicMP3Player/assets/PublicMP3Player.swf



조명 모듈

조명 모듈이 담당하는 역할은 꼭지점에 저장된 법선 벡터를 이용하여 광원에서 나오는 빛이 꼭지점에 미치는 영향을 벡터연산을 이용해 계산하는 것이다. 이렇게 꼭지점에 대해서만 계산된 색이나 밝기는 최종적으로 주사변환 모듈에 가서 삼각형을 구성하는 세 꼭지점의 값을 기초로 최종적인 삼각형 면의 색과 밝기를 계산하게 된다

삼각형 설정 모듈

삼각형 설정 모듈이 담당하는 역할은 다음 단계인 주사변환 모듈이 주사변환 작업을 하는 데 필요한 예비적인 처리를 미리 해주는 것이다. 좌표변환 모듈과 조명 모듈의 처리를 거치고 나면 꼭지점의 데이터는 최종적으로 2차원 화면 상에 표시될 위치로 좌표값이 변환돼 있고, 법선 벡터 대신에 조명모듈이 계산해준 꼭지점의 색과 밝기 값을 지니게 된다. 이 모듈은 이름이 말하듯 이들 꼭지점의 데이터를세 개씩 묶어 하나의 삼각형을 구성하는 역할을 한다.
이 때 하나의 삼각형을 구성하게 되는 세 꼭지점의 좌표 값과 색깔값을 기초로 삼각형의 전체 면을 채우는 데 필요한 여러 가지 그레디언트(gradient) 값들을 계산해 주사변환 모듈에 넘기는 일을 한다.
이러한 그레디언트 값들은 주사변환 모듈이 삼각형의 면을 세 꼭지점의 값을 기초로 면을 채울 나머지 값들을 내삽(Interpolation)하는데 사용된다.
이 모듈은 또한 삼각형의 클리핑 기능도 담당하는데 좌표변환 모듈이 각각의 꼭지점이 가시영역에 포함되는지를 판정해 표시한 일종의 플래그 값을 기초로, 가시영역 안팎에 걸쳐 있는 삼각형을 잘라 가시영역 밖에 있는 부분을 제거하는 역할을 한다. 이 때 잘려지고 남는 부분이 삼각형이 아니고 사각형이 되는 경우도 있으므로 이런 경우에는 이를 다시 두 개의 삼각형으로 쪼개는 일도 하게 된다.

주사변환 모듈
주사변환 모듈이 담당하는 역할은 삼각형 설정 모듈에서 계산한 각종 그레디언트 값을 기초로 삼각형의 면을 모니터의 주사선을 따라 픽셀로 채우는 것이다(<그림 7>). 이 단계가 렌더링 파이프라인의 최종적인 단계이며, 어쩌면 가장 복잡한 처리가 이뤄지는 단계이기도하다. 텍스처 매핑, 안개효과, 알파 블렌딩, z-버퍼링 등이 모두 이단계에서 이뤄진다. 특히 텍스처 매핑의 경우는 과거처럼 단순히 한장의 텍스처만 사용되는 게 아니라 여러 장의 텍스처를 다양한 방법으로 동시에 겹쳐 찍을 수 있다. 사실 이 모듈이야말로 그동안 3D 가속 카드들이 열심히 그 기능을 경쟁했던 영역으로 전체적인 렌더링 속도에 있어서 가장 중요한 부분이다.

그래서 과거 모든 렌더링 기능을 소프트웨어로만 처리했던 시절에는 빠른 주사변환 모듈의 작성이 3D 프로그래머들의 가장 중요한 관심사였다. 특히 텍스처 매핑 기능의 경우 국내 모 PC 통신 서비스의 게임 제작 동호회에서는 프로그래머들 간에 서로 자기가 만든 게 더 빠르다고 혹은 더 정교하다고 싸움(?)이 일어나기도 했었다. 그만큼 주사변환 모듈의 성능이 중요한 것이다. 사실 3D 가속 카드 시장에서의 경쟁도 알고 보면 바로 이 모듈의 성능과 지원되는 기능에 대한 경쟁인 것이다. 물론 엔비디아의 지포스 계열의 소위 GPU(Graphics Processing Unit)의 발표 이후 최근의 경쟁은 주사변환 모듈뿐만 아니라 다른 모듈로도 번졌지만, 일단 최종적으로 2차원 화면에다 다양한 효과로 픽셀들을 빨리 그려내는 게 가장 중요한 기능임에는 변화가 없다.

요즘은 3D 가속 카드 제조사들의 열띤 경쟁으로 인해 3D 프로그래머들이 빠른 주사변환 모듈을 작성해야 하는 수고로움은 없어졌지만 다양한 카드들의 호환성과 특성을 감안한 코드 작성이 필요해졌고, 특히 상업적인 목적의 게임 애플리케이션의 경우 사용자들이 모두 최신의 고사양 3D 가속 카드만 장착하고 있는 것도 아니고, 저사양의 조금 오래된 3D 가속 카드들도 지원해야 하기 때문에 결코 과거보다 편해진 건 아니다.

[Flash] http://labs.apollocation.co.kr/applications/publicMP3Player/assets/PublicMP3Player.swf



3D 가속 카드의 이해
PC에 장착되는 3D 가속 카드는 1995년경에 시장에 처음 선보였고,초기 모델은 열악한 화면과 느린 성능으로 인해 가속 카드가 아니라 감속 카드라는 욕을 먹으며 사라져 갔다. 그러나 1996년 그야말로 혜성처럼 나타나 3D 가속 카드의 성능을 유감없이 발휘한 3DFX의 부두 카드로 인해 3D 가속 카드는 폭발적인 관심을 모으며 서서히 대중화의 길을 걷게 되었고, 지금은 엔비디아의 지포스 계열이 명실상부한 시장의 왕자로 군림하고 있는 가운데 ATI의 라데온 계열이 그 뒤를 바짝 추격하고 있다.

돌이켜보면 필자는 1995년에 ATI에서 발표한 향후 그래픽 카드 시장의 판도 변화를 예측한 문서를 본 적이 있는데, 향후 모든 그래픽 카드가 3D 가속 카드가 되리라는 예측이었다. 당시는 과연 그렇게 될까 하는 의구심이 앞섰고, 초기 3D 가속 카드의 열악한 성능으로 인해 별로 기대도 하지 않았다. 그리고 3DFX의 부두 카드 광고를 외국 잡지를 통해 봤지만 거기에 실제 화면이라고 설명된 스크린샷을 도저히 믿을 수 없었다. 하지만 1996년 E3 쇼에서 실제로 부두카드의 성능을 눈으로 직접 확인할 기회가 있었고, 광고에서 본 스크린 샷이 실제 실행 화면임을 알고 무척이나 놀란 기억이 있다. 좌우지간 현재 3D 가속 카드의 핵심 부품인 3D 가속 칩의 성능은 대략 6개월을 단위로 2배씩 증가하고 있고, 이 추세대로 성능이 향상된다면 2007년경에는 CG 영화‘벅스 라이프’정도의 영상이 실시간으로 처리 가능하다는 예측도 있다. 여러분은 행여 5년 후에 절대 놀라는 일이 없길 바란다.

처음으로 시장에 선보였던 3D 가속 카드에서 최신의 지포스 4(마침 이 글을 쓰고 있는 중에 정식으로 발표됐다)에 이르기까지 3D 가속 카드는 성능 면이나 기능 면에서 많은 발전을 이룩했고, 최근의 GPU라고 불리우는 3D 가속 칩들의 경우 오히려 CPU보다 더 많은 수의 트랜지스터를 집적하고 있을 정도이다. 그리고 장착하고 있는 비디오 램의 용량도 초창기 4MB에서 지금은 무려 128MB에 이르는 고가 제품도 있다.

초창기 가속 카드의 경우 텍스처 매핑이 지원되지 않는 카드도 있었고, 설사 된다 하더라도 원근보정 텍스처 매핑이 지원되지 않아서 텍스처가 울렁거리는 것(아마도 대표적인 예가 소니의 플레이스테이션일 것이다)도 있었다. 1996년 부두의 등장으로 원근보정 텍스처매핑은 물론 텍스처 맵이 확대됐을 때 흉하게 보이던 픽셀들을 부드럽게 해주는 텍스처 필터링 기능, 색유리 같은 반투명한 물체를 표현할 수 있는 알파 블랜딩 기능, 자욱하게 낀 안개를 묘사할 수 있는 안개 효과 기능 등이 지원됐다.

하지만 부두의 경우도 렌더링 파이프라인의 모든 모듈이 하드웨어로 구현된 건 아니고, 주사변환 모듈만 하드웨어에 구현됐고, 나머지 모듈은 API에 소프트웨어적으로 구현돼서 CPU에 의존해야 했다.

주사변환의 경우 부동 소수점 연산 기능 없이도 정수 연산과 동일한 고정 소수점 연산을 이용해 처리할 수 있었지만, 주사변환에 필요한 그레디언트 값을 구하는 삼각형 설정 모듈에는 부동 소수점 연산이 필요했기 때문에 3D 가속 칩이 직접 부동소수점 연산을 처리할 수 있게 될 때까지는 오로지 주사변환 모듈만 하드웨어에 구현됐었다.

1997년부터 삼각형 설정 기능도 하드웨어에 구현된 가속 칩들이 등장하기 시작했고, 1999년에는 본격적인 부동 소수점 연산 기능을 갖춰야 가능한 소위 GPU 가속 칩이 등장했는데 바로 지포스 256이다.

이 제품은 처음으로 좌표변환 기능과 조명계산 기능을 가속 칩에서 처리할 수 있게 만들어졌고 이름하여 T&L (Transform & Lighting) 가속이 가능한 첫 제품이 됐다. 지포스 3에 이르러서는 미리 준비된 고정 기능만 처리하던 데서 프로그래머가 원하는 기능을 마음대로 프로그래밍할 수 있는 기능까지 지원하게 됐다.

3D 가속 카드의 역사를 아주 간략하게 살펴봤는데 몇 년 되지 않는 짧은 기간에 무서운 속도로 발전해온 걸 알 수 있다. 그러다 보니 3D 프로그래머들의 일도 힘들어지는데, 새로운 가속 카드의 등장과 함께 새로운 기능들이 마구 추가되니 새로 나온 따끈따끈한 기능도 빨리 공부해서 지원해야 하고, 예전의 구닥다리 카드도 외면할 수는 없는 딜레마에 빠지는 것이다. 특히 다이렉트3D의 경우 새로운 기능 지원을 위해 새로운 버전이 발표될 때마다 API의 인터페이스가 바뀌어서 프로그래머들을 짜증나게 만들고 있다. 그리고 조금 오래된 가속 카드의 경우 사용자들의 PC에는 여전히 장착돼 있지만 정작 시장에서 살 수 없는 경우도 있으니 저사양 가속 카드를 테스트하려고 중고 시장을 뒤져야 하는 경우도 있다.

비디오 램 용량을 증가시킨 텍스쳐 맵

앞에서도 언급했지만 초기 가속 카드의 비디오 램이 4MB였던 것에 비해 요즘의 가속 카드들은 보통 32MB 내지 64MB의 비디오 램을 장착하고 있다. 얼마 전까지 필자가 집에서 사용한 가속 카드는TNT2 M64 모델로 비디오 램이 32MB였고, 최근에 필자가 큰 맘먹고 구입한 지포스 2 MX400 모델의 비디오 램은 64MB이다. 아직 저 사양의 PC들이 시스템 메모리로 64MB만 장착하고 있는 경우도 있는 마당에 달랑 그림 그리는 일만 하는 그래픽 카드의 비디오 램이 이렇게 많아지는 이유는 무엇일까?

이렇게 그래픽 카드의 비디오 램 용량을 증가시키는 주범은 바로 텍스처 맵이다. 과거 16비트 컬러의 렌더링만 지원하는 가속 카드의 경우 텍스처 맵도 16비트 컬러 포맷이 최고였고 대개 8MB 내지 16MB 정도의 비디오 램을 장착하고 있었다. 하지만 24비트 트루 컬러 렌더링을 지원하는 가속 카드의 등장과 함께 텍스처 맵의 경우도 최대 32비트 컬러 포맷(트루 컬러를 표현하는 데 24비트면 충분하지만 알파 블렌딩을 위한 알파값이 포함된 텍스처 맵의 경우 32비트가 된다)까지 지원하게 됐다. 이렇게 되다 보니 16비트 컬러 렌더링만 지원하던 가속 카드의 비해 네 배 많은 32MB 내지 64MB의 비디오 램을 장착하게 된 것이다.

얼마만큼의 비디오 램 필요한가?

일반적인 3D 게임의 경우 대체 얼마의 비디오 램이 필요할까? 요즘 나오는 국내 3D 게임들은 대개 렌더링 속도를 위해 트루 컬러 렌더링보다는 16비트 컬러 렌더링을 주로 사용하지만 텍스처 맵은 보통 24비트 트루 컬러 포맷을 사용하는 경우가 많다.

우선 프레임 버퍼의 용량부터 계산해보자. 요즘 게임들은 해상도를 적어도 800×600 정도로 해야 한다. 해상도가 800×600 인 경우 한 픽셀 당 16비트 즉 2바이트가 소요되므로 800×600×2=937.5KB의 메모리가 소요된다. 그런데 페이지 플리핑을 위해 최소 두 개의 페이지가 필요하므로 프레임 버퍼 용량은 두 배가 된다. 즉 937.5KB×2=1.83MB이다. 여기에 Z-버퍼링을 위한 Z 버퍼의 용량도 프레임 버퍼 하나의 용량만큼 필요하므로 기본적으로 화면을 표시하는 데 들어가는 용량은 프레임 버퍼 하나 용량의 세 배가 된다. 즉 937.5KB×3=2.75MB가 된다. 2.75면 3MB도 안 되는 용량이므로 별로 걱정하지 않아도 될 듯 하다.

그러면 텍스처 맵의 용량도 한번 계산해보자. 일단 알파 블렌딩을 사용하지 않는다고 하면 텍스처 맵은 한 텍셀(화면상의 최소 화소 단위를 픽셀이라 부르는 것처럼 텍스처 맵 상의 최소 화소 단위를 텍셀이라고 한다)당 24비트, 즉 3바이트가 소요된다. 보통 요즘 게임에서 텍스처 맵의 사이즈는 평균 128×128 정도를 사용하므로 텍스처맵 한 장당 128×128×3=48 KB의 용량이 소요된다(대개 작은 맵의 경우 64×64 정도, 큰 맵의 경우 256×256 정도를 사용한다). 그러면 게임에서는 보통 몇 장의 텍스처 맵을 사용할까? 필자가 관여했던 한 게임의 경우 약 1000장의 텍스처 맵을 사용했었다. 그러면 48KB×1000=48MB 정도의 용량이 소요된다.

최근에 베타 테스트 중인 한 3D 온라인 게임의 경우 그래픽 데이터의 용량이 약 200MB이고, 또 다른 게임의 경우도 약 100MB 정도의 그래픽 데이터를 사용하고 있다. 3D 게임의 경우 대부분의 그래픽 데이터는 텍스처 맵의 형태로 사용되므로 이를 전체 텍스처 맵의 용량으로 보아도 크게 틀리지 않는다. 프레임 버퍼의 용량에 비해서 텍스처 맵의 용량은 64MB의 비디오 램이 모자랄 정도란 걸 알 수 있다.

물론 모든 텍스처 맵이 한꺼번에 비디오 램에 다 로드되는 경우는 없겠지만 게임의 그래픽이 화려해지고 더욱 다양한 캐릭터와 물체들이 등장하면 할수록 그에 비례해서 텍스처 맵의 용량도 늘어나고, 시스템적인 고려 없이 시각적인 퀄리티를 높이는 데에만 신경을 썼다면 텍스처 용량의 초과로 렌더링 속도에 지대한 악영향을 미치게 될것이라고 쉽게 짐작할 수 있다.

이렇게 비디오 램만으로 필요한 용량을 다 채우지 못할 경우 시스템 메모리를 사용하는 수밖에 없다. 3D 가속 칩에 직접 연결된 비디오 램에 비해 시스템 메모리는 느린 시스템 버스를 통해서 액세스되므로 시스템 메모리에 있는 텍스처 맵의 경우 물론 가속 기능을 전혀 기대할 수 없고, 가속되기 위해서는 반드시 비디오 램에 로드돼야 한다. 이런 경우 텍스처 맵을 관리하는 모듈이 있어 비디오 램에서 가장 덜 사용된 텍스처 맵을 골라서 제거하고 그 자리에 대신 지금 당장 필요한 텍스처 맵을 시스템 메모리로부터 로드하게 된다. 이렇게비디오 램의 용량 부족으로 텍스처 맵의 교환이 일어나게 되면 상대적으로 느린 시스템 메모리로부터 비디오 램으로 텍스처 맵 데이터를 로드하느라 시간을 소모하게 되고 전체적으로 렌더링 속도가 떨어지게 된다.

요구하는 사양보다 낮은 사양의 가속 카드에서 3D 게임을 실행하다 보면 화면이 순간적으로 멈칫하며 느려지는 경우를 종종 경험해봤을 것이다. 텍스처 맵의 교환 때문에 일어나는 현상이다. 물론 이런 현상을 방지하는 방법은 비디오 램을 충분히 늘리는 것인데, 좌우지간 비디오 카드의 가격을 올리지 않고 비디오 램을 무한정 늘릴 수도 없는 노릇이고, 상대적으로 용량이 많은 시스템 메모리를 좀 더 빠르게 이용할 수 있는 방법을 고안하게 됐다. 이런 고민에서 AGP가 탄생하게 된 것이다. AGP는 Accelerated Graphics Port의 약자로 그래픽 카드가 시스템 메모리에 빠르게 엑세스할 수 있는 전용의 데이터 포트를 말한다.

[Flash] http://labs.apollocation.co.kr/applications/publicMP3Player/assets/PublicMP3Player.swf



AGP는 무엇인가

요즘 나오는 3D 가속 카드들은 당연히 AGP 슬롯에 장착되고 AGP기능을 갖고 있다. AGP는 시스템 메모리의 일부를 비디오 카드용으로 전용해서 사용하게 해주는 특별한 메모리 포트이다. 물론 메인보드에서도 지원돼야 하고 당연히 가속 카드도 AGP 기능을 갖고 있어야 작동되는 것이다. 일단 AGP 기능을 사용할 수 있게 되면 설사 비디오 램이 모자라더라도 시스템 메모리의 일부를 AGP 포트를 통해 마치 비디오 램의 일부처럼 사용할 수 있다. 그리고 AGP 2배속, 4배속이라고 해서 한 클럭에 하나의 데이터가 아니라 두 개 혹은 네 개의 데이터를 한꺼번에 액세스할 수 있는 고성능의 AGP 카드도 있다. 하지만 설사 AGP 4배속이라고 하더라도 비디오 램보다는 느린 것이고, 많은 데이터를 사용하는 게임의 경우는 결코 시스템 메모리라고 넉넉한 건 아니다. 그래서 메모리가 이래저래 그렇게 많지 않은 시스템인 경우는 원하지 않는 부작용도 있다.

필자가 얼마 전까지 사용하던 시스템은 비디오 램이 32MB인 AGP 가속 카드에다 시스템 메모리가 128MB인 시스템으로 아직은 그런 데로 준수한 시스템이었는데, 최근에 나온 고사양의 3D 게임들을 실행하다 보면 하드 디스크가 작동 중이라는 램프가 바쁘게 깜박이는 걸 볼 수 있었다. 게임도 플레이하기가 불편할 정도로 느리게 실행됐음은 물론이다. 하드 디스크가 열심히 돌고 있다는 것은 시스템 메모리의 부족으로 시스템 메모리에 저장돼야 할 데이터의 일부가 하드 디스크로 스와핑되고 있다는 것이다. 이런 시스템 메모리 부족의 원인은 1차적으로 프로그램이 시스템에 로드해서 사용하는 데이터의 용량이 넘친 탓이겠지만 비디오 램에 로드하는 텍스처 맵의 용량이 넘친 게 부가적인 원인일 수도 있다. 일단 비디오 램의 용량이 부족하면 AGP 카드의 경우 우선적으로 AGP를 통해 시스템 메모리를 사용하게 될 것이고 이로 인해 프로그램이 다른 목적으로 사용할 시스템 메모리의 용량이 줄어들게 된다. 결국 최악의 사태는 시스템 메모리마저 부족하게 되고 하드 디스크를 이용하는 지경까지 가게 되는 것이다.

현명한 자는 멈춰야 할 때를 안다

이처럼 텍스처 맵의 용량은 전체적인 렌더링 성능에 지대한 영향을 미치는 중요한 요인이다. 이런 문제는 결코 최적화된 코드를 작성하는 것으로 해결되는 문제가 아니다. 초보 게임 프로그래머들의 경우 빠른 실행 코드 작성에는 무한한 열정을 보이면서도 데이터용량의 관리에는 무관심한 경우가 많다. 필자처럼 좀 오래된 프로그래머의 경우 지금과는 비교하기가 민망할 정도로 제한된 용량의 메모리에서 실행되는 프로그램을 작성하는 데 익숙하기 때문에 프로그램 코딩 못지 않게 사용하는 데이터의 용량에도 무척이나 신경을 쓴다. 요즘처럼 넉넉한 시스템이 일반화된 환경에서 프로그래밍을 공부한 프로그래머들의 경우는 이런 걸 챙기는 걸 몹시 귀찮아하거나 심지어 노인네의 보릿고개 얘기 정도로 흘려버리는 경우가 종종 있다.

무엇이든지 과하면 곤란해지는 것이다. 물론 게임 개발의 경우 이런 데이터 용량의 관리는 프로그래머 홀로 처리할 수 있는 일은 아니다. 특히 그래픽 아티스트와의 긴밀한 협조가 필요한 일이다. 물론 여기에 게임 디자이너도 포함된다. 훌륭한 게임 그래픽 아티스트는 제한된 용량에서도 시각적으로 뛰어난 그림을 그릴 수 있다.

그리고 만약 PC가 아닌 비디오 게임기용으로 게임을 만든다면 이러한 제약은 더욱 커지게 된다. 플레이스테이션2의 비디오 램 용량은 고작 4MB에 불과하다. 좀 더 고사양의 XBOX라 하더라도 통합 메모리로 모두 64MB가 있을 뿐이다. 그런데 여기에 비하면 아주 넉넉한 사양인 PC에서도 데이터 용량의 과다로 렌더링 성능이 떨어진다면 매우 부끄러운 일이다. 오랜 동안 많은 사용자들로부터 사랑을 받아온 블리자드의‘스타크래프트’의 경우 640×480의 저해상도에 8비트 256 컬러의 그래픽인 게임이다. ‘디아블로’의 경우도 마찬가지이다. 훌륭한 그래픽에도 불구하고 저사양의 PC에서도 잘 실행되는 게임으로 더욱 대중적인 인기를 누리고 있는 것이다.

T&L 가속은 만능인가

현재 T&L 가속을 지원하는 3D 가속 칩은 엔비디아의 지포스 계열과 ATI의 라데온 계열밖에 없고, 아직은 많은 사용자들이 T&L 가속 기능이 없는 3D 가속 카드를 사용하고 있다. 이전의 3D 가속 칩들은 주로 주사변환 기능만 지원했고, 나머지 좌표변환이나 조명은 CPU에 의존해서 처리했다. 3D 가속 카드들이 처음 등장했을 때 사용자들의 오해가 있었는데, 아무 3D 게임이나 3D 가속 카드에서 자동으로 가속되는 걸로 생각한 것이다. 지금도 T&L 가속 카드가 등장하자 이와 비슷한 오해가 있는데, T&L 가속 기능이 그냥 자동으로 지원되는 걸로 알고 있는 경우가 있다. 물론 이 경우는 사용자보다는 개발자들이 하는 오해인데, T&L 가속이 이뤄지려면 꼭지점의 데이터가 반드시 비디오 램에 있어야 한다. 마치 텍스처 맵이 비디오램에 있어야 텍스처 매핑이 가속되는 것과 같은 이치이다.

T&L 가속이 주는 이점이란 꼭지점 데이터를 처리하는 데 필요한 여러 가지 연산을 3D 가속 칩이 CPU보다 더 빠르게 처리한다는 것이다. 개별적인 꼭지점에 대한 처리가 빨라졌다는 것은 더 많은 꼭지점 데이터를 사용하더라도 제 성능을 낼 수 있다는 것이다. 당연한 결과로 더 많은 꼭지점을 이용해 더욱 정교한 3차원 물체를 묘사할 수 있게 되는 것이다. 하지만 주지해야 할 사실은 이러한 가속이 일어나려면 반드시 비디오 램에 꼭지점의 데이터가 저장돼야 하기 때문에 비디오 램의 일부를 꼭지점의 데이터를 저장하는 데 사용해야한다는 것이다. 텍스처 맵을 다 저장하기도 빡빡한 비디오 램에 이제는 꼭지점 데이터까지 저장해야 한다는 것이다.

일반적인 3차원 물체의 경우 꼭지점의 개수의 약 두 배 정도가 그 물체를 구성하는 삼각형의 개수가 된다. 다시 말하면 삼각형 1000개로 구성된 어떤 3차원 모델이 있다면 대충 500개 정도의 꼭지점으로 구성돼 있다고 봐도 크게 틀리지 않는다. 일반적인 메시의 경우 하나의 꼭지점을 6개의 삼각형이 공유하고 있었다. 삼각형이 1000개인 이 모델의 경우 삼각형을 단위로 꼭지점 데이터를 처리할 경우 각 삼각형 당 세 개씩 모두 3000번의 처리가 필요하다. 이는 실제 꼭지점의 개수 500개의 6배에 해당하고 앞에서 알아본 하나의 꼭지점을 6개의 삼각형이 공유하고 있는 경우와 일치한다. 이처럼 제 아무리 T&L 가속이 지원된다 하더라도 실제 처리해야 하는 횟수보다 6배나 많은 처리를 반복하고 있다면 T&L 가속이 무색해질 정도이다. 실제로 일선 개발 현장에서 이런 점을 간과하고 최적화를 소홀히 했다가 낭패를 당하는 경우가 있다. 결론적으로 말해서 하드웨어의 성능을 기대하기 이전에 데이터 처리가 효율적으로 이뤄지고 있는지, 하드웨어의 성능을 고려하지 않고 데이터를 마냥 밀어 넣고 있는 게 아닌지 심사숙고할 일이다.

3D 가속 카드가 진정한 가속 카드로서 제 역할을 하려면 애플리케이션이 너무 무리한 일을 시키면 안 되는 것이다. 사실 적절한 정밀도의 캐릭터 모델이나 물체를 만드는 1차적인 책임은 프로그래머가 아닌 그래픽 아티스트의 손에 있다. 실용적인 견지에서 삼각형 600개 정도로 그럴싸한 인체 모델을 만들 수 있어야 하고, 단순한 몬스터의 경우라면 200개 내지 300개의 삼각형만으로도 만들 수 있어야한다. 그렇지 못하다면 3D 아티스트의 실력을 심히 의심(?)해볼 일이다.

[Flash] http://labs.apollocation.co.kr/applications/publicMP3Player/assets/PublicMP3Player.swf



3D API

현재 PC상에서 사실상의 표준 역할을 하고 있는 3D API는 OpenGL과 다이렉트3D가 있다. 이전에 부두 카드 전용의 Glide라는 API도 있었지만 3DFX가 경쟁사인 엔비디아에 인수 합병됨으로써 그 수명을 다했다. 3DFX의 입장에서는 무척이나 슬픈 일이지만 개발자의 입장에서는 다뤄야 하는 API가 하나라도 줄었기 때문에 기쁜 일이다.
양대 3D API인 OpenGL과 다이렉트3D를 얘기하면서 이 둘을 비교하지 않을 수 없는데, 국내에서는 주로 다이렉트3D를 많이 사용하는 편이다. 이런 국내 개발자들의 경향은 두 API의 장단점을 신중히 고려해 선택한 것은 아닌 듯 하다. 필자의 추측으로는 윈도우 플랫폼에서 게임을 개발하기 위해서는 다이렉트X를 거의 필수적으로 사용하는 분위기이고 다이렉트3D가 다이렉트X의 컴포넌트이다 보니 별생각없이 사용하는 게 아닐까 한다.

OpenGL

OpenGL은 1992년에 SGI의 Iris GL이라는 그래픽 라이브러리를 기초로 공개 표준을 지향하며 발표된 3D API이다. OpenGL은 최근까지 1.3 버전의 스펙이 발표됐고, 현재 2.0 버전에 대해서 논의가이뤄지고 있다. OpenGL은 이제 발표된 지 10년이 되는 API로 윈도우 플랫폼뿐만 아니라 다양한 플랫폼에서 지원되며 역사가 오래된만큼 게임 외에 많은 분야에서 사용되고 있고, 참고 자료에 있어서도 책은 물론 인터넷을 통해 풍부한 자료와 여러 공개 프로그램 소스를 손쉽게 구할 수가 있다. 그리고 단순한 C 라이브러리 형태의 인터페이스를 채용해서 C++나 OOP에 익숙하지 못한 프로그래머도 쉽게 사용할 수 있고, GLU라는 유틸리티 라이브러리를 통해 여러 가지 편리한 기능을 풍부하게 제공하고 있다.

다이렉트 3D

다이렉트3D는 1996년에 다이렉트X 2.0과 함께 처음 발표됐는데 초기 버전은 Execute Buffer라는 시대착오적인 방식을 채용함으로써 게임 개발자들로부터 싸늘하게 외면당했고, 당시 미국의 게임 개발자인 Chris Hecker를 중심으로 OpenGL API를 표준 API로 선택할 것을 마이크로소프트에 요청하는 공개 서한이 인터넷에 발표되기도 했다. 물론 마이크로소프트는 그 공개 서한의 내용에 따라 다이렉트 3D를 포기하고 OpenGL을 선택하진 않았지만 버전을 거듭하면서 OpenGL API의 장점들을 수용하고 새로운 가속 카드의 기능들을 재빨리 수용함으로써 최근에 발표된 다이렉트X 8.0에 포함된 다이렉트3D에 이르러서는 OpenGL에 결코 뒤지지 않는 훌륭한 3D API가 됐다. 그리고 모든 다이렉트X 컴포넌트들이 그렇듯이 COM을 기반으로 만들어졌기 때문에 OOP나 COM에 대한 이해가 없으면 사용하기가 좀 까다로운 면이 있다. 하지만 버전 7.0부터 다이렉트 3DX라는 유틸리티 라이브러리를 추가함으로써 사용법도 이전보다 많이 편리해졌다.

OpenGL vs 다이렉트 3D

OpenGL과 다이렉트3D의 태생은 서로 다르지만 렌더링 파이프라인의 견지에서 본다면 사실상 동일한 과정으로 렌더링 작업을 처리한다. 물론 구체적인 수준에서야 서로 API의 함수 이름이나 변수명도 다르고 구체적인 사용법도 다르지만 렌더링 파이프라인의 작동원리를 잘 알고 있다면 겉보기의 차이가 있음에도 불구하고 두 개의 API는 사실상 1:1로 대응된다고 생각해도 아주 틀리지는 않다. 하나의 API만 선택해 사용한다면 굳이 둘의 유사점이나 차이점에 대해 신경쓸 일이 없겠지만 둘을 다 사용해보려는 용감한 이들을 위해 두API의 렌더링 파이프라인을 한번 비교해보자. 지면 관계상 모든 세부적인 사항을 비교할 수는 없고, 좌표변환 모듈의 처리과정만 비교하겠다. 꼭지점의 데이터 중에서 위치를 나타내는 좌표 값은 여러 단계의 좌표변환 행렬을 거치면서 최종적으로 실제 출력될 2D 화면의 좌표로 변환되는데, 여러 단계를 거치게 된다.

두 API의 처리 과정을 살펴보면 당장에는 차이가 눈에 띠는데, 실질적으로 본다면 같은 것이다. 먼저 Projection 변환 이후 단계를 살펴보면 서로 순서가 다른데 동치(Homogeneous) 좌표 w로 각 x, y, z 좌표값을 나누는 Perspective Division은 먼저 하든지 나중에 하든지 순서에 상관없이 최종 결과는 동일하기 때문이다. 실제로 다이렉트3D의 예제 중에 좌표변환을 API를 통하지 않고 애플리케이션에서 직접 하는 예가 있는데, 소스를 살펴보면 다이렉트3D의 순서대로 하지 않고 OpenGL처럼 Perspective Division을 먼저 하고 Viewport 변환을 나중에 하는 걸 발견할 수 있다. 결국 순서에 상관이 없는 것이다.

두 번째 차이가 나는 곳은 Projection 변환을 하기 전인데, 살펴보면 OpenGL은 ModelView 변환 행렬 하나만 설정하면 되는데 다이렉트3D는 World와 View 두 가지로 나눠 설정하게 돼 있다. 다이렉트3D의 경우 두 개로 나눠 설정하더라도 내부적으로는 어차피 하나로 합쳐지게 되므로, OpenGL처럼 한 번에 설정한 것이나 다이렉트3D처럼 따로 설정한 것이나 결국에는 같은 것이다. 다이렉트3D의 온라인 문서에서도 권고하기를 World 변환 행렬과 View 변환 행렬을 개별적으로 설정하기보다는, 하나는 항등 행렬로 설정하고 하나에다 둘을 미리 곱해서 넣어주는 게 속도에 유리하다고 돼 있다. 대개 내부적으로 행렬이 항등 행렬인지 체크해서 아예 곱하는 계산에서 제외시키는 방식으로 최적화돼 있기 때문이다.

결론적으로 OpenGL과 다이렉트3D의 좌표변환 모듈의 처리과정은 사실상 동일하다고 봐도 별 문제가 없다. 물론 두 API가 사용하는 좌표계는 서로 다르다.

그리고 좌표 값을 나타내는 행렬의 경우 OpenGL의 경우 열벡터를 사용하고 다이렉트3D의 경우 행백터를 사용하기 때문에 변환 행렬의 곱셈 순서가 서로 반대이고, 변환 행렬이 서로 행과 열이 바뀐(Transpose) 형태로 돼 있어 구체적인 행렬 값도 다르다. 하지만 projection 변환까지 거치고 나면 두 API 모두 좌표 값이 사실상 같아진다. 무슨 얘기인고 하니 둘 다 결국은 3D 클리핑을 수행하기 위한 일종의 표준적인 정규 좌표계로 변환돼 같은 좌표가 된다는 것이다(물론 세부적으로는 z 좌표값의 클리핑 영역이 서로 다르다. OpenGL은 -w < Z <= w 이고, 다이렉트3D는 0 < Z <= w 이다). 그래서소프트웨어로 좌표변환을 처리할 경우 두 API의 좌표계와 행렬 표시의 차이점을 개의치 않고, 적당히 하나의 공통 좌표계와 공통 행렬로 Projection 변환까지 수행한 후 두 API에 좌표값을 정규화된 좌표값으로 공급해주면 나머지는 사실상 동일하게 처리되는 것이다.

OpenGL과 다이렉트3D 궁합맞추기

주제에서 좀 벗어나는 얘기지만 앞에서 살펴본 두 API의 좌표 변환과정의 유사성을 이해한다면, 두 API를 동시에 지원하는 3D 엔진을 만드는 것도 그렇게 어려운 일이 아니다. OpenGL의 경우 Model View 행렬과 Projection 행렬을 둘 다 항등 행렬로 설정하고 행렬연산을 직접하면 임의로 마음에 드는 좌표계를 사용하면서도 번잡한 3D 클리핑과 최종적인 주사변환 처리는 API에게 맡길 수가 있다.

다이렉트3D의 경우는 좌표변환을 모두 직접하거나 아니면 모두 API에 맡기거나 하는 양단의 선택밖에 없는데, 다이렉트3D에서 좌표변환을 직접 하려고 한다면 까다로운 3D 클리핑까지 직접 해야 한다. 소프트웨어 엔진이 제대로 구축돼 있다면 3D 클리핑도 직접 수행하므로 별 문제가 없겠지만, 만약 소프트웨어 엔진없이 3D 가속 카드 전용으로 만들면서 OpenGL과 다이렉트3D API를 동시에 지원하려고 한다면, 3D 클리핑처럼 힘든 일은 3D API에게 맡기면서 가능한 공통의 코드를 공유할 수 있도록 하는 게 좋을 것이다.

이런 경우 적당한 전략은 좌표계를 다이렉트3D에 맞추고 행렬도 다이렉트3D용을 공용으로 사용하고, 조명계산도 직접 공동으로 처리하면서 OpenGL의 경우 Projection 변환까지는 직접 처리하고, 나머지는 OpenGL에 맡기고, 다이렉트3D의 경우 좌표변환을 통째로 다이렉트3D에 맡기는 것이다. 그럼 까다로운 3D 클리핑도 API에서 알아서 처리된다.

다시 꼭지점에 주목하자!

앞에서 두 API의 좌표변환 모듈의 유사성을 살펴봤지만, 가장 중요한 것은 이러한 좌표변환 모듈에 어떻게 꼭지점의 데이터를 공급하는가이다. 기본적으로는 두 API 모두 꼭지점의 데이터를 삼각형 단위로 공급하는 방법밖에 없다. 그러나 다행스럽게도 각 API는 공유 꼭지점에 대해 반복적으로 동일한 처리를 하게 되는 걸 막는 방법도 제공하고 있다. OpenGL은 꼭지점 배열(Vertex Array)을 통해, 다이렉트3D는 꼭지점 버퍼(Vertex Buffer)와 색인 버퍼(Index Buffer)를 통해 공유 꼭지점 문제를 피할 수 있다.

OpenGL의 경우 렌더링 파이프라인에 공급할 꼭지점 데이터들을 꼭지점 배열에 미리 저장하고 렌더링시에는 glArrayElement 함수를 통해 배열에 저장된 각 꼭지점 데이터의 색인 값만 넘겨주면 된다. 이렇게 하면 꼭지점 데이터의 여러 값을 넘기는 데 한번의 함수호출로 가능하므로 호출시의 부하가 줄고, 무엇보다도 OpenGL이 내부적으로 이미 좌표변환 등의 처리를 한 꼭지점 데이터와 동일한 색인의 꼭지점이 넘어오면 다시 처리하지 않고 기존에 이미 처리된 데이터를 재사용하도록 최적화돼 있어서 공유 꼭지점으로 인해 동일한 처리를 반복하는 걸 피할 수 있다.

다이렉트3D의 경우 꼭지점 버퍼에 꼭지점의 데이터를 저장하고, 색인 버퍼에 이들 꼭지점을 가리키는 색인을 저장한다. 그리고 DrawIndexedPrimitive 함수를 통해 렌더링 파이프라인에 색인 값을 넘기게 된다. OpenGL에 비해서 조금 복잡하긴 하지만 결국 OpenGL의 꼭지점 배열과 같은 효과를 기대할 수 있다.

그러나 사실 이러한 방법도 T&L 처리를 애플리케이션에서 미리 소프트웨어적으로 처리하면서 공유 꼭지점에 대해서도 적절한 처리를 했다면 굳이 사용하지 않아도 되는 기능이다. 물론 T&L 가속 기능이 있는 가속 카드를 대상으로 애플리케이션을 만들고 반드시 그러한 기능이 필요한 경우에는 최적화를 위해서 꼭 사용해야 하는 방법이기는 하다. 하지만 현재 시점에서 출시되는 3D 게임들이 T&L 가속 기능을 가진 가속 카드만 지원하겠다고 한다면 사용자들의 주머니 사정을 너무 무시한 처사라고 하겠다. 물론 올해 말이나 내년 초쯤이면 지금보다는 더 많은 상당수의 사용자들이 T&L 가속이 지원되는 가속 카드를 기본으로 장착하게 될 것이고, 용감하게 T&L 가속 카드를 기본으로 해서 개발하는 것도 고려해볼 일이다. 그렇지만 앞에서 언급한 공유 꼭지점 문제의 처리와 3차원 모델을 구성하는 삼각형의 개수나 텍스처 맵의 용량 관리를 적절히 하지 않는다면 렌더링 성능에 문제가 생기는 걸 피할 수가 없다.

물론 3차원 모델이나 텍스처 맵의 용량은 프로그래머의 손만으로 해결될 수 있는 문제는 아니다. 하지만 3D 프로그래머라면 꼭 주지하고 있어야 하는 중요한 사항임에는 틀림없다. 그리고 프로그램 코드의 수정 없이도 최적화할 수 있으니 프로그래머에게는 얼마나 행복한 방법인가? 하지만 너무 좋아하지 말지어다. 다음호에서는 프로그래머들에게 별로 행복하지 않은 골치 아픈 방법들 위주로 최적화기법을 설명할 것이다.

지면의 제약으로 세세한 얘기보다는 커다란 컨셉 위주로 얘기가 진행돼 좀 아쉽기는 하지만 그래도 필자가 전하고자 했던 핵심적인 부분은 충분히 잘 전달됐기를 바란다. 마지막으로 하나의 금언을 남기고 마감하고자 한다. “폴리곤을 가장 빨리 그리는 방법은 아예 그리지 않는 것이다.

출처 : 마소








반응형