将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中绘制,后面详细讲。