使用 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?