본문 바로가기
College Study/OpenGL

[그래픽스] OpenGL 프로그램 개요

by 2den 2020. 6. 1.
728x90

* Computer Graphics Programming in OpenGL with C++ 책을 참고하였습니다.

* 이 블로그 업로드는 그래픽스의 파이프라인을 어느정도 이해한 분이 OpenGL을 공부할 목적으로 본다고 생각하고 작성되었습니다. 사실 전혀 모르는 상태에서 보아도 무방하지만, 더 정확한 이해를 위해 한번 정도 찾아 읽어보고 오실 것을 추천합니다. 추후에 관련 내용을 업로드 하도록 하겠습니다.

* 책을 번역한 것이 아닌, 제가 독학 후 책을 참고하여 설명하는 게시물입니다. 따라서 책에 없는 부연 설명이 있기도 하며, 의역 또는 오역, 오개념이 있을 수 있습니다. 피드백은 댓글을 남겨주세요.

 

OpenGL/C++ 응용 프로그램(application)은 다음과 같은 단계를 거쳐 GLSL 프로그램을 쉐이더(Shader) 단계로 불러옵니다.

 

1. GLSL 쉐이더 코드는 C++ 언어로 작성됩니다. 보통의 쉐이더 코드는 텍스트 파일이나 하드코딩된 문자열로 존재합니다.

2. 프로그램은 OpenGL 쉐이더 개체(Object)를 생성합니다. 그리고 GLSL 쉐이더 코드를 개체 안으로 불러옵니다.

3. 프로그램은 (프로그래머가 작성한 또는 내장된) OpenGL 명령어를 사용해서 개체를 컴파일, 링크(연결)합니다. 그리고 컴파일, 링크된 코드를 GPU에 설치합니다.

 

OpenGL 파이프라인

 

파이프라인은 차차 알아보도록 합시다. 추상적으로 먼저 이해하는 것보다 코드를 보면서 구체적으로 그려나가는 것을 선호해서요.

간단한 프로그램을 만들어 보겠습니다. [Program 2.1 First C++/OpenGL Application]

// main.cpp

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>

using namespace std;

void init (GLFWwindow* window) {}

void display(GLFWwindow* window, double currentTime) {
    glClearColor(1.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
}

int main(void) {
    if (!glfwInit()) {exit(EXIT_FAILURE);}  // glfwInit()
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
    GLFWwindow* window = glfwCreateWindow(600, 600, "Chapter2 - program1", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    if (glewInit() != GLEW_OK) {exit(EXIT_FAILURE);}  // glewInit()
    glfwSwapInterval(1);

    init(window);

    while (!glfwWindowShouldClose(window)) {
        display(window, glfwGetTime());
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    
    glfwDestroyWindow(window);
    glfwTerminate();
    exit(EXIT_SUCCESS);
}

 

 

GLFW와 GLEW 라이브러리는 glfwInit()glewInit()을 이용해 각각 초기화됩니다.

// main.cpp main()

if (!glfwInit()) {exit(EXIT_FAILURE);} // glfwInit()
if (glewInit() != GLEW_OK) {exit(EXIT_FAILURE);} // glewInit()

 

GLFW 윈도우(window)와 해당 OpenGL 컨텍스트(context)는 glfwCreateWindow()를 이용해 생성됩니다. 이 함수는 바로 앞 glfwWindowHint()에서 설정된 윈도우 힌트에 의해 설정된 옵션으로 실행됩니다. GLFW_CONTEXT_VERSION_MAJOR GLFW_CONTEXT_VERSION_MINOR는 기계에서 호환되는 OpenGL 버전을 의미합니다. 책에는 4.3 으로 나와있는데, 맥에서는 4.1까지밖에 지원을 안해서... 4.1로 설정하겠습니다.

// main.cpp main()

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
GLFWwindow* window = glfwCreateWindow(600, 600, "Chapter2 - program1", nullptr, nullptr);

glfwCreateWindow()의 첫번째 두번째 파라미터는 윈도우의 가로(width) 세로(height) 길이를 픽셀로 나타냅니다. 세번째 파라미터는 윈도우 상단에 작성될 프로그램 제목을 나타냅니다.

 

VSync(vertical synchronization)*는 glfwSwapInterval()과 glfwSwapBuffers()에 의해 이뤄집니다. GLFW 윈도우는 기본으로 이중 버퍼(double-buffered)입니다.

// main.cpp main()

glfwSwapInterval(1);
while (...) {
    ...
    glfwSwapBuffers(window);
    ...
}

 

윈도우를 생성한다고 해서 자동적으로 OpenGL 컨텍스트 '커런트(current)'(현재 맥락)가 만들어지는 것은 아닙니다. 그래서 glfwMakeContextCurrent()를 직접 호출해줘야 합니다.

// main.cpp main()

glfwMakeContextCurrent(window);

 

main() 함수 안에 있는 while 반복문을 봅시다. 이 while문은 display() 함수를 반복적으로 호출합니다. 앞서 잠깐 살펴 보았던 glfwSwapBuffer()glfwPollEvents()도 함께 반복됩니다. glfwSwapBuffer()는 스크린을 색칠하며, glfwPollEvents()는 윈도우와 관련된 다른 이벤트(event)를 다룹니다. 이를테면 키가 눌리는 것 같은 이벤트요.

// main.cpp main()

while (!glfwWindowShouldClose(window)) {
    display(window, glfwGetTime());
    glfwSwapBuffers(window);
    glfwPollEvents();
}

glfwPollEvents() 함수가 사용자가 마우스로 윈도우의 X 버튼을 클릭하는 이벤트를 감지하면 glfwWindowShouldClose(해당 윈도우)의 값이 true로 바뀌나봅니다. 그렇게 되면 반복문을 나옵니다.

 

GLFW 윈도우 개체를 init()display()에 참조로 전달함으로써, 함수들이 윈도우 개체에 접근할 수 있게 합니다.

// main.cpp

void init(GLFWwindow* window) {}
void display(GLFWwindow* window, double currentTime) {...}
...
int main(void) {
    ...
    init(window);

    while (...) {
        display(window, glfwGetTime());
        ...
    }
    ...
}

display()glfwGetTime()을 통해 얻은 GLFW 초기화 후 경과시간을 전달 받습니다. (이 함수가 왜 반복문 안에서 실행되는지 생각해 볼 필요가 있겠죠?)

 

glClear()GL_COLOR_BUFFER_BIT를 비우고 있습니다. 여기서 GL_COLOR_BUFFER_BITGLbitfield로, 사전 정의된 상수입니다. 이 상수는 color buffer를 참조합니다. color buffer는 렌더될 픽셀들을 포함하고 있습니다. 아래 코드의 glClear()는 OpenGL이 가진 color buffer들을 모두 클리어합니다. (clear: 여기서는 '투명한'의 의미가 아닌 'reset'의 의미로 사용)

// main.cpp display()

glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);

glClearColor()glClear() 바로 직전에 호출됩니다. color buffer가 지워질 때 color buffer의 요소에 위치할 값을 설정합니다. (즉, 다시 리셋되는 색깔을 지정)

 

반복문에서 나오면, main() 함수는 GLFW가 윈도우를 제거하고 종료되기를 요청합니다. glfwDestroyWindow()glfwTerminate()를 각각 호출하면 됩니다.

// main.cpp main()

glfwDestroyWindow(window);
glfwTerminate();

프로그램 실행 결과입니다 :

 

* VSync(vertical synchronization, 수직동기화) : 테어링(화면이 찢겨져 보이는 현상)을 방지하기 위한 것으로, 컴퓨터 디스플레이에서 그래픽 카드의 프레임 생성과 모니터의 프레임 출력 타이밍을 맞추도록 하는 설정.

(출처: https://namu.wiki/w/%EC%88%98%EC%A7%81%EB%8F%99%EA%B8%B0%ED%99%94 )

OpenGL Application 같은 경우에는 이중 버퍼를 사용하여 매 프레임마다 버퍼를 전환한다. 하나의 버퍼는 계산을 위해, 하나의 버퍼는 렌더링을 위해 사용하고 계속하여 역할을 바꾸는 것이다.

728x90

댓글