Hazel game engine (032) vertex array abstraction

If there are errors in the code, terminology, etc. in the text, please correct me

Article directory

  • foreword
  • project related
    • the code
    • The effect remains the same

Foreword

  • of this program

    • In order to encapsulate the abstract OpenGL’s vertex array
    • Raw OpenGL function code that does not create vertex arrays in the rendering layer
  • Introduction to Vertex Arrays

    You have to understand it first, and then you can better encapsulate abstraction

    • Vertex arrays are entities that contain state, they don’t actually contain any actual data

      The ones that contain the actual data are the vertex buffers containing the vertices and the index buffers containing the indices

      The vertex array actually contains only references to the vertex buffer and index buffer, and the vertex attribute layout pointer

      Vertex arrays are a bit like pointers to existing vertex buffers and index buffers.

    • A vertex array can have multiple vertex buffers containing different information

    Graphic:

    Please add picture description

  • Design abstract vertex array class from API

    std::shared_ptr<VertexArray> m_VertexArray.reset(VertexArray::Create());
    m_VertexArray->AddVertexBuffer(vertexBuffer);
    m_VertexArray->SetIndexBuffer(indexBuffer);
    m_VertexArray->Bind();
    
  • Class diagram after completion of this section

    Please add picture description

    explain

    • dynamic polymorphism

      VertexArray has a static Create function, returns VertexArray*, can call return new OpenGLVertexArray, the base class pointer points to the subclass object, C++ dynamic polymorphism.

    • Why is there no D3DVertexArray

      Because Directxdoesn’tvertex array, the inheritance relationship is still written here for complete unity, and Vulkan may have

Project related

Code

  • VertexArray

    #pragma once
    #include <memory>
    #include "Hazel/Renderer/Buffer.h"
    namespace Hazel {<!-- -->
    class VertexArray{<!-- -->
    public:
    virtual ~VertexArray() {<!-- -->}
    virtual void Bind() const = 0;
    virtual void Unbind() const = 0;
    virtual void AddVertexBuffer(const std::shared_ptr<VertexBuffer> & amp; vertexBuffer) = 0;//Add vertex buffer object
    virtual void SetIndexBuffer(const std::shared_ptr<IndexBuffer> & amp; indexBuffer) = 0; // set the index buffer object, generally only one
    virtual const std::vector<std::shared_ptr<VertexBuffer>> & GetVertexBuffers() const = 0;
    virtual const std::shared_ptr<IndexBuffer> & GetIndexBuffer() const = 0;
    static VertexArray* Create();// The base class pointer points to the subclass object
    };
    }
    
    #include "hzpch.h"
    #include "VertexArray.h"
    #include "Renderer.h"
    #include "Platform/OpenGL/OpenGLVertexArray.h"
    namespace Hazel {<!-- -->
    VertexArray* VertexArray::Create(){<!-- -->
    switch (Renderer::GetAPI()){<!-- -->
    case RendererAPI::None: HZ_CORE_ASSERT(false, "RendererAPI::None is currently not supported!"); return nullptr;
    case RendererAPI::OpenGL: return new OpenGLVertexArray();
    }
    HZ_CORE_ASSERT(false, "Unknown RendererAPI!");
    return nullptr;
    }
    }
    
  • OpenGLVertexArray

    #pragma once
    #include "Hazel/Renderer/VertexArray.h"
    namespace Hazel {<!-- -->
    class OpenGLVertexArray : public VertexArray{<!-- -->
    public:
    OpenGLVertexArray();
    virtual ~OpenGLVertexArray();
    virtual void Bind() const override;
    virtual void Unbind() const override;
    virtual void AddVertexBuffer(const std::shared_ptr<VertexBuffer> & amp; vertexBuffer) override;//Add vertex buffer object
    virtual void SetIndexBuffer(const std::shared_ptr<IndexBuffer> & amp; indexBuffer) override; // Set the index buffer object, generally only one
    virtual const std::vector<std::shared_ptr<VertexBuffer>> & GetVertexBuffers() const {<!-- --> return m_VertexBuffers; }
    virtual const std::shared_ptr<IndexBuffer> & GetIndexBuffer() const {<!-- --> return m_IndexBuffer; }
    private:
    uint32_t m_RendererID;
    std::vector<std::shared_ptr<VertexBuffer>> m_VertexBuffers;// list of owned vertex buffer objects
    std::shared_ptr<IndexBuffer> m_IndexBuffer; // owned index buffer object
    };
    }
    
    #include "hzpch.h"
    #include "OpenGLVertexArray.h"
    #include <glad/glad.h>
    namespace Hazel {<!-- -->
    // Convert the custom Shader type to the type defined by OpenGL
    static GLenum ShaderDataTypeToOpenGLBaseType(ShaderDataType type){<!-- -->
    switch (type)
    {<!-- -->
    case Hazel::ShaderDataType::Float: return GL_FLOAT;
    case Hazel::ShaderDataType::Float2: return GL_FLOAT;
    case Hazel::ShaderDataType::Float3: return GL_FLOAT;
    case Hazel::ShaderDataType::Float4: return GL_FLOAT;
    case Hazel::ShaderDataType::Mat3: return GL_FLOAT;
    case Hazel::ShaderDataType::Mat4: return GL_FLOAT;
    case Hazel::ShaderDataType::Int: return GL_INT;
    case Hazel::ShaderDataType::Int2: return GL_INT;
    case Hazel::ShaderDataType::Int3: return GL_INT;
    case Hazel::ShaderDataType::Int4: return GL_INT;
    case Hazel::ShaderDataType::Bool: return GL_BOOL;
    }
    HZ_CORE_ASSERT(false, "Unknown ShaderDataType!");
    return 0;
    }
    OpenGLVertexArray::OpenGLVertexArray(){<!-- -->
    glCreateVertexArrays(1, &m_RendererID);
    }
    OpenGLVertexArray::~OpenGLVertexArray(){<!-- -->
    glDeleteVertexArrays(1, &m_RendererID);
    }
    void OpenGLVertexArray::Bind() const{<!-- -->
    glBindVertexArray(m_RendererID);
    }
    void OpenGLVertexArray::Unbind() const{<!-- -->
    glBindVertexArray(0);
    }
    void OpenGLVertexArray::AddVertexBuffer(const std::shared_ptr<VertexBuffer> & vertexBuffer){<!-- -->
    HZ_CORE_ASSERT(vertexBuffer->GetLayout().GetElements().size(), "Vertex Buffer has no layout!");
    // bind the vertex array object
    glBindVertexArray(m_RendererID);
    // bind vertex buffer object
    vertexBuffer->Bind();
    uint32_t index = 0;
    const auto & layout = vertexBuffer->GetLayout();
    for (const auto & amp; element : layout){<!-- -->
    glEnableVertexAttribArray(index);
    glVertexAttribPointer(index,
    element. GetComponentCount(),
    ShaderDataTypeToOpenGLBaseType(element.Type),
    element.Normalized? GL_TRUE : GL_FALSE,
    layout. GetStride(),
    (const void*)element. Offset);
    index + + ;
    }
    m_VertexBuffers.push_back(vertexBuffer);
    }
    void OpenGLVertexArray::SetIndexBuffer(const std::shared_ptr<IndexBuffer> & amp; indexBuffer){<!-- -->
    glBindVertexArray(m_RendererID);
    indexBuffer->Bind();
    m_IndexBuffer = indexBuffer;
    }
    }
    
  • Application

    std::shared_ptr<Shader> m_Shader;
    std::shared_ptr<VertexArray> m_VertexArray;
    
    std::shared_ptr<Shader> m_BlueShader;
    std::shared_ptr<VertexArray> m_SquareVA;
    
    Application::Application(){<!-- -->
        ?…
        // Preparations for rendering a triangle
        // 0. Vertex data
        float vertices[3 * 7] = {<!-- -->
            -0.5f, -0.5f, 0.0f, 0.8f, 0.2f, 0.8f, 1.0f,
            0.5f, -0.5f, 0.0f, 0.2f, 0.3f, 0.8f, 1.0f,
            0.0f, 0.5f, 0.0f, 0.8f, 0.8f, 0.2f, 1.0f
        };
        unsigned int indices[3] = {<!-- --> 0, 1, 2 }; // index data
        // 1. Generate vertex array object VAO
        m_VertexArray.reset(VertexArray::Create());///
        // 2. Vertex buffer
        std::shared_ptr<VertexBuffer> vertexBuffer;
        vertexBuffer.reset(VertexBuffer::Create(vertices, sizeof(vertices)));
        // Set the vertex attribute pointer to explain the layout of the vertex attributes in the vertex buffer
        BufferLayout layout = {<!-- -->
            {<!-- --> ShaderDataType::Float3, "a_Position" },
            {<!-- --> ShaderDataType::Float4, "a_Color" }
        };
        // 3. First set the vertex buffer layout - calculate the required values of each attribute
        vertexBuffer->SetLayout(layout);
        // 4. Add a vertex buffer to the vertex array - set the vertex attribute pointer of each attribute
        m_VertexArray->AddVertexBuffer(vertexBuffer);///
        // 5. Index buffer
        std::shared_ptr<IndexBuffer> indexBuffer;
        indexBuffer.reset(IndexBuffer::Create(indices, sizeof(indices) / sizeof(uint32_t)));
        // 6. Set the index buffer for the vertex array
        m_VertexArray->SetIndexBuffer(indexBuffer);///
        // shader code
        std::string vertexSrc = R"(
    #version 330 core
    
    layout(location = 0) in vec3 a_Position;
    layout(location = 1) in vec4 a_Color;
    out vec3 v_Position;
    out vec4 v_Color;
    void main()
    {
    v_Position = a_Position;
    v_Color = a_Color;
    gl_Position = vec4(a_Position, 1.0);
    }
    )";
        std::string fragmentSrc = R"(
    #version 330 core
    
    layout(location = 0) out vec4 color;
    in vec3 v_Position;
    in vec4 v_Color;
    void main()
    {
    color = vec4(v_Position * 0.5 + 0.5, 1.0);
    color = v_Color;
    }
    )";
        m_Shader.reset(new Shader(vertexSrc, fragmentSrc));
    
        // Preparations for rendering a quad
        // 0. Vertex data
        float squareVertices[3 * 4] = {<!-- -->
            -0.75f, -0.75f, 0.0f,
            0.75f, -0.75f, 0.0f,
            0.75f, 0.75f, 0.0f,
            -0.75f, 0.75f, 0.0f
        };
        uint32_t squareIndices[6] = {<!-- --> 0, 1, 2, 2, 3, 0 }; // index data
        // 1. Generate vertex array object VAO
        m_SquareVA.reset(VertexArray::Create());
        // 2. Vertex buffer
        std::shared_ptr<VertexBuffer> squareVB;
        squareVB.reset(VertexBuffer::Create(squareVertices, sizeof(squareVertices)));
        // Set the vertex attribute pointer to explain the layout of the vertex attributes in the vertex buffer
        BufferLayout layout2 = {<!-- -->
            {<!-- --> ShaderDataType::Float3, "a_Position" }
        };
        // 3. First set the vertex buffer layout - calculate the required values of each attribute
        squareVB->SetLayout(layout2);
        // 4. Add a vertex buffer to the vertex array - set the vertex attribute pointer of each attribute
        m_SquareVA->AddVertexBuffer(squareVB);
        // 5. Index buffer
        std::shared_ptr<IndexBuffer> squareIB;
        squareIB.reset(IndexBuffer::Create(squareIndices, sizeof(squareIndices) / sizeof(uint32_t)));
        // 6. Set the index buffer for the vertex array
        m_SquareVA->SetIndexBuffer(squareIB);
        // shader code
        std::string blueShaderVertexSrc = R"(
    #version 330 core
    layout(location = 0) in vec3 a_Position;
    out vec3 v_Position;
    void main()
    {
    v_Position = a_Position;
    gl_Position = vec4(a_Position, 1.0);
    }
    )";
        std::string blueShaderFragmentSrc = R"(
    #version 330 core
    layout(location = 0) out vec4 color;
    in vec3 v_Position;
    void main()
    {
    color = vec4(0.2, 0.3, 0.8, 1.0);
    }
    )";
        m_BlueShader.reset(new Shader(blueShaderVertexSrc, blueShaderFragmentSrc));
        ?…
    void Application::Run(){<!-- -->
         while (m_Running){<!-- -->
             glClearColor(0.1f, 0.1f, 0.1f, 1);
             glClear(GL_COLOR_BUFFER_BIT);
             // draw the quadrilateral
             m_BlueShader->Bind();// bind shader
             m_SquareVA->Bind();// Bind the vertex array object and draw
             glDrawElements(GL_TRIANGLES, m_SquareVA->GetIndexBuffer()->GetCount(), GL_UNSIGNED_INT, nullptr);
    
             // draw triangle
             m_Shader->Bind();// bind shader
             /
             m_VertexArray->Bind();// Bind the vertex array object and draw///
             glDrawElements(GL_TRIANGLES, m_VertexArray->GetIndexBuffer()->GetCount(), GL_UNSIGNED_INT, nullptr);
    
    

Effect unchanged

Please add a picture description