※ 본 포스팅은 learnopengl.com을 번역 및 가감한 포스팅이며, learnopengl에서는 번역 작업 참여를 적극 장려하고 있습니다!
아래 링크에서 원문을 확인할 수 있습니다.
Element Buffer Objects
이미 삼각형을 그렸지만 추가적으로 element buffer object EBO라는 개념이 있습니다. 일단 삼각형 두 개로 직사각형을 그리는 vertex data를 보죠.
float vertices[] = {
// 첫 번째 삼각형
0.5f, 0.5f, 0.0f, // 오른쪽 위
0.5f, -0.5f, 0.0f, // 오른쪽 아래
-0.5f, 0.5f, 0.0f, // 왼쪽 위
// 두 번째 삼각형
0.5f, -0.5f, 0.0f, // 오른쪽 아래
-0.5f, -0.5f, 0.0f, // 왼쪽 아래
-0.5f, 0.5f, 0.0f // 왼쪽 위
};
잘 보면 중복되는 vertex가 있습니다. 오른쪽 아래와 왼쪽 위를 두 번 중복해서 지정하고 있죠. 이럴 때 약간의 오버헤드가 발생합니다. 오버헤드가 많아지면 손실이 커지는데 이걸 방지하는 방법을 알아봅시다.
중복하지 않고 4개 vertex만으로 지정할 수 있는 방법이 있습니다.4개 vertex를 저장하고 순서를 따로 지정해주면 됩니다.
EBO는 이 처리를 해주기 위한 object인데요. 바로 vertex index를 저장하는 buffer입니다. 이로써 indexed drawing이 가능해서 오버헤드를 줄일 수 있습니다. 어떤 점들을 그릴지를 index 정보로 판단해서 중복되는 vertex를 줄입니다.
float vertices[] = {
0.5f, 0.5f, 0.0f, // 오른쪽 위
0.5f, -0.5f, 0.0f, // 오른쪽 아래
-0.5f, -0.5f, 0.0f, // 왼쪽 아래
-0.5f, 0.5f, 0.0f // 왼쪽 위
};
unsigned int indices[] = { // 0부터 시작하는 것에 유의하세요!
0, 1, 3, // 첫 번째 삼각형
1, 2, 3 // 두 번째 삼각형
};
이제 vertex가 4개만 있고 index로 그릴 vertex를 지정하게 되었습니다. 이제 이걸 담을 EBO를 생성합시다.
unsigned int EBO;
glGenBuffers(1, &EBO);
EBO도 VBO처럼 binding하고 glBufferData로 index를 buffer에 복사합니다. binding하는 buffer type의 경우 이번에는 GL_ELEMENT_ARRAY_BUFFER로 지정합니다.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
이전에는 glDrawArrays로 호출했지만 binding buffer를 GL_ELEMENT_ARRAY_BUFFER로 지정하였기 때문에 이번에는 glDrawElements를 사용해서 index를 기반으로 vertex를 그립니다.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
매개변수는 순서대로 draw mode, 그리는 index 개수, index type, offset 혹은 index array 입니다.
index type은 현재 GL_UNSIGNED_INT 로 사용하고, 마지막 인자의 경우 EBO를 사용하지 않는 경우 사용하기 때문에 0으로 하였습니다.
glDrawElements는 opengl에서 GL_ELEMENT_ARRAY_BUFFER에 아까 binding한 EBO에서 index를 가져와서 렌더링할 때 사용합니다. index를 사용하려면 EBO를 binding해야하는데, 번거롭다면 이것도 VAO에 담을(?)수 있습니다 (정확히는 담는게 아니라 가리키는 거죠). VAO를 binding하고 EBO를 binding하면 VAO에서 EBO를 자동으로 가리켜줍니다.
VAO가 glBindBuffer 호출을 자동으로 저장한다고 했습니다. 반대로 unbinding에 대해서도 자동으로 처리하니 VAO에 binding 하기 전에 EBO를 unbinding 해서 EBO를 설정하지 못하지 않도록 주의하시길 바랍니다.
EBO를 사용한 코드를 추가한 코드를 봅니다.
// ..:: 초기화 코드 :: ..
// 1. 정점 배열 객체 바인딩
glBindVertexArray(VAO);
// 2. 정점 배열을 OpenGL이 사용할 정점 버퍼에 복사
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 인덱스 배열을 OpenGL이 사용할 요소 버퍼에 복사
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 4. 정점 속성 포인터 설정
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
...
// ..:: 그리기 코드 (렌더 루프 내) :: ..
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
이제 실행하면 아래 이미지 처럼 나와야합니다. 오른쪽은 특별히 wireframe모드로 그렸습니다.
Wireframe mode
glPolyMode(GL_FRONT_AND_BACK, GL_LINE)을 사용하면 wireframe 모드로 primitive를 그릴 수 있습니다.
첫 번째 인수는 삼각형의 앞면 뒷면에 모두 적용한다는 것.
두 번째 인수는 선으로 그린다는 설정입니다.
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)을 이용하면 다시 원래대로 면으로 그립니다.
여기까지 정상적으로 따라오셨다면 opengl의 난관 중 하나 삼각형 그리기를 마친 겁니다. 자랑스러워 하셔도 됩니다. 여기까지 렌더링 과정을 전반적으로 이해하셨을 겁니다.
Additional resources
추가적인 자료입니다.