WAYNETS.ORG

Game and Program

基础

作者:

发表于

Context Polling System Post-Processing Renderer

索引缓冲对象(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;
}

Read Next:


Leave a comment