使用 glBufferSubData() 更新 VBO 数据

Posted

技术标签:

【中文标题】使用 glBufferSubData() 更新 VBO 数据【英文标题】:Updating VBO data with glBufferSubData() 【发布时间】:2012-08-14 19:21:25 【问题描述】:

我目前正在开发一个使用 Java 1.6 和 LWJGL 编写的二维自上而下 RPG。我在我的游戏中实现了 VBO 的使用,现在我维护其中的两个:一个用于顶点数据,一个用于纹理坐标。

除了我仍然没有真正合乎逻辑的方式来更新对象之外,一切都很好。例如,如果我希望某个图块更改其纹理(更改 VBO 内的纹理坐标以显示纹理表的另一个区域),我看不到仅更改对应于的纹理坐标的方法这个单一的瓷砖。我现在能想到的可能就是用每个循环所需的所有数据填充缓冲区,然后每帧用 glBufferData 上传它们。它有效,但似乎不是最好的方法(或者确实有效?)。

现在,glBufferSubData 命令不会分配新内存,而只会更改我告诉它更改的部分。问题是我不知道如何跟踪必须更改的区域(偏移量)。 LWJGL 提供了一个 glBufferSubData(target, offset, data);命令,它只需要缓冲区的开始即可工作。偏移量是否类似于索引?

所以如果我首先将这些缓冲区上传到 VBO,然后想更改第二个 float[] 的第二个值:

FloatBuffer vertexData = BufferUtils.createFloatBuffer(amountOfVertices * vertexSize);
vertexData.put(new float[]100, 100, 300, 100, 300, 300, 100, 300);
vertexData.put(new float[]400, 100, 600, 100, 600, 300, 400, 300);
vertexData.flip();

我会生成新数据,把它放在一个小的 FloatBuffer 中,然后用 glBufferSubData(GL_VERTEX_ARRAY, 10, newFloatBuffer); 上传? 10 是因为我想从第十个旧值开始更改值。

对吗?有更好的方法吗?再说一遍:如果我每帧重新上传整个数据可以吗?

【问题讨论】:

有更简单的方法可以做到这一点。要么你简单地用另一个纹理绘制相同的几何图形。或者你在顶点着色器中进行纹理坐标修改。 【参考方案1】:

LWJGL's glBufferSubData() 期望偏移量为字节数,因为应该能够将任意数据写入任意缓冲区位置。如果要更新索引i 处的浮点数,则偏移量将为i * 4,因为java 浮点数采用非常常见的4 个字节。所有剩余的缓冲区内容都将被写入,因此,目标缓冲区的容量必须至少为 (i + floatBuffer.remaining()) * 4 字节。 正确的准备请参考Buffersflip()方法或者position(int)和limit(int)一起指定要写入的剩余内容。

只要不使用临时 FloatBuffers 多次调用 glBufferSubData(),这种更新策略是可以的。 您还可以有第二个大小相同的目标缓冲区用于交换(乒乓球),其中一个可以使用 MapBuffer() 映射以进行整体更新,而另一个正在使用中。

我宁愿在客户端持有一个完整的 FloatBuffer 并使用glBufferData() 一次上传,而不是多次调用glBufferSubData()。但不知道一些数字,就很难说。

【讨论】:

非常感谢。我将实施 Client FloatBuffer 策略并暂时保留它。稍后我会尝试顶点着色器。 不客气!着色器是一个有趣的领域。如果您必须更改相同大小的纹理区域,阵列纹理可能是纹理贴图/图纸的替代品。 3D 纹理可用于额外平滑地融合变化。

以上是关于使用 glBufferSubData() 更新 VBO 数据的主要内容,如果未能解决你的问题,请参考以下文章

为啥 glBufferSubData 需要等到 glDrawElements 不使用 VBO?

使用glMapBuffer的缓冲区是否会分配比glBufferSubData更慢的VRAM?

结构的 glBufferSubData 偏移量

OpenGL glBufferSubData 偏移问题

在 OpenGL ES Android 中使用 glBufferSubData 和 glMultiDrawArrays

带有 glMapBuffer 的 Buffer 会被分配比 glBufferSubData 更慢的 VRAM 吗?