Getting Started with ImGui: A Beginner’s Tutorial

Introduction

Immediate Mode GUI (ImGui) is a popular graphical user interface library designed for simplicity and flexibility. Whether you’re building tools, debugging interfaces, or even game UI, ImGui’s focus on immediate rendering makes it highly efficient and intuitive.

In this tutorial, we’ll guide you through:

  1. Setting up ImGui in your project.
  2. Creating a basic UI.
  3. Handling user inputs.
  4. Customizing styles for a professional look.

Prerequisites

Before diving in, ensure you have the following:

Graphics Backend: ImGui supports various backends like OpenGL, DirectX, and Vulkan. We’ll use OpenGL in this tutorial.

Development Environment: A modern C++ compiler and an IDE (e.g., Visual Studio, CLion, or VS Code).

Step 1: Setting Up ImGui

  1. Clone the ImGui Repository:
git clone https://github.com/ocornut/imgui.git
cd imgui

Include ImGui in Your Project:

Copy these essential files into your project:

imgui.cpp

imgui.h

imgui_draw.cpp

imgui_widgets.cpp

imgui_tables.cpp

imgui_demo.cpp (optional, for examples).

Setup a Rendering Backend:

For OpenGL:

Add imgui_impl_opengl3.cpp and imgui_impl_opengl3.h.

Link against OpenGL libraries (e.g., -lGL on Linux).

For DirectX:

Use imgui_impl_dx11.cpp or your preferred DirectX version.

Integrate into Your Build System: Add the ImGui files to your CMakeLists.txt or your IDE’s project configuration.


Step 2: Creating a Basic UI

Here’s how to initialize and render a basic ImGui window.

Initialize ImGui:

// Initialize ImGui
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;  // Enable keyboard navigation
ImGui_ImplOpenGL3_Init("#version 130");                // Initialize OpenGL

Rendering Loop: Inside your rendering loop:

while (!glfwWindowShouldClose(window)) {
    // Start ImGui frame
    ImGui_ImplOpenGL3_NewFrame();
    ImGui::NewFrame();

    // Create a simple window
    ImGui::Begin("Hello, ImGui!");
    ImGui::Text("Welcome to ImGui!");
    ImGui::Button("Click Me");
    ImGui::End();

    // Render
    ImGui::Render();
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

    glfwSwapBuffers(window);
    glfwPollEvents();
}

Cleanup:

ImGui_ImplOpenGL3_Shutdown();
ImGui::DestroyContext();

Step 3: Handling User Inputs

ImGui integrates with your input system, such as GLFW for OpenGL. Ensure the input data flows to ImGui:

glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int scancode, int action, int mods) {
    ImGuiIO& io = ImGui::GetIO();
    if (action == GLFW_PRESS) io.KeysDown[key] = true;
    if (action == GLFW_RELEASE) io.KeysDown[key] = false;
});

Step 4: Customizing Styles

ImGui allows you to change the look and feel of your UI:

ImGuiStyle& style = ImGui::GetStyle();
style.WindowRounding = 5.0f;  // Rounded corners
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.2f, 0.2f, 0.2f, 1.0f);  // Background color

Experiment with the ImGui::ShowStyleEditor() to interactively adjust styles.


Step 5: Debugging with ImGui

Enable the demo window to explore features:

ImGui::ShowDemoWindow();

Use this for quick debugging and testing different widgets and layouts.


Best Practices

  1. Minimize Stateful Logic:
    • ImGui excels with immediate rendering; avoid storing unnecessary state.
  2. Use the Demo Code:
    • The included imgui_demo.cpp showcases advanced widgets and layouts.
  3. Optimize Rendering:
    • Batch your draw calls and avoid redundant calls in performance-critical applications.

Complete Code: Basic ImGui Application

#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <GLFW/glfw3.h>
#include <iostream>

// Callback for GLFW errors
void glfw_error_callback(int error, const char* description) {
    std::cerr << "GLFW Error " << error << ": " << description << std::endl;
}

int main() {
    // Set GLFW error callback
    glfwSetErrorCallback(glfw_error_callback);

    // Initialize GLFW
    if (!glfwInit()) {
        std::cerr << "Failed to initialize GLFW" << std::endl;
        return -1;
    }

    // Create a GLFW window
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    GLFWwindow* window = glfwCreateWindow(1280, 720, "ImGui Example", nullptr, nullptr);
    if (!window) {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    // Make OpenGL context current
    glfwMakeContextCurrent(window);
    glfwSwapInterval(1);  // Enable VSync

    // Initialize ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO();
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;  // Enable keyboard navigation
    io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;      // Enable docking

    // Initialize ImGui style
    ImGui::StyleColorsDark();

    // Initialize ImGui backends
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init("#version 330");

    // Main rendering loop
    while (!glfwWindowShouldClose(window)) {
        // Poll events
        glfwPollEvents();

        // Start a new ImGui frame
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        // Create a simple ImGui window
        ImGui::Begin("Hello, ImGui!");
        ImGui::Text("Welcome to ImGui!");
        if (ImGui::Button("Click Me")) {
            std::cout << "Button clicked!" << std::endl;
        }
        ImGui::End();

        // Rendering
        ImGui::Render();
        int display_w, display_h;
        glfwGetFramebufferSize(window, &display_w, &display_h);
        glViewport(0, 0, display_w, display_h);
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        // Swap buffers
        glfwSwapBuffers(window);
    }

    // Cleanup
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    glfwDestroyWindow(window);
    glfwTerminate();

    return 0;
}

Steps to Run

  1. Clone the ImGui Repository:
  2. Add ImGui Files to Your Project: Copy these files into your project directory:
    • imgui.h
    • imgui.cpp
    • imgui_draw.cpp
    • imgui_widgets.cpp
    • imgui_tables.cpp
    • Backends:
      • imgui_impl_glfw.h and .cpp
      • imgui_impl_opengl3.h and .cpp
  3. Link OpenGL and GLFW:\
    • Add OpenGL and GLFW to your build system (e.g., -lGL -lglfw).
  4. Build and Run:
    • If using g++:bashCopy code
g++ main.cpp -o imgui_app -lGL -lglfw -ldl -I<path-to-imgui> -I<path-to-glfw>
./imgui_app

Alternatively, configure your project in an IDE like Visual Studio, CLion, or VS Code.

Leave a Comment