If there are errors in the code, terminology, etc. in the text, please correct me
Article directory
- foreword
- project related
-
- Code additions and modifications
- Effect
Foreword
-
of this program
For the
generation
vertex buffer and index buffer of OpenGL, the original code is abstracted into classes.There are rendering codes for creating opengl, vertex buffers, and index buffers in the application. If these OpenGL functions are not included in the application, these operations need to be encapsulated into classes.
-
How to design classes
Start with the API form you want to use
VertexBuffer m_VertexBuffer = new VertexBuffer(vertices, sizeof(vertices)); // Complete the creation of vertex buffer objects, binding, and sending data from the CPU to the GPU in the m_VertexArray constructor
-
Rendering interface design
Since there can be multiple rendering graphics APIs: OpenGL, DX, if the engine supports two rendering graphics APIs, which one needs to be designed and selected
-
If selected at compile time
Disadvantages: If you change the rendering object, you need to recompile the engine, and you cannot switch at runtime
-
If selecting at runtime
Disadvantage: Both rendering-related objs must be compiled at compile time
Advantages: can dynamically switch
How to implement: Using the dynamic characteristics of C++, the base class pointer points to the subclass object to realize dynamic polymorphism
-
-
The class diagram for determining the rendering interface at runtime: taking vertex buffer objects and index buffer objects as examples
Interpretation:
For example: VertexBuffer has a static Create function, returns VertexBuffer*, and calls return new OpenGLVertexBuffer or DirectxVertexBuffer according to the choice of different rendering graphics API, the base class pointer points to the subclass object, C++ dynamic polymorphism. (However, later chapters will use macro definitions to determine which rendering graphics API to choose at compile time)
Project related
Code addition and modification
-
Application
private: std::unique_ptr<Window> m_Window; ImGuiLayer* m_ImGuiLayer; bool m_Running = true; LayerStack m_LayerStack; unsigned int m_VertexArray; std::unique_ptr<Shader> m_Shader; std::unique_ptr<VertexBuffer> m_VertexBuffer; std::unique_ptr<IndexBuffer> m_IndexBuffer;
...... // Render a triangle using OpenGL functions // vertex data float vertices[3 * 3] = {<!-- --> -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f }; unsigned int indices[3] = {<!-- --> 0, 1, 2 }; // index data // 0. Generate vertex array object VAO, vertex buffer object VBO, index buffer object EBO/ glGenVertexArrays(1, &m_VertexArray); // 1. Bind the vertex array object glBindVertexArray(m_VertexArray); // 2.1 Vertex buffer m_VertexBuffer.reset(VertexBuffer::Create(vertices, sizeof(vertices))); // here // 2.2 Set the vertex attribute pointer to explain the layout of the vertex attributes in the vertex buffer glEnableVertexAttribArray(0);//Open glsl's layout = 0 input glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr); // 3. Index buffer m_IndexBuffer.reset(IndexBuffer::Create(indices, sizeof(indices) / sizeof(uint32_t))); // here ?…
-
Renderer
#pragma once namespace Hazel {<!-- --> enum class RendererAPI {<!-- --> None = 0, OpenGL = 1 }; class Renderer{<!-- --> public: inline static RendererAPI GetAPI() {<!-- --> return s_RendererAPI; } private: static RendererAPI s_RendererAPI; }; }
#include "hzpch.h" #include "Renderer.h" namespace Hazel {<!-- --> RendererAPI Renderer::s_RendererAPI = RendererAPI::OpenGL; }
-
Add the VertexBuffer and IndexBuffer classes to be placed in the Buffer file
#pragma once namespace Hazel {<!-- --> class VertexBuffer{<!-- --> public: virtual ~VertexBuffer(){<!-- -->} virtual void Bind() const = 0; virtual void Unbind() const = 0; static VertexBuffer* Create(float* vertices, uint32_t size); }; class IndexBuffer {<!-- --> public: virtual ~IndexBuffer() {<!-- -->} virtual void Bind() const = 0; virtual void Unbind() const = 0; virtual uint32_t GetCount() const = 0; static IndexBuffer* Create(uint32_t* indices, uint32_t size); }; }
#include "hzpch.h" #include "Buffer.h" // Create different ones according to different platforms. . . #include "Renderer.h" #include "Platform/OpenGL/OpenGLBuffer.h" namespace Hazel {<!-- --> VertexBuffer* VertexBuffer::Create(float* vertices, uint32_t size){<!-- --> switch (Renderer::GetAPI()) {<!-- --> case RendererAPI::None: HZ_CORE_ASSERT(false, "RendererAPI:None is currently not supported!"); return nullptr; case RendererAPI::OpenGL: return new OpenGLVertexBuffer(vertices, size); } HZ_CORE_ASSERT(false, "Unknown RendererAPI!"); return nullptr; } IndexBuffer* IndexBuffer::Create(uint32_t* indices, uint32_t size){<!-- --> switch (Renderer::GetAPI()) {<!-- --> case RendererAPI::None: HZ_CORE_ASSERT(false, "RendererAPI:None is currently not supported!"); return nullptr; case RendererAPI::OpenGL: return new OpenGLIndexBuffer(indices, size); } HZ_CORE_ASSERT(false, "Unknown RendererAPI!"); return nullptr; } }
-
OpenGLBuffer
#pragma once #include "Hazel/Renderer/Buffer.h" namespace Hazel {<!-- --> class OpenGLVertexBuffer : public VertexBuffer{<!-- --> public: OpenGLVertexBuffer(float* vertices, uint32_t size); virtual ~OpenGLVertexBuffer(); virtual void Bind() const; virtual void Unbind() const; private: uint32_t m_RendererID; }; class OpenGLIndexBuffer : public IndexBuffer{<!-- --> public: OpenGLIndexBuffer(uint32_t* indices, uint32_t count); virtual ~OpenGLIndexBuffer(); virtual void Bind() const; virtual void Unbind() const; virtual uint32_t GetCount() const {<!-- --> return m_Count; } private: uint32_t m_RendererID; uint32_t m_Count; }; }
#include "hzpch.h" #include "OpenGLBuffer.h" #include <glad/glad.h> namespace Hazel {<!-- --> //VertexBuffer/ OpenGLVertexBuffer::OpenGLVertexBuffer(float* vertices, uint32_t size){<!-- --> // 1. Create a vertex buffer object glCreateBuffers(1, &m_RendererID); // 2. Bind the vertex buffer object glBindBuffer(GL_ARRAY_BUFFER, m_RendererID); // 3. Copy the vertex data of our CPU to the GPU vertex buffer for use by OpenGL glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW); } OpenGLVertexBuffer::~OpenGLVertexBuffer(){<!-- --> glDeleteBuffers(1, &m_RendererID); } void OpenGLVertexBuffer::Bind() const{<!-- --> glBindBuffer(GL_ARRAY_BUFFER, m_RendererID); } void OpenGLVertexBuffer::Unbind() const{<!-- --> glBindBuffer(GL_ARRAY_BUFFER, 0); } // IndexBuffer // OpenGLIndexBuffer::OpenGLIndexBuffer(uint32_t* indices, uint32_t count) : m_Count(count){<!-- --> // 1. Create a vertex buffer object glCreateBuffers(1, &m_RendererID); // 2. Bind the vertex buffer object glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID); // 3. Copy the index data of our CPU to the GPU index buffer for use by OpenGL glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint32_t), indices, GL_STATIC_DRAW); } OpenGLIndexBuffer::~OpenGLIndexBuffer(){<!-- --> glDeleteBuffers(1, &m_RendererID); } void OpenGLIndexBuffer::Bind() const{<!-- --> glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID); } void OpenGLIndexBuffer::Unbind() const{<!-- --> glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } }
Effect
Unchanged, it proves that the abstract vertex buffer and index buffer class are successful