I'm using a BufferGeometry to draw thousands of cubes which makes up the terrain, but having a hard time finding out how to update the geometry if I need to change the position of one of the cubes. For example, I have this code to initialize my geometry: (I'm doing my testing on an updated version of this example)
// 12 triangles per cube (6 quads)
var triangles = 12 * 150000;
var geometry = new THREE.BufferGeometry();
geometry.attributes = {
position: {
itemSize: 3,
array: new Float32Array( triangles * 3 * 3 ),
numItems: triangles * 3 * 3
},
normal: {
itemSize: 3,
array: new Float32Array( triangles * 3 * 3 ),
numItems: triangles * 3 * 3
},
color: {
itemSize: 3,
array: new Float32Array( triangles * 3 * 3 ),
numItems: triangles * 3 * 3
}
}
positions = geometry.attributes.position.array;
normals = geometry.attributes.normal.array;
colors = geometry.attributes.color.array;
If I move a cube, it won't appear to be moved until I do:
geometry.attributes.position.needsUpdate = true;
This causes the FPS to drop while it's being updated. Is there another way I could do this? Seems a bit unnecessary when I'm only changing one cube (12 triangles/36 vertices). Although it might be - I haven't checked what needsUpdate
actually does. Guessing it sends the array to the shader again.
I was thinking I could split the geometry into separate smaller BufferGeometries but I'm not sure if that would help the performance overall. From what I understand, more geometries = less FPS.
If anyone has any ideas how I would do this, it would be appreciated! :) Besides the updating issue, BufferGeometry seems to be exactly what I need. Thanks!
As of r71
threejs supports bufferSubData
.
Use a DynamicBufferAttribute
, (or just set updateRange
correctly)
var positionAttr = geometry.attributes.position
// if not using DynamicBufferAttribute initialize updateRange:
// positionAttr.updateRange = {};
positionAttr.updateRange.offset = 0; // where to start updating
positionAttr.updateRange.count = 1; // how many vertices to update
positionAttr.needsUpdate = true;
I had the same problem with a very large BufferGeometry
when updating a few vertices in it.
A solution is to use _gl.bufferSubData
instead of _gl.bufferData
to update just a part of the buffer. (three.js uses only _gl.bufferData
).
So if you update the positions you will do:
// Create a view of the data to update from index offsetSub to offsetSubEnd
var view = geometry.attributes.position.array.subarray(offsetSub, offsetSubEnd);
// Bind the buffer for positions (get the gl context with webglrenderer)
_gl.bindBuffer(_gl.ARRAY_BUFFER, geometry.attributes.position.buffer);
// Insert the new values at good index in gpu buffer (offsetGeometry is in byte)
_gl.bufferSubData(_gl.ARRAY_BUFFER, offsetGeometry,view);
Note that this solution can be slower if you change a large part of the buffer vertices compared to _gl.bufferData
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With