将矩阵建模为单个统一缓冲区对象

Batch model matrixes to single Uniform Buffer Object

本文关键字:缓冲区 对象 单个统 建模      更新时间:2023-10-16

我决定看看统一的缓冲区对象。但我不确定何时何地不使用它。

我尝试将所有模型转换批处理到单个数组中,然后立即发送到着色器。但它有它的后果。我还必须发送到每个顶点的着色器 id 以匹配这些转换。

所以我的问题是:它值得吗?还是在这种特殊情况下,我应该更喜欢使用常规的glUniform调用?

下面是我的着色器程序。

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in int id;
layout (std140) uniform scene_data
{
mat4 ViewProjectionMatrix;
mat4 ModelMatrix[128];
};

void main()
{
gl_Position = ViewProjectionMatrix * ModelMatrix[id] * vec4(position,1.0);  

}

以下是我创建统一缓冲区对象的方法:

Dot::UniformBuffer::UniformBuffer(const void*data, unsigned int size, unsigned int index)
:m_Index(index),m_size(size)
{
glGenBuffers(1, &m_UBO);
glBindBuffer(GL_UNIFORM_BUFFER, m_UBO);
glBufferData(GL_UNIFORM_BUFFER, size, data, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, index, m_UBO);  
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
Dot::UniformBuffer::~UniformBuffer()
{
glDeleteBuffers(1, &m_UBO);
}

void Dot::UniformBuffer::Update(const void* data)
{
glBindBuffer(GL_UNIFORM_BUFFER, m_UBO);
GLvoid* p = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY);
memcpy(p, data, m_size);
glUnmapBuffer(GL_UNIFORM_BUFFER);
}

一切都有其权衡,并且需要性能测量来验证。

glUniform视为按值传递,而 UBO 是按引用传递。 除了 GPU 着色器代码中 [通常] 没有性能损失,用于对 UBO 的额外间接寻址。

UBO的主要优点是在"绑定"阶段减少CPU开销,因为所有值都已经写入内存。渲染可以在加载时预先准备许多 UBO(每个"状态向量"一个 UBO),以避免在主渲染循环期间传输数据。 通常,您不希望在渲染器中就地修改 UBO,因为glMapBuffer将等待使用该 UBO 的先前绘制完成。

在这个特定示例中,着色器使用输入属性id执行"索引常量查找",这比统一常量查找慢。

其他注意事项:

  • layout (location = 1) in int id;要求 CPU 准备额外的顶点缓冲区和索引缓冲区。 这也可能意味着制作原始几何图形的数据副本,这会占用更多的 CPU 和内存。
  • glDrawArraysInstancedglDrawElementsInstanced可以让OpenGL为您生成id。 在着色器中,使用gl_InstanceID。 无需额外的顶点缓冲区。 这将类似于 https://learnopengl.com/Advanced-OpenGL/Instancing
  • glUniform将导致数据在每次调用时从应用程序代码(>驱动程序)> GPU 传输。
  • UBO需要更多的对象管理和规划,但通常集中在性能最快的渲染器上。 Vulkan 和 D3D12 仅支持 UBO 风格的编程。