Post

[OpenGL ES를 이용한 3차원 컴퓨터 그래픽스 입문] 3. 모델링

이 글은 제 개인적인 공부를 위해 작성한 글입니다. 틀린 내용이 있을 수 있고, 피드백은 환영합니다.

[OpenGL ES를 이용한 3차원 컴퓨터 그래픽스 입문] 3. 모델링

개요


3차원 물체를 표현하는 다양한 기법이 존재하지만, 실시간 그래픽스 영역에서는 폴리곤 메시를 압도적으로 많이 사용한다. 이 장은 폴리곤 메시가 어떻게 생성되는지, 어떤 형식으로 저장되는지, 게임과 같은 런타임 응용 프로그램으로 어떻게 전달되는지 등에 대해 설명할 것이다.


3.1 폴리곤 메시


img 그림 3.1. 구를 표현하는 두 가지 기법. (a) 음함수 곡면. (b) 폴리곤 메시

중심의 좌표가 (Cx, Cy, Cz)이고 반지름이 r인 구를 생각해 보자. 이를 표현하는 가장 간단한 방법은 그림 3.1.a와 같이 f(x,y,z)=0 형태의 음함수(implicit function), 즉, $(x - C_x)^2 + (y-C_y)^2 + (z-C_z)^2 - r^2 = 0$을 사용하는 것이다. 하지만 이러한 음함수로 표현된 물체를 스크린에 렌더링하는 것은 쉽지 않다.

우리는 그림 3.1.b의 폴리곤 메시처럼, 정점(vertex) 및 폴리곤과 같은 요소들을 명시적으로 정의하여 구를 표현할 수도 있다. 게임을 비롯한 실시간 응용에서는 폴리곤 메시를 선호하는데, 그 이유는 GPU가 폴리곤 메시 처리에 최적화되어 있기 때문이다. 그런데, 메시의 정점들은 부드러운 곡면을 샘플(sample)해서 얻어진 것이므로, 폴리곤 메시는 정확한 표현법이 아닌 근사적 표현법이라는 사실을 명심해 두자.

가장 간단한 폴리곤은 삼각형이다. 폴리곤 메시 중 가장 널리 쓰이는 것은 삼각형으로만 구성된 메시, 즉 삼각형 메시(triangle mesh)이다. 하지만, 3.1.1. 절에서 곧 살펴보게 될 것처럼, 모델링 작업을 위해서는 사각형 메시(quad mesh)가 선호된다. 그림 3.2는 동일한 물체를 표현하는 삼각형 메시와 사각형 메시를 비교해 놓았다. 사각형 메시가 주어졌을 때 이를 삼각형 메시로 바꾸는 가장 간단한 방법은, 각 사각형을 두 개의 삼각형으로 분할하는 것이다.

img 그림 3.2. 삼각형 메시와 사각형 메시

일반적인 닫힌 메시(closed mesh)에서 삼각형의 개수는 정점 개수의 두 배 정도이다. 즉, n개의 정점이 있다면 삼각형의 개수는 대략 2n이 된다.


삼각형 메시에서의 정점과 삼각형 비율을 알아보자.

구멍이 없는 닫힌 메시에 대한 오일러 공식은 다음과 같다.

\[v - e + f = 2 \tag{3.1}\]

여기에서 v, e, f는 각각 메시의 정점, 변, 면의 개수이다. 닫힌 삼각형 메시에 하나의 변은 두 개의 면에 의해 공유되고, 하나의 면은 세 개의 변을 가진다. 따라서 변을 하나씩 방문하면서 이를 공유하는 면 두 개에 표시를 남기면, 모든 변 방문이 마쳐졌을 때에는 각각의 면에 세 개의 표시가 남게 된다. 즉, 메시의 변의 개수의 2배는 면의 개수의 3배가 된다.

\[2e = 3f \tag{3.2}\]

예를 들어, 4면체인 경우 e는 6, f는 4이다. 식 (3.1)의 e를 식 (3.2)에서 계산된 $\frac{3}{2}f$로 치환하면 다음과 같은 결과를 얻는다.

\[f = 2v - 4 \tag{3.3}\]

따라서, 메시의 크기가 커질수록 면의 개수 f는 정점의 개수 v의 2배에 수렴한다.


img 그림 3.3

저해상도의 메시는 빠르게 렌더링할 수 있지만 폴리곤의 각진 특징이 드러나기 쉽다. 반면 고해상도 메시는 처리 시간이 더 걸리지만 폴리곤의 각진 특성을 가려줄 수 있다.


폴리곤 메시는 부드러운 곡면을 근사적으로 표현한 것이다. 따라서 얼마나 많은 정점을 사용하여 근사할 것인가는 중요한 문제가 된다. 정점의 개수가 많으면 메시의 해상도(resolution)가 높다고 말하며, 정점이 적으면 해상도가 낮다고 표현한다. 그림 3.3을 보면 해상도는 정확성과 효율성 사이의 상반관계(tradeoff)를 고려하여 결정된다. 메시의 해상도가 올라갈수록 본래의 곡면과 흡사해지지만 메시를 처리하기 위한 시간이 증가하여 효율성은 떨어진다.


3.1.2 폴리곤 메시 표현


삼각형 메시를 표현하는 가장 단순한 방법은 삼각형을 구성하는 세 개의 정점을 순서대로 나열하는 것인데, 한 번에 세 개의 정점을 읽어서 하나의 삼각형을 정의한다. 그림 3.5.a는 2차원 삼각형 메시의 예를 보여주는데, 정점들이 저장된 메모리 공간은 정점 배열(vertex array)이라 부른다.

img 그림 3.5.a

삼각형 메시 표현. 인덱스를 사용하지 않는 경우, n개의 삼각형을 가진 폴리곤 메시를 표현하기 위해서는 정점 배열에 3n개의 정점이 저장되어야 한다.


img 그림 3.5.b

중복된 정점이 없는 정점 배열을 만들고, 인덱스 배열을 사용한다.


3.5.a 표현법은 상당히 직관적이지만, 중복된 데이터를 가진다는 단점이 있다. 예를 들어 그림 3.5.a에서 (1,0)의 좌표를 가지는 정점은 모든 삼각형들에 속하므로 정점 배열에 세 번 나타나게 된다.

삼각형 메시에서 여러 개의 삼각형들에 공유되지 않는 정점은 거의 없다. 따라서, 정점 배열에는 정점들을 중복 없이 저장하고 이들을 가리키는 인덱스를 별도의 인덱스 배열(index array)에 기록하면 효율적으로 메모리를 사용할 수 있다. 인덱스 배열에는 삼각형 당 세 개의 인덱스가 저장된다. 그림 3.5.b는 그림 3.5.a와 동일한 메시를 정점 배열과 인덱스 배열을 사용해 표현하였다.

그림 3.5와 달리, 일반적으로 정점 배열에는 정점 위치뿐만 아니라 다종다양한 데이터가 포함된다(이들 데이터에 대해서는 이 책에서 차례차례 소개될 것이다). 따라서, 중복된 데이터를 제거하여 절약되는 정점 배열 공간은 인덱스 배열이 차지하는 공간 크기를 능가하게 된다. OpenGL ES는 인덱스를 사용하는 표현과 사용하지 않는 표현 모두를 지원한다. 하지만, 인덱스 없이 삼각형 메시를 표현하는 경우는 거의 없다고 보아도 무방하다.

16비트의 부호 없는 정수형(unsigned integer) 인덱스를 사용한다면, 2^16(65,536)개의 정점을 표현할 수 있다. 32비트 인덱스를 사용한다면, 2^32(=4,294,967,296)개의 정점을 표현할 수 있다. 정점 배열이 65,536개 이하의 정점을 저장하는 경우, 32비트 대신 16비트 형식을 쓰면 인덱스 배열의 크기를 줄일 수 있다.


표면 노멀


3차원 물체를 렌더링할 때 핵심적인 역할을 하는 것은 그 물체 표면의 노멀(surface normal), 즉 표면에 수직인 법선 벡터인데, 이를 삼각형 노멀과 정점 노멀로 분리해서 논의하자.


3.2.1 삼각형 노멀


img 그림 3.6. 삼각형 노멀. (a) 세 개의 정점 p1, p2, p3로 구성된 삼각형. (b) 삼각형 노멀은 벡터곱 v1 x v2로 정의된다.


그림 3.6.a의 삼각형 <p1, p2, p3>에 수직인 노멀은 간단하게 계산된다. 그림 3.6.b에 보인 바와 같이, p1과 p2를 잇는 벡터를 v1이라 하고, p1과 p3를 잇는 벡터를 v2라 하자. 삼각형 노멀은 v1과 v2의 벡터곱으로 정의된다.

\[n_{12} = \frac{v1 \times v2}{\|v1 \times v2\|} \tag{3.4}\]

컴퓨터 그래픽스에서 기본적으로 모든 노멀을 단위 벡터로 표현한다. 따라서 위 식에서는 v1 x v2를 자신의 길이로 나누는 정규화 과정을 수행했다. 한편, 삼각형의 세 정점이 p1 p3 p2 순서대로 주어진 경우를 생각해 보자. 이 경우에, 위와 동일한 원칙을 적용해, p1과 p3를 잇는 벡터 v2를 얻고, p1과 p2를 잇는 벡터 v1을 얻은 후, v2와 v1의 벡터곱을 계산하면 그 결과는 다음과 같을 것이다.

\[n_{21} = \frac{v2 \times v1}{\|v2 \times v1\|} \tag{3.5}\]

2.4절에서 논의한 바와 같이, 식 (3.4)의 $n_{12}$와 식 (3.5)의 $n_{21}$은 반대 방향을 가리킨다. 즉, 삼각형 노멀의 방향은 정점 순서에 따라 달라진다.

img 그림 3.7. 인덱스 배열은 정점을 반시계 방향으로 정렬하여 저장한다.

그림 3.6.a의 삼각형을 <p1, p2, p3>로 표현하면 정점을 반시계 방향으로 정렬한 것이 되고, <p1, p3, p2>로 표현하면 정점을 시계 방향으로 정렬한 것이 된다. 오른손 법칙에 따르면, 반시계 방향으로 정렬된 정점은 물체 표면에서 바깥쪽으로 향하는 노멀을 생성하고, 시계 방향으로 정렬된 정점은 물체 안쪽으로 향하는 노멀을 생성한다. 컴퓨터 그래픽스에서 노멀은 물체 바깥을 향하게 하는 것이 원칙이다. 따라서, 삼각형의 정점은 항상 반시계 방향으로 정렬되어 있어야 한다. 그림 3.7을 보면 각 삼각형의 정점들이 모두 반시계 방향으로 정렬되어 있음을 발견할 수 있다.


3.2.2 정점 노멀


img 그림 3.8.a. 정점 노멀은 해당 정점이 샘플한 곡면의 노멀을 표현한다.


img 그림 3.8.b. 정점 노멀은 해당 정점을 공유하는 삼각형들의 노멀 평균으로 정의할 수 있다.


이제까지 삼각형 노멀에 대해 논의했지만 컴퓨터 그래픽스에서 실제 중요한 것은 정점 노멀(vertex normal)이다. 삼각형에 수직인 벡터로 정의되는 삼각형 노멀에 비해, 정점 노멀이 무엇인지 직관적으로 이해하기 어려울 수도 있다. 그렇다면, 그림 3.8.a에 보인 것처럼 부드러운 곡면을 샘플하여 폴리곤 메시의 정점을 얻은 것임을 상기해 보자. 정점 노멀은 해당 정점이 샘플한 곡면에 수직이어야 함을 이해할 수 있을 것이다.

하지만 원래의 곡면 없이 폴리곤 메시만 주어졌을 때 정점 노멀을 계산하는 정답은 없다. 다만 그럴듯한 결과를 내는 기법들이 사용될 뿐이다. 가장 간단한 방법은 그림 3.8.b에 보인 바와 같이, 하나의 정점을 공유하는 모든 삼각형 노멀들의 평균을 취하는 것이다. 대개의 경우, 정점 노멀은 3ds max 등과 같은 패키지가 자동으로 계산한다. 정점 노멀은 9장에서 기술할 라이팅에 필수적이므로, 정점 위치와 더불어 정점 배열의 핵심 구성 요소가 된다.


3.3 폴리곤 메시 내보내기와 불러오기


폴리곤 메시는 그래픽스 패키지를 사용해 오프라인에서 만들어지고 파일로 저장되어 런타임 응용 프로그램으로 넘겨진다. 하나의 응용에서 만들어진 데이터를 다른 응용에 적합한 형태로 출력하는 과정을 내보내기 혹은 export라 부르고, 이렇게 출력된 데이터를 읽어오는 과정은 불러오기 혹은 import라 한다. 내보내기와 불러오기를 위해서는 간단한 프로그램이나 스크립트를 사용한다. 예를 들어, 3ds max에서 내보내기를 하려면 3ds max가 제공하는 MAXScript를 이용하면 된다.

img 그림 3.9.a. 이해하기 쉽도록 메시의 정점 노멀 5개에 아래첨자를 표기했다. 예를 들어, vn1은 첫 번째 노멀인 (0,1,0)이다.

img 그림 3.9.b. 불러오기 결과 런타임 프로그램에 정점 배열과 인덱스 배열이 생성되었다.


대개의 그래픽스 패키지는 내보내기를 위해 다양한 파일 포맷을 지원하는데, 3ds Max가 지원하는 대표적인 파일 포맷 중 하나는 .obj이다. 이는 기본적으로 정점 위치와 노멀, 그리고 삼각형 정보를 저장하는데, 정점의 위치는 vertex의 앞 글자 v, 정점 노멀은 vn, 삼각형은 face(면)의 앞 글자 f를 기호로 사용한다. 그림 3.9.a는 저해상도로 모델링된 반지름이 1인 구, 그리고 이 구에 대한 .obj 파일의 일부를 보여주는데, v와 vn으로 시작하는 줄은 각각 정점 위치와 노멀의 3차원 좌표를 기록하고 있다. 그림 3.9.a의 구는 26개의 정점으로 구성되는데, 이들 정점의 노멀은 모두 다르다. 따라서, 정점 노멀도 26개이다. 이와 달리, 여러 개의 정점이 동일한 노멀을 가지고 있는 경우라면, .obj 파일에서 정점 노멀의 개수는 정점 위치의 개수보다 작을 것이다.

그림 3.9.a의 구는 48개의 삼각형을 가지고 있다. 따라서 .obj 파일에서 f로 시작하는 줄은 48개이다. 하나의 삼각형이 가진 세 개의 정점 정보는 각각 위치와 노멀의 쌍으로 기록되는데, 이 둘은 //로 구분된다. 예를 들어, 첫 번째 삼각형의 첫 번째 정점은 1//1로 표기되었는데, 위치는 (0,1,0), 노멀도 (0,1,0)임을 말한다. 두 번째 정점은 2//2로 표기되었는데, 위치는 (0, 0.707, 0.707), 노멀은 (0, 0.663, 0.748)임을 말한다.

이와 같은 .obj 파일은 불러오기를 통해 그래픽스 응용 프로그램의 메모리 공간으로 옮겨지는데, 그림 3.9.b는 그렇게 생성된 정점 배열과 인덱스 배열을 보여준다. 삼각형 개수가 48개이므로, 인덱스 배열은 144(=48x3)개의 원소를 가진다.


참고

This post is licensed under CC BY 4.0 by the author.