본문 바로가기

Game/Graphics

Learn OpenGL - Getting started : Hello Window

link : https://learnopengl.com/Getting-started/Hello-Window


Hello Window


우선 GLFW를 가동시킬 수 있는지 보자. .cpp파일을 생성하고 맨 위에 다음 include를 추가해라.


#include <glad/glad.h>
#include <GLFW/glfw3.h>

 GLFW를 포함시키기 전에 GLAD를 포함해야한다. GLAD용 헤더파일에는 (GL/gl.h)와 같은 OpenGL 헤더파일이 포함되어있기 때문이다.

다음으로 main 함수를 작성한다.


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 함수에서 먼저 glfwInit을 이용해 GLFW를 초기화한다. 그 다음 glfwWindowHint를 사용해  GLFW를 설정할 수 있다.


glfwWindowHint의 첫 번째 파라미터는 우리가 설정하고자 하는 옵션을 알려주며 enum 클래스에 존재하는


GLFW_ 접두어가 붙어있는 옵션들을 선택할 수 있다.


두 번째 파라미터는 옵션의 값을 설정하는 정수이다. 모든 설정 가능한 옵션과 해당 값들의 목록은


[http://www.glfw.org/docs/latest/window.html#window_hints]에서 볼 수 있다.



 다음으로 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);

glfwCreateWindow함수는 처음 두 개의 파라미터로 창의 너비와 높이를 받는다.


세 번째 파라미터는 창의 이름을 생성할 수 있게 한다. 마지막 두 개의 파라미터는 무시해도 된다.


이 함수의 리턴값은 GLFWwindow 객체이고, 이 객체는 나중에 다른 GLFW 기능을 사용하기 위해 필요하다.


그런 다음 우리 window의 context를 현재 스레드의 주 context로 지정하겠다고 GLFW에 알려준다.




GLAD


 앞의 강좌에서 GLAD가 OpenGL 함수 포인터를 관리한다고 했다. 그래서 우리는 OpenGL 함수를


호출하기 전에 GLAD를 초기화해야한다.


if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
}    

 우리는 OS마다 다른 OpenGL 함수 포인터의 주소를 로드하기 위해 GLAD 함수를 거친다.


GLFW는 우리가 컴파일 할 환경인 OS에 따라 올바른 함수를 정의하는 glfwGetProcAddress를 제공한다.




Viewport


 렌더링을 시작하기전에 해야할 일이 하나 남았다. OpenGL에게 렌더링 윈도우의 사이즈를 알려주어야한다.


glViewport 함수를 통해 이러한 차원을 설정할 수 있다.


glViewport(0, 0, 800, 600);

 glViewport 함수의 처음 두 개의 파라미터는 윈도우의 왼쪽 아래 모서리의 위치를 설정한다.


그 뒤 두개의 파라미터는 렌더링 윈도우의 너비와 높이를 픽셀로 정한다.



 실제로 뷰포트의 차원을 GLFW의 차원보다 작은 값으로 설정할 수 있다.


그렇게 하면 모든 OpenGL 렌더링이 더 작은 창에 표시된다.


하지만 사용자가 창의 크기를 조정하는 순간의 뷰포트도 조정해야 한다.


창의 크기를 조정할 때마다 호출되는 콜백 함수를 등록할 수 있다. resize 콜백 함수는 다음과 같다.


void framebuffer_size_callback(GLFWwindow* window, int width, int height);  

 framebuffer size 함수는 첫 번째 파라미터로 GLFWwindow 객체를 받고, 창의 새로운 크기를 나타내는 두 개의 정수를


나머지 파라미터로 받는다. 창의 크기가 변결될 때마다 GLFW는 이 함수를 호출하고 파라미터에 적절한 데이터를 채운다.


void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}  

모든 창의 크기가 변결될 때마다 이 함수를 호출하겠다고 GLFW에게 등록하고 알려야한다.


glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);  



Ready your engines


 프로그램이 하나의 이미지를 그리고 바로 종료되지않고, 명시적으로 종료 명령을 받기 전까지는 이미지를 계속 그리고


사용자 입력을 처리하도록 해야한다. 이러한 이유 때문에 render loop라고 불리는 while을 만들어야한다.


이 루프는 GLFW에게 그만하라고 할때까지 계속해서 실행된다.

while(!glfwWindowShouldClose(window))
{
    glfwSwapBuffers(window);
    glfwPollEvents();    
}

 glfwWindowShouldClose 함수는 각 루프가 시작될 때마다 GLFW가 종료하도록 지시되었는지 확인한다.


만약 그렇다면 이 함수는 true를 반환해 루프를 중지한다.



 glfwPollEvents 함수는 이벤트가 발생했는지 확인하고 윈도우 상태를 업데이트하며 정해진 함수를 호출한다.



 glfwSwapBuffers 함수는 컬러 버퍼를 교체한다. 컬러 버퍼는 반복중에 이미지를 그리고 화면을 출력한다.

Double buffer
  응용 프로글매이 single buffer로 이미지를 그렸을 때 이미지가 깜박이는 문제가 생길 수 있다.
그 이유는 이미지가 픽셀 하나 하나 그려지기 때문이다. 이러한 문제를 피하기 위해서 윈도우 응용 프로그램은 double buffer 렌더링을 적용한다. fornt 버퍼에는 최종 출력 이미지를 담고, 모든 렌더링 명령은 back 버퍼에 그려진다.
모든 렌더링 명령이 완료되자마자 back 버퍼를 front 버퍼로 swap한다. 이렇게 함으로써 사용자에게 이미지를 즉시 표시해 결함을 방지한다.





One last thing


 렌더링 루프가 종료되면 할당되었던 모든 자원들을 정리하고 삭제해야 한다. glfwTerminate 함수를 호출해 이를 시행한다.


glfwTerminate();
return 0;

Image of GLFW window output as most basic example





Input


 GLFW에서 입력을 관리하는 방법에 대해 알아야한다. GLFW의 입력에 관한 함수 중 몇가지를 알아볼 수 있다.


glfwGetKey 함수를 사용해 키보드 키와 함께 윈도우의 입력을 받을 수 있다.


processInput 함수를 만들어 모든 입력 코드를 체계적으로 유지할 수 있다.


void processInput(GLFWwindow *window)
{
    if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

 이 코드에서는 사용자가 esc키를 눌렀는지 확인한다. (눌리지 않으면 GLFW_RELEASE를 리턴)


사용자가 esc를 눌렀다면 glfwSetwindowShouldClose 함수를 사용해 WindowShouldClose 속성을 true로 바꿔


GLFW를 닫는다.


while (!glfwWindowShouldClose(window))
{
    processInput(window);

    glfwSwapBuffers(window);
    glfwPollEvents();
}  




Rendering


 우리는 모든 렌더링 명령들이 각 루프마다 실행되기를 원하므로 모든 렌더링 명령을 루프에 넣는다.

.


// 렌더링 루프
while(!glfwWindowShouldClose(window))
{
    // 입력
    processInput(window);

    // 여기에 렌더링 명령
    ...

    // 이벤트를 확인하고 버퍼를 교체
    glfwPollEvents();
    glfwSwapBuffers(window);
}

 작동하는지 테스트하기 위해 우리가 선택한 색상으로 화면을 지우도록 하자.


렌더링 루프가 돌때마다 처음에 우리는 항상 화면을 지울 것이다. 그렇지 않으면 이전의 이미지들이


화면에 계속 남아있기 때문이다. glClear 함수를 사용하면 화면의 컬러 버퍼를 지울 수 있다.


이 함수는 우리가 지우려 하는 버퍼의 비트를 전달하게 된다. (GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT)


glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

 glClearColor 함수를 통해 어떠한 색으로 화면을 지울것인지 선택이 가능하다.


glClear함수를 호출해 컬러 버퍼를 지울 때마다 전체 컬러버퍼는 설정한 색으로 지워진다.


Image of GLFW's window creation with <function id='13'><function id='10'>glClear</function>Color</function> defined


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<iostream>
#include<glad/glad.h>
#include<GLFW/glfw3.h>
 
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
    glViewport(00, width, height);
}
 
void processInput(GLFWwindow *window) {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
        glfwSetWindowShouldClose(window, true);
    }
}
 
int main() {
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 
    GLFWwindow* window = glfwCreateWindow(800600"OpenGL Tutorial"NULLNULL);
    if (window == NULL) {
        std::cout << "윈도우를 여는데 실패했다.\n";
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
 
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "GLAD를 초기화하는데 실패했다.\n";
        return -1;
    }
    
    while (!glfwWindowShouldClose(window)) {
        processInput(window);
 
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
 
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
 
    glfwTerminate();
    return 0;
}
cs








** ** ** ** ** **


역시 초반에 시작할때는 마음이 잘 먹어진다. 여러번 해본 환경설정인 만큼 이제 헤매지 않고 끝냈다.


다음으로~