OpenGL 7. Test framework, batch rendering

Test function base class

This section builds a simple test framework to display a menu bar on the window and click on different options to enter different functions.
insert image description here
Add src to the additional directory to facilitate adding header files.
The new directory is as follows:
insert image description here

Test base class Test.h, test menu TestMenu, used to manage all tests

#pragma once

#include <functional>
#include <vector>
#include <string>
#include <iostream>

namespace test {
    
    
	class Test
	{
    
    
	public:
		Test(){
    
    }
		virtual ~Test(){
    
    }

		virtual void OnUpdate(float deltaTime){
    
    }
		virtual void OnRender(){
    
    }
		/// <summary>
		/// 使用ImGui绘制UI
		/// </summary>
		virtual void OnImGuiRender(){
    
    }
	};

	/// <summary>
	/// 包含所有测试的集合
	/// </summary>
	class TestMenu : public Test
	{
    
    
	public:
		TestMenu(Test*& currentTestPointer);

		void OnImGuiRender() override;

		template<typename T>
		void RegisterTest(const std::string& name) 
		{
    
    
			std::cout << "Registering test " << name << std::endl;
			//lambda函数
			m_Test.push_back(std::make_pair(name, []() {
    
     return new T(); }));
		}

	private:
		//当前测试,指针的引用
		Test*& m_CurrentTest;
		//测试名字,测试指针的函数
		std::vector<std::pair<std::string, std::function<Test*()>>> m_Test;
	};
}

Test.cpp

#include "Test.h"
#include "ImGui/imgui.h"

namespace test {
    
    

	TestMenu::TestMenu(Test*& currentTestPointer)
		: m_CurrentTest(currentTestPointer)
	{
    
    

	}

	void TestMenu::OnImGuiRender()
	{
    
    
		for (auto& test : m_Test) {
    
    
			if (ImGui::Button(test.first.c_str()))
				m_CurrentTest = test.second();
		}
	}

}

Menu Item – Modify Window Background Color

Clear color, here refers to modifying the background color TestClearColor.h

#pragma once

#include "Test.h"

namespace test {
    
    
	/// <summary>
	/// 清除颜色: 指的是用指定颜色填充背景色
	/// </summary>
	class TestClearColor : public Test
	{
    
    
	public:
		TestClearColor();
		~TestClearColor();

		void OnUpdate(float deltaTime) override;
		void OnRender() override;
		void OnImGuiRender() override;

	private:
		float m_ClearColor[4];
	};
}

TestClearColor.cpp

#include "TestClearColor.h"
#include "Renderer.h"
#include "imgui/imgui.h"

namespace test {
    
    
	TestClearColor::TestClearColor()
		:m_ClearColor{
    
     0.2f, 0.3f, 0.8f, 1.0f }
	{
    
    
	}

	TestClearColor::~TestClearColor()
	{
    
    
		GLCall(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
	}

	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("ClearColor", m_ClearColor);
	}
}

Menu Item – Render Textures

This part moves the previously written rendering texture code into a separate class for easy management
of TestTexture2D.h

#pragma once

#include "Test.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include "Texture.h"
#include <memory>

namespace test {
    
    
	/// <summary>
	/// 渲染图片
	/// </summary>
	class TestTexture2D : public Test
	{
    
    
	public:
		TestTexture2D();
		~TestTexture2D();

		void OnUpdate(float deltaTime) override;
		void OnRender() override;
		void OnImGuiRender() override;

	private:
		//unique_ptr持有对对象的独有权,即两个unique_ptr不能指向一个对象,不能进行复制操作只能进行移动操作
		std::unique_ptr<VertexArray> m_VAO;
		std::unique_ptr<VertexBuffer> m_VertexBuffer;
		std::unique_ptr<IndexBuffer> m_IndexBuffer;
		std::unique_ptr<Shader> m_Shader;
		std::unique_ptr<Texture> m_Texture;
		glm::mat4 m_Proj, m_View;
		glm::vec3 m_TranslationA, m_TranslationB;
	};
}

TestTexture2D.cpp

#include "TestTexture2D.h"
#include "Renderer.h"

#include "imgui/imgui.h"

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

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(-100, 0, 0))),
        m_TranslationA(200, 200, 0), m_TranslationB(400, 200, 0)
	{
    
    
        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));
        
        m_VAO = std::make_unique<VertexArray>();
        //4个顶点,每个顶点有4个浮点数
        m_VertexBuffer = std::make_unique<VertexBuffer>(positions, 4 * 4 * sizeof(float));
        VertexBufferLayout layout;
        layout.Push<float>(2);
        layout.Push<float>(2);
        m_VAO->AddBuffer(*m_VertexBuffer, layout);

        m_IndexBuffer = std::make_unique<IndexBuffer>(indices, 6);

        m_Shader = std::make_unique<Shader>(("res/shaders/Basic.shader"));
        m_Shader->Bind();

        m_Texture = std::make_unique<Texture>("res/textures/ChernoLogo.png");
        //纹理绑定到插槽0
        m_Shader->SetUniform1i("u_Texture", 0);
	}

	TestTexture2D::~TestTexture2D()
	{
    
    
		GLCall(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
	}

	void TestTexture2D::OnUpdate(float deltaTime)
	{
    
    
	}

	void TestTexture2D::OnRender()
	{
    
    
		GLCall(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
		GLCall(glClear(GL_COLOR_BUFFER_BIT));

        Renderer renderer;
        m_Texture->Bind();

        //绘制第一个icon
        {
    
    
            glm::mat4 model = glm::translate(glm::mat4(1.0f), m_TranslationA);
            glm::mat4 mvp = m_Proj * m_View * model;
            renderer.Draw(*m_VAO, *m_IndexBuffer, *m_Shader);
            m_Shader->SetUniformMat4f("u_MVP", mvp);
        }

        //修改mvp矩阵,绘制第二个icon
        {
    
    
            glm::mat4 model = glm::translate(glm::mat4(1.0f), m_TranslationB);
            glm::mat4 mvp = m_Proj * m_View * model;
            renderer.Draw(*m_VAO, *m_IndexBuffer, *m_Shader);
            m_Shader->SetUniformMat4f("u_MVP", mvp);
        }
	}

	void TestTexture2D::OnImGuiRender()
	{
    
    
        ImGui::SliderFloat3("Translation A", &m_TranslationA.x, 0.0f, 960.0f);
        ImGui::SliderFloat3("Translation B", &m_TranslationB.x, 0.0f, 960.0f);
        ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
	}
}

Menu Item – Batch Rendering

Batch rendering refers to putting the vertices of all graphics into the same vertex buffer, and correspondingly expanding the index buffer so that it is only drawn once.
insert image description here
For example, to draw two squares, if you don’t use batch rendering
Vertex buffer = { position0, position1, position2, position3 }
Index buffer = { 0, 1, 2, 2, 3, 0 }
Call glDrawElements once to render a square and
insert image description here
use batch rendering
Vertex buffer = { position0, position1, position2, position3, position4, position5, position6, position7 }
Index buffer = { 0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4 }
One call to glDrawElements renders two squares

Below is the code to implement
TestBatchRender.h

#pragma once

#include "Test.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include "Texture.h"
#include <memory>

namespace test {
    
    
	/// <summary>
	/// 批渲染
	/// </summary>
	class TestBatchRender : public Test
	{
    
    
	public:
		TestBatchRender();
		~TestBatchRender();

		void OnUpdate(float deltaTime) override;
		void OnRender() override;
		void OnImGuiRender() override;

	private:
		//unique_ptr持有对对象的独有权,即两个unique_ptr不能指向一个对象,不能进行复制操作只能进行移动操作
		std::unique_ptr<VertexArray> m_VAO;
		std::unique_ptr<VertexBuffer> m_VertexBuffer;
		std::unique_ptr<IndexBuffer> m_IndexBuffer;
		std::unique_ptr<Shader> m_Shader;
		std::unique_ptr<Texture> m_Textures[2];
		glm::mat4 m_Proj, m_View;
		glm::vec3 m_Translation;
		//控制第一个四边形左下角位置
		float m_QuadPosition[2] = {
    
     100, 100 };
	};
}

TestBatchRender.cpp

#include "TestBatchRender.h"
#include "Renderer.h"
#include <array>

#include "imgui/imgui.h"

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

namespace test {
    
    

    struct Vec2
    {
    
    
        float x, y;
    };

    struct Vec3
    {
    
    
        float x, y, z;
    };

    struct Vec4
    {
    
    
        float x, y, z, w;
    };

    /// <summary>
    /// 顶点
    /// </summary>
    struct Vertex
    {
    
    
        Vec3 Position;
        Vec4 Color;
        Vec2 TexCoords; //纹理坐标
        float TexID;    //第几个插槽 
    };

    //最多四边形数量
    const unsigned int MaxQuadCount = 1000;
    //最大顶点数量
    const unsigned int MaxVertexCount = MaxQuadCount * 4;
    //最大索引缓冲区的数量
    const unsigned int MaxIndexCount = MaxQuadCount * 6;

    /// <summary>
    /// 创建四边形
    /// </summary>
    static Vertex* CreateQuad(Vertex* target, float x, float y, float textureID)
    {
    
    
        //边长
        float size = 100.0f;

        //以左下角是四边形原点
        target->Position = {
    
     x, y, 0.0f };
        target->Color = {
    
     0.2f, 0.6f, 1.0f, 1.0f };
        target->TexCoords = {
    
     0.0f, 0.0f };
        target->TexID = textureID;
        target++;

        target->Position = {
    
     x + size, y, 0.0f };
        target->Color = {
    
     0.2f, 0.6f, 1.0f, 1.0f };
        target->TexCoords = {
    
     1.0f, 0.0f };
        target->TexID = textureID;
        target++;

        target->Position = {
    
     x + size, y + size, 0.0f };
        target->Color = {
    
     0.2f, 0.6f, 1.0f, 1.0f };
        target->TexCoords = {
    
     1.0f, 1.0f };
        target->TexID = textureID;
        target++;

        target->Position = {
    
     x, y + size, 0.0f };
        target->Color = {
    
     0.2f, 0.6f, 1.0f, 1.0f };
        target->TexCoords = {
    
     0.0f, 1.0f };
        target->TexID = textureID;
        target++;

        return target;
    }

    TestBatchRender::TestBatchRender()
        : 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(-100, 0, 0))),
        m_Translation(0, 0, 0)
	{
    
    
        //float vertices[] = {
    
    
        //    100.0f, 100.0f, 0.0f, 0.2f, 0.6f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
        //    200.0f, 100.0f, 0.0f, 0.2f, 0.6f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
        //    200.0f, 200.0f, 0.0f, 0.2f, 0.6f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
        //    100.0f, 200.0f, 0.0f, 0.2f, 0.6f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f,

        //    300.0f, 100.0f, 0.0f, 1.0f, 0.9f, 0.2f, 1.0f, 0.0f, 0.0f, 1.0f,
        //    400.0f, 100.0f, 0.0f, 1.0f, 0.9f, 0.2f, 1.0f, 1.0f, 0.0f, 1.0f,
        //    400.0f, 200.0f, 0.0f, 1.0f, 0.9f, 0.2f, 1.0f, 1.0f, 1.0f, 1.0f,
        //    300.0f, 200.0f, 0.0f, 1.0f, 0.9f, 0.2f, 1.0f, 0.0f, 1.0f, 1.0f,
        //};

        //unsigned int indices[] = {
    
    
        //    0, 1, 2, 2, 3, 0,
        //    4, 5, 6, 6, 7, 4
        //};

        unsigned int indices[MaxIndexCount];
        unsigned int offset = 0;
        for (unsigned int i = 0; i < MaxIndexCount; i += 6)
        {
    
    
            indices[i + 0] = 0 + offset;
            indices[i + 1] = 1 + offset;
            indices[i + 2] = 2 + offset;

            indices[i + 3] = 2 + offset;
            indices[i + 4] = 3 + offset;
            indices[i + 5] = 0 + offset;

            offset += 4;
        }

        GLCall(glEnable(GL_BLEND));
        GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
        GLCall(glBlendEquation(GL_FUNC_ADD));
        
        m_VAO = std::make_unique<VertexArray>();
        m_VertexBuffer = std::make_unique<VertexBuffer>(nullptr, sizeof(Vertex) * MaxVertexCount);
        VertexBufferLayout layout;
        layout.Push<float>(3); //坐标x y z
        layout.Push<float>(4); //颜色
        layout.Push<float>(2); //纹理坐标
        layout.Push<float>(1); //第几个纹理插槽
        m_VAO->AddBuffer(*m_VertexBuffer, layout);

        m_IndexBuffer = std::make_unique<IndexBuffer>(indices, MaxIndexCount);

        m_Shader = std::make_unique<Shader>(("res/shaders/Batch.shader"));
        m_Shader->Bind();

        m_Textures[0] = std::make_unique<Texture>("res/textures/ChernoLogo.png");
        m_Textures[1] = std::make_unique<Texture>("res/textures/HazelLogo.png");
        //纹理绑定到两个插槽上
        m_Textures[0]->Bind(0);
        m_Textures[1]->Bind(1);

        int samplers[2] = {
    
     0, 1 };
        m_Shader->SetUniform1iv("u_Textures", 2, samplers);
	}

    TestBatchRender::~TestBatchRender()
	{
    
    
		GLCall(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
        GLCall(glBufferSubData(GL_ARRAY_BUFFER, 0, 0, nullptr));
        GLCall(glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW));
	}

	void TestBatchRender::OnUpdate(float deltaTime)
	{
    
    
        std::array<Vertex, MaxVertexCount> vertices;
        Vertex* buffer = vertices.data();
        for (int y = 0; y < 500; y += 100) {
    
    
            for (int x = 300; x < 800; x += 100) {
    
    
                buffer = CreateQuad(buffer, x, y, (x + y) / 100 % 2);
            }
        }

        //创建一个可以移动的四边形
        buffer = CreateQuad(buffer, m_QuadPosition[0], m_QuadPosition[1], 0.0f);
        //每一帧,动态更新缓冲区数据
        glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(Vertex), vertices.data());
	}

	void TestBatchRender::OnRender()
	{
    
    
		GLCall(glClearColor(0.6f, 1.0f, 1.0f, 1.0f));
		GLCall(glClear(GL_COLOR_BUFFER_BIT));

        Renderer renderer;

        glm::mat4 model = glm::translate(glm::mat4(1.0f), m_Translation);
        glm::mat4 mvp = m_Proj * m_View * model;
        renderer.Draw(*m_VAO, *m_IndexBuffer, *m_Shader);
        m_Shader->SetUniformMat4f("u_MVP", mvp);
	}

	void TestBatchRender::OnImGuiRender()
	{
    
    
        ImGui::Begin("Controls");
        ImGui::DragFloat2("Quad Position", m_QuadPosition, 0.1f);
        ImGui::End();
	}
}

In order to support dynamic modification of the vertex buffer, the buffer type is changed to dynamic
VertexBuffer.cpp

VertexBuffer::VertexBuffer(const void* data, unsigned int size)
{
    
    
    GLCall(glGenBuffers(1, &m_RendererID));
    GLCall(glBindBuffer(GL_ARRAY_BUFFER, m_RendererID));
    //可以动态填充顶点缓冲区
    GLCall(glBufferData(GL_ARRAY_BUFFER, size, data, GL_DYNAMIC_DRAW));
}

In order to support batch rendering, add a new shader
Batch.shader

#shader vertex
#version 330 core

layout(location = 0) in vec4 a_Position;  //因为gl_Position是vec4,所有这里转换
layout(location = 1) in vec4 a_Color;     //顶点颜色
layout(location = 2) in vec2 a_TexCoord;  //纹理坐标
layout(location = 3) in float a_TexIndex; //第几个纹理

out vec4 v_Color;
out vec2 v_TexCoord;
out float v_TexIndex;

uniform mat4 u_MVP;

void main()
{
    
    
	//正交投影与顶点位置相乘,把它们转换到-1至1空间(NDC空间)
	gl_Position = u_MVP * a_Position;
	v_Color = a_Color;
    v_TexCoord = a_TexCoord;
	v_TexIndex = a_TexIndex;
};

#shader fragment
#version 330 core

layout(location = 0) out vec4 color;

in vec4 v_Color;
in vec2 v_TexCoord;
in float v_TexIndex;

//采样数组
uniform sampler2D u_Textures[2];

void main()
{
    
    
	int index = int(v_TexIndex);
	//从纹理上采样的颜色
	vec4 texColor = texture(u_Textures[index], v_TexCoord);
	color = texColor;
};

Finally the main program
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"

#include "tests/TestClearColor.h"
#include "tests/TestTexture2D.h"
#include "tests/TestBatchRender.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;

    {
    
    
        GLCall(glEnable(GL_BLEND));
        GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
        GLCall(glBlendEquation(GL_FUNC_ADD));

        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);

        test::Test* currentTest = nullptr;
        test::TestMenu* testMenu = new test::TestMenu(currentTest);
        //从菜单开始
        currentTest = testMenu;

        testMenu->RegisterTest<test::TestClearColor>("Clear Color");
        testMenu->RegisterTest<test::TestTexture2D>("2D Texture");
        testMenu->RegisterTest<test::TestBatchRender>("Batch Render");

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

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

            if (currentTest)
            {
    
    
                currentTest->OnUpdate(0.0f);
                currentTest->OnRender();
                ImGui::Begin("Test");
                //当前不在测试菜单,而在某个具体的测试中,上面加个返回箭头
                if (currentTest != testMenu && ImGui::Button("<-"))
                {
    
    
                    delete currentTest;
                    currentTest = testMenu;
                }
                currentTest->OnImGuiRender();
                ImGui::End();
            }

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

            glfwSwapBuffers(window);
            glfwPollEvents();
        }

        delete currentTest;
        if (currentTest != testMenu)
            delete testMenu;
    }

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}

Effect
insert image description here

Reference code: address

Guess you like

Origin blog.csdn.net/sinat_34014668/article/details/127197156