Hazel游戏引擎(061)开始2D批渲染

文中若有代码、术语等错误,欢迎指正

前言

  • 目的

    实现OpenGL的批处理渲染,减少OpenGL绘制命令的调用,用一次OpenGL绘制命令,绘制多个图形

  • 大致思路

    CPU和GPU都开辟同样大小的一大块内存(为了存储顶点信息)

    索引在程序运行时生成对应规则后绑定到索引缓冲中

    动态生成顶点信息(现在改成Drawquad只是确定图形顶点的位置)

    然后在Endscene,将CPU的动态生成的顶点数据上传给GPU,然后再绘制出来。

关键代码流程

  • CPU和GPU都开辟同样大小的一大块内存(为了存储顶点信息)

    // 0.在CPU开辟存储s_Data.MaxVertices个的QuadVertex的内存
    s_Data.QuadVertexBufferBase = new QuadVertex[s_Data.MaxVertices];
    
    // 2.创建顶点缓冲区,先在GPU开辟一块s_Data.MaxVertices * sizeof(QuadVertex)大小的内存
    // 与cpu对应大,是为了传输顶点数据
    s_Data.QuadVertexBuffer = VertexBuffer::Create(s_Data.MaxVertices * sizeof(QuadVertex));
    
  • 索引在程序运行时生成对应规则后绑定到索引缓冲中

    (规则就是2个三角形组成的Quad,本来2个三角形共6个顶点,用索引后可以重复利用顶点,从而减少到4个顶点组成一个四方形)

    // 3.索引缓冲
    //uint32_t flatIndices[] = { 0, 1, 2, 2, 3, 0 };
    uint32_t* quadIndices = new uint32_t[s_Data.MaxIndices];
    
    // 一个quad用6个索引,012 230,456 674
    uint32_t offset = 0;
    for (uint32_t i = 0; i < s_Data.MaxIndices; i += 6) {
          
          
        quadIndices[i + 0] = offset + 0;
        quadIndices[i + 1] = offset + 1;
        quadIndices[i + 2] = offset + 2;
    
        quadIndices[i + 3] = offset + 2;
        quadIndices[i + 4] = offset + 3;
        quadIndices[i + 5] = offset + 0;
    
        offset += 4;
    }
    
    Ref<IndexBuffer> flatIB = IndexBuffer::Create(quadIndices, s_Data.MaxIndices);
    
    // 1.2顶点数组设置索引缓冲区
    s_Data.QuadVertexArray->SetIndexBuffer(flatIB);
    // cpu上传到gpu上了可以删除cpu的索引数据块了
    delete[] quadIndices;
    
  • 动态生成顶点信息,主要是位置、纹理坐标

    void Hazel::Renderer2D::DrawQuad(const glm::vec3& position, const glm::vec2& size, const glm::vec4& color)
    {
          
          
        HZ_PROFILE_FUNCTION();
        /
        /
    	/*
    	根据参数position确定当前顶点的位置,应该是基于本地空间
    	注意,并没有计算图形的transform来偏移顶点位置
    	而是手动根据position、size确定一个四方形的四个点的位置
    	*/
        // quad的左下角为起点
        s_Data.QuadVertexBufferPtr->Position = position;
        s_Data.QuadVertexBufferPtr->Color = color;
        s_Data.QuadVertexBufferPtr->TexCoord = {
          
           0.0f, 0.0f };
        s_Data.QuadVertexBufferPtr++;
    
        s_Data.QuadVertexBufferPtr->Position = {
          
           position.x + size.x, position.y, 0.0f };
        s_Data.QuadVertexBufferPtr->Color = color;
        s_Data.QuadVertexBufferPtr->TexCoord = {
          
           1.0f, 0.0f };
        s_Data.QuadVertexBufferPtr++;
    
        s_Data.QuadVertexBufferPtr->Position = {
          
           position.x + size.x, position.y + size.y, 0.0f };
        s_Data.QuadVertexBufferPtr->Color = color;
        s_Data.QuadVertexBufferPtr->TexCoord = {
          
           1.0f, 1.0f };
        s_Data.QuadVertexBufferPtr++;
    
        s_Data.QuadVertexBufferPtr->Position = {
          
           position.x, position.y +size.y , 0.0f };
        s_Data.QuadVertexBufferPtr->Color = color;
        s_Data.QuadVertexBufferPtr->TexCoord = {
          
           0.0f, 1.0f };
        s_Data.QuadVertexBufferPtr++;
    
        s_Data.QuadIndexCount += 6;// 每一个quad用6个索引
    }
    
  • 然后在Endscene,将CPU的动态生成的顶点数据(主要是位置信息)上传给GPU,然后再绘制出来

    void Hazel::Renderer2D::EndScene()
    {
          
          
        HZ_PROFILE_FUNCTION();
    
        // 计算当前绘制需要多少个顶点数据
        uint32_t dataSize = (uint8_t*)s_Data.QuadVertexBufferPtr - (uint8_t*)s_Data.QuadVertexBufferBase;
        // 截取部分CPU的顶点数据上传OpenGL
        s_Data.QuadVertexBuffer->SetData(s_Data.QuadVertexBufferBase, dataSize);
    
        Flush();
    }
    
    void Renderer2D::Flush()
    {
          
          
        RenderCommand::DrawIndexed(s_Data.QuadVertexArray, s_Data.QuadIndexCount);
    }
    ......
    void OpenGLVertexBuffer::SetData(const void* data, uint32_t size)
    {
          
          
        glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
        // 截取部分CPU的顶点数据上传OpenGL
        glBufferSubData(GL_ARRAY_BUFFER, 0, size, data);
    }
    
  • Sandbox2D

    Hazel::Renderer2D::DrawQuad({
          
           -1.0f, 0.0f }, {
          
           0.8f,0.8f }, m_FlatColor);
    Hazel::Renderer2D::DrawQuad({
          
           0.5f, -0.5f }, {
          
           0.5f, 0.8f }, {
          
          0.2f, 0.8f, 0.9f, 1.0f});
    
  • Glsl的代码

    #type vertex
    #version 330 core
    
    layout(location = 0) in vec3 a_Position;
    layout(location = 1) in vec4 a_Color;
    layout(location = 2) in vec2 a_TexCoord;
    
    uniform mat4 u_ViewProjection;
    // uniform mat4 u_Transform;
    
    out vec4 v_Color;
    out vec2 v_TexCoord;
    
    void main() {
          
          
    	v_Color = a_Color;
    	v_TexCoord = a_TexCoord;
        // 由规则动态生成的顶点位置(基于本地空间)没有涉及transform变换顶点位置
        // gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0); 
    	gl_Position = u_ViewProjection * vec4(a_Position, 1.0);
    }
    #type fragment
    #version 330 core
    
    layout(location = 0) out vec4 color;
    
    in vec4 v_Color;
    in vec2 v_TexCoord;
    
    uniform vec4 u_Color;
    uniform float u_TilingFactor;
    
    uniform sampler2D u_Texture;
    
    void main() {
          
          
    	color = v_Color;
    }
    

结果

请添加图片描述

完整代码

Renderer2D.cpp
#include "hzpch.h"
#include "Renderer2D.h"
#include "VertexArray.h"
#include "Buffer.h"
#include "Shader.h"
#include "Texture.h"
#include "RenderCommand.h"
#include <glm/gtc/matrix_transform.hpp>

namespace Hazel {
    
    
	struct QuadVertex {
    
    
		glm::vec3 Position;
		glm::vec4 Color;
		glm::vec2 TexCoord;
	};
    // 包含顶点的各个信息
	struct Renderer2DData{
    
    
		const uint32_t MaxQuads = 10000;
		const uint32_t MaxVertices = MaxQuads * 4;
		const uint32_t MaxIndices = MaxQuads * 6;

		Ref<VertexArray> QuadVertexArray;
		Ref<VertexBuffer> QuadVertexBuffer;
		Ref<Shader> TextureShader;
		Ref<Texture2D> WhiteTexture;

		uint32_t QuadIndexCount = 0;
		QuadVertex* QuadVertexBufferBase = nullptr;
		QuadVertex* QuadVertexBufferPtr = nullptr;
	};
	static Renderer2DData s_Data;
	void Hazel::Renderer2D::Init()
	{
    
    
		HZ_PROFILE_FUNCTION();

		// 0.在CPU开辟存储s_Data.MaxVertices个的QuadVertex的内存
		s_Data.QuadVertexBufferBase = new QuadVertex[s_Data.MaxVertices];

		// 1.创建顶点数组
		s_Data.QuadVertexArray = VertexArray::Create();

		// 2.创建顶点缓冲区,先在GPU开辟一块s_Data.MaxVertices * sizeof(QuadVertex)大小的内存
		// 与cpu对应大,是为了传输顶点数据
		s_Data.QuadVertexBuffer = VertexBuffer::Create(s_Data.MaxVertices * sizeof(QuadVertex));

		// 2.1设置顶点缓冲区布局
		s_Data.QuadVertexBuffer->SetLayout({
    
    
			{
    
    Hazel::ShaderDataType::Float3, "a_Position"},
			{
    
    Hazel::ShaderDataType::Float4, "a_Color"},
			{
    
    Hazel::ShaderDataType::Float2, "a_TexCoord"}
			});

		// 1.1顶点数组添加顶点缓冲区,并且在这个缓冲区中设置布局
		s_Data.QuadVertexArray->AddVertexBuffer(s_Data.QuadVertexBuffer);

		// 3.索引缓冲
		//uint32_t flatIndices[] = { 0, 1, 2, 2, 3, 0 };
		uint32_t* quadIndices = new uint32_t[s_Data.MaxIndices];
		
		// 一个quad用6个索引,012 230,456 674
		uint32_t offset = 0;
		for (uint32_t i = 0; i < s_Data.MaxIndices; i += 6) {
    
    
			quadIndices[i + 0] = offset + 0;
			quadIndices[i + 1] = offset + 1;
			quadIndices[i + 2] = offset + 2;
										
			quadIndices[i + 3] = offset + 2;
			quadIndices[i + 4] = offset + 3;
			quadIndices[i + 5] = offset + 0;

			offset += 4;
		}

		Ref<IndexBuffer> flatIB = IndexBuffer::Create(quadIndices, s_Data.MaxIndices);

		// 1.2顶点数组设置索引缓冲区
		s_Data.QuadVertexArray->SetIndexBuffer(flatIB);
		// cpu上传到gpu上了可以删除cpu的索引数据块了
		delete[] quadIndices;

		//s_Data.FlatColorShader = (Hazel::Shader::Create("assets/shaders/FlatColor.glsl"));

		// 纹理的shader
		s_Data.TextureShader = Shader::Create("assets/shaders/Texture.glsl");
		s_Data.TextureShader->SetInt("u_Texture", 0);

		// 创建一个白色Texture
		s_Data.WhiteTexture = Texture2D::Create(1, 1);
		uint32_t whiteTextureData = 0xffffffff;
		s_Data.WhiteTexture->SetData(&whiteTextureData, sizeof(uint32_t));
	}

	void Hazel::Renderer2D::Shutdown()
	{
    
    
		HZ_PROFILE_FUNCTION();

	}

	void Hazel::Renderer2D::BeginScene(const OrthographicCamera& camera)
	{
    
    
		HZ_PROFILE_FUNCTION();

		s_Data.TextureShader->Bind();		// 绑定shader
		s_Data.TextureShader->SetMat4("u_ViewProjection", camera.GetViewProjectionMatrix());

		// 相当于初始化此帧要绘制的索引数量,上传的顶点数据
		s_Data.QuadIndexCount = 0;
		// 指针赋予
		s_Data.QuadVertexBufferPtr = s_Data.QuadVertexBufferBase;
	}

	void Hazel::Renderer2D::EndScene()
	{
    
    
		HZ_PROFILE_FUNCTION();

		// 计算当前绘制需要多少个顶点数据
		uint32_t dataSize = (uint8_t*)s_Data.QuadVertexBufferPtr - (uint8_t*)s_Data.QuadVertexBufferBase;
		// 截取部分CPU的顶点数据上传OpenGL
		s_Data.QuadVertexBuffer->SetData(s_Data.QuadVertexBufferBase, dataSize);

		Flush();
	}

	void Renderer2D::Flush()
	{
    
    
		RenderCommand::DrawIndexed(s_Data.QuadVertexArray, s_Data.QuadIndexCount);
	}

	void Hazel::Renderer2D::DrawQuad(const glm::vec2& position, const glm::vec2& size, const glm::vec4& color)
	{
    
    
		DrawQuad({
    
     position.x, position.y, 0.0f }, size, color);
	}

	void Hazel::Renderer2D::DrawQuad(const glm::vec3& position, const glm::vec2& size, const glm::vec4& color)
	{
    
    
		HZ_PROFILE_FUNCTION();

		// quad的左下角为起点
		s_Data.QuadVertexBufferPtr->Position = position;
		s_Data.QuadVertexBufferPtr->Color = color;
		s_Data.QuadVertexBufferPtr->TexCoord = {
    
     0.0f, 0.0f };
		s_Data.QuadVertexBufferPtr++;

		s_Data.QuadVertexBufferPtr->Position = {
    
     position.x + size.x, position.y, 0.0f };
		s_Data.QuadVertexBufferPtr->Color = color;
		s_Data.QuadVertexBufferPtr->TexCoord = {
    
     1.0f, 0.0f };
		s_Data.QuadVertexBufferPtr++;

		s_Data.QuadVertexBufferPtr->Position = {
    
     position.x + size.x, position.y + size.y, 0.0f };
		s_Data.QuadVertexBufferPtr->Color = color;
		s_Data.QuadVertexBufferPtr->TexCoord = {
    
     1.0f, 1.0f };
		s_Data.QuadVertexBufferPtr++;

		s_Data.QuadVertexBufferPtr->Position = {
    
     position.x, position.y +size.y , 0.0f };
		s_Data.QuadVertexBufferPtr->Color = color;
		s_Data.QuadVertexBufferPtr->TexCoord = {
    
     0.0f, 1.0f };
		s_Data.QuadVertexBufferPtr++;

		s_Data.QuadIndexCount += 6;// 每一个quad用6个索引
		//s_Data.TextureShader->SetFloat4("u_Color", color);
		//s_Data.TextureShader->SetFloat("u_TilingFactor", 1.0f);
		// 绑定纹理
		//s_Data.WhiteTexture->Bind();

		// 设置transform
		/*glm::mat4 tranform = glm::translate(glm::mat4(1.0f), position) *
			glm::scale(glm::mat4(1.0f), { size.x, size.y, 1.0f });

		s_Data.TextureShader->SetMat4("u_Transform", tranform);

		s_Data.QuadVertexArray->Bind();		// 绑定顶点数组
		RenderCommand::DrawIndexed(s_Data.QuadVertexArray);*/
	}
	void Renderer2D::DrawQuad(const glm::vec2& position, const glm::vec2& size, const Ref<Texture2D>& texture, float tilingFactor, const glm::vec4& tintColor)
	{
    
    
		DrawQuad({
    
     position.x, position.y, 0.0f }, size, texture, tilingFactor, tintColor);
	}
	void Renderer2D::DrawQuad(const glm::vec3& position, const glm::vec2& size, const Ref<Texture2D>& texture, float tilingFactor, const glm::vec4& tintColor)
	{
    
    
		HZ_PROFILE_FUNCTION();

		s_Data.TextureShader->SetFloat4("u_Color", tintColor);
		s_Data.TextureShader->SetFloat("u_TilingFactor", tilingFactor);
		// 绑定纹理
		texture->Bind();

		// 设置transform
		glm::mat4 tranform = glm::translate(glm::mat4(1.0f), position) *
			glm::scale(glm::mat4(1.0f), {
    
     size.x, size.y, 1.0f });

		s_Data.TextureShader->SetMat4("u_Transform", tranform);

		s_Data.QuadVertexArray->Bind();		// 绑定顶点数组
		RenderCommand::DrawIndexed(s_Data.QuadVertexArray);
	}
	void Renderer2D::DrawrRotatedQuad(const glm::vec2& position, const glm::vec2& size, float rotation, const glm::vec4& color)
	{
    
    
		DrawrRotatedQuad({
    
    position.x, position.y, 0.0f}, size, rotation, color);
	}
	void Renderer2D::DrawrRotatedQuad(const glm::vec3& position, const glm::vec2& size, float rotation, const glm::vec4& color)
	{
    
    
		HZ_PROFILE_FUNCTION();

		s_Data.TextureShader->SetFloat4("u_Color", color);
		s_Data.TextureShader->SetFloat("u_TilingFactor", 1.0f);

		// 绑定纹理
		s_Data.WhiteTexture->Bind();

		// 设置transform
		glm::mat4 tranform = glm::translate(glm::mat4(1.0f), position) *
			glm::rotate(glm::mat4(1.0f), rotation, {
    
     0.0f, 0.0f, 1.0f }) *
			glm::scale(glm::mat4(1.0f), {
    
     size.x, size.y, 1.0f });

		s_Data.TextureShader->SetMat4("u_Transform", tranform);

		s_Data.QuadVertexArray->Bind();		// 绑定顶点数组
		RenderCommand::DrawIndexed(s_Data.QuadVertexArray);
	}
	void Renderer2D::DrawrRotatedQuad(const glm::vec2& position, const glm::vec2& size, float rotation, const Ref<Texture2D>& texture, float tilingFactor, const glm::vec4& tintColor)
	{
    
    
		DrawrRotatedQuad({
    
     position.x, position.y, 0.0f }, size, rotation, texture, tilingFactor, tintColor);
	}
	void Renderer2D::DrawrRotatedQuad(const glm::vec3& position, const glm::vec2& size, float rotation, const Ref<Texture2D>& texture, float tilingFactor, const glm::vec4& tintColor)
	{
    
    
		HZ_PROFILE_FUNCTION();

		s_Data.TextureShader->SetFloat4("u_Color", tintColor);
		s_Data.TextureShader->SetFloat("u_TilingFactor", tilingFactor);
		// 绑定纹理
		texture->Bind();

		// 设置transform
		glm::mat4 tranform = glm::translate(glm::mat4(1.0f), position) *
			glm::rotate(glm::mat4(1.0f), rotation, {
    
     0.0f, 0.0f, 1.0f }) *
			glm::scale(glm::mat4(1.0f), {
    
     size.x, size.y, 1.0f });

		s_Data.TextureShader->SetMat4("u_Transform", tranform);

		s_Data.QuadVertexArray->Bind();		// 绑定顶点数组
		RenderCommand::DrawIndexed(s_Data.QuadVertexArray);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_34060370/article/details/131882930