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