imgui 初探

2021/2/6 guiimgui

简单的尝试一下 imgui(采用 OpenGL 和 glfw3 以支持跨平台)

# 前置依赖

# linux

需要安装 opengl 以及 glfw3 库

sudo apt-get install libglu1-mesa-dev freeglut3-dev mesa-common-dev
sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev libglut-dev
sudo apt-get install libglfw3-dev libglfw3
1
2
3

# windows

下载地址 (opens new window)

随便找个地方放着就可以了,为了方便,我放在了项目目录下

当然,你也得有 vs,我这里是 2019

# 目录结构

.
├── glfw
│   ├── LICENSE.md
│   ├── include
│   │   ├── GL
│   │   │   ├── gl3w.c
│   │   │   ├── gl3w.h
│   │   │   └── glcorearb.h
│   │   └── GLFW
│   │       ├── glfw3.h
│   │       └── glfw3native.h
│   └── lib-vc2019
│       ├── glfw3.dll
│       ├── glfw3.lib
│       └── glfw3dll.lib
├── imgui
│   ├── imconfig.h
│   ├── imgui.cpp
│   ├── imgui.h
│   ├── imgui_demo.cpp
│   ├── imgui_draw.cpp
│   ├── imgui_impl_glfw.cpp
│   ├── imgui_impl_glfw.h
│   ├── imgui_impl_opengl3.cpp
│   ├── imgui_impl_opengl3.h
│   ├── imgui_internal.h
│   ├── imgui_tables.cpp
│   ├── imgui_widgets.cpp
│   ├── imstb_rectpack.h
│   ├── imstb_textedit.h
│   └── imstb_truetype.h
└── main.cpp
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
32

# CMakeLists

为了跨平台,还是选用 Cmake 构建系统,由于其官方为自带 CMakeList.txt 故以上面的目录为基础自行编写一份

# Linux

cmake_minimum_required(VERSION 3.14)
project(try_imgui)

set(CMAKE_CXX_STANDARD 14)

aux_source_directory(imgui IMGUI_CPP_LIST)
aux_source_directory(glfw/include/GL GL_CPP_LIST)

add_executable(try_imgui main.cpp ${IMGUI_CPP_LIST} ${GL_CPP_LIST})
target_link_libraries(try_imgui GL dl glfw)
1
2
3
4
5
6
7
8
9
10

# Windows

cmake_minimum_required(VERSION 3.14)
project(try_imgui)

set(CMAKE_CXX_STANDARD 14)

aux_source_directory(imgui IMGUI_CPP_LIST)
aux_source_directory(glfw/include/GL GL_CPP_LIST)

add_executable(try_imgui main.cpp ${IMGUI_CPP_LIST} ${GL_CPP_LIST})

target_include_directories(try_imgui PUBLIC "glfw/include")

target_link_directories(try_imgui PUBLIC
            "glfw/lib-vc2019"
            "E:\\Windows Kits\\10\\Lib\\10.0.18362.0\\um\\x64"
            "E:\\Microsoft Visual Studio\\VC\\Tools\\MSVC\\14.28.29333\\lib\\x64"
            "E:\\Windows Kits\\10\\Lib\\10.0.18362.0\\ucrt\\x64")
    target_include_directories(try_imgui PUBLIC
            "E:\\Windows Kits\\10\\Include\\10.0.18362.0\\um"
            "E:\\Windows Kits\\10\\Include\\10.0.18362.0\\ucrt"
            "E:\\Windows Kits\\10\\Include\\10.0.18362.0\\shared"
            "E:\\Microsoft Visual Studio\\VC\\Tools\\MSVC\\14.28.29333\\include")
    target_link_libraries(try_imgui glfw3 opengl32)
target_link_options(try_imgui PUBLIC /NODEFAULTLIB:msvcrt.lib)

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

这里的路径要注意修改,我暂时还不太了解 windows 下的这些小问题

# MacOS

TODO:留坑,等我有了苹果设备再说吧

# Finally

cmake_minimum_required(VERSION 3.14)
project(try_imgui)

set(CMAKE_CXX_STANDARD 14)

aux_source_directory(imgui IMGUI_CPP_LIST)
aux_source_directory(glfw/include/GL GL_CPP_LIST)

add_executable(try_imgui main.cpp ${IMGUI_CPP_LIST} ${GL_CPP_LIST})

if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
    target_link_libraries(try_imgui GL dl glfw)
    target_link_directories(try_imgui PUBLIC "glfw/lib-vc2019")
    target_include_directories(try_imgui PUBLIC "glfw/include")
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
    target_link_directories(try_imgui PUBLIC
            "glfw/lib-vc2019"
            "E:\\Windows Kits\\10\\Lib\\10.0.18362.0\\um\\x64"
            "E:\\Windows Kits\\10\\Lib\\10.0.18362.0\\ucrt\\x64"
            "E:\\Microsoft Visual Studio\\VC\\Tools\\MSVC\\14.28.29333\\lib\\x64")
    target_include_directories(try_imgui PUBLIC
            "glfw/include"
            "E:\\Windows Kits\\10\\Include\\10.0.18362.0\\um"
            "E:\\Windows Kits\\10\\Include\\10.0.18362.0\\ucrt"
            "E:\\Windows Kits\\10\\Include\\10.0.18362.0\\shared"
            "E:\\Microsoft Visual Studio\\VC\\Tools\\MSVC\\14.28.29333\\include")
    target_link_libraries(try_imgui glfw3 opengl32)
    if (CMAKE_BUILD_TYPE STREQUAL "DEBUG")
        target_link_options(try_imgui PUBLIC /NODEFAULTLIB:msvcrt.lib)
    endif ()
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Macos")
endif ()
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
32

# main.cpp

其实示例代码中关键信息或者说绘图的代码核心只有以下几行,其实就是一个语句绘制一个元素或一个控件,其余的都是初始化设备呀之类的,这个库封装的很易用了

{
static float f = 0.0f;
static int counter = 0;

ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.

ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
ImGui::Checkbox("Another Window", &show_another_window);

ImGui::SliderFloat("float", &f, 0.0f, 1.0f);            // Edit 1 float using a slider from 0.0f to 1.0f
ImGui::ColorEdit3("clear color", (float *) &clear_color); // Edit 3 floats representing a color

if (ImGui::Button("Button")) counter++;                 // Buttons return true when clicked (most widgets return true when edited/activated)
    
ImGui::SameLine();
ImGui::Text("counter = %d", counter);

ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate,
            ImGui::GetIO().Framerate);
ImGui::End();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

下方是完整的示例代码

#include "imgui/imgui.h"
#include "imgui/imgui_impl_glfw.h"
#include "imgui/imgui_impl_opengl3.h"
#include <stdio.h>

// About Desktop OpenGL function loaders:
//  Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
//  Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
//  You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
#include <GL/gl3w.h>            // Initialize with gl3wInit()

// Include glfw3.h after our OpenGL definitions
#include <GLFW/glfw3.h>

// [Win32] Our example includes a copy of glfw3.lib pre-compiled with VS2010 to maximize ease of testing and compatibility with old VS compilers.
// To link with VS2010-era libraries, VS2015+ requires linking with legacy_stdio_definitions.lib, which we do using this pragma.
// Your own project should not be affected, as you are likely to link with a newer binary of GLFW that is adequate for your version of Visual Studio.
//#if defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
//#pragma comment(lib, "legacy_stdio_definitions")
//#endif

static void glfw_error_callback(int error, const char *description) {
    fprintf(stderr, "Glfw Error %d: %s\n", error, description);
}

int main(int, char **) {
    // Setup window
    glfwSetErrorCallback(glfw_error_callback);
    if (!glfwInit())
        return 1;

    // Decide GL+GLSL versions
    // GL 3.0 + GLSL 130
    const char *glsl_version = "#version 130";
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
    //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);  // 3.2+ only
    //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);            // 3.0+ only

    // Create window with graphics context
    GLFWwindow *window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+OpenGL3 example", NULL, NULL);
    if (window == NULL)
        return 1;
    glfwMakeContextCurrent(window);
    glfwSwapInterval(1); // Enable vsync

    // Initialize OpenGL loader
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
    bool err = gl3wInit() != 0;
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
    bool err = glewInit() != GLEW_OK;
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
    bool err = gladLoadGL() == 0;
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
    bool err = gladLoadGL(glfwGetProcAddress) == 0; // glad2 recommend using the windowing library loader instead of the (optionally) bundled one.
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
    bool err = false;
    glbinding::Binding::initialize();
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
    bool err = false;
    glbinding::initialize([](const char* name) { return (glbinding::ProcAddress)glfwGetProcAddress(name); });
#else
    bool err = false; // If you use IMGUI_IMPL_OPENGL_LOADER_CUSTOM, your loader is likely to requires some form of initialization.
#endif
    if (err) {
        fprintf(stderr, "Failed to initialize OpenGL loader!\n");
        return 1;
    }

    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO &io = ImGui::GetIO();
    (void) io;
    //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
    //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls

    // Setup Dear ImGui style
    ImGui::StyleColorsDark();
    //ImGui::StyleColorsClassic();

    // Setup Platform/Renderer backends
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init(glsl_version);

    // Load Fonts
    // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
    // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
    // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
    // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
    // - Read 'docs/FONTS.md' for more instructions and details.
    // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
    //io.Fonts->AddFontDefault();
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
    //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
    //IM_ASSERT(font != NULL);

    // Our state
    bool show_demo_window = true;
    bool show_another_window = false;
    ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);

    // Main loop
    while (!glfwWindowShouldClose(window)) {
        // Poll and handle events (inputs, window resize, etc.)
        // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
        // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
        // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
        // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
        glfwPollEvents();

        // Start the Dear ImGui frame
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
        if (show_demo_window)
            ImGui::ShowDemoWindow(&show_demo_window);

        // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
        {
            static float f = 0.0f;
            static int counter = 0;

            ImGui::Begin(
                    "Hello, world!");                          // Create a window called "Hello, world!" and append into it.

            ImGui::Text(
                    "This is some useful text.");               // Display some text (you can use a format strings too)
            ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
            ImGui::Checkbox("Another Window", &show_another_window);

            ImGui::SliderFloat("float", &f, 0.0f, 1.0f);            // Edit 1 float using a slider from 0.0f to 1.0f
            ImGui::ColorEdit3("clear color", (float *) &clear_color); // Edit 3 floats representing a color

            if (ImGui::Button(
                    "Button"))                            // Buttons return true when clicked (most widgets return true when edited/activated)
                counter++;
            ImGui::SameLine();
            ImGui::Text("counter = %d", counter);

            ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate,
                        ImGui::GetIO().Framerate);
            ImGui::End();
        }

        // 3. Show another simple window.
        if (show_another_window) {
            ImGui::Begin("Another Window",
                         &show_another_window);   // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
            ImGui::Text("Hello from another window!");
            if (ImGui::Button("Close Me"))
                show_another_window = false;
            ImGui::End();
        }

        // Rendering
        ImGui::Render();
        int display_w, display_h;
        glfwGetFramebufferSize(window, &display_w, &display_h);
        glViewport(0, 0, display_w, display_h);
        glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        glfwSwapBuffers(window);
    }

    // Cleanup
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    glfwDestroyWindow(window);
    glfwTerminate();

    return 0;
}

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

# Error

遇到的错误和解决方法:

# 报错 GL/gl.h: No such file or directory

sudo apt-get install libglu1-mesa-dev freeglut3-dev mesa-common-dev
1

# /usr/bin/ld: cannot find -lglfw

sudo apt-get install libglfw3-dev
1

添加链接参数 /NODEFAULTLIB:msvcrt.lib 即可,不过这个问题仅仅在 Debug 模式编译才会遇到

target_link_options(try_imgui PUBLIC /NODEFAULTLIB:msvcrt.lib)
1

# Other

# 查看 OpenGL version

$ sudo apt-get install mesa-utils

$ glxinfo | grep "OpenGL version"

OpenGL version string: 1.4 (2.1 Mesa 7.7.1)

$ glxinfo | grep 'version'

server glx version string: 1.4
client glx version string: 1.4
GLX version: 1.4
    Max core profile version: 4.1
    Max compat profile version: 3.0
    Max GLES1 profile version: 1.1
    Max GLES[23] profile version: 3.0
OpenGL core profile version string: 4.1 (Core Profile) Mesa 11.1.2
OpenGL core profile shading language version string: 4.10
OpenGL version string: 3.0 Mesa 11.1.2
OpenGL shading language version string: 1.30
OpenGL ES profile version string: OpenGL ES 3.0 Mesa 11.1.2
OpenGL ES profile shading language version string: OpenGL ES GLSL ES 3.00
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Last Updated: 2023-10-29T08:26:04.000Z