Update vertices and color of thousands of meshes - asking for a strategy

geometry
performance

#1

Hi all!

I am working on the following application:

This is a globe made up of cells. Each cell is a mesh of 7 vertices (1 center and 6 on the edge). I am using the MeshStandardMaterial with different color codes. When the user zooms in an update is triggered and a new layer (= smaller cells) will be loaded for a smaller area, not the entire globe. The previous cells will be disposed() like this.

As you can guess, this is not very effective as I am essentially running,

dataFromServer.map( ( cell ) => {
    var geometry = createRegionGeom( cell.verticies );
    var material = createRegionMaterial( cell.color );
    scene.add(new THREE.Mesh( geometry, material ));
})
render();

where dataFromServer contains the information for thousands of cells coming from a webworker every time there is an update.

The Problem with that is, that scene.add() and the subsequent render() cause a pretty significant lag which I want to avoid.

My Idea is to pre-create, let’s say 10000 meshes, at the world center (each mesh with 7 vertices) reusing the same MeshStandardMaterial in #FFFFFF and storing their UUIDs in an array.

var material = createRegionMaterial( '#FFFFFF' );
var geometry = createRegionGeom( SomeSevenVerticies );
geometry.verticesNeedUpdate = true;
geometry.dynamic = true;
var arrayOfPrecreatedMeshes = [ ];
for ( var i = 0 ; i < 10000 ; i++ ){
    var mesh = new THREE.Mesh( geometry, material );
    arrayOfPrecreatedMeshes.push( mesh.uuid );
    scene.add( mesh );
}
render();

At this point all the meshes that will ever be displayed are part of the scene.

Question: Could I use this method to update verticies, and also set the color of the mesh as shown below?

dataFromServer( ( cell ) => {
    const precreatedCell = scene.getObjectByProperty( 'uuid', arrayOfPrecreatedMeshes[idx] );
    for ( var v = 0; v < 7 ; v++ ){
        precreatedCell.geometry.vertices[ v ] = cell.verticies[ v ]; 
    }
    precreatedCell.material.color.setStyle( cell.color );
});

Or is my idea non-sense and will not improve performance?

Any other ideas or pointers are much appreciated. Thank you.


#2

Have you considered to move away from Geometry and use BufferGeometry instead? Geometry is internally converted to BufferGeometry and the related overhead might be (partly) responsible for your performance problem. Also the pure creation of a Geometry is more costly since it involves much more object creation.


#3

You will want to avoid having thousands of meshes and materials, to maintain 60 fps. Perhaps start with a single large BufferGeometry using vertex colors, see how performant the updates are, and try to optimize that?


#4

Thanks!

I did not before. But I have just finished implementing BufferGeometry using VertexColors as both of you suggested. The lag is a little less.


What about the idea of pre-creating the meshes using BufferGeometry and VertexColors and then just updating their vertices and color? Or does that trigger a complete upload of the mesh to the GPU again?

Just tried it, not a good idea.


I am not exactly sure what you mean by that. I could Group() the cells and add the group to the scene, I suppose. Or do you mean to reduce the number of vertices by reusing vertices of one cell in an adjacent cell? I need the cells to be clickable later on, and I thought working on a cell by cell basis may be easier.


#5

I’m suggesting having only a single Mesh in the scene, with a single BufferGeometry. With thousands of meshes, you’re very likely to be CPU-constrained on your framerate. For the easy test, you could construct all of the cell geometries individually, give them vertex colors and an ID in a new attribute, and then merge them with BufferGeometryUtils.mergeBufferGeometries( geometries ). But it would probably be more efficient to just construct the BufferGeometry from scratch.

Once you’ve done that, you can update colors with geometry.attributes.position.updateRange(). Also consider .setDynamic(true).

How so? This sounds like the right idea, except (ideally) without a separate mesh for each cell.

How often do you expect to update colors? E.g. if you plan to animate all off the cell colors independently, on every frame, that will be more complicated…


#6

I would suggest taking a look at geometry instancing. Each hexagon is then a single instance with attributes for color, offsetposition and quaternion. This should support perhaps 10k changed “meshes” per frame.


#7

@donmccurdy, thanks for the clarification, this works beautifully. I could not reduce it to one single mesh though as I could not figure out how to merge meshes when they come from multiple web-workers, I will tackle that later.

Yeah, that was for separate meshes for each cell, the way you proposed is fast enough for now.

The goal is to visualize time series data, i.e. there will be a slider that allows the user to ‘slide’ through time, this (potentially) changes the color of every cell.

@Oskasb, thanks for the suggestion, I will check if I can use that to change the color of my cells on each frame.


#8

I used this as a starting point for something similar: https://threejs.org/examples/#webgl_buffergeometry_instancing_dynamic

On top of that you probably want to add at least a color attribute, possibly also scale) .-)