Handling Vertices And Indices In Unity
여태까지 많은 게임들은 Graphics API 를 사용하여 만들어졌다. 1992년에 OpenGL 의 첫버젼이 릴리즈 되었고 이어서 1995년에 DirectX 가 Windows Game SDK 안에 포함되어 릴리즈 되었다. 그 이후로 수많은 게임들이 이 두가지의 Graphics API 를 사용하여 개발되었다. 다만 Graphics API 를 직접 사용하려면 꽤 많은 배경지식과(선형대수학, Graphics 이론 등) 해당 Graphics API 에 대한 경험이 많이 필요했다. 즉 일반적인 프로그래머들이 접근하기 조금 어려운 분야였다. 하지만 이를 꽤 뚫어본 많은 사람들이 게임을 만들기 위한 소프트웨어 이른바 게임 엔진이라는 소프트웨어를 개발하면서 널리 퍼지게 되었고 요즘에는 많은 지식 없이 게임을 만들 수 있게 되었다.
하지만 프로그래머로써 성장하려면 한계단씩 내려가 보면서 원리를 깨우쳐야 한다. 특히 게임 클라이언트 프로그래머는 결국 Graphics API 를 활용한 프로그램을 짜는 것이기 때문에 지식이 없으면 없을수록 난항을 겪기 마련이다. 수학적인 지식이 부족하면 직접 계산하는 코드를 짤수가 없고, Graphics API 의 구성을 모른다면 최적화를 할때 하나하나 삽질해가며 바꿔보아야 한다.
이 게시물에서는 Graphics API 를 공부하면 처음 나오게는 지식들(정점, 폴리곤, UV)에 대해서 알아보고 Unity 에서 이 지식들을 시험해보겠다.
공간을 구성하는 기본 단위 : 정점(vertex)
가장 먼저 알아야것은 정점이다. 영어로는 vertex 인데 이 단어는 다양한 이론에서 상이하게 다뤄지므로 헷갈릴 수도 있다. 우선 그래픽스 분야에서의 정점은 3차원 공간에서 특정 위치를 나타내는 단어다. 수학에서 흔이 쓰이는 점의 정의와 비슷하다.
또한 정점은 위의 수학의 점 P 의 정의와 같은 말이다. 모든 3차원 상의 물체는 점으로 이루어져 있다. 하나하나의 점이 특정한 구성 방식으로 모여 선과 면을 정의한다. 정점은 모든 물체를 표현하기 위한 기본적인 단위다.
그려지는 면의 기본 단위 : 삼각형(polygon, triangle)
위에서 정점들은 물체를 표현하기 위한 기본적인 단위라고 설명했다. 그리고 물체를 표현하려면 표면(surface)를 표현할 수 있어야 한다. 정점을 가지고 표면을 정의할 수 있는 방법은 여러가지가 있지만 정점을 최소의 갯수만 가지고 표현하려면 삼각형으로 표현하는 것이 최적의 방법이다. 그리고 Graphics API 에서도 삼각형을 기본 단위로 물체를 그린다.
위와 같이 모든 물체는 삼각형을 기본으로 만들어진다.
그려지는 정보의 집합체 : Mesh
이 개념은 위에서 언급한 개념들과는 조금 다르다. 점이나 삼각형은 위상 수학에서 나올법한 내용이지만 Mesh 에 대한 내용은 소프트웨어에서 나온 개념이다. Mesh 는 3D 오브젝트가 그려질 때 필요한 정보들을 담아놓고 있는 정보 덩어리라고 할 수 있다. 위에서 언급한 여러개의 정점들과 폴리곤에 대한 여러 정보들을 가지고 있다. 위의 토끼는 결국 여러 정점들과 여러 폴리곤 정보들로 그려진다고 볼 수 있다.
여기까지 기본적으로 쓰이는 용어에 대한 간단한 설명들을 살펴보았다. 이제 Unity 에서 직접 이것들을 만져볼 차례다. 우선 3DBasicExample 을 받아서 edu/plane 브랜치로 이동한다.
Unity 에서 직접 정점과 폴리곤을 조작하는 방법에 대해서 알아보기 위해 Scripts 디렉토리에 MeshTest.cs 소스코드를 보자.
public void ModifyMesh(Mesh mesh)
{
mesh.vertices = new Vector3[]
{
new Vector3(0f, 0f, 0f),
new Vector3(1f, 0f, 0f),
new Vector3(0f, 0f, 1f),
new Vector3(1f, 0f, 1f)
};
mesh.triangles = new int[]
{
0, 2, 3,
0, 3, 1
};
...
}
위 코드는 정점과 폴리곤을 구성하는 정점의 인덱스를 직접 만들어 넣어주는 코드다. Mesh 클래스는 위에서 설명한 정점과 폴리곤의 정보 등 렌더링에 필요한 정보를 가지고 있는 데이터 뭉치다. 여기에 정점과 폴리곤 정보를 넣는다. ModifyMesh 함수에서는 Mesh 의 프로퍼티에 두개의 배열을 새로 생성하여 넣어준다. vertices 는 3차원 좌표계의 정점 정보 배열로써 가장 핵심적인 데이터다. 코드에서는 4개의 점을 넣어준다. X,Z축으로 정사각형을 구성하는 정점들을 가지고 있다. triangles 는 폴리곤을 표현하기 위한 정수 배열인데 여기에는 vertices 배열의 인덱스들이 들어가 있다. 당연히 3개의 인덱스가 하나의 폴리곤을 구성하며 위의 Mesh 인스턴스는 두개의 폴리곤을 그리게 될것이다. 아래 소스에 주석으로 위의 정점과 인덱스들이 나타내는 것을 표현해 보았다.
/*
(0,0,1) 2 3 (1,0,1)
* - *
| / |
* - *
(0,0,0) 0 1 (1,0,0)
*/
Y축에서 아래로 바라보는 뷰로 표현했다. 그리고 폴리곤을 구성하는 인덱스 배열에서 중요한게 하나있다. 바로 인덱스의 순서다. 이 코드에서 인덱스 배열이 각각 가르키는 정점의 순서를 보면 전부다 시계 방향(cw: clockwise)인 것을 알 수 있다. 만약 방향이 반시계 방향(ccw: countclockwise)으로 구성된다면 보이는 방향이 밑으로 바뀔 것이다. mesh.triangles 에 들어가는 순서를 살짝 바꾸어 실행해보자.
...
mesh.triangles = new int[]
{
0, 3, 2
0, 1, 3
};
...
원래 보이던 방향에서 반대로 바뀐 것을 알 수 있다. 이건 꽤나 중요한 사항이다. 다른 어플리케이션에는 어떤 방향으로 설정하는지 모르겠지만 Unity 에서는 시계 방향을 기준으로 윗 방향을 보이는 기준으로 잡는다. 물론 직접 인덱스를 건드릴일은 거의 없다. 복잡한 모델들은 대부분 파일에서 불러와서 사용하기 때문이다. 하지만 모른다면 꽤나 난처해질 것이다.
여기까지 직접 정점과 폴리곤을 구성하는 방법에 대해서 알아보았다. 다만 정리가 조금 안된사항들이 있다. Graphics API 에서는 정점의 배열들을 Vertex Buffer 라고 칭한다. 폴리곤을 구성하는 인덱스의 배열은 Index Buffer 라고 칭한다. 그리고 vertices 는 vertex 의 복수형이다. 비슷한 표현으로는 indcies 가 있다. 명칭들을 잘 알아두면 문서를 읽거나 소통할때 말하기 편하니 알아두길 바란다.
자동으로 생성되는 Unity 컴포넌트가 궁금하다면 Mesh components in unity에서 확인하라.