Search code examples
c++openglglfwimgui

Can't render a triangle using GLFW, GLAD and ImGui


I am trying to write a program with a graphical user interface using ImGui and draw 3-D figures with modern OpenGL methods in this program. I want to use GLFW and openGL3 to make this interface so I use the backends: imgui_impl_opengl3_loader.h, imgui_impl_opengl3.cpp, imgui_impl_opengl3.h with all ImGui basic files: ImGui_....

I use GLFW with the static library libglfw3.a file (build for Mac OS) and header glfw3.h.

As ImGui library loads gl functions using imgui_impl_opengl3_loader.h and it "is custom-generated, based on gl3w but automatically filtered to only include enums/functions that we use in our imgui_impl_opengl3.cpp source file in order to be small", I use GLAD to load gl functions with glad.c, glad.h and khrplatform.h.

A simplified version of my code is shown below:

#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";

// Fragment shader

int main()
{
    // Initiate GLFW
    glfwInit();  

    // Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
    // GL ES 2.0 + GLSL 100
    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__)
    // GL 3.2 + GLSL 150
    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); // 3.2+ only
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);           // Required on Mac
#else
    // 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
#endif

    // demonstrate a new window
    GLFWwindow *window = glfwCreateWindow(1280, 720, "TestWindow1", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "ERROR" << std::endl;
        return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSwapInterval(1);

    // Setup GLAD
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        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::StyleColorsLight();

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

    ImGui_ImplOpenGL3_Init(glsl_version);

    ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);

    // a bool value to control the show figure window
    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;
    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;
    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))
    {
        // check and call events and swap the buffers
        glfwPollEvents();

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

        // Main Window
        ImVec2 mainWindowSize = ImVec2(1000, 600);
        ImGui::SetNextWindowSize(mainWindowSize);
        ImGui::Begin("TestGui");


        // This button is used to set the showFigure bool value from false to true
        if (ImGui::Button("Show Figure"))
        {
            showFigure = true;
        }
        
        // Show Figure in a new GLFW window
        if (showFigure == true)
        {
            showFigure = false;
            GLFWwindow *figureWindow = glfwCreateWindow(500, 500, "Figure", NULL, NULL);
            glfwMakeContextCurrent(figureWindow);
            glfwSwapInterval(1);

            while (!glfwWindowShouldClose(figureWindow))
            {
                // Background color
                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();

        // Render Main Window
        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);

    }
    
    // Cleanup
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &vertexBuffer0);
    glDeleteProgram(shaderProgram);
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    glfwDestroyWindow(window);

    glfwTerminate();

    return 0;

}

I use gcc to compile and link all the files:

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

The result is as follows: ./aTest.out

When I clicked the button, it failed to demonstrate the triangle as I expected. Click the "Show Figure" buttom

However, this "drawing" code works just fine without ImGui, I'm wondering is there anything wrong/confilicts with the ImGui rendering process and GL rendering process?

Any advice?


Solution

  • The GLFW Window to show the figure should be created using:

    GLFWwindow *figureWindow = glfwCreateWindow(500, 500, "Figure", NULL, window);
    

    instead of

    GLFWwindow *figureWindow = glfwCreateWindow(500, 500, "Figure", NULL, NULL);
    

    The new window should share the context of the main window because the shader programs are defined in the main window's context.