Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to quickly update a large BufferGeometry?

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!

like image 599
Joey Morani Avatar asked Jul 01 '13 18:07

Joey Morani


2 Answers

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;
like image 116
Jason Harwig Avatar answered Jan 02 '23 05:01

Jason Harwig


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.

like image 27
alexandre Avatar answered Jan 02 '23 04:01

alexandre