본문 바로가기

프로그래밍/OpenGL

[OpenGL] 오픈지엘 EBO를 이용한 사각형 그리기

이번 시간에는 오픈지엘에서 EBO를 이용하여 삼각형을 그려보겠다.

 

 

Element Buffer Object (EBO)

 

VBO와 EBO의 차이

 

EBO란 그리려는 도형의 정점 인덱스를 배치하여 인덱스 리스트 순서대로 그리는 방법이다.

VBO에 비해 중복되는 정점을 막을 수 있고 도형이 복잡해졌을 때 인덱스 리스트 만으로 수월하게 그릴 수 있다.

하지만 obj나 fbx 형식의 오브젝트는 EBO를 이용하여 사용하지 못하는 점을 주의해야한다.

 

 

 

01. EBO 추가 버퍼 생성

 

GLuint VAO, VBO[2], EBO;

GLvoid InitBuffer()
{
	glGenVertexArrays(1, &VAO);
	glGenBuffers(2, VBO);
	glGenBuffers(1, &EBO);
}

 

 

 

02. EBO에 사용할 도형 인덱스 배열 추가

 

void DrawRect()
{
	// 사각형 위치
	const float vertexPosition[] =
	{
		-0.5f, 0.5f, 0.0f,
		-0.5f, -0.5f, 0.0f,
		0.5f, -0.5f, 0.0f,
		0.5f, 0.5f, 0.0f
	};

	// 사각형 인덱스
	const int vertexIndeces[] =
	{
		0, 1, 2,
		0, 2, 3
	};

	// 사각형 색상
	const float vertexColor[] =
	{
		1.0, 0.0, 0.0,
		0.0, 1.0, 0.0,
		0.0, 0.0, 1.0
	};
    
	...
}

 

 

 

03. EBO를 바인드 하고 glDrawElements 함수 이용하여 도형 그리기

 

void DrawTriangle()
{	
	...
    
	// VAO 바인드
	glBindVertexArray(VAO);

	// 사각형 위치 버퍼 바인드
	glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPosition), vertexPosition, GL_STATIC_DRAW);

	// 사각형 인덱스 버퍼 바인드
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vertexIndeces), vertexIndeces, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
	glEnableVertexAttribArray(0);

	// 사각형 색상 버퍼 바인드
	glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexColor), vertexColor, GL_STATIC_DRAW);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
	glEnableVertexAttribArray(1);

	// 사각형 그리기
	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}

 

 

전체코드

 

#include <iostream>
#include <gl/glew.h> 
#include <gl/freeglut.h>
#include <gl/freeglut_ext.h>

#pragma comment(lib, "glew32.lib")
#pragma comment(lib, "freeglut.lib")

#define _CRT_SECURE_NO_WARNINGS

#define WIDTH 800
#define HEIGHT 600

#define BYTE_SIZE_RECT 48

using namespace std;

// 콜벡 함수
GLvoid Render(GLvoid);
GLvoid Reshape(int w, int h);

// VAO, VBO, EBO
GLuint VAO, VBO[2], EBO;

void DrawRect();

GLchar* vertexSource, * fragmentSource; // 소스코드 저장 변수
GLuint vertexShader, fragmentShader; //세이더 객체
GLuint shaderProgramID; // 세이더 프로그램

GLint CreateShader(const char* file, int type);
GLvoid CreateShaderProgram();

GLvoid InitBuffer();

void main(int argc, char** argv)
{
	// 윈도우 생성
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(0, 0);
	glutInitWindowSize(WIDTH, HEIGHT);
	glutCreateWindow("Draw Rect by using EBO");

	// GLEW 초기화하기
	glewExperimental = GL_TRUE;
	glewInit();

	InitBuffer();
	CreateShaderProgram();

	glutDisplayFunc(Render);

	glutMainLoop();
}

GLvoid Render()
{
	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	DrawRect();
	glutSwapBuffers();
}

GLvoid Reshape(int w, int h)
{
	// 뷰포트 기본 WIDTH HEIGHT로 설정
	glViewport(0, 0, w, h);
}

GLvoid InitBuffer()
{
	glGenVertexArrays(1, &VAO);
	glGenBuffers(2, VBO);
	glGenBuffers(1, &EBO);
}

char* GetBuf(const char* file)
{
	FILE* fptr;
	long length;
	char* buf;

	// 파일 읽기 형식으로 열기
	fopen_s(&fptr, file, "rb"); 

	// 예외처리
	if (!fptr) return NULL;

	// 파일 buf로 변환
	fseek(fptr, 0, SEEK_END); 
	length = ftell(fptr); 
	buf = (char*)malloc(length + 1); 
	fseek(fptr, 0, SEEK_SET);
	fread(buf, length, 1, fptr); 

	// 파일 종료
	fclose(fptr); 

	buf[length] = 0; 
	return buf; 
}

GLint CreateShader(const char* file, int type)
{
	// glsl 파일 읽기
	GLchar* source = GetBuf(file);

	// 객체 생성
	GLint shader = glCreateShader(type);
	glShaderSource(shader, 1, (const GLchar**)&source, 0);
	glCompileShader(shader);

	// 컴파일 에러 체크
	GLint result;
	GLchar errorLog[512];
	glGetShaderiv(shader, GL_COMPILE_STATUS, &result);

	if (!result)
	{
		glGetShaderInfoLog(shader, 512, NULL, errorLog);
		std::cerr << "ERROR: 컴파일 실패\n" << errorLog << std::endl;
		return 0;
	}
	
	return shader;
}

void CreateShaderProgram()
{
	vertexShader = CreateShader("vertex.glsl", GL_VERTEX_SHADER);
	fragmentShader = CreateShader("fragment.glsl", GL_FRAGMENT_SHADER);

	// 세이더 프로그램 생성
	shaderProgramID = glCreateProgram();
	glAttachShader(shaderProgramID, vertexShader);
	glAttachShader(shaderProgramID, fragmentShader);
	glLinkProgram(shaderProgramID);

	// 세이더 삭제
	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);

	// 세이더 프로그램 사용
	glUseProgram(shaderProgramID);
}

void DrawRect()
{
	// 사각형 위치
	const float vertexPosition[] =
	{
		-0.5f, 0.5f, 0.0f,
		-0.5f, -0.5f, 0.0f,
		0.5f, -0.5f, 0.0f,
		0.5f, 0.5f, 0.0f
	};

	// 사각형 인덱스
	const int vertexIndeces[] =
	{
		0, 1, 2,
		0, 2, 3
	};

	// 사각형 색상
	const float vertexColor[] =
	{
		1.0, 0.0, 0.0,
		0.0, 1.0, 0.0,
		0.0, 0.0, 1.0
	};

	// VAO 바인드
	glBindVertexArray(VAO);

	// 사각형 위치 버퍼 바인드
	glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPosition), vertexPosition, GL_STATIC_DRAW);

	// 사각형 인덱스 버퍼 바인드
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vertexIndeces), vertexIndeces, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
	glEnableVertexAttribArray(0);

	// 사각형 색상 버퍼 바인드
	glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexColor), vertexColor, GL_STATIC_DRAW);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
	glEnableVertexAttribArray(1);

	// 사각형 그리기
	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}

 

 

EBO를 사용하여 사각형 그리기 성공