본문 바로가기

프로그래밍/OpenGL

[OpenGL] 오픈지엘 EBO를 이용한 2차원 도형 그리기 및 응용

이번 시간에는 EBO를 사용해 다양한 도형을 그려보고 콜벡 함수를 사용해 응용 프로그램을 만들어보도록 하겠다.

 

구체적으로 이번 프로젝트에서 구현할 목록은 다음과 같다.

 

점, 선, 삼각형, 사각형, 오각형, 육각형 도형 그리기

GameObjectArray를 이용하여 도형 오브젝트 관리하는 프레임워크 구현

지정 번호를 눌러 도형 선택(1: 점 / 2: 선 / 3: 삼각형 / 4: 사각형 / 5: 오각형 / 6: 육각형)

● 마우스 클릭 한 위치에 지정한 도형 그리기

 

 

01. 프레임워크 구성

 

우선, 프로젝트를 진행하기 위한 프레임워크를 구성해야한다.

 

프레임워크 구성도

 

위 그림에서 각 클래스와 헤더 파일의 역할은 다음과 같다.

 

main 응용 프로그램
pch 미리 컴파일된 헤더
ObjectManager 오브젝트를 관리할 매니저 클래스
Object 오브젝트 클래스
Figure 도형 정의 클래스

 

 

1) pch.h

#pragma once

#include <iostream>
#include <gl/glew.h>
#include <gl/freeglut.h>
#include <gl/freeglut_ext.h>
#include <gl/glm/glm.hpp>
#include <gl/glm/ext.hpp>
#include <gl/glm/gtc/matrix_transform.hpp>

#include <vector>

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

#define _CRT_SECURE_NO_WARNINGS

#define WIDTH 800
#define HEIGHT 600

 

pch.h 파일은 프리컴파일 헤더(Precompiled Header)로 프로젝트에 사용할 헤더 및 define 변수들을 정의하면 된다.

 

미리 컴파일된 헤더 지정

 

헤더를 미리 컴파일하기 위해 [프로젝트] -> [속성] -> [C/C++] -> [미리 컴파일된 헤더]로 들어가서 pch.h 헤더파일을 지정해줘야 한다.

 

 

 

2) Figure

 

#pragma once

#include "pch.h"

class Figure
{
public:
	// 삼각형
	static GLfloat	TriangleVertexs[9];
	static GLfloat	TriangleColors[9];
	static GLint	TriangleIndecies[3];

	// 사각형
	static GLfloat	RectVertexs[12];
	static GLfloat	RectColors[12];
	static GLint	RectIndecies[6];

	// 오각형
	static GLfloat	PentaVertexs[15];
	static GLfloat	PentaColors[15];
	static GLint	PentaIndecies[12];

	// 육각형
	static GLfloat	HexaVertexs[18];
	static GLfloat	HexaColors[18];
	static GLint	HexaIndecies[18];
};
#include "Figure.h"


GLfloat Figure::TriangleVertexs[9] = {
	0.0f, 0.1f, 0.0f,
	-0.1f, -0.1f, 0.0f,
	0.1f, -0.1f, 0.0f
};

GLfloat Figure::TriangleColors[9] = {
	1.0f, 1.0f, 0.0f,
	1.0f, 1.0f, 0.0f,
	1.0f, 1.0f, 0.0f
};

GLint Figure::TriangleIndecies[3] = {
	0, 1, 2
};

GLfloat Figure::RectVertexs[12] = {
	-0.1f, 0.1f, 0.0f,
	-0.1f, -0.1f, 0.0f,
	0.1f, -0.1f, 0.0f,
	0.1f, 0.1f, 0.0f
};

GLfloat Figure::RectColors[12] = {
	160.0f / 255.0f, 212.0f / 255.0f, 104.0f / 255.0f,
	160.0f / 255.0f, 212.0f / 255.0f, 104.0f / 255.0f,
	160.0f / 255.0f, 212.0f / 255.0f, 104.0f / 255.0f,
    160.0f / 255.0f, 212.0f / 255.0f, 104.0f / 255.0f
};

GLint Figure::RectIndecies[6] = {
	0, 1, 2,
	0, 2, 3
};

GLfloat Figure::PentaColors[15] = {
	1.0f, 0.0f, 0.0f,
	1.0f, 0.0f, 0.0f,
	1.0f, 0.0f, 0.0f,
	1.0f, 0.0f, 0.0f,
	1.0f, 0.0f, 0.0f,
};

GLfloat Figure::HexaColors[18] = {
	0.0f, 1.0f, 0.0f,
	0.0f, 1.0f, 0.0f,
	0.0f, 1.0f, 0.0f,
	0.0f, 1.0f, 0.0f,
	0.0f, 1.0f, 0.0f,
	0.0f, 1.0f, 0.0f,
};

 

 

 

3) Object

 

#pragma once

#include "pch.h"

class Object
{
private:
	vec3		m_position;
	vec3		m_rotate;
	vec3		m_scale;

	highp_vec3	m_colors;
	bool		m_bActive;

public:
	vector<GLfloat> m_pos;
	vector<GLfloat> m_col;
	vector<GLint> m_index;

public:
	Object();
	~Object();

	void SetPosition(float x, float y, float z);
	void SetRotate(float x, float y, float z);
	void SetScale(float x, float y, float z);
	void SetActive(bool bActive);

	void Move(float x, float y, float z);
	void Rotate(float x, float y, float z);
	void Scale(float x, float y, float z);

	bool IsActive();
};

 

#include "Object.h"

Object::Object()
{
	m_position = vec3(0.f);
	m_rotate = vec3(0.f);
	m_scale = vec3(1.f);

	m_bActive = true;
}

Object::~Object()
{
}

void Object::SetPosition(float x, float y, float z)
{
	m_position.x = x;
	m_position.y = y;
	m_position.z = z;
}

void Object::SetRotate(float x, float y, float z)
{
}

void Object::SetScale(float x, float y, float z)
{
}

void Object::SetActive(bool bActive)
{
	m_bActive = bActive;
}

void Object::Move(float x, float y, float z)
{
}

void Object::Rotate(float x, float y, float z)
{
}

void Object::Scale(float x, float y, float z)
{
}

bool Object::IsActive()
{
	return m_bActive;
}

 

 

 

4) ObjectManager

 

#pragma once

#include "pch.h"
#include "Object.h"
#include "Figure.h"

class ObjectManager
{
public:
	vector<Object*>	m_ObjectList;

public:
	ObjectManager();
	~ObjectManager();

	void CreateTriangle(int* idx);
	void CreateRect(int* idx);

	void SetPosition(int idx, float x, float y, float z);

	void Reset();

	bool IsActive(int idx);
};

 

#include "ObjectManager.h"

ObjectManager::ObjectManager()
{
}

ObjectManager::~ObjectManager()
{
	for (int i = 0; i < m_ObjectList.size(); i++)
	{
		delete m_ObjectList[i];
	}
}

void ObjectManager::CreateTriangle(int* idx)
{
	*idx += 1;

	Object* object = new Object();

	for (int i = 0; i < 9; i++) object->m_pos.emplace_back(Figure::TriangleVertexs[i]);
	for (int i = 0; i < 9; i++) object->m_col.emplace_back( Figure::TriangleColors[i]);
	for (int i = 0; i < 3; i++) object->m_index.emplace_back(Figure::TriangleIndecies[i]);

	m_ObjectList.emplace_back(object);
}

void ObjectManager::CreateRect(int* idx)
{

}

void ObjectManager::SetPosition(int idx, float x, float y, float z)
{
	if (m_ObjectList.empty()) return;

	m_ObjectList[idx]->SetPosition(x, y, z);
}

void ObjectManager::Reset()
{
	m_ObjectList.clear();
}

bool ObjectManager::IsActive(int idx)
{
	return m_ObjectList[idx]->IsActive();
}

 

 

 

5) main

 

#include "pch.h"
#include "ObjectManager.h"

#define BYTE_SIZE_TRIANGLE 36

using namespace std;

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

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

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

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

GLvoid InitBuffer();
GLvoid InitObjects();

// 삼각형 그리기 함수
void DrawObjects(int idx);
void DrawTriangle();

// 오브젝트 리스트
ObjectManager* m_ObjectManager = new ObjectManager();

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

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

	InitBuffer();
	CreateShaderProgram();

	glutDisplayFunc(Render);

	glutMainLoop();
}

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

GLvoid InitBuffer()
{
	glGenVertexArrays(1, &VAO);

	glGenBuffers(2, VBO);
	glGenBuffers(1, &EBO);

	InitObjects();
}

GLvoid InitObjects()
{
	m_ObjectManager->Reset();

	int idx = 0;

	m_ObjectManager->CreateTriangle(&idx);
}

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);
}

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

	for (int i = 0; i < m_ObjectManager->m_ObjectList.size(); i++)
	{
		if (m_ObjectManager->m_ObjectList[i]->IsActive())
		{
			DrawObjects(i);
		}
	}

	glutSwapBuffers();
}

void DrawObjects(int idx)
{
	glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
	glBufferData(GL_ARRAY_BUFFER, m_ObjectManager->m_ObjectList[idx]->m_pos.size() * 4, &m_ObjectManager->m_ObjectList[idx]->m_pos[0], GL_STATIC_DRAW);
	
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_ObjectManager->m_ObjectList[idx]->m_index.size() * 4, &m_ObjectManager->m_ObjectList[idx]->m_index[0], GL_STATIC_DRAW);

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

	glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
	glBufferData(GL_ARRAY_BUFFER, m_ObjectManager->m_ObjectList[idx]->m_col.size() * 4, &m_ObjectManager->m_ObjectList[idx]->m_col[0], GL_STATIC_DRAW);

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

	glDrawElements(GL_TRIANGLES, m_ObjectManager->m_ObjectList[idx]->m_index.size(), GL_UNSIGNED_INT, 0);
}