상세 컨텐츠

본문 제목

OpenGL과 GLFW, GLEW 설명과 마름모 생성 튜토리얼

프로그래밍

by 경밤 2020. 8. 11. 10:17

본문

반응형

OpenGL과 GLFW, GLEW 설명과 마름모 생성 튜토리얼


#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h> //OpenGL 확장팩
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
using namespace glm;

GLFWwindow* window;

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
	if (key == GLFW_KEY_E && action == GLFW_PRESS) {
		printf("E Pressed\n");
	}
	if (key == GLFW_KEY_Q && action == GLFW_RELEASE) {
		printf("Q Released\n");
	}
}

int main()
{
	// Initialise GLFW
	if( !glfwInit() )
	{
		fprintf( stderr, "GLFW 초기화 실패\n" );
		getchar();
		return -1;
	}

	glfwWindowHint(GLFW_SAMPLES, 4); //안티엘리어싱 4배(멀티 픽셀 샘플링)
	glfwWindowHint(GLFW_RESIZABLE,GL_FALSE); //창 크기 조절
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //최대 버전 요구
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //최소 버전 요구
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //향후 지원되지 않는 것은 제외함을 의미

	//창 열기
	window = glfwCreateWindow( 1024, 768, "Window", NULL, NULL);
	if( window == NULL ){
		fprintf( stderr, "GLFW 창 열기 실패\n" );
		getchar();
		glfwTerminate();
		return -1;
	}
	//현재 무슨 window를 사용할지 설정
	glfwMakeContextCurrent(window);
	//window 창에 대해서 입력받기 설정
	glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
	//키 입력 콜백 설정
	glfwSetKeyCallback(window, key_callback);
	
	glewExperimental = true; // GLEW에 코어프로필 사용 설정
	if (glewInit() != GLEW_OK) {
		fprintf(stderr, "GLEW을 초기화 하는데에 실패했습니다.\n");
		getchar();
		glfwTerminate();
		return -1;
	}

	glClearColor(0.0f, 0.0f, 0.7f, 0.0f);

	//VAO, VBO 설정 ....
    //코드

	do{
		glClear(GL_COLOR_BUFFER_BIT);

		//활성화, 그리기 코드..


		glfwSwapBuffers(window); //더블 버퍼링, 버퍼 교체
		glfwPollEvents(); //설정한 콜백들(key callback.. 등) 호출

	}
	while( glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS
		&& glfwWindowShouldClose(window) == 0);
	
	//VAO, VBO 정리 코드

	glfwTerminate();

	return 0;
}

기본코드를 작성 했습니다. 이 글에서는 VAO, VBO를 이용하여서 마름모를 그리는 것을 할 것입니다.

1. VAO 생성

VAO는 Vertex Array Object의 줄임말입니다. 이름에서도 알 수 있듯이 배열입니다. 길이는 1이 될 수도 있습니다. 생성하기 나름이죠. 또 VAO의 한개의 element는 공식문서에서 '이름'으로 불리기도 합니다.

GLuint VertexArrayID; //GLuint는 '이름' 역할을 하며, OpenGL의 객체입니다.
glGenVertexArrays(1, &VertexArrayID); //VAOs 생성 (한개) = 길이 1짜리 배열
glBindVertexArray(VertexArrayID); //이 VAO를 사용하겠다고 설정

VAO를 생성했습니다. 잊지마세요 이건 배열입니다. 하지만 그저 한개의 GLuint 를 넣어도 됩니다. 그렇담 결국에 길이 1짜리 배열인거와 같은겁니다. 

어쨌거나 VAO는 배열이나, VAO는 한 덩어리 처럼 사용합니다.

//마름모 꼭지점 벡터들. 3개의 꼭지점이 한개의 면입니다.
static const GLfloat g_vertex_buffer_data[] = {
	-1.0f, 0.0f, 0.0f,    //
	1.0f, 0.0f, 0.0f,     // 마름모 부분의 아랫면
	0.0f, -1.0f, 0.0f,    //
    
	-1.0f, 0.0f, 0.0f,    //
	1.0f, 0.0f, 0.0f,     // 마름모 부분의 윗면
	0.0f,  1.0f, 0.0f,    //
};

마름모는 2개의 삼각형으로 구성되어있습니다.(뭐 다르게 자른다면 4개, 8개도 되겠네요) 따라서 2개의면, 즉 6개의 점을 만들었습니다. 아래와 같은 좌표계입니다.

2. VBO 생성과 VBO를 VAO에 적용

GLuint vertexbuffer; //한개의 객체가 될수 있으며, '이름'입니다.
glGenBuffers(1, &vertexbuffer); //VBO 생성 (Vertex buffer object)
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); //버퍼 사용 설정합니다.

//사용하기로한 vertexbuffer에 좌표점 설정
//STATIC_DRAW는 추후 변화 없음을 나타낸다. DYNAMIC_DRAW는 추후 변경 가능
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

3. 마름모 그리기

do{
	glClear(GL_COLOR_BUFFER_BIT);

	glEnableVertexAttribArray(0); //첫번째 VAO (아까 생성한것) 활성화
	glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); //VBO 사용 설정
	glVertexAttribPointer( //속성 지정(렌더링 할때 혹은, 초기화 할때 등..)
		0,                  // 0번째의 Attribute라는것을 지정. 다른 수도 가능
		3,                  // xyz임으로 3. xyzw이면 4
		GL_FLOAT,           // glBufferData로 넘긴 데이터의 타입 -> Float
		GL_FALSE,           // 정규화(normalized) 
		0,                  // 간격
		(void*)0            // 배열 오프셋
	);
	glDrawArrays(GL_TRIANGLES, 0, 6); //점 0부터 6까지 그립니다.
	glDisableVertexAttribArray(0);

	glfwSwapBuffers(window); //더블 버퍼링, 버퍼 교체
	glfwPollEvents(); //설정한 콜백들(key callback.. 등) 호출
}
while( glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS
	&& glfwWindowShouldClose(window) == 0);

주석으로 다 적어놨습니다.

 

4. 정리

glDeleteBuffers(1, &vertexbuffer);
glDeleteVertexArrays(1, &VertexArrayID);

5. 전체코드

#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h> //OpenGL 확장팩
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
using namespace glm;

GLFWwindow* window;

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
	if (key == GLFW_KEY_E && action == GLFW_PRESS) {
		printf("E Pressed\n");
	}
	if (key == GLFW_KEY_Q && action == GLFW_RELEASE) {
		printf("Q Released\n");
	}
}

int main()
{
	// Initialise GLFW
	if( !glfwInit() )
	{
		fprintf( stderr, "GLFW 초기화 실패\n" );
		getchar();
		return -1;
	}

	glfwWindowHint(GLFW_SAMPLES, 4); //안티엘리어싱 4배(멀티 픽셀 샘플링)
	glfwWindowHint(GLFW_RESIZABLE,GL_FALSE); //창 크기 조절
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //최대 버전 요구
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //최소 버전 요구
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //향후 지원되지 않는 것은 제외함을 의미

	//창 열기
	window = glfwCreateWindow( 1024, 768, "Window", NULL, NULL);
	if( window == NULL ){
		fprintf( stderr, "GLFW 창 열기 실패\n" );
		getchar();
		glfwTerminate();
		return -1;
	}
	//현재 무슨 window를 사용할지 설정
	glfwMakeContextCurrent(window);
	//window 창에 대해서 입력받기 설정
	glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
	//키 입력 콜백 설정
	glfwSetKeyCallback(window, key_callback);
	
	glewExperimental = true; // GLEW에 코어프로필 사용 설정
	if (glewInit() != GLEW_OK) {
		fprintf(stderr, "GLEW을 초기화 하는데에 실패했습니다.\n");
		getchar();
		glfwTerminate();
		return -1;
	}

	
	glClearColor(0.0f, 0.3f, 0.1f, 0.0f);

	GLuint VertexArrayID; //GLuint는 '이름' 역할을 하며, OpenGL의 객체입니다.
	glGenVertexArrays(1, &VertexArrayID); //VAOs 생성 (한개) = 길이 1짜리 배열
	glBindVertexArray(VertexArrayID); //이 VAO를 사용하겠다고 설정

	//마름모 꼭지점 벡터들. 3개의 꼭지점이 한개의 면입니다.
	static const GLfloat g_vertex_buffer_data[] = {
		-1.0f, 0.0f, 0.0f,    //
		1.0f, 0.0f, 0.0f,     // 마름모 부분의 아랫면
		0.0f, -1.0f, 0.0f,    //

		-1.0f, 0.0f, 0.0f,    //
		1.0f, 0.0f, 0.0f,     // 마름모 부분의 윗면
		0.0f,  1.0f, 0.0f,    //
	};

	GLuint vertexbuffer; //한개의 객체가 될수 있으며, '이름'입니다.
	glGenBuffers(1, &vertexbuffer); //VBO 생성 (Vertex buffer object)
	glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); //버퍼 사용 설정합니다.

	//사용하기로한 vertexbuffer에 좌표점 설정
	//STATIC_DRAW는 추후 변화 없음을 나타낸다. DYNAMIC_DRAW는 추후 변경 가능
	glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

	do{
		glClear(GL_COLOR_BUFFER_BIT);

		glEnableVertexAttribArray(0); //첫번째 VAO (아까 생성한것) 활성화
		glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); //VBO 사용 설정
		glVertexAttribPointer( //속성 지정(렌더링 할때 혹은, 초기화 할때 등..)
			0,                  // 0번째의 Attribute라는것을 지정. 다른 수도 가능
			3,                  // xyz임으로 3. xyzw이면 4
			GL_FLOAT,           // glBufferData로 넘긴 데이터의 타입 -> Float
			GL_FALSE,           // 정규화(normalized)
			0,                  // 간격
			(void*)0            // 배열 버퍼의 오프셋(offset; 옮기는 값)
		);
		glDrawArrays(GL_TRIANGLES, 0, 6); //점 0부터 6까지 그립니다.
		glDisableVertexAttribArray(0);


		glfwSwapBuffers(window); //더블 버퍼링, 버퍼 교체
		glfwPollEvents(); //설정한 콜백들(key callback.. 등) 호출

	}
	while( glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS
		&& glfwWindowShouldClose(window) == 0);
	
	glDeleteBuffers(1, &vertexbuffer);
	glDeleteVertexArrays(1, &VertexArrayID);

	glfwTerminate();

	return 0;
}

반응형

관련글 더보기