본문 바로가기

Graphics Note

OpenGL 강좌 - 4. 삼각형 그리기(Vertex Buffer Object)

OpenGL 강좌 네 번째 포스팅. 이번에는 삼각형의 실체를 알아보자. 이번 강좌를 통해 다음의 것들을 배우게 된다.


* Primitive types & draw mode

* 정점 버퍼 오브젝트(VBO: vertex buffer object)


지난 강좌에서 알게된 셰이더(shader)는 '어떻게' 그려야 하는가에 대한 소개라고 볼 수 있는 반면, 이번 글에서 배우게 될 primitive type과 VBO는 '무엇을' 그릴것인지 정의하는 부분이라 볼 수 있다.


InitApp 함수의 정점 정의 및 VBO 생성 부분을 먼저 훑어보자.

// 정점 버퍼 오브젝트
GLuint gVertexBufferObject;
std::vector<glm::vec3> gVertices;

bool InitApp() {
	...

	// 정점 정의
	gVertices = {
		glm::vec3(-0.5f, -0.5f, 0.f), glm::vec3(0.5f, -0.5f, 0.f), glm::vec3(0.5f, 0.5f, 0.f)
	};

	// 정점 버퍼 생성 및 버퍼 데이터 정의
	glCreateBuffers(1, &gVertexBufferObject);
	glNamedBufferData(gVertexBufferObject, gVertices.size()*sizeof(glm::vec3), &gVertices[0], GL_STATIC_DRAW);

	...
}

정점(vertex) 이라는 것은 도형을 이루는 기본 요소이고, 정점의 집합으로 도형의 형태를 정의한다.

 - 점(point)은 하나의 정점으로 이루어져 있다.

 - 선(line)은 두 개의 정점을 가진다.

 - 삼각형(triangle)은 세 개의 정점을 가진다.

 - 사각형(quad)은 네 개의 정점을 가진다.


컴퓨터 그래픽스에서는 3차원 공간상의 위치 벡터를 요소로 가지는 연속된 정점 집합으로 이러한 도형들을 저장한다. 하지만 정점 집합만으로는 도형의 형태를 정의하지 못하며 적절한 해석 방식을 지정해 줘야 한다. 어떻게 해석하는가를 그리기 모드(mode)라 하고 해석된 결과를 프리미티브 타입(primitive type) 이라 부른다.



프리미티브 타입들을 그려보았다. 정점들이 문자 순서대로 열거되어있을 때, 화살표는 해석 방향을 설명한다.

예를들어 {A, B, C, D}의 순서로 정점이 저장되어 있는데 Lines 모드로 그려진다면 두 개의 세로선이 그려지고, Triangle strips 모드로 그려진다면 두 개의 삼각형이 이루는 사각형으로 그려진다.

3차원 모델은 이런 프리미티브 타입의 연속으로 표현되는데, 일반적으로 삼각형(혹은 사각형)의 집합으로 정의된다. 이런 물체는 아래 그림과같이 그물망 같은 구조로 보이기 때문에 메쉬(mesh)라 부른다.



삼각형으로 물체를 정의할 때 Triangle strips 타입이 가장 적은 정점으로 많은 삼각형을 표현하겠지만, 정점 순서도 헷갈리고 다루기가 여러모로 불편해서 일반적으로 Triangles 타입을 많이 쓴다.


우리의 삼각형 그리기 코드에서는 정점을 어떻게 정의했는지 살펴보자.

gVertices = {
	glm::vec3(-0.5f, -0.5f, 0.f), glm::vec3(0.5f, -0.5f, 0.f), glm::vec3(0.5f, 0.5f, 0.f)
};

GLM 라이브러리의 vec3 타입(x, y, z 요소를 가지는 벡터)인 위치(position)값 집합으로 삼각형을 정의했다. 이 정점 집합을 Triangles 모드로 그릴 예정이고, OpenGL은 기본적으로 오른손 좌표계를 사용하므로 앞면을 바라보는 삼각형을 정의한 샘이다.


정점을 제대로 정의했다면 다음으로 할 일은 GL에게 '이것이 나의 정점집합이다' 라고 알려주는 작업이다.

이를 위해 버퍼 오브젝트(buffer object)를 만든 후 이 버퍼를 정점 버퍼(vertex buffer)로써 사용하여 정점 데이터를 GPU에게 전해준다. 아래 두 줄의 코드가 이를 수행한다.

glCreateBuffers(1, &gVertexBufferObject);
glNamedBufferData(gVertexBufferObject, gVertices.size()*sizeof(glm::vec3), &gVertices[0], GL_STATIC_DRAW);

glCreateBuffers 함수로 1개의 버퍼 오브젝트를 생성한 후 glNamedBufferData 함수로 버퍼의 데이터를 전달하고 어떻게 사용할지(STATIC_DRAW) 알려주었다.


이로써 그릴 대상도 만들어졌다. 다음 강좌를 통해 실질적으로 그려지는(렌더링되는) 부분을 살펴볼 것이다.


강좌의 예제코드는 모두 github에 올려두었다.

https://github.com/alleysark/OpenGL-Tutorials