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 pletely off track and should stop wasting my time.
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 pletely off track and should stop wasting my time.
Share Improve this question asked Nov 10, 2013 at 16:20 pixelmikepixelmike 1,9831 gold badge20 silver badges33 bronze badges 2- 2 Is ArrayBufferView maybe the missing trick here? I'm not too fluent on buffer management yet, but I'm pretty sure you can reuse your buffer, and do partial updates as you want... Thanks for this question, now I want to learn more! – david van brink Commented Nov 10, 2013 at 17:36
- Aha! Yes, that appears to work! So we can use the Float32Array.subarray method to choose the source array range, and bufferSubData to specify the destination replacement range. Then the only 'new' object we need to create each frame is the ArrayBufferView. Thanks very much, it did not occur to me to look into the TypedArray methods, doh. – pixelmike Commented Nov 10, 2013 at 19:06
2 Answers
Reset to default 3Okay, 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
The biggest problem is gl.bufferSubData( gl.ARRAY_BUFFER, 3 * 3 * 4, arrPos );
You're about to upload the whole array on an offset. which is ofcourse too large. The post is serveral years old, but it might help others.
You were probably looking for this one:
var offset = 3 * 3 * 4;
var count = 7 * 4; // x1, y1, z1, x2, y2, z2, x3 (arrPos[9] -> arrPos[15])
gl.bufferSubData(this.gl.ARRAY_BUFFER, offset, arrView, offset, count);