CMake와 GLEW로 Dear ImGui 첫 창 띄우기

Dear ImGui는 게임 엔진의 디버그 패널, 그래픽 도구, 사내 편집기처럼 빠르게 상호작용 UI를 만들 때 유용한 C++ GUI 라이브러리입니다. 이 글에서는 OpenGL 함수를 불러오는 GLEW, 창과 입력을 담당하는 GLFW, 빌드를 관리하는 CMake 를 조합해 첫 번째 ImGui 창을 실행합니다.
완성할 프로그램
예제는 다음 기능을 제공합니다.
1280×720 GLFW 창과 OpenGL 3.3 Core Profile 컨텍스트 생성
GLEW를 통한 OpenGL 함수 로딩
꼭짓점마다 빨강, 초록, 파랑을 지정한 OpenGL 삼각형 렌더링
버튼, 체크박스, FPS가 포함된 ImGui 창 표시
체크박스로 ImGui 공식 데모 창 열기
프로젝트 구조는 작게 유지합니다.
Text
. ├── CMakeLists.txt ├── docs/ │ └── imgui-glew-first-window.md └── src/ ├── main.cpp ├── TriangleRenderer.cpp └── TriangleRenderer.h
도구와 라이브러리 설치
macOS
Xcode Command Line Tools와 Homebrew가 준비되어 있다면 다음 명령으로 설치합니다.
Bash
xcode-select --install brew install cmake glew glfw
Ubuntu/Debian
Bash
sudo apt update sudo apt install build-essential cmake git libglew-dev libglfw3-dev
CMake 구성 이해하기
최상위 CMakeLists.txt는 먼저 시스템에 설치된 라이브러리를 찾습니다.
Cmake
find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) find_package(glfw3 CONFIG REQUIRED)
REQUIRED가 붙은 패키지를 찾지 못하면 CMake는 구성 단계에서 즉시 원인을 알려줍니다. 이후 FetchContent가 Dear ImGui 소스를 내려받습니다.
Cmake
include(FetchContent) FetchContent_Declare( imgui GIT_REPOSITORY https://github.com/ocornut/imgui.git GIT_TAG v1.91.9b GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(imgui)
Dear ImGui 저장소는 일반적인 CMake 라이브러리 타깃을 제공하지 않으므로, 코어 소스와 GLFW/OpenGL3 백엔드를 직접 imgui_backend 정적 라이브러리로 묶습니다.
Cmake
add_library(imgui_backend STATIC ${imgui_SOURCE_DIR}/imgui.cpp ${imgui_SOURCE_DIR}/imgui_demo.cpp ${imgui_SOURCE_DIR}/imgui_draw.cpp ${imgui_SOURCE_DIR}/imgui_tables.cpp ${imgui_SOURCE_DIR}/imgui_widgets.cpp ${imgui_SOURCE_DIR}/backends/imgui_impl_glfw.cpp ${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.cpp )
의존성 구성을 바꿀 때 유용한 원칙은 다음과 같습니다.
라이브러리 버전은 Git 태그나 커밋으로 고정합니다.
외부 의존성은 애플리케이션 코드와 분리된 CMake 타깃으로 묶습니다.
PUBLIC은 소비자에게도 필요한 의존성, PRIVATE은 현재 타깃에만 필요한 의존성에 사용합니다.
소스 디렉터리 안에서 빌드하지 말고 별도의 build/ 디렉터리를 사용합니다.
초기화 순서
그래픽 프로그램은 초기화 순서가 중요합니다.
glfwInit()으로 GLFW를 시작합니다.
GLFW 창과 OpenGL 컨텍스트를 생성합니다.
컨텍스트를 현재 스레드에 연결합니다.
glewInit()으로 OpenGL 함수를 불러옵니다.
삼각형의 셰이더, VAO, VBO를 생성합니다.
ImGui 컨텍스트와 GLFW/OpenGL3 백엔드를 초기화합니다.
GLEW는 활성 OpenGL 컨텍스트가 있어야 함수를 찾을 수 있으므로 glfwMakeContextCurrent()보다 먼저 호출하면 안 됩니다.
예제의 전체 애플리케이션 코드는 다음 파일에서 확인할 수 있습니다.
첨부 소스 파일을 찾을 수 없습니다.
RGB 삼각형 그리기
TriangleRenderer는 삼각형 렌더링에 필요한 OpenGL 리소스를 ImGui 코드와 분리합니다. 각 꼭짓점은 2차원 위치와 RGB 색상으로 구성됩니다.
Cpp
constexpr float vertices[] = { // 위치(x, y) 색상(r, g, b) 0.0F, 0.6F, 1.0F, 0.0F, 0.0F, -0.6F, -0.6F, 0.0F, 1.0F, 0.0F, 0.6F, -0.6F, 0.0F, 0.0F, 1.0F, };
위쪽 꼭짓점은 빨강, 왼쪽 아래는 초록, 오른쪽 아래는 파랑입니다. 프래그먼트 셰이더에 전달되는 색상은 래스터라이저가 삼각형 내부에서 자동으로 보간하므로 세 색상이 자연스럽게 섞인 결과가 만들어집니다.
VBO(Vertex Buffer Object)는 꼭짓점 데이터를 GPU 메모리에 보관하고, VAO(Vertex Array Object)는 데이터의 해석 방법을 기억합니다. 한 꼭짓점은 float 5개로 구성되므로 stride는 5 * sizeof(float)입니다.
Cpp
constexpr GLsizei stride = 5 * sizeof(float); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, nullptr); glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<void*>(2 * sizeof(float)));
location 0은 위치의 두 성분을, location 1은 그 뒤에 이어지는 색상의 세 성분을 읽습니다. 매 프레임에는 VAO와 셰이더 프로그램을 바인딩한 뒤 꼭짓점 세 개를 그립니다.
Cpp
glUseProgram(shader_program_); glBindVertexArray(vertex_array_); glDrawArrays(GL_TRIANGLES, 0, 3);
ImGui 프레임 루프
Dear ImGui는 retained-mode GUI처럼 위젯 객체를 한 번 만들어 보관하지 않습니다. 매 프레임 ImGui::Button()과 같은 함수를 호출해 현재 UI를 다시 기술합니다. 핵심 루프는 다음 순서를 따릅니다.
glfwPollEvents()로 입력 이벤트를 처리합니다.
각 백엔드의 NewFrame()과 ImGui::NewFrame()을 호출합니다.
이번 프레임에 표시할 위젯을 선언합니다.
ImGui::Render()로 draw data를 생성합니다.
OpenGL 화면을 지우고 RGB 삼각형을 렌더링합니다.
ImGui draw data를 삼각형 위에 렌더링합니다.
glfwSwapBuffers()로 완성된 프레임을 화면에 표시합니다.
버튼 라벨의 ##starter_button은 화면에는 보이지 않지만 ImGui ID에는 포함됩니다. 같은 이름의 위젯이 여러 개 있을 때 ##고유ID를 붙이면 ID 충돌을 방지할 수 있습니다.
상태는 매 프레임 유지되어야 하므로 AppState가 루프 바깥에 존재합니다. 반면 UI 함수 DrawStarterWindow()는 상태를 받아 그 프레임의 위젯만 구성합니다. 이 분리는 메인 루프를 간결하게 유지하고 UI 확장을 쉽게 만듭니다.
구성, 빌드, 실행
프로젝트 루트에서 다음 명령을 실행합니다.
Bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug cmake --build build --parallel ./build/imgui_glew_starter
cmake -S . -B build는 소스 위치와 빌드 결과 위치를 분리합니다. Xcode나 Visual Studio처럼 multi-config generator를 사용한다면 빌드 시 구성을 지정합니다.
Bash
cmake --build build --config Debug
자주 발생하는 문제
CMake가 GLEW 또는 GLFW를 찾지 못함
먼저 패키지가 실제로 설치됐는지 확인합니다. Homebrew를 사용자 지정 prefix에 설치했다면 해당 경로를 CMake에 알려줄 수 있습니다.
Bash
cmake -S . -B build -DCMAKE_PREFIX_PATH="$(brew --prefix)"
기존의 실패한 탐색 결과가 캐시에 남아 있다면 build/를 지운 뒤 다시 구성합니다.
창은 열리지만 아무것도 보이지 않음
ImGui::Render()가 위젯 선언 뒤에 호출되는지 확인합니다.
ImGui_ImplOpenGL3_RenderDrawData()가 버퍼 교환 전에 호출되는지 확인합니다.
프레임버퍼 크기로 glViewport()를 갱신하는지 확인합니다. Retina 화면에서는 창 크기와 프레임버퍼 크기가 다를 수 있습니다.
GLEW 초기화 실패
glewInit() 전에 현재 OpenGL 컨텍스트가 설정됐는지 확인합니다. Core Profile 호환성을 위해 glewExperimental = GL_TRUE도 먼저 설정해야 합니다.
다음 단계
첫 창이 실행되면 ImGui::ShowDemoWindow()에서 제공하는 위젯 예제를 살펴보는 것이 좋습니다. 다음 단계로는 폰트와 한글 글리프 설정, UI 상태의 클래스화, 도킹 활성화, 사용자 정의 DrawList 위젯 구현을 확장할 수 있습니다.