索引缓冲对象(IBO)
作用:
- 存储绘制索引(Index),即告诉 GPU 用哪些顶点组成三角形。
- 允许复用顶点,减少顶点数据冗余。
特点:
- 每个索引对应 VBO 中的一个顶点。
- 绘制函数通常用
glDrawElements(带索引)而不是glDrawArrays(不带索引)。
举例:
例如绘制一个四边形:
顶点顺序: 0, 1, 2, 3
索引顺序: 0, 1, 2, 2, 3, 0 => 两个三角形组成一个四边形
实现(OpenGLBuffer.cpp):
OpenGLIndexBuffer::OpenGLIndexBuffer(uint32_t* indices, uint32_t count) : m_Count(count)
{
glCreateBuffers(1, &m_RendererID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID);
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);
}
顶点缓冲对象(VBO)
作用:
- 存储顶点数据(Position、Color、TexCoord 等)在 GPU 内存中。
- 避免每次绘制都从 CPU 拷贝数据到 GPU,提高效率。
特点:
- GPU 内存中的一块连续区域。
- 数据类型可以是 float、int 等,通常对应顶点属性。
- 不包含索引信息,只存顶点本身。
实现(OpenGLBuffer.cpp):
// 绑定顶点缓冲数据:动态顶点缓冲,用于批处理渲染
OpenGLVertexBuffer::OpenGLVertexBuffer(uint32_t size)
{
glCreateBuffers(1, &m_RendererID);
glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
}
// 绑定顶点缓冲数据:静态顶点缓冲
OpenGLVertexBuffer::OpenGLVertexBuffer(float* vertices, uint32_t size)
{
glCreateBuffers(1, &m_RendererID);
glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
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);
}
// 批处理渲染:向已分配好的顶点缓存区中写入和更新顶点数据
void OpenGLVertexBuffer::SetData(const void* data, uint32_t size)
{
glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
glBufferSubData(GL_ARRAY_BUFFER, 0, size, data);
}
顶点数组对象(VAO)
作用:管理 VBO 和 IBO 的绑定状态。
一个 VAO 可以记录:
- 哪些 VBO 被绑定;
- 每个 VBO 的顶点布局;
- 哪个 IBO 被绑定。
特点:
- VAO 本身不存顶点数据,只存绑定状态。
- 使用 VAO 后,只需绑定 VAO,就能一次性恢复顶点缓冲和索引缓冲状态。
实现(OpenGLVertexArray.cpp):
// 构造函数:创建一个OpenGL的VAO
OpenGLVertexArray::OpenGLVertexArray()
{
glCreateVertexArrays(1, &m_RendererID);
}
// 析构函数:销毁一个OpenGL的VAO
OpenGLVertexArray::~OpenGLVertexArray()
{
glDeleteVertexArrays(1, &m_RendererID);
}
// 绑定VAO,从此所有顶点属性设置都会记录在这个VAO中
void OpenGLVertexArray::Bind() const
{
glBindVertexArray(m_RendererID);
}
// 解绑VAO,放置污染后续状态
void OpenGLVertexArray::Unbind() const
{
glBindVertexArray(0);
}
// 添加顶点缓冲区(VBO)
void OpenGLVertexArray::AddVertexBuffer(const Ref<VertexBuffer>& vertexBuffer)
{
HZ_PROFILE_FUNCTION();
HZ_CORE_ASSERT(vertexBuffer->GetLayout().GetElements().size(), "Vertex Buffer has no layout!");
glBindVertexArray(m_RendererID);
vertexBuffer->Bind();
const auto& layout = vertexBuffer->GetLayout();
for (const auto& element : layout)
{
switch (element.Type)
{
case ShaderDataType::Float:
case ShaderDataType::Float2:
case ShaderDataType::Float3:
case ShaderDataType::Float4:
{
glEnableVertexAttribArray(m_VertexBufferIndex);
glVertexAttribPointer(m_VertexBufferIndex,
element.GetComponentCount(),
ShaderDataTypeToOpenGLBaseType(element.Type),
element.Normalized ? GL_TRUE : GL_FALSE,
layout.GetStride(),
(const void*)element.Offset);
m_VertexBufferIndex++;
break;
}
case ShaderDataType::Int:
case ShaderDataType::Int2:
case ShaderDataType::Int3:
case ShaderDataType::Int4:
case ShaderDataType::Bool:
{
glEnableVertexAttribArray(m_VertexBufferIndex);
glVertexAttribIPointer(m_VertexBufferIndex,
element.GetComponentCount(),
ShaderDataTypeToOpenGLBaseType(element.Type),
layout.GetStride(),
(const void*)element.Offset);
m_VertexBufferIndex++;
break;
}
case ShaderDataType::Mat3:
case ShaderDataType::Mat4:
{
uint8_t count = element.GetComponentCount();
for (uint8_t i = 0; i < count; i++)
{
glEnableVertexAttribArray(m_VertexBufferIndex);
glVertexAttribPointer(m_VertexBufferIndex,
count,
ShaderDataTypeToOpenGLBaseType(element.Type),
element.Normalized ? GL_TRUE : GL_FALSE,
layout.GetStride(),
(const void*)(element.Offset + sizeof(float) * count * i));
glVertexAttribDivisor(m_VertexBufferIndex, 1);
m_VertexBufferIndex++;
}
break;
}
default:
HZ_CORE_ASSERT(false, "Unknown ShaderDataType!");
}
}
m_VertexBuffers.push_back(vertexBuffer);
}
// 设置索引缓冲区(IBO)
void OpenGLVertexArray::SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer)
{
glBindVertexArray(m_RendererID);
indexBuffer->Bind();
m_IndexBuffer = indexBuffer;
}

Leave a comment