일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 리뷰리뷰
- C++
- vulkan
- 책리뷰
- tutorial
- 혼공C
- 혼자공부하는C언어
- 데이터분석
- 나는리뷰어다
- 자바스크립트
- 혼공단5기
- 혼공S
- 제이펍
- 혼공스
- 혼공단
- 벌칸
- 혼공머신
- 네트워크
- 혼공학습단
- 혼자공부하는네트워크
- 혼공컴운
- 컴퓨터그래픽스
- 불칸
- 파이썬
- 머신러닝
- OpenGL
- 혼공네트
- 딥러닝
- 혼공
- 한빛미디어
- Today
- Total
Scientia Conditorium
[OpenGL003] Refresh Callback 함수 등록 본문
이전글 : [OpenGL002] glew 설치 및 에러 수정
이전 코드 정리
OpenGL 사용 방법을 알려주는 것이 목적이라면 굳이 이렇게 코드를 정리할 필요는 없습니다. 그러나 이 블로그에서는 개인적인 공부도 포함하여 작성하고 있기 때문에 중간중간 코드를 정리하고 있습니다. 먼저 모든 코드를 main에서 작성하면 여러 파일을 왔다갔다 하지 않아도 되니 편리합니다. 그러나 코드량이 많아지고 각 코드별로 하는 역할들이 많아지면 관리하기 어려워집니다. 따라서 기능별로 역할에 따라 코드를 분리해주려고 합니다.
우선은 OpenGL 초기화 시키는 코드를 Renderer 클래스로 만들고 main 함수는 아래와 같이 정리하였습니다.
#include "Renderer.h"
int main()
{
Renderer game;
if (game.initialize())
{
game.run();
}
return 0;
}
다음은 Renderer 헤더와 구현부 입니다.
#pragma once
#include <iostream>
#include "GL/glew.h"
#include "GLFW/glfw3.h"
class Renderer
{
public:
Renderer();
~Renderer();
bool initialize();
void run();
private:
GLFWwindow* mWindow = nullptr;
};
#include "Renderer.h"
Renderer::Renderer()
{
}
Renderer::~Renderer()
{
glfwTerminate();
}
bool Renderer::initialize()
{
if (!glfwInit())
{
std::cerr << "glfwInit failed\n";
return false;
}
mWindow = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (mWindow == nullptr)
{
glfwTerminate();
std::cerr << "glfwCreateWindow failed\n";
return false;
}
glfwMakeContextCurrent(mWindow);
// Must be used after glfwMakeContextCurrent()
GLenum err = glewInit();
if (GLEW_OK != err)
{
std::cerr << "glewInit failed : " << glewGetErrorString(err) << "\n";
return false;
}
return true;
}
void Renderer::run()
{
while (!glfwWindowShouldClose(mWindow))
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(-0.5f, 0.0f);
glVertex2f(0.0f, 0.5f);
glVertex2f(0.5f, 0.0f);
glEnd();
glfwSwapBuffers(mWindow);
glfwPollEvents();
}
}
즉, main 함수에서는 Renderer 객체를 생성해서 초기화 시킨 다음, 정상적으로 초기화되면 run() 함수를 통해 렌더링이 시작하게 됩니다. 코드를 보면 우선은 단순히 삼각형 하나를 계속해서 렌더링하는 형태이며, 이 프로그램이 종료되는 것은 OpenGL 윈도우 창이 닫힐 때만 입니다. 여기서 ESC 버튼을 누른다던가 특정 키보드 값을 입력받으면 동작할 수 있도록 콜백 함수를 추가해보겠습니다.
Refresh Callback 함수
콜백 함수는 미리 등록해두었다가 나중에 특별한 이벤트가 발생하면 자동으로 호출되는 함수입니다. GLFW에서 제공하는 많은 콜백 함수들 중 refresh callback 함수라고 있습니다. OpenGL 윈도우 창이 생성되어 화면에 뜰 때 호출되고 윈도우 창 크기가 조절될 때 호출됩니다. 전체 화면 크기를 조절할 때마다 호출된다고 보시면 되겠습니다.
glfw3.h 내부에 들어가 약 1675번째 줄 정도를 보면 glfw refresh callback 함수에 대해 설명이 적혀있습니다. 헤더 파일에 적혀있는 것처럼 GLFW에서 refresh 콜백 함수 형태는 다음과 같이 정의되어 있습니다.
typedef void (* GLFWwindowrefreshfun)(GLFWwindow* window);
...
GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun callback);
즉, 위와 같은 형태로 만들어야 하며 어떤 윈도우창을 refresh 할 것인지가 파라미터로 들어가게 됩니다. 내가 콜백 함수를 만들었다면 glfw에 등록해주어야 합니다. 위 이미지에서 주석처리 되어있는 부분 중 @sa @ref glfwSetWindowRefreshCallback 함수가 바로 그것입니다. 내가 어떤 refresh 콜백 함수를 작성하고 어떤 윈도우 창에 이 콜백을 등록할 것이냐 하고 작성한 함수 이름을 넣어주면 됩니다. 등록을 해두면 그 다음부터 refresh 이벤트가 발생하면 GLFW 에서 해당 콜백 함수를 호출하게 됩니다.
Refresh callback 함수 적용 방법
그러면 실제 refresh callback 함수를 적용해보도록 하겠습니다. 먼저 콜백 함수가 호출되었을 때 하는 일들을 만들어주어야 합니다. 콜백 함수 역할은 렌더러와 밀접한 연관이 있기 때문에 Renderer 클래스 내부에 public으로 만들었습니다.
#pragma once
#include <iostream>
#include "GL/glew.h"
#include "GLFW/glfw3.h"
class Renderer
{
public:
Renderer();
~Renderer();
bool initialize();
void run();
private:
static void refreshFunc(GLFWwindow* window);
GLFWwindow* mWindow = nullptr;
};
...
glfwMakeContextCurrent(mWindow);
// Must be used after glfwMakeContextCurrent()
GLenum err = glewInit();
if (GLEW_OK != err)
{
std::cerr << "glewInit failed : " << glewGetErrorString(err) << "\n";
return false;
}
glfwSetWindowRefreshCallback(mWindow, refreshFunc);
return true;
}
...
void Renderer::refreshFunc(GLFWwindow* window)
{
//std::cout << "Refresh CallBack!\n";
// refresh
glClear(GL_COLOR_BUFFER_BIT);
glFinish();
// GLFW action
glfwSwapBuffers(window);
}
여기서 주의할 점은 정적 멤버 함수 즉, static 키워드를 붙여야 합니다. 물론 이렇게 하지 않고 전역 함수를 만들어서 사용해도 됩니다. 그러나 그냥 클래스 내부의 멤버 함수 형태로 생성하면 콜백 함수를 등록할 수 없습니다. 자세한 오류 내용은 아래 Callback 함수 등록 오류를 참고해주세요. 이제 화면은 refresh 하는데 필요한 기능들은 이 함수 안에서 동작하게 됩니다. 콜백 함수가 호출되었는지 시각적으로 확인하는 가장 간단한 방법은 로그 출력 입니다. 위 코드에서는 주석 처리를 해주었지만 "std::cou << "Refresh CallBack!\n"; 부분을 주석 해제하여 화면의 크기를 조절할 때마다 출력되는 것을 확인할 수 있습니다.
또 하나 중요한 점은 refresh callback 함수에서 마지막에는 전체 화면을 업데이트하기 위해서 glfwSwapBuffers 함수를 호출해야 합니다. 그렇지 않으면 마지막으로 렌더링한 화면을 계속해서 표시하고 있게 됩니다.
이 블로그에서 콜백 함수 등록 위치는 glewInit 다음으로 해주었습니다.
Callback 함수 등록 오류
이 부분은 C++ 문법이므로 자세한 내용은 넘어가도록 하겠습니다. glfwSetWindowRefreshCallback 함수는 C 스타일 콜백 함수로, C++ 클래스 멤버 함수를 직접 전달할 수 없습니다. 클래스 멤버 함수는 암시적으로 this 포인터를 받기 때문에 일반 함수 포인터와는 다릅니다. 따라서 단순 멤버 함수를 콜백 함수로 등록하려고 하면 아래와 같이 컴파일 오류가 발생합니다.
따라서 static 키워드를 붙여 정적 멤버 함수로 만들어주면 컴파일 오류가 해결됩니다.
'프로그래밍 > 컴퓨터 그래픽스' 카테고리의 다른 글
[OpenGL004] Keyboard Callback 등록 (0) | 2024.12.18 |
---|---|
[OpenGL002] glew 설치 및 에러 수정 (3) | 2024.09.30 |
[Vulkan006][번역] Vulkan Tutorial - 삼각형 그리기 유효성 검사(Drawing a triangle - Validation layers) (0) | 2023.12.03 |
[Vulkan][오류] VUID-vkGetDeviceQueue-queueFamilyIndex 오류 (1) | 2023.12.02 |
[Vulkan][오류] Validation Error : VUIT-vkDestroyInstance 오류 (0) | 2023.11.29 |