使用GLFW、GLAD与ImGui无法渲染三角形的问题求助
开发结合ImGui与现代OpenGL 3的3D图形程序,基于GLFW(Mac平台静态库libglfw3.a),通过GLAD加载OpenGL函数,使用ImGui的imgui_impl_opengl3_loader.h等后端文件。程序主窗口正常显示,但点击"Show Figure"按钮弹出的窗口无法渲染三角形,而相同绘制逻辑脱离ImGui环境可正常运行,怀疑是ImGui与OpenGL渲染流程冲突。
简化版代码:
#include <iostream> #include "imgui.h" #include "glad.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" #define GL_SILENCE_DEPRECATION #if defined(IMGUI_IMPL_OPENGL_ES2) #include <GLES2/gl2.h> #endif #include "glfw3.h" // Vertex shader source const char *vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "void main()\n" "{\n" " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" "}\0"; // Fragment shader source const char *fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" "void main()\n" "{\n" " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\0"; int main() { // Initiate GLFW glfwInit(); // Decide GL+GLSL versions #if defined(IMGUI_IMPL_OPENGL_ES2) const char *glsl_version = "#version 100"; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); #elif defined(__APPLE__) const char *glsl_version = "#version 150"; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); #else const char *glsl_version = "#version 130"; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); #endif GLFWwindow *window = glfwCreateWindow(1280, 720, "TestWindow1", NULL, NULL); if (window == NULL) { std::cout << "ERROR" << std::endl; return -1; } glfwMakeContextCurrent(window); glfwSwapInterval(1); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; } IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO &io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; ImGui::StyleColorsLight(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(glsl_version); ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); bool showFigure = false; // Compile and link shaders 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 << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; } unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; } 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 << "ERROR::SHADER_PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; } unsigned int VAO; glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); float vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f}; unsigned int vertexBuffer0; glGenBuffers(1, &vertexBuffer0); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer0); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0); glEnableVertexAttribArray(0); while (!glfwWindowShouldClose(window)) { glfwPollEvents(); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); ImVec2 mainWindowSize = ImVec2(1000, 600); ImGui::SetNextWindowSize(mainWindowSize); ImGui::Begin("TestGui"); if (ImGui::Button("Show Figure")) { showFigure = true; } if (showFigure == true) { showFigure = false; GLFWwindow *figureWindow = glfwCreateWindow(500, 500, "Figure", NULL, NULL); glfwMakeContextCurrent(figureWindow); glfwSwapInterval(1); while (!glfwWindowShouldClose(figureWindow)) { glClearColor(0.2, 0.3, 0.3, 1.0); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shaderProgram); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3); glfwSwapBuffers(figureWindow); glfwPollEvents(); } glfwDestroyWindow(figureWindow); glfwMakeContextCurrent(window); } ImGui::End(); 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.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(window); } glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &vertexBuffer0); glDeleteProgram(shaderProgram); ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwDestroyWindow(window); glfwTerminate(); return 0; }
编译命令:
gcc -std=c++17 -I/INCLUDE...PATH/ -c Test.cpp imgui.cpp imgui_widgets.cpp imgui_tables.cpp imgui_impl_opengl3.cpp imgui_impl_glfw.cpp imgui_draw.cpp imgui_demo.cpp gcc -c glad.c gcc -L/LIBRARY...PATH/ -lglfw3 -std=c++17 -lstdc++ -framework Carbon -framework Cocoa -framework OpenGL -framework IOKit -o aTest.out Test.o imgui.o imgui_widgets.o imgui_tables.o imgui_impl_opengl3.o imgui_impl_glfw.o imgui_draw.o imgui_demo.o glad.o
OpenGL上下文资源不共享:GLFW默认创建的新窗口不会共享原上下文的VAO、VBO、着色器程序等资源,你在主窗口上下文创建的
shaderProgram、VAO无法在新窗口上下文直接使用。解决方法是创建新窗口时指定主窗口为共享上下文:GLFWwindow *figureWindow = glfwCreateWindow(500, 500, "Figure", NULL, window);GLAD未在新上下文初始化:GLAD加载的函数指针与当前上下文绑定,切换到新窗口上下文后必须重新初始化GLAD。在
glfwMakeContextCurrent(figureWindow);后添加:if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD for figure window" << std::endl; glfwDestroyWindow(figureWindow); glfwMakeContextCurrent(window); return -1; }嵌套事件循环阻塞主窗口:当前代码点击按钮后进入新窗口的
while循环,会完全阻塞主窗口的事件处理与渲染,导致主窗口假死。正确做法是将新窗口逻辑整合到主循环中,用变量跟踪窗口状态:// 定义变量跟踪窗口状态 GLFWwindow *figureWindow = nullptr; // 主循环内处理窗口创建 if (showFigure == true) { showFigure = false; figureWindow = glfwCreateWindow(500, 500, "Figure", NULL, window); glfwMakeContextCurrent(figureWindow); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { // 错误处理逻辑 } glfwSwapInterval(1); } // 主循环内处理新窗口渲染 if (figureWindow != nullptr && !glfwWindowShouldClose(figureWindow)) { glfwMakeContextCurrent(figureWindow); glClearColor(0.2, 0.3, 0.3, 1.0); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shaderProgram); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3); glfwSwapBuffers(figureWindow); // 切回主窗口上下文继续渲染ImGui glfwMakeContextCurrent(window); } else if (figureWindow != nullptr) { glfwDestroyWindow(figureWindow); figureWindow = nullptr; }ImGui状态污染OpenGL上下文:ImGui渲染会修改OpenGL状态(如混合、深度测试等),切换到新窗口渲染前需重置这些状态。在新窗口渲染循环中添加:
glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); // 根据绘制需求调整其他状态着色器版本不匹配:新窗口创建时未设置与主窗口一致的OpenGL版本提示,可能导致上下文版本不兼容,着色器无法运行。创建新窗口前复用主窗口的版本配置逻辑,确保上下文版本一致。
内容的提问来源于stack exchange,提问作者Christ Liu

