OpenGL 6.ImGui

将ImGui集成到项目中

ImGui是一个GUI(图形用户接口)库,用于绘制UI,地址
新建目录如下
在这里插入图片描述
将仓库根目录下的.h和.cpp文件和examples\example_glfw_opengl3\main.cpp拷贝到vendor/imgui文件夹下,这个main.cpp不需要包含在项目中,我们只是参考它里面的一些代码。
在这里插入图片描述
然后将仓库backends目录下的这5个文件也拷贝到vendor/imgui文件夹下。
注意:原本视频教程中使用的是ImGui 1.6,现在最新版是1.8,文件目录结构发生变化,一些函数也发生了变化。

使用ImGui绘制窗口

这里使用官方提供的3个例子演示
Application.cpp

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Renderer.h"
#include "VertexBufferLayout.h"
#include "Texture.h"

#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"

#include "imgui/imgui.h"
#include "imgui/imgui_impl_glfw.h"
#include "imgui/imgui_impl_opengl3.h"

int main(void)
{
    
    
    GLFWwindow* window;

    if (!glfwInit())
        return -1;

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

    window = glfwCreateWindow(960, 540, "Hello World", NULL, NULL);
    if (!window)
    {
    
    
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);

    glfwSwapInterval(1);

    GLenum err = glewInit();
    if (GLEW_OK != err)
        std::cout << err << std::endl;
    std::cout << glGetString(GL_VERSION) << std::endl;

    //添加一个作用域,作用域结束时,会调用VertexBuffer和IndexBuffer的析构函数,
    //不添加作用域的话,需要在glfwTerminate之前显式删除两个缓冲区,
    //因为glfwTerminate会破坏OpenGL上下文,导致glGetError返回一个错误
    {
    
    
        //每个顶点添加纹理坐标,如(100.0f, 100.0f)这个点在左下角
        float positions[] = {
    
    
            100.0f, 100.0f, 0.0f, 0.0f,//0
            200.0f, 100.0f, 1.0f, 0.0f,//1
            200.0f, 200.0f, 1.0f, 1.0f,//2
            100.0f, 200.0f, 0.0f, 1.0f,//3
        };

        unsigned int indices[] = {
    
    
            0, 1, 2,
            2, 3, 0,
        };

        //启用混合(默认不会启用)
        GLCall(glEnable(GL_BLEND));
        /**
         * glBlendFunc(src, dest) 指定颜色因子
         * src 指定输出颜色(RGBA)因子的计算方式, 默认为GL_ONE
         * dest 指定目标颜色因子的计算方式, 默认为GL_ZERO
         * GL_SRC_ALPHA 因为src的alpha为0, GL_ONE_MINUS_SRC_ALPHA 1-src.alpha
         * RGBA = Srgba * GL_SRC_ALPHA + Drgba * GL_ONE_MINUS_SRC_ALPHA
         **/
        GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
        GLCall(glBlendEquation(GL_FUNC_ADD));

        VertexArray va;
        //4个顶点,每个顶点有4个浮点数
        VertexBuffer vb(positions, 4 * 4 * sizeof(float));
        VertexBufferLayout layout;
        layout.Push<float>(2);
        layout.Push<float>(2);
        va.AddBuffer(vb, layout);

        IndexBuffer ib(indices, 6);

        //我们创建的窗口是960 x 540,是16:9的窗口,而图片宽高是1:1,
        //直接加载图片会有拉伸,定义4x4投影矩阵来修正,生成一个正交矩阵
        //定义视口的左、右、上、下的边界,近平面和远平面的距离
        glm::mat4 proj = glm::ortho(0.0f, 960.0f, 0.0f, 540.0f, -1.0f, 1.0f);
        //观察矩阵,创建一个单位矩阵并乘以(-100, 0, 0),模拟摄像机右移
        glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(-100, 0, 0));
        //模型矩阵
        glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(200, 200, 0));
        //opengl里mvp矩阵的乘法顺序是相反的
        glm::mat4 mvp = proj * view * model;

        Shader shader("res/shaders/Basic.shader");
        shader.Bind();
        shader.SetUniformMat4f("u_MVP", mvp);

        Texture texture("res/textures/ChernoLogo.png");
        texture.Bind();
        //纹理绑定到插槽0
        shader.SetUniform1i("u_Texture", 0);
        
        va.Unbind();
        vb.Unbind();
        ib.Unbind();
        shader.Unbind();

        Renderer renderer;

        //创建上下文环境,初始化
        ImGui::CreateContext();
        ImGui_ImplGlfw_InitForOpenGL(window, true);
        ImGui::StyleColorsDark();

        //需要指定GLSL版本, 也就是shader中的version
        const char* glsl_version = "#version 330";
        ImGui_ImplOpenGL3_Init(glsl_version);

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

        while (!glfwWindowShouldClose(window))
        {
    
    
            renderer.Clear();

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

            renderer.Draw(va, ib, shader);

            //1.显示一个大的演示窗口
            if (show_demo_window)
                ImGui::ShowDemoWindow(&show_demo_window);

            //2.显示一个简单窗口
            {
    
    
                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.显示另一个简单窗口
            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();
            }

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

            glfwSwapBuffers(window);
            glfwPollEvents();
        }

        //作用域结束后会调用所有析构函数
    }

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    glfwTerminate();
    return 0;
}

在这里插入图片描述
示例中实现了单选框,滑块,按钮,拾色器等常见UI样式。

使用滑块来控制图片位置

Application.cpp

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Renderer.h"
#include "VertexBufferLayout.h"
#include "Texture.h"

#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"

#include "imgui/imgui.h"
#include "imgui/imgui_impl_glfw.h"
#include "imgui/imgui_impl_opengl3.h"

int main(void)
{
    
    
    GLFWwindow* window;

    if (!glfwInit())
        return -1;

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

    window = glfwCreateWindow(960, 540, "Hello World", NULL, NULL);
    if (!window)
    {
    
    
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);

    glfwSwapInterval(1);

    GLenum err = glewInit();
    if (GLEW_OK != err)
        std::cout << err << std::endl;
    std::cout << glGetString(GL_VERSION) << std::endl;

    //添加一个作用域,作用域结束时,会调用VertexBuffer和IndexBuffer的析构函数,
    //不添加作用域的话,需要在glfwTerminate之前显式删除两个缓冲区,
    //因为glfwTerminate会破坏OpenGL上下文,导致glGetError返回一个错误
    {
    
    
        //每个顶点添加纹理坐标,如(100.0f, 100.0f)这个点在左下角
        float positions[] = {
    
    
            100.0f, 100.0f, 0.0f, 0.0f,//0
            200.0f, 100.0f, 1.0f, 0.0f,//1
            200.0f, 200.0f, 1.0f, 1.0f,//2
            100.0f, 200.0f, 0.0f, 1.0f,//3
        };

        unsigned int indices[] = {
    
    
            0, 1, 2,
            2, 3, 0,
        };

        //启用混合(默认不会启用)
        GLCall(glEnable(GL_BLEND));
        /**
         * glBlendFunc(src, dest) 指定颜色因子
         * src 指定输出颜色(RGBA)因子的计算方式, 默认为GL_ONE
         * dest 指定目标颜色因子的计算方式, 默认为GL_ZERO
         * GL_SRC_ALPHA 因为src的alpha为0, GL_ONE_MINUS_SRC_ALPHA 1-src.alpha
         * RGBA = Srgba * GL_SRC_ALPHA + Drgba * GL_ONE_MINUS_SRC_ALPHA
         **/
        GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
        GLCall(glBlendEquation(GL_FUNC_ADD));

        VertexArray va;
        //4个顶点,每个顶点有4个浮点数
        VertexBuffer vb(positions, 4 * 4 * sizeof(float));
        VertexBufferLayout layout;
        layout.Push<float>(2);
        layout.Push<float>(2);
        va.AddBuffer(vb, layout);

        IndexBuffer ib(indices, 6);

        //我们创建的窗口是960 x 540,是16:9的窗口,而图片宽高是1:1,
        //直接加载图片会有拉伸,定义4x4投影矩阵来修正,生成一个正交矩阵
        //定义视口的左、右、上、下的边界,近平面和远平面的距离
        glm::mat4 proj = glm::ortho(0.0f, 960.0f, 0.0f, 540.0f, -1.0f, 1.0f);
        //观察矩阵,创建一个单位矩阵并乘以(-100, 0, 0),模拟摄像机右移
        glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(-100, 0, 0));
        
        Shader shader("res/shaders/Basic.shader");
        shader.Bind();

        Texture texture("res/textures/ChernoLogo.png");
        texture.Bind();
        //纹理绑定到插槽0
        shader.SetUniform1i("u_Texture", 0);
        
        va.Unbind();
        vb.Unbind();
        ib.Unbind();
        shader.Unbind();

        Renderer renderer;

        //创建上下文环境,初始化
        ImGui::CreateContext();
        ImGui_ImplGlfw_InitForOpenGL(window, true);
        ImGui::StyleColorsDark();

        //需要指定GLSL版本, 也就是shader中的version
        const char* glsl_version = "#version 330";
        ImGui_ImplOpenGL3_Init(glsl_version);

        glm::vec3 translation(200, 200, 0);

        while (!glfwWindowShouldClose(window))
        {
    
    
            renderer.Clear();

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

            //每帧重新计算mvp矩阵
            glm::mat4 model = glm::translate(glm::mat4(1.0f), translation);
            glm::mat4 mvp = proj * view * model;

            renderer.Draw(va, ib, shader);
            shader.SetUniformMat4f("u_MVP", mvp);

            //通过滑块控制图片位置
            {
    
    
                //translation.x表示第一个变量的内存地址
                ImGui::SliderFloat3("Translation", &translation.x, 0.0f, 960.0f);
                ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
            }

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

            glfwSwapBuffers(window);
            glfwPollEvents();
        }

        //作用域结束后会调用所有析构函数
    }

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    glfwTerminate();
    return 0;
}

效果
在这里插入图片描述

批量渲染对象

方法1:绘制前修改mvp矩阵,调整icon的位置,再调用一次glDrawElements命令,这样每个icon就是一个drawcall,消耗大。
Application.cpp

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Renderer.h"
#include "VertexBufferLayout.h"
#include "Texture.h"

#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"

#include "imgui/imgui.h"
#include "imgui/imgui_impl_glfw.h"
#include "imgui/imgui_impl_opengl3.h"

int main(void)
{
    
    
    GLFWwindow* window;

    if (!glfwInit())
        return -1;

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

    window = glfwCreateWindow(960, 540, "Hello World", NULL, NULL);
    if (!window)
    {
    
    
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);

    glfwSwapInterval(1);

    GLenum err = glewInit();
    if (GLEW_OK != err)
        std::cout << err << std::endl;
    std::cout << glGetString(GL_VERSION) << std::endl;

    //添加一个作用域,作用域结束时,会调用VertexBuffer和IndexBuffer的析构函数,
    //不添加作用域的话,需要在glfwTerminate之前显式删除两个缓冲区,
    //因为glfwTerminate会破坏OpenGL上下文,导致glGetError返回一个错误
    {
    
    
        //每个顶点添加纹理坐标,如(100.0f, 100.0f)这个点在左下角
        float positions[] = {
    
    
            -50.0f, -50.0f, 0.0f, 0.0f,//0
             50.0f, -50.0f, 1.0f, 0.0f,//1
             50.0f,  50.0f, 1.0f, 1.0f,//2
            -50.0f,  50.0f, 0.0f, 1.0f,//3
        };

        unsigned int indices[] = {
    
    
            0, 1, 2,
            2, 3, 0,
        };

        //启用混合(默认不会启用)
        GLCall(glEnable(GL_BLEND));
        /**
         * glBlendFunc(src, dest) 指定颜色因子
         * src 指定输出颜色(RGBA)因子的计算方式, 默认为GL_ONE
         * dest 指定目标颜色因子的计算方式, 默认为GL_ZERO
         * GL_SRC_ALPHA 因为src的alpha为0, GL_ONE_MINUS_SRC_ALPHA 1-src.alpha
         * RGBA = Srgba * GL_SRC_ALPHA + Drgba * GL_ONE_MINUS_SRC_ALPHA
         **/
        GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
        GLCall(glBlendEquation(GL_FUNC_ADD));

        VertexArray va;
        //4个顶点,每个顶点有4个浮点数
        VertexBuffer vb(positions, 4 * 4 * sizeof(float));
        VertexBufferLayout layout;
        layout.Push<float>(2);
        layout.Push<float>(2);
        va.AddBuffer(vb, layout);

        IndexBuffer ib(indices, 6);

        //我们创建的窗口是960 x 540,是16:9的窗口,而图片宽高是1:1,
        //直接加载图片会有拉伸,定义4x4投影矩阵来修正,生成一个正交矩阵
        //定义视口的左、右、上、下的边界,近平面和远平面的距离
        glm::mat4 proj = glm::ortho(0.0f, 960.0f, 0.0f, 540.0f, -1.0f, 1.0f);
        //观察矩阵,创建一个单位矩阵并乘以(-100, 0, 0),模拟摄像机右移
        glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, 0));
        
        Shader shader("res/shaders/Basic.shader");
        shader.Bind();

        Texture texture("res/textures/ChernoLogo.png");
        texture.Bind();
        //纹理绑定到插槽0
        shader.SetUniform1i("u_Texture", 0);
        
        va.Unbind();
        vb.Unbind();
        ib.Unbind();
        shader.Unbind();

        Renderer renderer;

        //创建上下文环境,初始化
        ImGui::CreateContext();
        ImGui_ImplGlfw_InitForOpenGL(window, true);
        ImGui::StyleColorsDark();

        //需要指定GLSL版本, 也就是shader中的version
        const char* glsl_version = "#version 330";
        ImGui_ImplOpenGL3_Init(glsl_version);

        //两个icon位置
        glm::vec3 translationA(200, 200, 0);
        glm::vec3 translationB(400, 200, 0);

        while (!glfwWindowShouldClose(window))
        {
    
    
            renderer.Clear();

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

            //绘制第一个icon
            {
    
    
                glm::mat4 model = glm::translate(glm::mat4(1.0f), translationA);
                glm::mat4 mvp = proj * view * model;
                renderer.Draw(va, ib, shader);
                shader.SetUniformMat4f("u_MVP", mvp);
            }

            //修改mvp矩阵,绘制第二个icon
            {
    
    
                glm::mat4 model = glm::translate(glm::mat4(1.0f), translationB);
                glm::mat4 mvp = proj * view * model;
                renderer.Draw(va, ib, shader);
                shader.SetUniformMat4f("u_MVP", mvp);
            }
            
            //通过滑块控制图片位置
            {
    
    
                //translation.x表示第一个变量的内存地址
                ImGui::SliderFloat3("Translation A", &translationA.x, 0.0f, 960.0f);
                ImGui::SliderFloat3("Translation B", &translationB.x, 0.0f, 960.0f);
                ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
            }

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

            glfwSwapBuffers(window);
            glfwPollEvents();
        }

        //作用域结束后会调用所有析构函数
    }

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    glfwTerminate();
    return 0;
}

在这里插入图片描述

方法2:批处理,把所有的贴图都塞进一个顶点缓冲区里,改变其中的顶点位置,在一个单独的drawcall中绘制,后面详细讲。

猜你喜欢

转载自blog.csdn.net/sinat_34014668/article/details/127188495
今日推荐