Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebGL: Optimizing a vertex buffer that changes values & vertex count every frame

I'd like to implement a renderer with vertex buffers that will be updated on the application side every frame. Additionally, the number of vertices (i.e., number of triangles) will be changing every frame as well.

My approach would be to pre-allocate the maximum needed once as a Float32Array, then update just the values that change, and update the buffer data with bufferSubData. Then draw the ones I want by sending a range from the index buffer.

As a minimal example, let's say I have allocated position vertices for 2 separate triangles in a Float32Array, and for this frame I just want to move and draw the 2nd triangle. I would think I'd do:

arrPos[9] += 1.0; // move the X coordinates in the Float32Array
arrPos[12] += 1.0;
arrPos[15] += 1.0;
gl.bindBuffer(gl.ARRAY_BUFFER, bufPos); // tell GL which buffer to use
gl.bufferSubData( gl.ARRAY_BUFFER, 3 * 3 * 4, arrPos ); // update the vertices - ERROR

// then just draw the 2nd traingle by sending its indices only
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufId); // tell GL which buffer to use
gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_SHORT, 3 * 2); // draw just this range

Problem is, bufferSubData throws the error: "GL ERROR :GL_INVALID_VALUE : glBufferSubData: out of range"

I've tried this using offsets 3, 9, 12 for bufferSubData just for the heck of it, but they all give me the same error.

On another note: if I can ever get this working, it appears to me that if I want to draw a variable number of triangles every frame while re-using this pre-allocated Float32Array, I'll need to update the values at the END of the array, not the start, since I can only specify the offset, not the start index, to bufferSubData. I'm hoping someone can tell me if this approach can work or not, or if I'm completely off track and should stop wasting my time.

like image 990
pixelmike Avatar asked Nov 10 '13 16:11

pixelmike


1 Answers

Okay, I think I just realized what the problem is. I don't think you can specify a range of a Float32Array to bufferSubData; instead it must take the ENTIRE array. (A Float32Array that is smaller than the buffer can be used to update a range of the buffer.)

In other words, I think a new Float32Array must be created EVERY TIME the number of vertices to be updated changes. This seems pretty ineffecient to me, but I guess that's just the way it is. IMO this distinction should be better explaind in the OpenGL docs.

david van brink suggested using ArrayBufferView to create a sub array of the Float32Array, and from my initial testing it seems to work. My previous code can be fixed simply by doing:

arrPos[9] += 1.0; // move the X coordinates in the Float32Array
arrPos[12] += 1.0;
arrPos[15] += 1.0;
var arrView = arrPos.subarray(9); // specify a range of the Float32Array
gl.bindBuffer(gl.ARRAY_BUFFER, bufPos); // tell GL which buffer to use
gl.bufferSubData( gl.ARRAY_BUFFER, 3 * 3 * 4, arrView ); // update the vertices

// then just draw the 2nd traingle by sending its indices only
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufId); // tell GL which buffer to use
gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_SHORT, 3 * 2); // draw just this range
like image 101
pixelmike Avatar answered Oct 25 '22 18:10

pixelmike