修改OpenGL顶点缓冲区的正确方法是啥?

Posted

技术标签:

【中文标题】修改OpenGL顶点缓冲区的正确方法是啥?【英文标题】:What is the proper way to modify OpenGL vertex buffer?修改OpenGL顶点缓冲区的正确方法是什么? 【发布时间】:2013-04-04 21:22:36 【问题描述】:

我正在 OpenGL 中设置一个顶点缓冲区,如下所示:

int vboVertexHandle = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
glBufferData(GL_ARRAY_BUFFER, vertexData, GL_DYNAMIC_DRAW);

稍后,如果我想向“vertexData”添加或删除顶点,那么正确的方法是什么?甚至可能吗?我假设我不能直接修改数组而不将其重新发送到 GPU。

如果我修改了 vertexData 数组,那么再次调用它:

glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
glBufferData(GL_ARRAY_BUFFER, vertexData, GL_DYNAMIC_DRAW);

...这会用我的新数据覆盖旧缓冲区吗?还是我也必须删除旧的?有没有更好的办法?

【问题讨论】:

【参考方案1】:

当您调用glBufferData 时,会设置任何 OpenGL 缓冲区对象的大小。也就是说,OpenGL 将分配您在 glBufferData 的第二个参数中指定的内存量(在 OP 中没有列出)。事实上,如果你调用,例如glBufferData( GL_ARRAY_BUFFER, bufferSize, NULL, GL_DYNAMIC_DRAW ); OpenGL 将创建一个bufferSize 字节的未初始化数据 的缓冲区。

您可以使用glBufferSubDataglMapBuffer 或任何其他用于传递数据的例程加载任意数量的数据(最多为缓冲区大小)。调整缓冲区大小的唯一方法是调用 glBufferData 并为相同的缓冲区 id(从 glGenBuffers 返回的值)设置新的大小。

也就是说,您始终可以使用缓冲区中的数据子集(这类似于删除顶点),如果您使用glDrawElements 进行渲染,则可以随机访问缓冲区中的元素。向缓冲区添加顶点需要分配更大的缓冲区,然后您需要重新加载缓冲区中的所有数据。

【讨论】:

【参考方案2】:

http://www.opengl.org/wiki/GLAPI/glBufferData

glBufferData 为当前绑定到目标的缓冲区对象创建一个新的数据存储。任何预先存在的数据存储都将被删除。

这解释了对glBufferData 的调用将“重新分配”数据,缓冲区将具有新的大小。所有旧数据都将丢失。如果您只想写入缓冲区的一部分,请改用glBufferSubData

http://www.opengl.org/wiki/GLAPI/glBufferSubData

编辑:glBufferSubData 仅用于替换数据;要调整缓冲区大小,您必须调用 glBufferData

此外,您不必先销毁缓冲区并生成新缓冲区。请记住,GLuint 只是 OpenGL 的一种“指针”。它不是实际的存储,因此重复使用相同的“指针”是完全可以的(当然,如果你还没有删除它)。

【讨论】:

与其说 GLuint 是一个指针,不如说 GLuint 是一个句柄。【参考方案3】:

1 使用 glMapBuffer

void mapBuffer(uint &id, void *data, uint size, uint type) 
    glBindBuffer(type, id);
    // get pointer
    void *ptr = glMapBuffer(type, GL_WRITE_ONLY);
    // now copy data into memory
    memcpy(ptr, data, size);
    // make sure to tell OpenGL we're done with the pointer
    glUnmapBuffer(type);

2 使用 glBufferSubData

 void updateBuffer(uint &id, uint offset, void *data, uint size, uint type) 
        glBindBuffer(type, id);
        glBufferSubData(type, offset, size, data);
    

指定要替换的数据存储区域的大小(以字节为单位)

mapBuffer(vbo, vertex.data(), sizeof(uint)*vertex.size(), GL_ARRAY_BUFFER);
resetBuffer(vbo, vertex.data(), sizeof(uint)*vertex.size(), GL_ARRAY_BUFFER);
updateBuffer(vbo, 0, vertex.data(), sizeof(uint)*vertex.size(), GL_ARRAY_BUFFER);

如果你想要你也可以使用 glBufferData -> 但它会删除所有旧数据并重用相同的缓冲区 glInvalidateBufferSubData -> 到处都是 NULL,现在你可以提供自己的数据了。

【讨论】:

以上是关于修改OpenGL顶点缓冲区的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL顶点缓冲区不正确的渲染

OpenGL:如何通过 API 知道顶点是不是正在命中帧缓冲区

QT OpenGL,顶点缓冲区对象和 GLEW?

JOGL/OpenGL VBO - 如何渲染顶点?

OpenGL顶点缓冲区混淆

从顶点着色器中修改着色器存储缓冲区对象