如何在OpenGL ES 2.0中正确更新顶点数组?

10

当我在OpenGL 2.0中更新iOS上的顶点数组时,原始顶点数据仍然停留在屏幕上--即第一次刷新是持久的(我向GPU发送的最初的点集每帧都会被渲染),但第二次、第三次、第四次...n次刷新似乎都会覆盖同一块内存。

所以我正在做:

vector<VertexType> rawDynamicData ;

glGenVertexArraysOES( 1, &va ) ;        CHECK_GL ;
glBindVertexArrayOES( va ) ;            CHECK_GL ;
glGenBuffers( 1, &vb ) ;                CHECK_GL ;
glBindBuffer( GL_ARRAY_BUFFER, vb ) ;   CHECK_GL ;

glBufferData( glBufferData(
  GL_ARRAY_BUFFER, //Specifies the target buffer object.
  rawDynamicData.size() * sizeof( VertexType ),
  &rawDynamicData[0],
  GL_DYNAMIC_DRAW  // I plan to update the data every frame
) ;  CHECK_GL ; 
在后续的帧中,我只是再次调用:
// update the data
glBufferData( glBufferData(
  GL_ARRAY_BUFFER, //Specifies the target buffer object.
  rawDynamicData.size() * sizeof( VertexType ),
  &rawDynamicData[0],
  GL_DYNAMIC_DRAW  // I plan to update the data every frame
) ;  CHECK_GL ; 

我也尝试过

//update the data
GLvoid* vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES); 
memcpy(vbo_buffer, &rawDynamicData[0], rawDynamicData.size()*sizeof(VertexType) );
glUnmapBufferOES(GL_ARRAY_BUFFER); 

然而,不论采用哪种方式,我都得到了这个结果:

enter image description here

白点是初始数据,红点是随后调用glBufferData的结果。

因此,关于OpenGL ES 2.0中动态VBOs,这个问题有几个方面:

  1. 创建一个元素将在每一帧完全更新的动态顶点缓冲区的正确命令顺序是什么?

  2. 顶点缓冲区是否可以在帧之间增长?还是必须精确地刷新相同的大小?

  3. 我知道使用“客户端内存”指针,你能在OpenGL ES 2.0中这样做(避免memcpy),还是这已经过时了,应该只使用顶点缓冲区?


赏金疯狂! - Fattie
2个回答

6

未触发 #1

我在这里找到了一个答案,它指向使用 glSubBufferData 来将数据存储在数组中,并仅在最初分配时使用 glBufferData。但最终这并没有起作用(如果vb太大,则只会更新前3个元素),

所以,

glBufferData( glBufferData(
  GL_ARRAY_BUFFER, //Specifies the target buffer object.
  rawDynamicData.size() * sizeof( VertexType ),
  0, // NO INITIAL DATA
  GL_DYNAMIC_DRAW  // I plan to update the data every frame
) ;  CHECK_GL ; 

那么对于第一个和随后的更新:

// Update the whole buffer
glBufferSubData(GL_ARRAY_BUFFER, 0,
  rawDynamicData.size()*sizeof(VertexType), &rawDynamicData[0]) ;

看起来好像可以。

“好像”并不行。

唯一能让它正常工作的方法是停止使用顶点缓冲区,改为使用客户端内存中的顶点数组

具体操作如下:

// do all your vertex attrib/glVertexAttrib enable commands:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexPC), &debugPoints->data[0].pos.x) ;
CHECK_GL ;
glEnableVertexAttribArray(0);  CHECK_GL ;

// ..do glVertexAttrib* for all attributes you have..

// then just flush your draw command
glDrawArrays(GL_POINTS, 0, debugPoints->data.size());  

简而言之,我发现对于动态数据,使用顶点缓冲区 可能 会面临挑战,或者 不被支持。

2
你最终解决了这个问题吗?我也遇到了同样的问题,似乎它可以工作,但实际上并没有。 - John Riselvato
2
现在我只在模型需要加载一次且之后不再更改的情况下使用VB。它们在变换下表现良好,但如果你尝试更改实际顶点数据,则会出现问题。对于可变形几何体,我仅使用顶点数组。在绘制顶点数组之前,请确保使用以下行禁用顶点缓冲区:glBindBuffer(GL_ARRAY_BUFFER, 0); // DISABLE THE STUPID VERTEX BUFFER RAT POISON。我之前也被这个问题绊倒了。 - bobobobo
1
哦,看起来三个月后不幸在谷歌上找到了相同的答案。唉。我真的不认为有一种干净的可能的方法来动态更新顶点数组。 - John Riselvato
@ bobobobo “我在这里找到了一个答案,它指向使用glSubBufferData来停放数组中的数据,并仅在最初分配时使用glBufferData。” 这是错误且具有误导性的。如果您将更新小节,请使用glBufferSubData。如果您将更新整个VBO,请使用glBufferData。更新整个VBO的另一种方法是使用空指针调用glBufferData(告诉驱动程序您不关心旧内容),然后使用glBufferSubData将缓冲区更新为新内容。 - arkon
@bobobobo 我在Android上遇到了类似的问题,并注意到如果与VAO一起使用glBufferSubData()来更新顶点数据,那么它就能够正常工作。实际上,如果我禁用VAO,在桌面上也会遇到同样的问题(在更新数据时出现奇怪的故障)。然而,这会导致另一个问题:VAO扩展并不适用于所有的GLES设备。因此,也许现在最好使用你的顶点数组方法 :) - juzzlin
显示剩余4条评论

1

对于Android用户而言,我刚刚确认在OpenGL ES 2.0上使用顶点属性数组可以实现可变形几何体,通过一个小测试在Android上实现。让人不爽的是你不能传递指向自己数组的指针,而是必须使用FloatBuffer(或类似类型)。示例代码:

初始化:

    // initialize vertex byte buffer for shape coordinates
    ByteBuffer bb = ByteBuffer.allocateDirect(
            // (number of coordinate values * 4 bytes per float)
            coords.length * 4);
    // use the device hardware's native byte order
    bb.order(ByteOrder.nativeOrder());

    // create a floating point buffer from the ByteBuffer
    vertexBuffer = bb.asFloatBuffer();
    // add the coordinates to the FloatBuffer
    vertexBuffer.put(coords);
    // set the buffer to read the first coordinate
    vertexBuffer.position(0);

绘图代码:
    // get handle to vertex shader's vPosition member
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
    checkGlError("glGetAttribLocation");

    // Enable a handle to the triangle vertices
    GLES20.glEnableVertexAttribArray(mPositionHandle);
    checkGlError("glEnableVertexAttribArray");

    float newValue = (float) Math.sin((float) (frame++) / 1000) + 1;
    System.out.println(newValue);

    vertexBuffer.put(0, newValue);

    // Prepare the triangle coordinate data
    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, VERTEX_STRIDE, vertexBuffer);

@juzzlin 你是在说那个设备支持GLES20,但不是所有的方法都被支持吗? - Andy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接