Vertex Arrays

引言

上一节我们讨论了顶点缓冲区布局,设计了一个类可以优雅地为我们处理所有事情。今天我们来看看顶点数组。

从本质上讲,顶点数组是一种包含实体的状态。它们最好实际上包含所有数据,例如你的顶点缓冲区和索引缓冲区,这两部分是你存储数据的实际缓冲区。

而实际上顶点数组只有链接,它们仅有指向现有顶点缓冲区和索引缓冲区的指针。

顶点数组抽象

Infinite/Renderer/ 目录下新建 VertexArray.hVertexArray.cpp

#pragma once

#include <memory>
#include "../Renderer/Buffer.h"

namespace Infinite {


class VertexArray
{
public:
virtual ~VertexArray() {}

virtual void Bind() const = 0;
virtual void Unbind() const = 0;

virtual void AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer) = 0;
virtual void SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer) = 0;

virtual const std::vector<std::shared_ptr<VertexBuffer>>& GetVertexBuffers() const = 0;
virtual const std::shared_ptr<IndexBuffer>& GetIndexBuffer() const = 0;

static VertexArray* Create();
};


}
#include "ifnpch.h"
#include "VertexArray.h"

#include "Renderer.h"
#include "./Platform/OpenGL/OpenGLVertexArray.h"

namespace Infinite {

VertexArray* VertexArray::Create()
{
switch (Renderer::GetAPI())
{
case RendererAPI::None:
IFN_ASSERT(false, "RendererAPI::None is currently not supported!");
return nullptr;
case RendererAPI::OpenGL:
return new OpenGLVertexArray();
}

IFN_ASSERT(false, "Unknown RendererAPI!");
return nullptr;
}


}

OpenGL顶点数组实现

src/Platform/OpenGL/ 目录下新建 OpenGLVertexArray.hOpenGLVertexArray.cpp

#pragma once

#include "Infinite/Renderer/VertexArray.h"

namespace Infinite {

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>& vertexBuffer) override;
virtual void SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer) override;

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_RedererID;
std::vector<std::shared_ptr<VertexBuffer>> m_VertexBuffers;
std::shared_ptr<IndexBuffer> m_IndexBuffer;
};

}
#include "ifnpch.h"
#include "OpenGLVertexArray.h"

#include <glad/glad.h>

namespace Infinite {

static GLenum ShaderDataTypeToOpenGLBaseType(ShaderDataType type)
{
switch (type)
{
case Infinite::ShaderDataType::Float: return GL_FLOAT;
case Infinite::ShaderDataType::Float2: return GL_FLOAT;
case Infinite::ShaderDataType::Float3: return GL_FLOAT;
case Infinite::ShaderDataType::Float4: return GL_FLOAT;
case Infinite::ShaderDataType::Mat3: return GL_FLOAT;
case Infinite::ShaderDataType::Mat4: return GL_FLOAT;
case Infinite::ShaderDataType::Int: return GL_INT;
case Infinite::ShaderDataType::Int2: return GL_INT;
case Infinite::ShaderDataType::Int3: return GL_INT;
case Infinite::ShaderDataType::Int4: return GL_INT;
case Infinite::ShaderDataType::Bool: return GL_BOOL;
}

IFN_CORE_ASSERT(false, "Unknown ShaderDataType!");
return 0;
}

OpenGLVertexArray::OpenGLVertexArray()
{
glCreateVertexArrays(1, &m_RedererID);
}

void OpenGLVertexArray::Bind() const
{
glBindVertexArray(m_RedererID);
}

void OpenGLVertexArray::Unbind() const
{
glBindVertexArray(0);
}

void OpenGLVertexArray::AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer)
{
IFN_CORE_ASSERT(vertexBuffer->GetLayout().GetElements().size(), "Vertex buffer has no layout!");

glBindVertexArray(m_RedererID);
vertexBuffer->Bind();

uint32_t index = 0;
const auto& layout = vertexBuffer->GetLayout();
for (const auto& 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>& indexBuffer)
{
glBindVertexArray(m_RedererID);
indexBuffer->Bind();

m_IndexBuffer = indexBuffer;
}


}

应用类调整

Application.h 用顶点数组取代之前的缓冲:

#include "./Renderer/Shader.h"
#include "./Renderer/Buffer.h"
+#include "./Renderer/VertexArray.h"

namespace Infinite {
class INFINITE_API Application
@@ -35,10 +36,11 @@ namespace Infinite {
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;
+ std::shared_ptr<Shader> m_Shader;
+ std::shared_ptr<VertexArray> m_VertexArray;
+
+ std::shared_ptr<Shader> m_BlueShader;
+ std::shared_ptr<VertexArray> m_SquareVertexArray;
private:
static Application* s_Instance;
};

Application.cpp 继续解耦:

@@ -14,27 +14,6 @@ namespace Infinite {

Application* Application::s_Instance = nullptr;

- static GLenum ShaderDataTypeToOpenGLBaseType(ShaderDataType type)
- {
- switch (type)
- {
- case Infinite::ShaderDataType::Float: return GL_FLOAT;
- case Infinite::ShaderDataType::Float2: return GL_FLOAT;
- case Infinite::ShaderDataType::Float3: return GL_FLOAT;
- case Infinite::ShaderDataType::Float4: return GL_FLOAT;
- case Infinite::ShaderDataType::Mat3: return GL_FLOAT;
- case Infinite::ShaderDataType::Mat4: return GL_FLOAT;
- case Infinite::ShaderDataType::Int: return GL_INT;
- case Infinite::ShaderDataType::Int2: return GL_INT;
- case Infinite::ShaderDataType::Int3: return GL_INT;
- case Infinite::ShaderDataType::Int4: return GL_INT;
- case Infinite::ShaderDataType::Bool: return GL_BOOL;
- }
-
- IFN_CORE_ASSERT(false, "Unknown ShaderDataType!");
- return 0;
- }

Application::Application()
{
IFN_CORE_ASSERT(!s_Instance, "Application already exists!");
@@ -46,9 +25,7 @@ namespace Infinite {
m_ImGuiLayer = new ImGuiLayer();
PushOverlay(m_ImGuiLayer);

- // Vertex Array
- glGenVertexArrays(1, &m_VertexArray);
- glBindVertexArray(m_VertexArray);
- m_VertexArray.reset(VertexArray::Create());


float vertices[3 * 7] = {
@@ -57,34 +34,42 @@ namespace Infinite {
0.0f, 0.5f, 0.0f, 0.8f, 0.8f, 0.2f, 1.0f
};

- m_VertexBuffer.reset(VertexBuffer::Create(vertices, sizeof(vertices)));
+ std::shared_ptr<VertexBuffer> vertexBuffer;
+ vertexBuffer.reset(VertexBuffer::Create(vertices, sizeof(vertices)));
+ BufferLayout layout = {
+ { ShaderDataType::Float3, "a_Position" },
+ { ShaderDataType::Float4, "a_Color" }
+ };

- {
- BufferLayout layout = {
- { ShaderDataType::Float3, "a_Position" },
- { ShaderDataType::Float4, "a_Color" }
- };
+ vertexBuffer->SetLayout(layout);
+ m_VertexArray->AddVertexBuffer(vertexBuffer);

+ uint32_t indices[3] = { 0, 1, 2 };
+ std::shared_ptr<IndexBuffer> indexBuffer;
+ indexBuffer.reset(IndexBuffer::Create(indices, sizeof(indices) / sizeof(uint32_t)));
+ m_VertexArray->SetIndexBuffer(indexBuffer);

- m_VertexBuffer->SetLayout(layout);
- }

- uint32_t index = 0;
- const auto& layout = m_VertexBuffer->GetLayout();
- for (const auto& 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_SquareVertexArray.reset(VertexArray::Create());
+
+ 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
+ };
+ std::shared_ptr<VertexBuffer> squareVertexBuffer;
+ squareVertexBuffer.reset(VertexBuffer::Create(squareVertices, sizeof(squareVertices)));
+ squareVertexBuffer->SetLayout({
+ { ShaderDataType::Float3, "a_Position" }
+ });
+ m_SquareVertexArray->AddVertexBuffer(squareVertexBuffer);
+
+ uint32_t squareIndices[6] = { 0, 1, 2, 2, 3, 0 };
+ std::shared_ptr<IndexBuffer> squareIndexBuffer;
+ squareIndexBuffer.reset(IndexBuffer::Create(squareIndices, sizeof(squareIndices) / sizeof(uint32_t)));
+ m_SquareVertexArray->SetIndexBuffer(squareIndexBuffer);

- uint32_t indices[3] = { 0, 1, 2 };
- m_IndexBuffer.reset(IndexBuffer::Create(indices, sizeof(indices) / sizeof(uint32_t)));


std::string vertexSrc = R"(
@@ -119,9 +104,35 @@ namespace Infinite {
}
)";

+ m_Shader.reset(new Shader(vertexSrc, fragmentSrc));


- m_Shader.reset(new Shader(vertexSrc, fragmentSrc));

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


}

@@ -156,16 +167,17 @@ namespace Infinite {
glClearColor(0.1f, 0.1f, 0.1f, 1);
glClear(GL_COLOR_BUFFER_BIT);

- m_Shader->Bind();
- glBindVertexArray(m_VertexArray);
- glDrawElements(GL_TRIANGLES, m_IndexBuffer->GetCount(), GL_UNSIGNED_INT, nullptr);
+ m_BlueShader->Bind();
+ m_SquareVertexArray->Bind();
+ glDrawElements(GL_TRIANGLES, m_SquareVertexArray->GetIndexBuffer()->GetCount(), GL_UNSIGNED_INT, nullptr);

+ m_Shader->Bind();
+ m_VertexArray->Bind();
+ glDrawElements(GL_TRIANGLES, m_VertexArray->GetIndexBuffer()->GetCount(), GL_UNSIGNED_INT, nullptr);

for (Layer* layer : m_LayerStack)
layer->OnUpdate();

- // auto [x, y] = Input::GetMousePosition();
- // IFN_CORE_TRACE("{0}, {1}", x, y);

m_ImGuiLayer->Begin();
for (Layer* layer : m_LayerStack)

加入一个矩形 Shader 观察效果。

调试