※ 본 포스팅은 learnopengl.com을 번역 및 가감한 포스팅이며, learnopengl에서는 번역 작업 참여를 적극 장려하고 있습니다!
아래 링크에서 원문을 확인할 수 있습니다.
Ready your engines
프로그램은 이미지를 한 번 딱 그리고 바로 창이 닫혀버리면 안됩니다. 프로그램이 종료되라는 명령을 받기 전까지는 반복해서 화면을 그려주어야하고, 사용자 입력도 처리처리하도록 해야합니다.
그래서 GLFW에 중지 지시를 내릴 때까지 실행되는 while loop, 즉 render loop를 만들어야합니다. 아래 코드는 기본적인 루프를 만듭니다.
while(!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwWindowShouldClode 함수는 루프를 반복할 때 매번 GLFW에게 종료하라는 지시가 내려졌는지 확인합니다. 종료 지시를 받으면 함수가 true를 반환해서 렌더 루프가 멈추고, 프로그램이 종료됩니다.
glfwPollEvents 함수는 키보드 입력이나 마우스 움직임 등 이벤트가 발생했는지 확인하고, window 상태를 업데이트 하고, 그 이벤트에 연결된 함수들을 호출합니다. (연결할 때는 callback method를 이용해서 등록합니다.)
glfwSwapBuffers는 렌더링을 반복하면서 화면에 픽셀마다 색상 값을 저장하고 있는 2차원 버퍼를 계속해서 교체해준 다음, 화면에 뿌립니다.
Double buffer
프로그램이 버퍼가 하나라면 화면을 그려내는 순간 깜박임 문제가 발생할 수 있습니다. 그건 출력하는 이미지가 바로 그려지지 않고 픽셀 단위로 좌에서 우로, 위에서 아래로 그려지기 때문에 깜박임이 보이는 것입니다.
그래서 사용자가 실시간으로 화면을 보는 동시에 렌더링이 진행되면 화면에 아티팩트(인위적인 어색함)이 나타날 수 있습니다. 이를 해결하기 위해서 윈도우의 경우 렌더링에 더블 버퍼를 적용합니다.
프론트 버퍼의 경우 화면에 표시되는 최종 출력 이미지를 갖고 있고, 백 버퍼에서 렌더링 명령에 따른 화면을 그리는 작업이 담깁니다. 렌더링 명령이 완료되면 백 퍼버를 프론트와 교체해서 사람이 화면을 보는 동안 화면 이미지 렌더링이 진행되지 않도록 하고, 아티팩트도 제거됩니다.
One last thing
렌더 루프를 종료하게 되면 미리 할당해두었던 GLFW 리소스들을 삭제해버려야합니다. main함수의 끝에서 glfwTerminate 함수를 호출해서 삭제합니다.
glfwTerminate();
return 0;
이를 통해서 할당한 리소스들을 정리하고, 프로그램을 정상 종료시킵니다. 이제 프로젝트를 컴파일해봅시다. 에러가 나지 않으면 아래 화면이 나올 겁니다.
"창에서 아무 것도 안나와요"
네, 아무것도 안나오고 검은 화면만 나옵니다. 성공입니다. 중요한 것은 창이 꺼지지 않고 잘 뜨는 것입니다.
혹시 안된다면 아래 전체 코드를 참고해보세요. (화면이 다른색으로 깜박이는 경우는 일단 쭉 읽어보세요)
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
컴파일 하는 과정에 문제가 있다면, 먼저 링커 옵션 설정을 다시 확인하고, IDE에 올바른 디렉터리를 프로젝트에 포함했는지 확인하세요. 위 전체 코드와 비교해서 코드의 문제인지도 확인해보세요.
Input
이제 GLFW에서 입력 관련 구현을 진행해봅시다. GLFW에는 여러 입력 함수가 있습니다. 함수에서는 window와 key를 입력으로 받고 해당 키가 눌려있는지 여부를 반환하는 glfwGetKey 함수를 사용합니다. 입력 코드를 관리하기 위한 함수 processInput를 만들어서 관리합시다.
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
여기서는 사용자가 Escape 키를 눌렀는지 확인하고 안 눌렀으면 glfwGetKey는 GLFW_RELEASE를 반환합니다. 키를 눌렀다면, glfwSetWindowShouldClose를 사용해서 WindowShouldClose 속성을 true로 설정하고 GLFW를 close합니다. 그러면 while 루프의 조건이 false로 바뀌어 프로그램이 종료됩니다.
그 다음 루프 할 때마다 processInput 함수를 호출합니다.
while (!glfwWindowShouldClose(window))
{
processInput(window);
glfwSwapBuffers(window);
glfwPollEvents();
}
이러면 키가 입력되는 것이 확인되고 프레임마다 상호작용할 수 있습니다. 렌더 루프의 한 루틴을 보통 frame이라고 합니다.
Rendering
모든 렌더링 명령을 루프함수 안에 배치하려고 합니다 .루프의 한 프레임마다 모든 렌더링 명령을 실행한다는 말입니다.
// render loop
while(!glfwWindowShouldClose(window))
{
// input
processInput(window);
// rendering commands here
...
// check and call events and swap the buffers
glfwPollEvents();
glfwSwapBuffers(window);
}
검은 화면으로는 제대로 작동을 하는지 모르니까 화면 색을 바꿉시다. 프레임 시작시 화면을 지우지 않으면 이전 결과가 남아있게 되기 때문에, 화면 색상 버퍼를 지우는 glClear 함수를 사용합니다.
glClear는 지울 버퍼를 버퍼 비트를 통해 지정합니다. 설정 가능한 비트는
GL_COLOR_BUFFER_BIT
GL_DEPTH_BUFFER_BIT
GL_STENCIL_BUFFER_BIT
이렇게 있습니다. 현재 색상 값을 지우려고 하니까 color buffer만 지웁시다.
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glClearColor를 사용해서 화면을 지울 색상도 정해야합니다. glCear로 색상 버퍼를 지우고 난 후 glClearColor로 새로운 색상을 채웁니다. 그러면 화면이 어두운 녹청색으로 바뀝니다.
glClearColor는 상태를 설정하는 함수이고, glClear 함수는 현재 상태를 사용해서 화면을 지우고 새로운 색을 덮어씌우는 함수입니다.
전체 코드는 아래와 같습니다.
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
여기까지 렌더 루프를 준비하였고 다음 포스팅에서 렌더링 함수들을 호출합시다.
'개발 · 컴퓨터공학 > LearnOpenGL' 카테고리의 다른 글
OpenGL Vertex Shader 버텍스(정점) 셰이더 [Learn OpenGL 쉬운 번역] (2) | 2024.08.22 |
---|---|
OpenGL 그래픽 파이프라인 [Learn OpenGL 쉬운 번역] (0) | 2024.08.22 |
OpenGL 윈도우 띄우기 [Learn OpenGL 쉬운 번역] (0) | 2024.08.22 |
opengl 창 띄우기 라이브러리 준비 [Learn OpenGL 쉬운 번역] (0) | 2024.08.22 |
opengl 이란? opengl 개념 정리 [Learn OpenGL 쉬운 번역] (0) | 2024.08.22 |