게임공장
[Learn OpenGL 번역] 2-3. 시작하기 - Hello Window 본문
Hello Window
시작하기/Hello Window
우리가 GLFW를 가동시킬 수 있는지 봅시다. 먼저 .cpp
파일을 만들고 새로 만든 파일의 맨 위에 다음 include를 추가합니다.
#include <glad/glad.h>
#include <GLFW/glfw3.h>
GL/gl.h
)와 같은 OpenGL 헤더파일이 포함되어 있으므로 OpenGL을 필요로 하는 다른 헤더파일보다 먼저 GLAD를 포함시켜야 합니다.
다음으로 GLFW 창을 인스턴스화 할
int main()
{
glfwInit ();
glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
return 0;
}
main 함수에서 먼저 GLFW_
접두어가 붙은 설정 가능한 옵션들을 선택할 수 있습니다. 두 번째 파라미터는 옵션의 값을 설정하는 정수입니다. 모든 설정 가능한 옵션과 해당 값들의 목록은 GLFW's window handling 문서에서 찾아볼 수 있습니다. 지금 으용 프로그램을 실행했는데 많은 undefined reference 오류가 발생했다면 GLFW 라이브러리를 성공적으로 연결하지 않았음을 의미합니다.
이 강좌의 초점은 OpenGL 3.3 버전이므로 우리는 GLFW에게 우리가 3.3버전의 OpenGL을 사용하려고 한다는 사실을 알려주어야 합니다. OpenGL 컨텍스트를 생성할 때 GLFW는 위 방법으로 적절한 조치를 취할 수 있습니다. 이렇게 하면 사용자가 적절한 OpenGL 버전을 가지고 있지 않을 때 GLFW가 실행되지 않습니다. 우리는 major version, minor version 모두 3
으로 설정했습니다. 또한 우리는 GLFW에게 명시적으로 core-profile을 사용하기를 원한다고 알려주었습니다. 우리가 GLFW에게 core-profile을 사용한다고 알려줌으로써 우리는 OpenGL 기능의 작은 집합에 접근할 수 있습니다(더이상 필요하지 않는 하위 호환 기능들을 제외합니다). Mac OS X 에서는
를 추가해야 코드가 성공적으로 작동할 것입니다.
다음으로 window 객체를 생성해야 합니다. 이 window 객체는 모든 window 데이터를 보유하고 있으며 GLFW의 다른 기능에 의해 자주 사용됩니다.
GLFWwindow* window = glfwCreateWindow (800, 600, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate ();
return -1;
}
glfwMakeContextCurrent (window);
"LearnOpenGL"
로 설정했지만 여러분이 좋아하는 이름으로 설정할 수 있습니다. 마지막 두개의 파라미터는 무시하셔도 됩니다. 이 함수의 리턴값은
GLAD
앞의 강좌에서 우리는 GLAD가 OpenGL용 함수 포인터를 관리한다고 했습니다. 그래서 우리는 OpenGL 함수들을 호출하기 전에 GLAD를 초기화해야 합니다.
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
우리는 OS마다 다른 OpenGL 함수 포인터의 주소를 로드하기 위해 GLAD 함수를 거칩니다. GLFW는 우리가 컴파일 할 환경인 OS에 따라 올바른 함수를 정의하는
Viewport
렌더링을 시작하기전에 해야할 일이 하나 남았습니다. 우리는 OpenGL에게 렌더링 윈도우의 사이즈를 알려주어야 합니다. 그렇게 해야 OpenGL이 윈도우와 관련하여 데이터와 좌표를 어떻게 표시할지 알 수 있기 때문입니다.
glViewport (0, 0, 800, 600);
실제로 뷰포트의 차원을 GLFW의 차원보다 작은 값으로 설정할 수 있습니다. 그렇게 하면 모든 OpenGL 렌더링이 더 작은 창에 표시됩니다. 그리고 예를 들어 OpenGL 뷰포트 밖의 요소들을 출력할 수 있습니다.
(-0.5,0.5)
은 스크린 좌표에서 (200,450)
에 매핑됩니다. OpenGL에서 처리된 좌표는 -1과 1 사이에 있으므로 범위(-1 에서 1)에서 (0, 800) 및 (0, 600)까지 효과적으로 매핑됩니다.
하지만 사용자가 창의 크기를 조정하는 순간의 뷰포트도 조정해야 합니다. 창의 크기를 조정할 때마다 호출되는 콜백 함수를 등록할 수 있습니다. 이 resize 콜백 함수는 다음과 같습니다.
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
framebuffer size 함수는 첫 번째 파라미터로
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport (0, 0, width, height);
}
모든 창의 크기가 변경될 때마다 이 함수를 호출하겠다고 GLFW에게 등록하여 알려주어야 합니다.
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
창을 처음 표시할 때 초기의 창 크기로
함수들을 등록하기 위해 설정할 수 있는 콜백 함수들은 많이 존재합니다. 예를 들어 조이스틱 입력 변경, 프로세스 오류 메시지 등을 처리하는 콜백 함수를 만들 수 있습니다. 윈도우를 생성하고 게임 루프가 시작되기 전에 콜백 함수를 등록할 수 있습니다.
Engine 준비
우리는 응용 프로그램이 하나의 이미지를 그리고 난 후 바로 종료되는 것을 원하지 않습니다. 프로그램이 명시적으로 중지하라는 메시지를 받기 전까지 계속해서 이미지들을 그리고 사용자 입력을 처리하도록 해야합니다. 이러한 이유 때문에 우리는
while(!glfwWindowShouldClose (window))
{
glfwSwapBuffers (window);
glfwPollEvents ();
}
true
를 반환하여 게임 루프를 중지합니다. 그 후 우리는 응용 프로그램을 닫을 수 있습니다.
응용 프로그램이 single buffer로 이미지를 그렸을 때 이미지가 깜박이는 문제가 발생될 수 있습니다. 이는 이미지가 순식간에 그려지는 것이 아니라 픽셀 하나 하나 그려지기 때문입니다. 일반적으로 왼쪽 픽셀에서 오른쪽 픽셀순으로 그리고 위쪽 픽셀에서 아랫쪽 픽셀 순으로 그려집니다. 이러한 이미지들은 사용자에게 순간적으로 동시에 표시되지 않고 단계별로 보여지기 때문에 결함이 보일 수 있는 것입니다. 이러한 문제를 피하기 위해 윈도우 응용 프로그램은 double buffer 렌더링을 적용합니다. 앞(front) 버퍼에는 최종 출력 이미지를 담고 모든 렌더링 명령은 뒤(back) 버퍼에 그려집니다. 모든 렌더링 명령이 완료되자마자 back 버퍼를 front 버퍼로 교체(swap)합니다. 이렇게 함으로써 사용자에게 이미지를 즉시 표시하여 앞에 설명한 모든 결함을 제거할 수 있습니다.
마지막 하나
렌더링 루프가 종료되자마자 할당되었던 모든 자원들을 정리 / 삭제해야 합니다.
glfwTerminate ();
return 0;
이렇게하면 모든 자원들이 정리되고 응용 프로그램이 올바르게 종료됩니다. 이제 응용 프로그램을 컴파일 해보세요. 모든 것이 잘 되었다면 다음과 같은 결과를 볼 수 있을 것입니다.
화면이 보잘 것 없는 검은 화면이라면, 여러분은 아주 잘 하고 있는 것입니다. 올바른 화면이 나오지 않거나 모든 것들이 어떻게 작동하는지 혼란스럽다면 여기에서 전체 소스 코드를 확인하세요.
컴파일하는 데 문제가 있다면, 먼저 모든 링커 설정이 올바르게 설정되어 있고 IDE에 올바른 디렉터리를 설정하였는지 확인하세요(이전 강좌에서 설명한대로). 또한 코드가 올바른지 확인하세요. 소스 코드를 보고 쉽게 확인할 수 있을 것입니다. 여전히 문제가 있따면, 댓글을 달아주세요.
입력
우리는 GLFW에서의 입력을 관리하는 방법에 대해 알아야 합니다. GLFW의 입력에 관한 함수 중 몇가지를 알아볼 수 있습니다.
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
여기에서 사용자가 esc 키를 눌렀는지 확인합니다(눌려지지 않았다면, WindowShouldClose
속성을 true
로 세팅함으로써 GLFW를 닫습니다 . 그러면 while
루프의 다음 조건이 거짓이 되기 때문에 응용 프로그램이 닫히게 됩니다.
우리는 렌더링 루프가 반복될 때마다
while (!glfwWindowShouldClose (window))
{
processInput(window);
glfwSwapBuffers (window);
glfwPollEvents ();
}
이를 통해 모든 프레임에서 특정 키가 눌려져 있음을 확인한 후 그에 대한 반응을 할 수 있습니다.
렌더링
우리는 모든 렌더링 명령들이 각 루픞마다 실행되기 원하기 때문에 모든 렌더링 명령을 렌더링 루프 안에 넣어야 합니다. 이는 다음과 같이 보일 것입니다.
// 렌더링 루프
while(!glfwWindowShouldClose (window))
{
// 입력
processInput(window);
// 여기에 렌더링 명령
...
// 이벤트를 확인하고 버퍼를 교체
glfwPollEvents ();
glfwSwapBuffers (window);
}
실제로 작동하는지 테스트하기 위해 우리가 선택한 색상으로 화면을 지우도록 하겠습니다. 렌더링 루프 돌때마다 처음에 우리는 항상 화면을 지울 것입니다. 그렇게 하지 않으면 이전 루프의 결과 이미지들이 화면에 계속 남아 있게 되기 때문입니다(이것은 여러분이 사용하고 싶은 효과일 수도 있지만 대개는 그렇지 않습니다).
glClear Color (0.2f, 0.3f, 0.3f, 1.0f);
glClear (GL_COLOR_BUFFER_BIT);
응용 프로그램의 전체 소스 코드는 여기에서 볼 수 있습니다.
우리는 이제 게임 루프를 많은 렌더링 명령으로 채울 준비가 되어있습니다. 하지만 이는 다음 강좌에서 다루도록 하겠습니다. 이 강좌는 충분히 길었다가 생각합니다.
'OpenGL' 카테고리의 다른 글
[Learn OpenGL 번역] 2-5. 시작하기 - Shaders (6) | 2018.07.15 |
---|---|
[Learn OpenGL 번역] 2-4. 시작하기 - Hello Triangle (2) | 2018.07.15 |
[Learn OpenGL 번역] 2-2. 시작하기 - Window 생성 (0) | 2018.07.15 |
[Learn OpenGL 번역] 1. 소개 (1) | 2018.07.15 |
LearnOpenGL 번역 시작 (5) | 2018.07.15 |