Quick Flow
한 번: 컨텍스트·로더 준비 → 버퍼·셰이더 등 자원 생성
반복: 이벤트 처리 → 크기 확인 → 상태·viewport 설정
→ 출력 지우기 → 그리기 → back buffer 표시
종료: GL 자원 정리 → 창 파괴 → 창 라이브러리 종료glClear는 출력 버퍼를 지우고, draw는 그 버퍼에 씁니다. glfwSwapBuffers는 이중 버퍼 창의 표시를 진행합니다. 화면 표시와 GPU 작업 완료 대기는 같은 작업이 아닙니다.
실행 가능한 최소 창
GLFW 3.4 + GLAD 2, GL 3.3 Core 기준의 전체 코드는 frame.cpp에 있습니다. 초기화·빌드 방법대로 빌드하면 파란 창이 나타나고 Escape 또는 창 닫기로 종료합니다. 셰이더 없이 clear만 수행하므로 정점 준비 문제와 창 표시 문제를 먼저 분리할 수 있습니다.
#include <glad/gl.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <cstdio>
int main() {
glfwSetErrorCallback([](int code, const char* text) {
std::fprintf(stderr, "GLFW %d: %s\n", code, text);
});
if (!glfwInit()) return 1;
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, GLFW_TRUE);
GLFWwindow* window = glfwCreateWindow(640, 480, "RefDock frame", nullptr, nullptr);
if (!window) {
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGL(glfwGetProcAddress) || !GLAD_GL_VERSION_3_3) {
glfwDestroyWindow(window);
glfwTerminate();
return 1;
}
glfwSwapInterval(1);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) break;
int width = 0, height = 0;
glfwGetFramebufferSize(window, &width, &height);
if (width == 0 || height == 0) {
glfwWaitEventsTimeout(0.05);
continue;
}
glViewport(0, 0, width, height);
glClearColor(0.1f, 0.3f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window);
}
glfwDestroyWindow(window);
glfwTerminate();
}반복 구간의 조건
glfwGetFramebufferSize는 렌더링에 사용할 픽셀 크기입니다. 창의 논리 크기와 다를 수 있으므로 고해상도 화면에서 glfwGetWindowSize 결과를 그대로 viewport에 사용하지 않습니다. 최소화 등으로 크기가 0이면 그리기를 건너뛰되 이벤트는 계속 처리합니다.
glfwSwapInterval(1)은 표시 간격을 요청합니다. 드라이버나 창 시스템이 이를 조정할 수 있으므로 정확한 FPS 보장으로 보지 않습니다. 위치 이동 같은 갱신은 프레임 개수 대신 실제 경과 시간을 사용합니다. 정지 화면은 glfwWaitEvents 계열로 기다릴 수 있고, 애니메이션은 이벤트 대기 때문에 갱신이 멈추지 않도록 루프를 선택합니다.
초기 컨텍스트에서 색 쓰기는 활성화되어 있지만, 나중에 color mask나 scissor를 바꿨다면 clear에도 영향을 줍니다. 매 패스가 사용할 상태를 명시하고 이전 패스의 설정이 우연히 남아 있다는 전제로 작성하지 않습니다. 깊이·스텐실을 쓰는 장면은 해당 버퍼를 요청·구성하고 clear 비트와 기록 마스크도 확인합니다.
freeglut과 비교
| 단계 | GLFW 기본 루프 | freeglut 기본 루프 |
|---|---|---|
| 반복의 소유자 | 애플리케이션의 while | glutMainLoop |
| 입력 전달 | glfwPollEvents 등 | 등록한 콜백으로 전달 |
| 그리기 시점 | 루프에서 직접 선택 | display 콜백 |
| 재그리기 요청 | 다음 반복에서 그리기 | glutPostRedisplay |
| 표시 | glfwSwapBuffers | glutSwapBuffers |
GLUT 계열 예제의 idle 콜백을 GLFW의 draw 루프에 그대로 대응시키기보다 갱신·그리기·이벤트 역할로 나누어 옮깁니다. freeglut 콜백 실행은 등록과 종료의 차이까지 다룹니다.
버전과 흔한 실패
버퍼 표시 함수는 OpenGL 1.0 또는 3.3의 렌더링 명령 자체가 아니라 창 시스템 연결입니다. 그리기 부분을 즉시 모드에서 셰이더 방식으로 바꿔도 지우기·그리기·표시라는 역할은 남습니다.
glFlush를 버퍼 교체의 대체로 쓰지 않습니다.- 매 프레임 창·셰이더를 다시 만들지 않습니다.
- swap 뒤 back buffer의 내용이 이전 프레임과 같다고 가정하지 않습니다.
- 이벤트 처리를 빼서 창이 응답하지 않는 상태를 만들지 않습니다.
- 프레임마다
glFinish로 강제 대기하는 코드를 기본 루프로 넣지 않습니다.
참고 링크
3 sources