gpt4 book ai didi

c++ - 如何使用多个统一缓冲区对象

转载 作者:行者123 更新时间:2023-12-05 03:30:20 25 4
gpt4 key购买 nike

在我的 OpenGL ES 3.0 程序中,我需要有两个独立的统一缓冲区对象 (UBO)。只要有一个最终受益人,事情就会按预期进行。该案例的代码如下所示:

GLSL 顶点着色器:

version 300 es

layout (std140) uniform MatrixBlock
{
mat4 matrix[200];
};

C++头文件成员变量:

GLint  _matrixBlockLocation;
GLuint _matrixBuffer;

static constexpr GLuint _matrixBufferBindingPoint = 0;

glm::mat4 _matrixBufferContent[200];

初始化 UBO 的 C++ 代码:

_matrixBlockLocation = glGetUniformBlockIndex(_program, "MatrixBlock");

glGenBuffers(1, &_matrixBuffer);
glBindBuffer(GL_UNIFORM_BUFFER, _matrixBuffer);
glBufferData(GL_UNIFORM_BUFFER, 200 * sizeof(glm::mat4), _matrixBufferContent, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, _matrixBufferBindingPoint, _matrixBuffer);
glUniformBlockBinding(_program, _matrixBlockLocation, _matrixBufferBindingPoint);

为了更新 UBO 的内容,我修改了 _matrixBufferContent 数组,然后调用

glBufferSubData(GL_UNIFORM_BUFFER, 0, 200 * sizeof(glm::mat4), _matrixBufferContent);

这如我所料。在顶点着色器中,我可以访问矩阵,生成的图像应该是这样的。


OpenGL ES 3.0 规范定义每个 UBO 的最大可用存储空间为 16K (GL_MAX_UNIFORM_BLOCK_SIZE)。因为我的矩阵数组的大小接近这个限制,所以我想创建第二个 UBO 来存储额外的数据。但是,一旦我添加了第二个 UBO,我就遇到了问题。下面是创建两个 UBO 的代码:

GLSL 顶点着色器:

version 300 es

layout (std140) uniform MatrixBlock
{
mat4 matrix[200];
};

layout (std140) uniform HighlightingBlock
{
int highlighting[200];
};

C++头文件成员变量:

GLint  _matrixBlockLocation;
GLint _highlightingBlockLocation;
GLuint _uniformBuffers[2];

static constexpr GLuint _matrixBufferBindingPoint = 0;
static constexpr GLuint _highlightingBufferBindingPoint = 1;

glm::mat4 _matrixBufferContent[200];
int32_t _highlightingBufferContent[200];

初始化两个 UBO 的 C++ 代码:

_matrixBlockLocation       = glGetUniformBlockIndex(_program, "MatrixBlock");
_highlightingBlockLocation = glGetUniformBlockIndex(_program, "HighlightingBlock");

glGenBuffers(2, _uniformBuffers);
glBindBuffer(GL_UNIFORM_BUFFER, _uniformBuffers[0]);
glBufferData(GL_UNIFORM_BUFFER, 200 * sizeof(glm::mat4), _matrixBufferContent, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, _uniformBuffers[1]);
glBufferData(GL_UNIFORM_BUFFER, 200 * sizeof(int32_t), _highlightingBufferContent, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, _matrixBufferBindingPoint, _uniformBuffers[0]);
glBindBufferBase(GL_UNIFORM_BUFFER, _highlightingBufferBindingPoint, _uniformBuffers[1]);
glUniformBlockBinding(_program, _matrixBlockLocation, _matrixBufferBindingPoint);
glUniformBlockBinding(_program, _highlightingBlockLocation, _highlightingBufferBindingPoint);

要更新第一个 UBO,我仍然修改 _matrixBufferContent 数组,然后调用

glBindBuffer(GL_UNIFORM_BUFFER, _uniformBuffers[0]);
glBufferSubData(GL_UNIFORM_BUFFER, 0, 200 * sizeof(glm::mat4), _matrixBufferContent);

为了更新第二个 UBO,我修改了 _highlightingBufferContent 数组的内容,然后调用

glBindBuffer(GL_UNIFORM_BUFFER, _uniformBuffers[1]);
glBufferSubData(GL_UNIFORM_BUFFER, 0, 200 * sizeof(int32_t), _highlightingBufferContent);

据我所知,第一个 UBO 仍然按预期工作。但是我在顶点着色器中获取的数据并不是我最初放入_highlightingBufferContent中的数据。如果我将此代码作为 WebGL 2.0 代码运行,我会在 Google Chrome 中收到以下警告:

GL_INVALID_OPERATION: It is undefined behaviour to use a uniform buffer that is too small.

在 Firefox 中,我得到以下信息:

WebGL warning: drawElementsInstanced: Buffer for uniform block is smaller than UNIFORM_BLOCK_DATA_SIZE.

所以,不知何故,第二个 UBO 没有正确映射。但是我看不出哪里出了问题。如何创建两个单独的 UBO 并在同一个顶点着色器中使用它们?


编辑

查询 OpenGL 期望的 GL_UNIFORM_BLOCK_DATA_SIZE 背后的值表明它需要比现在大 4 倍。以下是我查询值的方式:

GLint matrixBlock       = 0;
GLint highlightingBlock = 0;
glGetActiveUniformBlockiv(_program, _matrixBlockLocation, GL_UNIFORM_BLOCK_DATA_SIZE, &matrixBlock);
glGetActiveUniformBlockiv(_program, _highlightingBlockLocation, GL_UNIFORM_BLOCK_DATA_SIZE, &highlightingBlock);

本质上,这意味着缓冲区大小必须是

200 * sizeof(int32_t) * 4

不仅仅是

200 * sizeof(int32_t)

但是,我不清楚为什么会这样。我将 32 位整数放入该数组,我希望它的大小为 4 个字节,但不知何故它们似乎有 16 个字节长。还不确定发生了什么。

最佳答案

正如问题的编辑部分和 Beko 的评论所暗示的那样,有与 OpenGL 的 std140 布局相关的特定对齐规则。 OpenGL ES 3.0 standard指定以下内容:

  1. If the member is a scalar consuming N basic machine units, the base alignmentis N.
  2. If the member is a two- or four-component vector with components consumingN basic machine units, the base alignment is 2N or 4N, respectively.
  3. If the member is a three-component vector with components consuming Nbasic machine units, the base alignment is 4N.
  4. If the member is an array of scalars or vectors, the base alignment and arraystride are set to match the base alignment of a single array element, accordingto rules (1), (2), and (3), and rounded up to the base alignment of a vec4. Thearray may have padding at the end; the base offset of the member followingthe array is rounded up to the next multiple of the base alignment.

注意强调“四舍五入到 vec4 的基本对齐方式”。这意味着数组中的每个整数不仅仅占用 4 个字节,而是占用 vec4 的 4 倍大小。

因此,数组必须是原始大小的 4 倍。此外,在使用 glBufferSubData 复制数组内容之前,需要将每个整数填充到相应的大小。如果不这样做,数据就会错位,因此会被 GLSL 着色器误解。

关于c++ - 如何使用多个统一缓冲区对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70832377/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com