在OpenGL项目中添加ImGui支持

OpenTK 2026-03-16 16:41:13

第一步:通过vcpkg安装ImGui(带绑定)

打开PowerShell,进入vcpkg目录:

powershell

cd C:\Users\Candy.CHINAMI-6LP8UH3\vcpkg

# 安装ImGui(带glfw和opengl3绑定)
.\vcpkg install imgui[core,glfw-binding,opengl3-binding] --triplet x64-windows

第二步:检查安装是否成功

powershell

.\vcpkg list | findstr imgui

应该看到类似:

text

imgui:x64-windows                1.90.5#1    Bloat-free Graphical User Interface...
imgui[core]:x64-windows          1.90.5#1    Bloat-free Graphical User Interface...
imgui[glfw-binding]:x64-windows  1.90.5#1    Make glfw-binding available for imgui
imgui[opengl3-binding]:x64-windows 1.90.5#1  Make opengl3-binding available for imgui

第三步:更新项目链接器设置

  1. 在Visual Studio中打开您的项目

  2. 右键点击项目 -> 属性

  3. 配置选 "所有配置",平台选 "x64"

  4. 导航到:链接器 -> 输入 -> 附加依赖项

  5. 点击编辑,添加以下内容(每行一个):

text

glfw3.lib
opengl32.lib

(您可能已经有这两个,检查一下即可)

第四步:添加DLL文件(重要!)

vcpkg安装的ImGui可能依赖DLL文件。将以下DLL复制到您的可执行文件输出目录(通常是 x64\Debug\ 或 x64\Release\):

text

C:\Users\Candy.CHINAMI-6LP8UH3\vcpkg\installed\x64-windows\bin\glfw3.dll

如何找到输出目录

  • 在VS中,右键点击项目 -> 属性

  • 配置属性 -> 常规 -> 输出目录 就是可执行文件生成的位置

第五步:添加您的代码

将您提供的完整代码复制到现有的 main.cpp 中,覆盖原有内容。

第六步:准备中文字体(如果需要)

您的代码会自动尝试加载系统字体,通常不需要额外操作。如果中文显示为方框:

  1. 从网上下载微软雅黑字体文件 msyh.ttc

  2. 将其复制到您的可执行文件输出目录(与exe同一文件夹)

第七步:编译运行

点击 "本地Windows调试器" 运行程序。


如果遇到问题

问题1:找不到头文件

text

#include <imgui.h>          // 找不到
#include <imgui_impl_glfw.h> // 找不到

解决方案
检查vcpkg的include目录是否已添加到项目:

  • 项目属性 -> C/C++ -> 常规 -> 附加包含目录

  • 确认包含:C:\Users\Candy.CHINAMI-6LP8UH3\vcpkg\installed\x64-windows\include

问题2:链接错误 LNK2019

text

error LNK2019: 无法解析的外部符号 "void __cdecl ImGui::Text..."

解决方案

  1. 确认链接器附加依赖项中有 glfw3.lib 和 opengl32.lib

  2. 如果还是报错,尝试添加 imgui.lib

    • 查看 C:\Users\Candy.CHINAMI-6LP8UH3\vcpkg\installed\x64-windows\lib 下是否有 imgui.lib

    • 如果有,在链接器附加依赖项中添加 imgui.lib

问题3:运行时缺少DLL

text

程序无法启动,因为计算机中丢失 glfw3.dll

解决方案
将 C:\Users\Candy.CHINAMI-6LP8UH3\vcpkg\installed\x64-windows\bin\glfw3.dll 复制到exe所在目录

问题4:中文显示为方框

解决方案
检查控制台输出,看字体加载是否成功。如果失败,可以:

  1. 确认系统有中文字体(Windows一般都有)

  2. 或手动下载 msyh.ttc 放到exe目录

  3. 或在代码中指定其他字体路径


验证安装

如果一切正常,运行后会看到:

  1. 深灰色背景的OpenGL窗口

  2. 一个彩色的三角形

  3. 左上角有ImGui窗口,可以调整三角形颜色

  4. 控制台输出OpenGL版本和字体加载信息

#include <iostream>
#include <vector>
#include <windows.h>
#include <fstream>
#include <string>

// 【关键】GLFW DLL 定义
#define GLFW_DLL 
#include <GLFW/glfw3.h>
#include <glad/glad.h>

// ImGui 头文件
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>

// --- 着色器源码 ---
const char* vertexShaderSource = R"(
    #version 330 core
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec3 aColor;
    out vec3 ourColor;
    void main() {
        gl_Position = vec4(aPos, 1.0);
        ourColor = aColor;
    }
)";

const char* fragmentShaderSource = R"(
    #version 330 core
    in vec3 ourColor;
    out vec4 FragColor;
    void main() {
        FragColor = vec4(ourColor, 1.0);
    }
)";

// 全局变量:三角形的颜色
float triColorR = 1.0f;
float triColorG = 0.0f;
float triColorB = 0.0f;

void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
    glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window) {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// 辅助函数:检查文件是否存在
bool fileExists(const std::string& filename) {
    std::ifstream f(filename.c_str());
    return f.good();
}

int main() {
    // 设置控制台输出编码为UTF-8
    SetConsoleOutputCP(CP_UTF8);

    // 1. 初始化 GLFW
    if (!glfwInit()) {
        std::cout << "Failed to initialize GLFW" << std::endl;
        return -1;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL + ImGui Color Picker", NULL, NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSwapInterval(1);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // 2. 初始化 GLAD
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // ==========================================
    // 3. 初始化 ImGui(支持中文)
    // ==========================================
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;

    std::cout << "尝试加载中文字体..." << std::endl;

    // 获取当前可执行文件路径
    char exePath[MAX_PATH];
    GetModuleFileNameA(NULL, exePath, MAX_PATH);
    std::string exeDir = std::string(exePath);
    exeDir = exeDir.substr(0, exeDir.find_last_of("\\/"));

    // 要尝试的字体路径列表
    std::vector<std::string> fontPaths;

    // 首先尝试系统字体目录(从输出看这个成功了)
    fontPaths.push_back("C:\\Windows\\Fonts\\msyh.ttc");
    fontPaths.push_back("C:\\Windows\\Fonts\\simhei.ttf");
    fontPaths.push_back("C:\\Windows\\Fonts\\simkai.ttf");

    // 然后尝试当前目录
    fontPaths.push_back(exeDir + "\\msyh.ttc");
    fontPaths.push_back(exeDir + "\\simhei.ttf");

    ImFont* font = nullptr;
    for (const auto& path : fontPaths) {
        std::cout << "尝试字体: " << path << std::endl;

        if (fileExists(path)) {
            std::cout << "文件存在,尝试加载..." << std::endl;

            // 【关键修复】先清除之前的字体配置
            io.Fonts->Clear();

            // 尝试加载字体
            font = io.Fonts->AddFontFromFileTTF(
                path.c_str(),
                18.0f,
                NULL,
                io.Fonts->GetGlyphRangesChineseFull()
            );

            if (font) {
                std::cout << "✓ 成功加载字体: " << path << std::endl;

                // 【关键】加载字体后必须构建
                io.Fonts->Build();
                break;
            }
            else {
                std::cout << "✗ 文件存在但加载失败" << std::endl;
            }
        }
        else {
            std::cout << "✗ 文件不存在" << std::endl;
        }
    }

    if (font == NULL) {
        std::cout << "⚠ 警告:所有字体加载失败,使用默认字体" << std::endl;
        io.Fonts->AddFontDefault();
        io.Fonts->Build();
    }

    // 设置 ImGui 样式
    ImGui::StyleColorsDark();

    // ==========================================
    // 4. 初始化 ImGui 后端(在字体加载之后)
    // ==========================================
    std::cout << "初始化 ImGui 后端..." << std::endl;

    // 必须先初始化后端,再使用 ImGui
    if (!ImGui_ImplGlfw_InitForOpenGL(window, true)) {
        std::cout << "Failed to initialize ImGui GLFW backend" << std::endl;
        return -1;
    }

    if (!ImGui_ImplOpenGL3_Init("#version 330")) {
        std::cout << "Failed to initialize ImGui OpenGL3 backend" << std::endl;
        return -1;
    }

    std::cout << "ImGui 初始化完成!" << std::endl;

    // ==========================================
    // 5. 编译着色器
    // ==========================================
    std::cout << "编译着色器..." << std::endl;

    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    // 检查顶点着色器编译错误
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "顶点着色器编译失败:\n" << infoLog << std::endl;
        return -1;
    }

    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "片段着色器编译失败:\n" << infoLog << std::endl;
        return -1;
    }

    unsigned int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << "着色器程序链接失败:\n" << infoLog << std::endl;
        return -1;
    }

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // ==========================================
    // 6. 准备三角形数据
    // ==========================================
    std::cout << "准备顶点数据..." << std::endl;

    float vertices[] = {
         0.0f,  0.5f, 0.0f,  1.0f, 0.0f, 0.0f,
         0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,
        -0.5f, -0.5f, 0.0f,  0.0f, 0.0f, 1.0f
    };

    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    glBindVertexArray(0);

    float inputR = 1.0f, inputG = 0.0f, inputB = 0.0f;

    std::cout << "初始化完成,开始渲染循环..." << std::endl;

    // ==========================================
    // 7. 渲染循环
    // ==========================================
    while (!glfwWindowShouldClose(window)) {
        processInput(window);

        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        // --- 创建 UI 窗口(全部添加 u8 前缀)---
        ImGui::Begin(u8"颜色控制器");

        ImGui::Text(u8"调整三角形的颜色:");
        ImGui::Separator();

        ImVec4 color = ImVec4(triColorR, triColorG, triColorB, 1.0f);
        if (ImGui::ColorEdit3(u8"选择颜色", (float*)&color)) {
            triColorR = color.x;
            triColorG = color.y;
            triColorB = color.z;
            inputR = triColorR;
            inputG = triColorG;
            inputB = triColorB;
        }

        ImGui::Separator();
        ImGui::Text(u8"或者手动输入 (0.0 - 1.0):");

        ImGui::InputFloat(u8"红色", &inputR, 0.1f, 1.0f, "%.2f");
        ImGui::InputFloat(u8"绿色", &inputG, 0.1f, 1.0f, "%.2f");
        ImGui::InputFloat(u8"蓝色", &inputB, 0.1f, 1.0f, "%.2f");

        if (ImGui::Button(u8"应用输入框颜色")) {
            triColorR = (inputR < 0) ? 0 : (inputR > 1 ? 1 : inputR);
            triColorG = (inputG < 0) ? 0 : (inputG > 1 ? 1 : inputG);
            triColorB = (inputB < 0) ? 0 : (inputB > 1 ? 1 : inputB);
        }

        ImGui::Separator();
        ImGui::Text(u8"当前颜色:");
        ImGui::ColorButton(u8"当前颜色", ImVec4(triColorR, triColorG, triColorB, 1.0f),
            ImGuiColorEditFlags_NoTooltip, ImVec2(100, 20));

        ImGui::End();

        // 更新三角形颜色
        float updatedVertices[] = {
             0.0f,  0.5f, 0.0f,  triColorR, triColorG, triColorB,
             0.5f, -0.5f, 0.0f,  triColorR, triColorG, triColorB,
            -0.5f, -0.5f, 0.0f,  triColorR, triColorG, triColorB
        };

        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(updatedVertices), updatedVertices);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        // OpenGL 渲染
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);

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

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // 清理资源
    std::cout << "清理资源..." << std::endl;

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

    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteProgram(shaderProgram);

    glfwTerminate();

    std::cout << "程序正常退出" << std::endl;
    return 0;
}

 

 

 

...全文
59 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

3

社区成员

发帖
与我相关
我的任务
社区描述
openTK、OpenGL、WebGL技术学习交流
图形渲染c#程序人生 技术论坛(原bbs) 广东省·深圳市
社区管理员
  • 亿只小灿灿
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧