Vertex Buffer Layouts

### 引言

上一节我们构造了渲染 API 的抽象、OpenGL 顶点缓冲区和索引缓冲区的类。今天我们将着重讨论缓冲区布局。

当我在讨论缓冲区布局时,意思是说我真的是在讨论顶点缓冲区布局。之前我们提到,无需处理顶点缓冲区就无法真正渲染任何内容。

缓冲布局类

#pragma once

namespace Infinite {

enum class ShaderDataType
{
None = 0, Float, Float2, Float3, Float4, Mat3, Mat4, Int, Int2, Int3, Int4, Bool
};

static uint32_t ShaderDataTypeSize(ShaderDataType type)
{
switch (type)
{
case ShaderDataType::Float: return 4;
case ShaderDataType::Float2: return 4 * 2;
case ShaderDataType::Float3: return 4 * 3;
case ShaderDataType::Float4: return 4 * 4;
case ShaderDataType::Mat3: return 4 * 3 * 3;
case ShaderDataType::Mat4: return 4 * 4 * 4;
case ShaderDataType::Int: return 4;
case ShaderDataType::Int2: return 4 * 2;
case ShaderDataType::Int3: return 4 * 3;
case ShaderDataType::Int4: return 4 * 4;
case ShaderDataType::Bool: return 1;
}

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

struct BufferElement
{
std::string Name;
ShaderDataType Type;
uint32_t Offset;
uint32_t Size;
bool Normalized;

BufferElement(ShaderDataType type, const std::string& name, bool normalize = false)
: Name(name), Type(type), Size(ShaderDataTypeSize(type)), Offset(0), Normalized(false)
{
}

uint32_t GetComponentCount() const
{
switch (Type)
{
case ShaderDataType::Float: return 1;
case ShaderDataType::Float2: return 2;
case ShaderDataType::Float3: return 3;
case ShaderDataType::Float4: return 4;
case ShaderDataType::Mat3: return 3 * 3;
case ShaderDataType::Mat4: return 4 * 4;
case ShaderDataType::Int: return 1;
case ShaderDataType::Int2: return 2;
case ShaderDataType::Int3: return 3;
case ShaderDataType::Int4: return 4;
case ShaderDataType::Bool: return 1;
}

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

class BufferLayout
{
public:
BufferLayout() {}
BufferLayout(const std::initializer_list<BufferElement>& elements)
:m_Elements(elements)
{
CalculateOffsetsAndStride();
}

inline uint32_t GetStride() const { return m_Stride; }
inline const std::vector<BufferElement>& GetElements() const { return m_Elements; }

std::vector<BufferElement>::iterator begin() { return m_Elements.begin(); }
std::vector<BufferElement>::iterator end() { return m_Elements.end(); }
std::vector<BufferElement>::const_iterator begin() const { return m_Elements.begin(); }
std::vector<BufferElement>::const_iterator end() const { return m_Elements.end(); }
private:
void CalculateOffsetsAndStride()
{
m_Stride = 0;
uint32_t offset = 0;
for (auto& element : m_Elements)
{
element.Offset = offset;
offset += element.Size;
m_Stride += element.Size;
}
}

private:
uint32_t m_Stride = 0;
std::vector<BufferElement> m_Elements;
};

class VertexBuffer
{
@@ -10,6 +106,9 @@ namespace Infinite {
virtual void Bind() const = 0;
virtual void Unbind() const = 0;

virtual void SetLayout(const BufferLayout& layout) = 0;
virtual const BufferLayout& GetLayout() const = 0;

static VertexBuffer* Create(float* vertices, uint32_t size);
};

Buffer.h 加入着色器数据类型映射以及缓冲布局类。

应用类调整

@@ -14,6 +14,27 @@ 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!");
@@ -30,18 +51,37 @@ namespace Infinite {
glBindVertexArray(m_VertexArray);


- float vertices[3 * 3] = {
- -0.5f, -0.5f, 0.0f,
- 0.5f, -0.5f, 0.0f,
- 0.0f, 0.5f, 0.0f
+ 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
};

m_VertexBuffer.reset(VertexBuffer::Create(vertices, sizeof(vertices)));

- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ {
+ BufferLayout layout = {
+ { ShaderDataType::Float3, "a_Position" },
+ { ShaderDataType::Float4, "a_Color" }
+ };
+
+ m_VertexBuffer->SetLayout(layout);
+ }

- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
+ 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++;
+ }


uint32_t indices[3] = { 0, 1, 2 };
m_IndexBuffer.reset(IndexBuffer::Create(indices, sizeof(indices) / sizeof(uint32_t)));
@@ -51,10 +91,15 @@ namespace Infinite {
#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);
}
)";
@@ -63,10 +108,14 @@ namespace Infinite {
#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;
}
)";

Application.cpp 中替换部分顶点缓冲区代码,并对着色器代码稍加修改。

调试