使用统一缓冲区对象进行批量渲染

Posted

技术标签:

【中文标题】使用统一缓冲区对象进行批量渲染【英文标题】:Batched rendering with Uniform Buffer Object 【发布时间】:2016-05-24 12:18:05 【问题描述】:

我正在更改我的基本渲染器,使其成为批处理渲染器以提高性能。 我已经成功地更改了我的顶点数据以在一个批次中绘制我的所有网格,但是当我想要处理多种材质时出现了问题。

显然我不能使用普通的uniforms,因为我要批处理很多网格,所以我考虑使用统一缓冲区对象来存储所有材质数据。

问题是,如何更新这个缓冲区?

如果我这样设置我的 UBO:

layout(std140) uniform MATERIAL

    vec4 Color[20];
    float Specular[20];
    float Roughness[20];
    float Metallic[20];
    float ReflectionIntensity[20];
;

我不这么认为,那么我在提交单一材料的数据时,可以使用根据材料索引计算的起始偏移量,然后像这样提交数据:

struct Material 
    float color[4];
    float specular;
    float roughness;
    float metallic;
    float reflectionIntensity;
;

int bufferStride = 32; // 16 (vec4) + 4 (float) + 4 (float) + 4 (float) + 4 (float)
int offset = bufferStride * updatingMaterialIndex;

Material m;
m.color...

glBufferSubData(GL_UNIFORM_BUFFER, offset, &m, sizeof(Material));

因为我假设 OpenGL 将在内存中有 20 个vec4,然后是 80 个float

那么我该怎么做呢?我必须单独计算每个元素的偏移量吗?

另外,我应该如何索引网格使用的材料?我应该将材质索引作为顶点属性传递吗?

【问题讨论】:

【参考方案1】:

您最初的建议是包含数组的结构。那么为什么不做相反的事情:一个包含结构的数组呢?

struct Material

    vec4 Color;
    float Specular;
    float Roughness;
    float Metallic;
    float ReflectionIntensity;


layout(std140) uniform Materials

    Material mtls[20];
;

然后您将在 CPU 上构建一个类似的数组。

现在要执行此操作,您必须确保遵循std140 的对齐规则。特别重要的是结构和数组的对齐。由于Material 中有一个vec4,它的基本对齐是16。并且数组元素之间的步幅必须是数组元素基本对齐的倍数。

因此,mtls[20] 的大小将是 32 * 20 字节。即使您从 Material 中删除了一个浮点数,它仍然是那个大小。

如果您真的想保留 struct-of-array 方法,您只需在 CPU 上使用相应的数据结构即可。

【讨论】:

好吧,我真的不需要坚持数组结构的方法,我可以按照你的建议去做(这也更容易想到)。另外,我认为对齐不会成为问题,因为我正在查询统一块以了解偏移量和所有内容。我会试试的。 从我的方法 (struct-of-array) 到您的方法发生了一些变化:使用我的方法,缓冲区大小为 1600 字节,每个元素与前一个元素的偏移量为 320 字节;通过您的方法,总缓冲区大小现在为 640 字节,并且该结构的每个元素的偏移量为 [0, 16, 20, 24, 28] 字节。这样对吗?使用您的方法,我也可以拥有更大的缓冲区! @zeb:“按照我的方法,缓冲区大小为 1600 字节”不,不是。 (16+4+4+4+4) * 20 == (16*20 + 4*20 + 4*20 + 4*20 + 4*20)。无论哪种方式,数学计算结果都是一样的。 “我正在查询统一块以了解偏移量和所有内容。” 为什么? std140 布局的重点 是您没有 查询偏移量和对齐方式。您可以从结构定义中先验地了解它们。 好的,我明白你的意思。我只是想确定一下。顺便说一句,“缓冲区大小为 1600”,我通过使用 glGetActiveUniformBlockivGL_UNIFORM_BLOCK_DATA_SIZE 得到了这个值 @zeb:我忘记了规则 #4:数组的步幅总是向上舍入到 vec4 的对齐。因此,数组结构中的每个float 数组元素占用的大小与vec4 相同。这就是差异的来源:5 * (16*20) = 1600。

以上是关于使用统一缓冲区对象进行批量渲染的主要内容,如果未能解决你的问题,请参考以下文章

如何在多采样纹理上渲染帧缓冲区对象?

OpenGL/C++:使用顶点缓冲区进行从后到前渲染

我是不是允许在 OpenGL 2.1 的多个共享上下文中同时从同一个缓冲区对象进行渲染?

绘制渲染缓冲区对象的内容

使用 glMapBuffer 用一个缓冲区渲染多个对象?

webGL2统一缓冲区对象和布局的使用(std140)