目录
由于代码不好抽离,所有的代码最终我会上传至百度网盘,附上连接,需要的可以对照的看文章,包括前面所有的学习笔记的,代码都有详细中文注释。Application为就main函数,ApplicationNew为新main函数,如有其他疑问,可以留言评论。
链接:https://pan.baidu.com/s/1gmosgzliBi4eqL22vmP1Cw?pwd=o2wg 提取码:o2wg
一、引入imgui库
二、制作一个可操控的模型变换矩阵的demo
三、测试框架
四、利用测试框架写一个颜色调试界面
编辑
五、利用测试框架写一个纹理调试界面
话不多说,我把我看的视频链接贴出来,下面的笔记是由视频学习和自己的补充而来。这次是(22-26)的笔记
跟着这个小哥的教学视频学的(YouTube原视频,科学上网AI字幕) ► http://bit.ly/2lt7ccM
这个是哔哩哔哩网站有人搬运的 ►https://www.bilibili.com/video/BV1MJ411u7Bc/?share_source=copy_web&vd_source=80ce9fa9cc5a33fdc2b9a467859dd047
现在我们来做一个可视化的调试工具,方便我们动态的创建调试更改我们的OpenGL
https://github.com/ocornut/imgui
引入开源imgui,然后按照例子学一个demo出来,可视化的界面就显示出来了。
我下载的是1.6.0版本的imgui,然后下载源码,把需要的的头文件,源文件拷贝到项目源代码的一个文件夹下,我的是:xxxxxx项目文件夹\src\vender\imgui
然后记得把main排除在项目外
我们可以通过这个main查看imgui这个demo是怎么使用实现的,我大致抽离了一下再OpenGL中imgui 的使用流程如下:
// Setup ImGui binding
ImGui::CreateContext();
ImGui_ImplGlfwGL3_Init(window, true);
ImGui::StyleColorsDark();
ImGui_ImplGlfwGL3_NewFrame();
ImGui::Render();
ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
// Cleanup
ImGui_ImplGlfwGL3_Shutdown();
ImGui::DestroyContext();
然后我们根据demo做一个可以操控的模型变换矩阵的视图
// Setup ImGui binding
ImGui::CreateContext();
ImGui_ImplGlfwGL3_Init(window, true);
ImGui::StyleColorsDark();
glm::vec3 translationA(200, 200, 0);
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
//glClear(GL_COLOR_BUFFER_BIT);
renderer.Clear();
ImGui_ImplGlfwGL3_NewFrame();
texture.Bind(0);
{
glm::mat4 model = glm::translate(glm::mat4(1.0f), translationA);
glm::mat4 mvp = proj * view * model;
shader.Bind();
shader.SetUniformMat4f("u_MVP", mvp);
shader.SetUniform1i("u_Texture", 0);
//我们可以在每次画之前从cpu更改uniform变量的值然后传入,这样就可以有变化的效果了
//shader.SetUniform4f("u_Color", r, 0.3f, 0.8f, 1.0f);
//当我们使用索引缓冲区之后,我们就不是DrawArrays了,而是DrawElement了
//GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));
renderer.Draw(va, ib, shader);
}
{
// Edit 1 float using a slider from 0.0f to 1.0f
ImGui::SliderFloat3("translationB", &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_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
ImGui_ImplGlfwGL3_Shutdown();
ImGui::DestroyContext();
glfwTerminate();
效果如下gif
现在我们如果要渲染另一种东西,也就是比如着色器动态化,顶点动态化,顶点索引都变化了的情况,怎么做到呢,就拿我们要在屏幕的不同位置画两个一样的图标为例,无非就是再画一个。
// Setup ImGui binding
ImGui::CreateContext();
ImGui_ImplGlfwGL3_Init(window, true);
ImGui::StyleColorsDark();
glm::vec3 translationA(200, 200, 0);
glm::vec3 translationB(400, 200, 0);
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
//glClear(GL_COLOR_BUFFER_BIT);
renderer.Clear();
ImGui_ImplGlfwGL3_NewFrame();
texture.Bind(0);
{
glm::mat4 model = glm::translate(glm::mat4(1.0f), translationA);
glm::mat4 mvp = proj * view * model;
shader.Bind();
shader.SetUniformMat4f("u_MVP", mvp);
shader.SetUniform1i("u_Texture", 0);
//我们可以在每次画之前从cpu更改uniform变量的值然后传入,这样就可以有变化的效果了
//shader.SetUniform4f("u_Color", r, 0.3f, 0.8f, 1.0f);
//当我们使用索引缓冲区之后,我们就不是DrawArrays了,而是DrawElement了
//GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));
renderer.Draw(va, ib, shader);
}
{
glm::mat4 model = glm::translate(glm::mat4(1.0f), translationB);
glm::mat4 mvp = proj * view * model;
shader.Bind();
shader.SetUniformMat4f("u_MVP", mvp);
shader.SetUniform1i("u_Texture", 0);
//我们可以在每次画之前从cpu更改uniform变量的值然后传入,这样就可以有变化的效果了
//shader.SetUniform4f("u_Color", r, 0.3f, 0.8f, 1.0f);
//当我们使用索引缓冲区之后,我们就不是DrawArrays了,而是DrawElement了
//GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));
renderer.Draw(va, ib, shader);
}
{
// Edit 1 float using a slider from 0.0f to 1.0f
ImGui::SliderFloat3("translationA", &translationA.x , 0.0f, 960.0f);
// Edit 1 float using a slider from 0.0f to 1.0f
ImGui::SliderFloat3("translationB", &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_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
ImGui_ImplGlfwGL3_Shutdown();
ImGui::DestroyContext();
glfwTerminate();
效果如下:
但是这样如果要画满屏幕呢,每次画一个就要渲染一次,
想象我们现在要画一个2D的游戏地图,都是由一样的方块组成,我们如果按照模型矩阵的方式去画,那就要画一千次,那是相当慢的,
所以策略不止一次,还有种方法就是我拿到2D地图的所有顶点,然后塞到一个顶点缓冲区去,然后用顶点索引缓冲区去找位置,然后再画,这样就会快很多,所以这个例子只是展示可以这样动态改变uniform变量的方法来改变画的位置,需要举一反三,然后深入进去,灵活变通着用。
活用测试框架,搭建你想搭建的所有测试。
#pragma once
#include
#include
#include
#include
//仿照着imgui这个命名空间式的方式创建一种插入式的调试类,我们先写调试类的抽象类
//可以根据这个类来做拓展,可以动态的变化出来许多实例类,然后这些类可以动态的在
//我们渲染图像的时候进行调试,比如图形的颜色啊等等,方便我们可视化我们要达到的效果
namespace test {
class Test {
public:
Test(){}
virtual ~Test() {}
//继承的子类都可重写
//更新虚函数
virtual void OnUpdate(float deltaTime) {}
//渲染器虚函数
virtual void OnRender() {}
//渲染界面虚函数
virtual void OnImGuiRender() {}
};
//创建一个测试菜单类,帮助我们动态创建和选择测试窗口
class TestMenu : public Test {
public:
TestMenu(Test*& CurrentTestPoniter);
void OnImGuiRender() override;
template
void RegisterTest(const std::string& name)
{
std::cout << "RegisterTest Test :" << name << std::endl;
m_Tests.push_back(std::make_pair(name, []() {
return new T();
}));
}
private:
//引用自 Test*& CurrentTestPoniter,就是Test的指针,它是抽象类,所以可以多态成为所有继承它的类
Test*& m_CurrentTest;
std::vector>> m_Tests;
};
}
#include "Test.h"
#include "imgui/imgui.h"
namespace test
{
TestMenu::TestMenu(Test*& CurrentTestPoniter)
: m_CurrentTest(CurrentTestPoniter)
{
}
void TestMenu::OnImGuiRender()
{
for (auto& test : m_Tests)
{
if (ImGui::Button(test.first.c_str()))
m_CurrentTest = test.second();
}
}
}
接下来我们利用调试框架写一个颜色调试和纹理调试类
类如下:
#include "Test.h"
namespace test {
class TestClearColor : public Test
{
public:
TestClearColor() ;
~TestClearColor() ;
void OnUpdate(float deltaTime) override;
void OnRender() override;
void OnImGuiRender() override;
private:
float m_ClearColor[4];
};
#include "TestClearColor.h"
#include "GL/glew.h"
#include "Renderer.h"
#include "imgui/imgui.h"
namespace test {
TestClearColor::TestClearColor()
:m_ClearColor{ 0.2f, 0.3f, 0.8f, 1.0f }
{
}
TestClearColor::~TestClearColor()
{
}
void TestClearColor::OnUpdate(float deltaTime)
{
}
void TestClearColor::OnRender()
{
GLCall(glClearColor(m_ClearColor[0], m_ClearColor[1], m_ClearColor[2], m_ClearColor[3]))
GLCall(glClear(GL_COLOR_BUFFER_BIT));
}
void TestClearColor::OnImGuiRender()
{
ImGui::ColorEdit4("Clear_Color", m_ClearColor);
}
}
如何调用:
#include
#include
#include
#include
#include
#include "Renderer.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include "IndexBuffer.h"
#include "VertexArray.h"
#include "Shader.h"
#include "Texture.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "imgui/imgui.h"
#include "imgui/imgui_impl_glfw_gl3.h"
#include "tests/TestClearColor.h"
int main()
{
GLFWwindow* window;
/* Initialize the library */
if (!glfwInit())
return -1;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//if (glewInit() != GLEW_OK)
// std::cout << "GLEWInit ERROR!" << std::endl;
/* Create a windowed mode window and its OpenGL context */
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
//设置一些框架因素
glfwSwapInterval(2);
if (glewInit() != GLEW_OK)
std::cout << "GLEWInit ERROR!" << std::endl;
std::cout << "OpenGL的版本是:" << glGetString(GL_VERSION) << std::endl;
{
//启用混合和透明功能
GLCall(glEnable(GL_BLEND));
//设置混合和透明功能。。。暂不清楚详细情况
GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
//创建一个渲染器实例
Renderer renderer;
// Setup ImGui binding
ImGui::CreateContext();
ImGui_ImplGlfwGL3_Init(window, true);
ImGui::StyleColorsDark();
test::TestClearColor testClearColor;
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
GLCall(glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
//glClear(GL_COLOR_BUFFER_BIT);
renderer.Clear();
testClearColor.OnUpdate(0.0f);
testClearColor.OnRender();
ImGui_ImplGlfwGL3_NewFrame();
testClearColor.OnImGuiRender();
ImGui::Render();
ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
delete currenttest;
if (currenttest != testMenu)
delete testMenu;
//glDeleteProgram(shaderID);
}
// Cleanup
ImGui_ImplGlfwGL3_Shutdown();
ImGui::DestroyContext();
glfwTerminate();
return 0;
}
#pragma once
#include "Test.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include "Texture.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include
namespace test {
class TestTexture2D : public Test
{
public:
TestTexture2D() ;
~TestTexture2D() ;
void OnUpdate(float deltaTime) override;
void OnRender() override;
void OnImGuiRender() override;
private:
//这里使用智能指针的原因,就是自动管理生命周期,防止在调用中的内存泄漏问题
std::unique_ptr m_VAO;
std::unique_ptr m_IndexBuffer;
std::unique_ptr m_VertexBuffer;
std::unique_ptr m_VertexBufferLayout;
std::unique_ptr m_Shader;
std::unique_ptr m_Texture;
glm::mat4 m_proj;
glm::mat4 m_model;
glm::mat4 m_view;
glm::vec3 m_TranslationA;
glm::vec3 m_TranslationB;
};
}
#include "TestTexture2D.h"
#include "GL/glew.h"
#include "Renderer.h"
#include "imgui/imgui.h"
namespace test {
TestTexture2D::TestTexture2D()
:m_proj(glm::ortho(0.0f, 960.0f, 0.0f, 540.0f, -1.0f, 1.0f)),
m_view(glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, 0))),
m_TranslationA(200, 200, 0), m_TranslationB(400, 200, 0)
{
float positions[] = {
/*顶点坐标*/-50.0f, -50.0f,
/*这个纹理坐标就是告诉着色器,
我们的图像需要的纹理应该对应关系,
比如我一个矩形的左下角-0.5f, -0.5f,
对应纹理的左下角, 0.0f, -0.0f,
因为纹理是0,0坐标原点*/ 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));
//设置混合和透明功能。。。暂不清楚详细情况
GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
//通过封装的顶点阵列类来完成绑定缓冲区以及设置顶点布局
m_VAO = std::make_unique();
//通过封装好的VertexBuffer类进行上述操作
m_VertexBuffer = std::make_unique(positions, 4 * 4 * sizeof(float));
//声明一个布局对象,然后写入布局的参数,操作顶点阵列类中的AddBuffer,完成自动绑定操作
m_VertexBufferLayout = std::make_unique();
m_VertexBufferLayout->Push(2);
//再加入的两组数是用来传纹理坐标的
m_VertexBufferLayout->Push(2);
m_VAO->AddBuffer(*m_VertexBuffer, *m_VertexBufferLayout);
//通过封装好的IndexBuffer类进行上述操作
m_IndexBuffer = std::make_unique(indices, 6);
//通过封装好的着色器类来创建着色器
m_Shader = std::make_unique("res/shaders/Basic.shader");
//通过封装好的纹理类创建纹理对象
m_Texture = std::make_unique("res/textures/OpenGL-removebg-preview.png");
m_Shader->SetUniform1i("u_Texture", 0);
}
TestTexture2D::~TestTexture2D()
{
}
void TestTexture2D::OnUpdate(float deltaTime)
{
}
void TestTexture2D::OnRender()
{
GLCall(glClearColor(0.0f, 0.0f, 0.0f, 0.0f))
GLCall(glClear(GL_COLOR_BUFFER_BIT));
Renderer renderer;
//绑定纹理槽(物理意义的槽)
m_Texture->Bind(0);
{
m_model = glm::translate(glm::mat4(1.0f), m_TranslationA);
glm::mat4 mvp = m_proj * m_view * m_model;
m_Shader->Bind();
m_Shader->SetUniformMat4f("u_MVP", mvp);
//我们可以在每次画之前从cpu更改uniform变量的值然后传入,这样就可以有变化的效果了
//shader.SetUniform4f("u_Color", r, 0.3f, 0.8f, 1.0f);
//当我们使用索引缓冲区之后,我们就不是DrawArrays了,而是DrawElement了
//GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));
renderer.Draw(*m_VAO, *m_IndexBuffer, *m_Shader);
}
{
m_model = glm::translate(glm::mat4(1.0f), m_TranslationB);
glm::mat4 mvp = m_proj * m_view * m_model;
m_Shader->Bind();
m_Shader->SetUniformMat4f("u_MVP", mvp);
//我们可以在每次画之前从cpu更改uniform变量的值然后传入,这样就可以有变化的效果了
//shader.SetUniform4f("u_Color", r, 0.3f, 0.8f, 1.0f);
//当我们使用索引缓冲区之后,我们就不是DrawArrays了,而是DrawElement了
//GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));
renderer.Draw(*m_VAO, *m_IndexBuffer, *m_Shader);
}
}
void TestTexture2D::OnImGuiRender()
{
ImGui::SliderFloat3("translationA", &m_TranslationA.x, 0.0f, 960.0f); // Edit 1 float using a slider from 0.0f to 1.0f
ImGui::SliderFloat3("translationB", &m_TranslationB.x, 0.0f, 960.0f); // Edit 1 float using a slider from 0.0f to 1.0f
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
}
}
如何调用:
#include
#include
#include
#include
#include
#include "Renderer.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include "IndexBuffer.h"
#include "VertexArray.h"
#include "Shader.h"
#include "Texture.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "imgui/imgui.h"
#include "imgui/imgui_impl_glfw_gl3.h"
#include "tests/TestClearColor.h"
#include "tests/TestTexture2D.h"
int main()
{
//std::cout << "Hello OpenGL" << std::endl;
//std::cin.get();
GLFWwindow* window;
/* Initialize the library */
if (!glfwInit())
return -1;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//if (glewInit() != GLEW_OK)
// std::cout << "GLEWInit ERROR!" << std::endl;
/* Create a windowed mode window and its OpenGL context */
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
//设置一些框架因素
glfwSwapInterval(2);
if (glewInit() != GLEW_OK)
std::cout << "GLEWInit ERROR!" << std::endl;
std::cout << "OpenGL的版本是:" << glGetString(GL_VERSION) << std::endl;
{
//启用混合和透明功能
GLCall(glEnable(GL_BLEND));
//设置混合和透明功能。。。暂不清楚详细情况
GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
//创建一个渲染器实例
Renderer renderer;
// Setup ImGui binding
ImGui::CreateContext();
ImGui_ImplGlfwGL3_Init(window, true);
ImGui::StyleColorsDark();
//作为抽象类的指针,可以接收所有的子类的对象指针
test::Test* currenttest = nullptr;
test::TestMenu* testMenu = new test::TestMenu(currenttest);
currenttest = testMenu;
testMenu->RegisterTest("Clear Color");
testMenu->RegisterTest("TestTexture2D");
生成一个testClearColor类
//test::TestClearColor testClearColor;
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
GLCall(glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
//glClear(GL_COLOR_BUFFER_BIT);
renderer.Clear();
//testClearColor.OnUpdate(0.0f);
//testClearColor.OnRender();
ImGui_ImplGlfwGL3_NewFrame();
if (currenttest)
{
currenttest->OnUpdate(0.0f);
currenttest->OnRender();
ImGui::Begin("Test");
//因为一直循环,所以当执行下面if的时候,如果currenttest != testMenu
//则会继续执行ImGui::Button("<-"),就相当于把按钮变为返回按键了。
//这个时候如果你在按下Button("<-"),那么这个if才是成立的,才会把菜单testMenu赋值回来
if (currenttest != testMenu && ImGui::Button("<-"))
{
delete currenttest;
currenttest = testMenu;
}
//当这里渲染测试界面的时候,作为菜单类先进入TestMenu::OnImGuiRender()
//找到对应的注册好的按钮对应的类,然后把类赋值给m_CurrentTest
//这个时候currenttest就是指向m_CurrentTest实例类的指针了。
currenttest->OnImGuiRender();
ImGui::End();
}
//testClearColor.OnImGuiRender();
ImGui::Render();
ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
delete currenttest;
if (currenttest != testMenu)
delete testMenu;
//glDeleteProgram(shaderID);
}
// Cleanup
ImGui_ImplGlfwGL3_Shutdown();
ImGui::DestroyContext();
glfwTerminate();
return 0;
}
之后我们就要接触批量渲染,材质,3D等等由浅入深的内容了