ImGui 範例解析

標頭檔區

#include <GLFW/glfw3.h>Code language: plaintext (plaintext)

引入 GLFW 視窗函式庫標頭檔:負責建立視窗、處理滑鼠 / 鍵盤輸入、管理 OpenGL 繪圖環境、視窗訊息迴圈。

GLFW 是一套用來建立與管理視窗的函式庫,以 C 語言開發。它本身獨立,不屬於 ImGui,但是 ImGui 會使用這套函式庫,所以必須引用。讀者在上一節已經把它下載到專案資料夾,並且完成專案整合設定。

#include "imgui.h"Code language: plaintext (plaintext)

這是 ImGui 的核心標頭檔,內含所有 UI 控制項、繪圖環境、樣式、底層繪製 API 等等。

#include "backends/imgui_impl_glfw.h"Code language: plaintext (plaintext)

這是 ImGui 和 GLFW 的銜接橋樑,負責將 GLFW 取得的視窗、滑鼠、鍵盤、視窗尺寸等資訊傳送給 ImGui。

#include "backends/imgui_impl_opengl3.h"Code language: plaintext (plaintext)

ImGui OpenGL3 繪製後端適配層:產生 OpenGL 著色器、頂點緩衝區,把 ImGui UI 圖形資料交給 GPU 繪製。

#include <cstdio>Code language: plaintext (plaintext)

C++ 標準 C 輸入輸出函式庫,用於透過 printf 印出錯誤記錄。


int main()Code language: plaintext (plaintext)

程式進入點,這邊就不多做解釋了。

GLFW 初始化

    // Initialize GLFW
    if (!glfwInit())
    {
        printf("GLFW initialization failed\n");
        return -1;
    }Code language: plaintext (plaintext)

glfwInit():初始化 GLFW 全域資源,像是顯卡、視窗驅動、輸入裝置等。

如果初始化失敗,就會印出錯誤訊息並回傳 -1,程式直接異常結束,作業系統會收到這個錯誤代碼。

OpenGL 版本設定

    // OpenGL 3.3
    const char* glsl_version = "#version 330";Code language: plaintext (plaintext)

定義 GLSL 著色器版本字串,OpenGL 3.3 對應 GLSL 330。

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);Code language: plaintext (plaintext)

設定 OpenGL 主版本號為 3。

glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);Code language: plaintext (plaintext)

設定 OpenGL 次版本號為 3。

 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);Code language: plaintext (plaintext)

使用核心模式 Core Profile

建立視窗

    // Create window
    GLFWwindow* window = glfwCreateWindow(1000, 600, "ImGui Minimal Demo (VS2022)", nullptr, nullptr);Code language: plaintext (plaintext)

視窗寬高為 1000*600,視窗標題是「ImGui Minimal Demo (VS2022)」;第四個參數傳入 nullptr 空指標,代表預設視窗模式;第五個參數用來指定要共用 OpenGL 繪圖環境的視窗,填 nullptr 代表不共用。

函式會回傳視窗控制代指標 GLFWwindow*

 if (!window)
    {
        glfwTerminate();
        return -1;
    }Code language: plaintext (plaintext)

如果上面建立視窗失敗,window 指標會是 nullptr。

glfwTerminate() 會銷毀所有 GLFW 佔用的資源。

接著回傳 -1,通知作業系統程式執行錯誤並中斷。

glfwMakeContextCurrent(window);Code language: plaintext (plaintext)

將當前視窗綁定為 OpenGL 繪圖環境,後續所有 OpenGL 繪製指令都會作用在這個視窗上。

glfwSwapInterval(1);Code language: plaintext (plaintext)

開啟垂直同步,參數填 1 代表每幀等待螢幕更新,讓畫面更新頻率跟螢幕同步,避免畫面撕裂;填 0 則關閉垂直同步,不會限制畫面張數。

這項設定非常重要,如果沒有開啟,GLFW 畫面更新與螢幕刷新不同步,很容易出現畫面錯亂的狀況。

ImGui 全域初始化

IMGUI_CHECKVERSION(); // 檢查標頭檔與後端版本是否相符,版本不一致會直接觸發斷言錯誤
ImGui::CreateContext(); // 建立 ImGui 全域繪圖環境,儲存 UI 所有執行狀態
ImGuiIO& io = ImGui::GetIO(); // 取得 IO 設定物件,管控輸入、字型、全域開關等參數
ImGui::StyleColorsDark(); // 載入官方深色 UI 配色樣式Code language: plaintext (plaintext)

綁定 GLFW+OpenGL 後端

ImGui_ImplGlfw_InitForOpenGL(window, true);Code language: plaintext (plaintext)

第二個參數設為 true 時,會讓 ImGui 接管所有輸入事件(滑鼠、鍵盤),自動阻擋輸入穿透到遊戲底層。

 ImGui_ImplOpenGL3_Init(glsl_version);Code language: plaintext (plaintext)

初始化 OpenGL3 繪製後端,傳入 GLSL 版本字串,編譯 UI 繪製專用的頂點/片段著色器、建立 GPU 緩衝區。

float slider_value = 0.5f;    // 滑動條綁定數值
int click_count = 0;          // 按鈕點擊次數計數
bool show_full_demo = false;  // 控制是否顯示 ImGui 官方示範視窗Code language: plaintext (plaintext)

上面幾個變數用來存放控制項的數值,每次畫面重新整理時都會讀取或修改,數值會跨幀保留。

主繪製迴圈

程式核心區塊,每秒會執行幾十到上百次。

while (!glfwWindowShouldClose(window))Code language: plaintext (plaintext)

只要視窗沒有收到關閉訊號(像是點右上角 X 關閉按鈕或是按下 Alt+F4),就持續執行繪製流程。

glfwPollEvents();Code language: plaintext (plaintext)

輪詢視窗系統所有訊息,包含滑鼠點擊、移動、按鍵觸發、視窗縮放、關閉等事件。

 ImGui_ImplOpenGL3_NewFrame();Code language: plaintext (plaintext)

OpenGL 後端每幀前置更新:重置繪製資源、準備下一幀繪製快取。

ImGui_ImplGlfw_NewFrame();Code language: plaintext (plaintext)

GLFW 後端每幀前置更新:讀取當前滑鼠座標、按鍵狀態、視窗尺寸,寫入 ImGui IO 輸入資料。

ImGui::NewFrame();Code language: plaintext (plaintext)

開啟新一幀 UI 版面計算,必須寫在所有 ImGui::xxx() 控制項程式碼最前面;內部會清空上一幀的繪製指令,準備收集本幀的 UI 資料。

UI 控制項資料更新建構區塊

// Draw custom UI panel
ImGui::Begin("Control Panel");                // 開啟自訂控制面板視窗
ImGui::Text("Hello ImGui + VS2022");          // 輸出靜態文字
ImGui::SliderFloat("Slider Value", &slider_value, 0.f, 1.f); // 浮點數滑動條,綁定數值變數

if (ImGui::Button("Click to Count"))          // 建立點擊按鈕,按下時回傳 true
    click_count++;                            // 按下按鈕後計數器加一
ImGui::SameLine();                            // 讓下一個控制項和目前元件同一行顯示
ImGui::Text("Click Count: %d", click_count);  // 印出按鈕點擊次數

ImGui::Checkbox("Show Full ImGui Demo Window", &show_full_demo); // 核取方塊控制示範視窗顯示與隱藏
ImGui::End();                                 // 結束目前自訂視窗的繪製

if (show_full_demo)
    ImGui::ShowDemoWindow(&show_full_demo);    // 開啟 ImGui 官方完整示範視窗Code language: plaintext (plaintext)

這段程式會在每次迴圈執行時繪製介面,同時偵測按鈕是否被點擊,有點擊就累加計數並顯示數字。

實際繪製輸出區塊

// Render ImGui draw data
ImGui::Render();                                        // 生成本幀 UI 頂點繪製資料
int width, height;
glfwGetFramebufferSize(window, &width, &height);        // 取得視窗實際繪製解析度
glViewport(0, 0, width, height);                       // 設定 OpenGL 繪製檢視範圍填滿整個視窗
glClearColor(0.12f, 0.12f, 0.12f, 1.f);                // 設定視窗清除底色(深灰色)
glClear(GL_COLOR_BUFFER_BIT);                           // 清空畫布色彩緩衝區
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());// 透過 OpenGL 繪製所有 ImGui 介面

glfwSwapBuffers(window);                                // 交換前後緩衝區,顯示最新畫面Code language: plaintext (plaintext)

程式結束資源釋放區塊

下面這段是使用者關閉程式後,用來清理所有佔用資源的程式碼。

// Cleanup resources
ImGui_ImplOpenGL3_Shutdown();  // 釋放 OpenGL 繪製後端資源(著色器、緩衝區等)
ImGui_ImplGlfw_Shutdown();     // 釋放 GLFW 視窗對應後端資源
ImGui::DestroyContext();       // 銷毀 ImGui 全域繪圖環境,釋放 UI 記憶體
glfwDestroyWindow(window);     // 關閉 GLFW 視窗、銷毀視窗控制代
glfwTerminate();               // 完整卸載 GLFW 函式庫,釋放全域視窗、輸入相關資源
return 0;                      // 程式正常結束,回傳成功代碼 0Code language: plaintext (plaintext)

資源銷毀順序跟初始化順序完全相反,避免記憶體洩漏、殘留資源的問題。

好了,這一章完整解析上一堂課的範例,如果哪個地方看不懂,都可以在下方留言提問。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *