OpenGL Study

OpenGL "Hello Window"

lvneux 2022. 11. 15. 01:43

[WINDOW]

1. 헤더파일

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

 GLAD 헤더파일에는 (GL/gl.h)와 같은 OpendGL 헤더파일이 포함되어 있다. 따라서 헤더파일을 include 할 때에는 GLAD를 가장 먼저 include 한다.

 

2. 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;
}

glfwInit() : GLFW를 초기화하여 window를 생성할 수 있는 상태로 만든다

glfwWindowHint(GLFW_options, 설정값) : GLFW 설정

- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3) : OPENGL 버전 (Ex) 3.4 중 3 설정)

- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3) : Ex) 3.4 중 4 설정, 이 코드에서는 3.3에 맞춰 실행

- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE) : 더이상 쓰지 않는 하위 호환 기능을 에러 처리, core-profile 사용을 명시

 

3. Window 객체 생성

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

GLFWwindow* window = glfwCreateWindow(width, height, name, monitor, share)

- 이 코드에서 width와 height는 main 함수 밖에 정의 해두었다 (SCR_WIDTH = 800 / SCR_HEIGHT = 600)

  : width와 height의 길이는 0보다 커야한다

- name : Window창의 이름 설정

- monitor : 전체화면 mode 사용의 유무를 설정

               : 전체화면을 사용하지 않을 경우에는  NULL을 입력하고, 전체화면을 원할경우 아래 코드로 변경하여 작성한다

GLFWmonitor* primary = glfwGetPrimaryMonitor();
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", primary, NULL);

if문을 통해 window 창 만들기를 실패한 경우, 종료하도록 설정한다

- std::cout << "Failed to create GLFW window" << std::endl : 실패 메세지 출력

- glfwTerminate() : 프로그램 종료 함수로, 할당된 모든 메모리를 삭제

glfwMakeContextCurrent(window) : window 창과 함께 생성된 OpenGL context를 current context로 변형한다

 

4. GLAD

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

gladLoadGLLoader() : GLAD 초기화 및 GLFW에 설정하는 함수

GLFW는 사용자의 OS에 따라 올바른 함수를 정의하는 glfwGetProcAddress를 제공한다

GLAD는 OpenGL의 function pointer를 관리하므로 OpenGL 함수를 호출하기 전에 초기화 해야한다

위의 코드는 이에 해당하는 부분을 처리하는 코드다

 

5. Viewport

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

glViewport(x, y, width, height)

- x, y는 viewport 좌하단 픽셀의 값이며 디폴트 값은 0, 0이다

- width, height는 viewport의 가로, 세로 크기이며 픽셀 단위로 설정한다

- 창의 크기를 조절할 때마다 viewport도 조정해야하므로 resize callback 함수를 등록하여 사용한다

glfwSetFramebufferSizeCallback(window, framebuffer_size_callback)

glfwSetFramebufferSizeCallback : Window창을 처음 표시할 때나 창의 크기를 조절할 때 호출한다

 

6. Ready Your Engine

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

glfwWindowShouldClose(window): 현재 window가 종료되었는지 확인하는 함수 (종료 되었을 때 True가 된다)

glfwSwapBuffers(window) 

- 이전 프레임에 그려진 buffer와 현재 프레임에 그려진 buffer를 swap 

- GLFW 창 안의 각 픽셀들에 대한 color 값을 가지고 있는 큰 buffer를 교체하며, 반복 중에 이미지를 그리고 화면에 출력

- 그래픽 데이터를 버퍼에 모두 저장한 후 while 반복문의 말미에 호출

glfwPollEvents() : 현재 프레임의 입출력 이벤트를 모두 처리 (이벤트의 발생 여부 확인, window 상태 업데이트, callback 함수 호출)

+) Double buffer

- Single buffer 사용의 문제 : 이미지 플리커링 발생 (이미지가 픽셀 하나하나 그려지기 때문에 발생한다)

- front buffer : 최종 출력 이미지 / back buffer : 모든 렌더링 명령

- back buffer의 모든 렌더링 명령이 완료되면 back과 front를 교체하여 이미지를 즉시 표시한다

 

7. End

glfwTerminate();
return 0;

glfwTerminate() 함수를 사용하여 할당된 모든 메모리를 정리하고 삭제한다

 

[INPUT]

void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}
 while (!glfwWindowShouldClose(window))
{
        processInput(window);
        glfwSwapBuffers(window);
        glfwPollEvents();
}

processInput(GLFWwindow* window) : 해당 키가 현재 눌려져 있는지의 여부를 return 하는 함수

- glfwGetKey : 키보드 키와 함께 윈도우의 입력을 받을 수 있는 함수

- 현재 코드의 if문에서는 사용자가 Esc를 눌렀는지 확인하고 있다. (Esc가 눌렸을 경우에는 WindowShouldClose의 속성을 True로 설정하여 GLFW를 종료하고, 눌리지 않은 경우에는 GLFW_RELEASE를 return한다) 

- render loop에서 processInput 함수를 호출하여 모든 프레임에서의 특정 키 입력을 확인하고 그에 따라 반응할 수 있다

 

[RENDERING]

 

 while (!glfwWindowShouldClose(window))
{
        processInput(window);
        
        // here
        
        glfwSwapBuffers(window);
        glfwPollEvents();
}

모든 rendering 명령은 render loop 안에 넣어야한다

- 해당 코드 속 주석으로 처리된 here 부분에 작성하면 된다

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

glClearColor(R, G, B, Alpha) 

- state-setting (상태 설정) 함수

- 창의 컬러 버퍼를 초기화할 때 사용할 색을 설정한다 (0 ~ 1 사이 float값을 사용)

- R : red, G : green, B : blue, Alpha : transparency(투명도)

glClear(GL_COLOR_BUFFER_BIT)

- state-using (상태 사용) 함수

- 컬러 버퍼를 위에서 설정한 색으로 초기화

 

[CODE]

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

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


const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    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

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

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        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;
}

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

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