Seeking Advice on Three.js Instance Placement Performance

Hello, everyone.

I am once again requiring your aid.

I’m currently working on a project where my goal is to dynamically place instances of objects with clicks (like voxels) to make it more mobile friendly, but I’ve encountered performance issues. Specifically, when I place around 400 instances of a single object (see video), my gaming computer stutters, and CPU & GPU usage spikes. It does get better if I refresh the page though. The instances are supposed to be synced with all connected clients.

I suspect that I might not be handling instancing correctly or that there’s a memory leak somewhere, but i cant seem to find the culprit. Any assistance would be appreciated!

The asset in question has:

  • Vertices: 1259
  • Edges: 1766
  • Faces: 744
  • Triangles: 744

server.js (6.7 KB)
client.js (9.1 KB)
bitbox.js (19.0 KB)

I looked at your code and it looks mostly correct…
One thing that stood out though is this:


        instancedMeshes[selectedAsset] = { mesh: instancedMesh, count: 0 };

and

    const index = instancedMeshes[selectedAsset].count++;

I think the .count field shouldn’t be declared by you, you should use the .count inside the instancedMesh itself.

What might be happening is that you declare an instancedMesh of 1000 by default. This means you instantly get 1000 copies of the object all stacked on each other at 0,0,0
You adjust your own count, but never write it back to the instancedMesh itself… in your case:

instancedMeshes[selectedAsset].mesh.count

This might mean that you’re always drawing the default 1000 instances no matter what.
And I have also noticed that, generally you can have 1000 instanced of something no problem, but if you stack them up on top of each other identically… it can cause a weird slowdown, since the GPU is constantly overdrawing the same region, or it breaks some kind of early z rejection or something… so maybe check if you’re using the correct instanceMesh.count, let me know either way?

Sorry for the delay. i’ve been trying to get it to work all day (quite literally) with no luck so far. I am way over my head with this one. I am honestly considering starting from scratch

I updated this part, but i am not sure i did it right. Isaw some performance improvement but after 200 boxes, it starts getting choppy:

function createTileInstance(position, faceNormal, id) {
const { geometry, material } = assets[selectedAsset];

if (!instancedMeshes[selectedAsset]) {
    const instancedMesh = new THREE.InstancedMesh(geometry, material, 1000);
    instancedMesh.count = 0;  // Initialize the count property
    instancedMeshes[selectedAsset] = instancedMesh;
    scene.add(instancedMesh);
}

const instancedMesh = instancedMeshes[selectedAsset];

if (faceNormal) {
    position.add(faceNormal.clone().multiplyScalar(25));
}

if (snapToGrid) {
    position.x = Math.floor(position.x / 50) * 50 + 25;
    position.y = Math.floor(position.y / 50) * 50 + 25;
    position.z = Math.floor(position.z / 50) * 50 + 25;
}

position.y += bitboxHeightOffset; // Apply the height adjustment

const dummy = new THREE.Object3D();
dummy.position.copy(position);
dummy.scale.set(bitboxSize, bitboxSize, bitboxSize); // Apply the size control
dummy.rotation.set(
    THREE.MathUtils.degToRad(rotation.x),
    THREE.MathUtils.degToRad(rotation.y),
    THREE.MathUtils.degToRad(rotation.z)
);

dummy.updateMatrix();
instancedMesh.setMatrixAt(instancedMesh.count, dummy.matrix);
instancedMesh.instanceMatrix.needsUpdate = true;
instancedMesh.count++;  // Increment the count property

console.log(`Placed instance at: ${position.x}, ${position.y}, ${position.z} with size: ${bitboxSize * 100}% and height offset: ${bitboxHeightOffset}`);

socket.emit('placeBitbox', { id, asset: selectedAsset, subdirectory: selectedSubdirectory, position, rotation, scale: bitboxSize, color: tintColor, opacity, heightOffset: bitboxHeightOffset });

render();

}

bitbox.js (19.0 KB)

Your changes look correct.

How many triangles is your model that you’re instancing?

Instancing reduces draw calls, but it can’t reduce total triangle count.

If the number of triangles of the thing * the number of instances is over 1 . 5 million, you’re gonna have a rough time. :slight_smile:

I looked over your code and I think I might know what’s happening.

It maybe be the raycast happening in the mousemove handler. As the number of instances increases, this raycast has to check a lot more triangles… just a wild guess.

The way I check these things sometimes is via the chrome profiler. but as a test you could disable that and just create 1000 instances manually… and see if its ok.

To improve the raycasting situation, one approach is to keep a separate list of simple Boxes to raycast against instead of the high poly geometry itself. But I’d only mess with that once you figure out the bottleneck.

Hi thank you for the offer, Michael. I’ll take you up on that tomorrow if you’re still available. I’m mentally exhausted and need some sleep.

Btw, I removed the ray casting bit with the mouse and that improved the performance substantially. still tutters though. I’ll try loading the 1000 instances as you suggested;

1 Like