OpenGL 7.测试框架,批渲染

测试功能基类

本节搭建一个简单的测试框架,实现在窗口上显示一个菜单栏,点击不同的选项,进入不同的功能。
在这里插入图片描述
附加目录中添加src,方便添加头文件。
新建目录如下:
在这里插入图片描述

测试基类 Test.h,测试菜单TestMenu,用于管理所有的测试

#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();
		}
	}

}

菜单项 – 修改窗口背景色

清除颜色,这里指修改背景色 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);
	}
}

菜单项 – 渲染贴图

这部分将之前写的渲染贴图的代码移动到单独的类中方便管理
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);
	}
}

菜单项 – 批渲染

批渲染是指将所有图形的顶点都放入同一个顶点缓冲区里,相应的扩大索引缓冲区,从而只绘制一次。
在这里插入图片描述
比如画两个正方形,如果不用批渲染
顶点缓冲区 = { position0, position1, position2, position3 }
索引缓冲区 = { 0, 1, 2, 2, 3, 0 }
渲染一个正方形就调用一次glDrawElements
在这里插入图片描述
使用批渲染
顶点缓冲区 = { position0, position1, position2, position3, position4, position5, position6, position7 }
索引缓冲区 = { 0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4 }
调用一次glDrawElements就可以渲染两个正方形

下面是代码实现
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();
	}
}

为了支持动态修改顶点缓冲区,缓冲区使用类型改为动态
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));
}

为了支持批渲染,新增一个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;
};

最后主程序
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;
}

效果
在这里插入图片描述

参考代码:地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值