본문 바로가기

프로그래밍/OpenGL

[OpenGL] 오픈지엘 VAO와 VBO를 이용한 삼각형 그리기

이번 시간에는 오픈지엘에서 여러가지 도형을 화면에 그려보는 시간을 가지겠다.

 

지난 시간에 작성한 세이더 프로그램을 이용하여 도형을 그리기 위해서는 다음 개념이 필요하다.

 

● Vertex Array Object (VAO)

● Vertex Buffer Objects (VBO)

 

VAO와 VBO

 

VBO란 정점 데이터를 저장하기 위한 메모리 버퍼로 속성 값으로 Position, Normal, Color 등을 저장한다.

VAO는 한 개 이상의 VBO를 포함한 객체의 정보를 저장하는 메모리 버퍼이다.

 

 

 

VAO VBO 추상화

 

즉, 어떤 오브젝트를 그릴 때 오브젝트가 갖고 있는 속성(위치, 색상 등)의 정보를 담고 있는 버퍼가 VBO이고

속성들의 정보값의 위치를 갖고 있는 버퍼가 VAO이다.

 

 

 

개념은 이해했으니 실제로 VAO와 VBO를 이용하여 간단한 삼각형을 그려보자.

오픈지엘에서 삼각형을 그리는 과정은 다음과 같다.

 

VAO와 VBO를 이용해 삼각형을 그리는 과정

 

01. 버퍼 생성

 

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

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

 

우선, 그릴 도형의 위치, 색상 속성값을 저장할 VBO 2개와 VAO 1개를 생성한다.

 

 

 

02. 오브젝트 배열 생성

 

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

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

 

 

 

03. VAO, VBO 버퍼 바인드 및 속성 값 저장하고 각각 세이더에 전달하기

 

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

	// 삼각형 위치 버퍼 바인드
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, BYTE_SIZE_TRIANGLE * 1, vertexPosition, 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, BYTE_SIZE_TRIANGLE * 1, vertexColor, GL_STATIC_DRAW);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
	glEnableVertexAttribArray(1);
    
	...
}

 

glBufferData 함수와 glVertexAtrribPointer 함수의 매개변수 값은 다음과 같다.

 

● glBufferData

glBufferData GLenum target GLsizeiptr size const void * data GLenum usage
사용 버퍼 종류 버퍼 사이즈 버퍼 사용 패턴

 

- target으로 대표적으로 쓰이는 버퍼

GL_ARRAY_BUFFER Vertex attributes
GL_ELEMENT_ARRAY_BUFFER Vertex array indices (EBO 이용)

 

 

glVertexAtrribPointer

glVertexAtrribPointer GLuint index GLint size GLenum type GLboolean normalized GLsizei stride const void * pointer
설정할 vertex 속성 인덱스 vertex 크기 데이터 타입 정규화 여부 vertex 사이 공백 크기 데이터 시작 위치 값(초기 0)

 

glVertexAtrribPointer을 사용하여 위치 속성을 0번 인덱스 색상 속성을 1번 인덱스에 설정해야한다.

우리는 이전에 세이더 코드에 layout (location = index)을 이용하여 설정한 것을 알 수 있다.

 

#version 330 core

layout (location = 0) in vec3 in_Position;	<-- 0번 인덱스로 position 정보를 받겠다.
layout (location = 1) in vec3 in_Color;  	<-- 1번 인덱스로 color 정보를 받겠다.

out vec3 out_Color; 

void main(void) 
{
	gl_Position = vec4 (in_Position.x, in_Position.y, in_Position.z, 1.0);
	out_Color = in_Color;
}

 

 

 

04. 도형 그리기

 

void DrawTriangle()
{
	...

	// 삼각형 그리기
	glDrawArrays(GL_TRIANGLES, 0, 3 * 1);
}

 

● glDrawArrays

 

glDrawArrays GLenum mode GLint first GLsizei count
프리미티브 render 종류 버퍼 시작 위치 그릴 정점 개수

 

- mode로 대표적으로 쓰이는 값

GL_POINTS 포인트로 그리기
GL_LINES 라인으로 그리기
GL_LINE_LOOP
라인으로 끊지 않고 계속해서 그리기
GL_TRIANGLES 삼각형으로 그리기

 

 

 

전체코드

 

#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_TRIANGLE 36

using namespace std;

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

// VAO, VBO
GLuint vao, vbo[2];

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

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 Triangle");

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

	DrawTriangle();
    
	glutSwapBuffers();
}

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

GLvoid InitBuffer()
{
	glGenVertexArrays(1, &vao);
	glGenBuffers(2, vbo);
}

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 DrawTriangle()
{
	// 삼각형 버퍼 생성
	const float vertexPosition[] =
	{
		0.0f, 0.5f, 0.0f,
		-0.5f, -0.5f, 0.0f,
		0.5f, -0.5f, 0.0f
	};

	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, BYTE_SIZE_TRIANGLE * 1, vertexPosition, 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, BYTE_SIZE_TRIANGLE * 1, vertexColor, GL_STATIC_DRAW);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
	glEnableVertexAttribArray(1);

	// 삼각형 그리기
	glDrawArrays(GL_TRIANGLES, 0, 3 * 1);
}

 

 

삼각형 그리기 성공!

 

 

| Reference

 

https://registry.khronos.org/OpenGL-Refpages/gl4/html/glBufferData.xhtml

https://registry.khronos.org/OpenGL-Refpages/gl4/html/glVertexAttribPointer.xhtml

https://registry.khronos.org/OpenGL-Refpages/gl4/html/glDrawArrays.xhtml