Performance issue after cloning mesh in THREE.js

I am trying to create a building scene using threejs, I am attaching the code below. Not adding full code… used ambient light, cubemap as environment and background.

               const load_gltf = (path) => {
                return new Promise((resolve, reject) => {
                const loader = new THREE.GLTFLoader();
                loader.load(path, (gltf) => {
                    gltf.scene.scale.set(0.3, 0.3, 0.3);
                    resolve(gltf.scene); // Resolve with the scene
                }, undefined, reject);
            });
        };

        const gltf_loder = new THREE.GLTFLoader();
        (async () => {
            try {
                const gltf_1 = await load_gltf('/assets/bulding.glb');

                const flor = gltf_1.getObjectByName("flor");
                const corridor = gltf_1.getObjectByName("corridor");

                const flor_1 = flor.clone();
                flor_1.rotation.set(0, Math.PI, 0);
                const flor_2 = flor.clone();
                flor_2.scale.x = -1;
                const flor_3 = flor.clone();
                flor_3.scale.z = -1;

                const gp = new THREE.Group();
                scene.add(gp);
                gp.add(flor, flor_1, flor_2, flor_3, corridor);
                for (let i = 1; i <= 12; i++) {
                    const clone = gp.clone(); 
                    clone.position.set(0, i * 2.5, 0); 
                    scene.add(clone); 
                }

            } catch (error) {
                console.error('Error loading GLTF:', error);
            }
        })();

here, I am loading GLB file which is 461 kb in size consisting main two parent meshes, first one is flor and second one is corridor, I am using flor to create first floor and adding them into group after that I am using for loop to create clone for that gp group. code work perfectly but the problem is, I am having only 23 or 20 fps. Is there any efficient way to create this ??? I am new to threejs so pls let me know. Thank you

I think there are a lot of draw calls after cloning. Must be used:
batchedMesh (ok)
or instancing (ok)
or merge geometry (not good)

3 Likes

On the topic of efficiency - you’re currently rendering 11x quite detailed meshes of floors / walls / doors / windows. That makes sense for the top-most floor, but rendering aaaaall of that for the occluded floors below is rather definitely not super efficient.

Consider adding some basic raycast-based occlusion testing that’d would help determine which floors the user can look into, and which aren’t visible at all.

The problem is, I dont know how, I have tried using createInstancedMesh and it will only instance mesh object(first object like flor and corridor) , my gltf have empty as parents,
I dont know how to instance those, and i have used group, in which I add clone and after that I clone that group…
I have tried to do instance in blender and export it but its also have performance issue,

Thank you for answer, Ill try to implement it. and let you know

After exporting maybe they became not instance.

Hey abd :slight_smile:

I think I had a similar problem that the guys in this forum helped me a lot with. Please see this post, it got actually solved and still runs like a charm.

The basic idea I hade to face was to figure out which objects use the same material. And all those meshes with the same material-properties (except color, wich can be set individually) get merged together into one object. Therefore I only have 1 draw-call for each object and just about 5-10 objects left and that works so good.

https://discourse.threejs.org/t/bad-performance-when-loading-more-than-3500-meshes-into-the-scene/63960

Another idea ist to put objects into group and then raycast only these groups. Each none raycasted group can be switched to invisible. That way you can probably reduce the amount of objects to be rendered. Though I have to admit, I also have never done this before.

I tried to implement the instances as you mentioned and it worked, I remodeled my building such way that any objects have only and only one material, cause I cant find solution for multiple material, but one more problem has raised, when I use scale negative its somehow affect the shading.

here the code…

try {
    const floorGLTF = await load_gltf('assets/b2.glb');
    floorGLTF.traverse((child) => {
        if (child instanceof THREE.Mesh) {
            const geometry = child.geometry;
            const material = child.material;

            const instanceCount = 12;
            const instancedMesh = new THREE.InstancedMesh(geometry, material, instanceCount);
            const instancedMesh_1 = new THREE.InstancedMesh(geometry, material, instanceCount);
            const instancedMesh_2 = new THREE.InstancedMesh(geometry, material, instanceCount);
            const instancedMesh_3 = new THREE.InstancedMesh(geometry, material, instanceCount);

            for (let i = 0; i < instanceCount; i++) {
                const rotationMatrix = new THREE.Matrix4().makeRotationX(0);
                const translationMatrix = new THREE.Matrix4().makeTranslation(0, i * 3.015, 0);
                const transformMatrix = new THREE.Matrix4().multiplyMatrices(rotationMatrix, translationMatrix);

                instancedMesh.setMatrixAt(i, transformMatrix);
            }
            for (let i = 0; i < instanceCount; i++) {
                const scaleMatrix = new THREE.Matrix4().makeScale(-1, 1, 1);
                const translationMatrix = new THREE.Matrix4().makeTranslation(0, i * 3.015, 0);
                const transformMatrix = new THREE.Matrix4().multiplyMatrices(scaleMatrix, translationMatrix);

                instancedMesh_1.setMatrixAt(i, transformMatrix);
            }
            for (let i = 0; i < instanceCount; i++) {
                const scaleMatrix = new THREE.Matrix4().makeScale(1, 1, -1);
                const translationMatrix = new THREE.Matrix4().makeTranslation(0, i * 3.015, 0);
                const transformMatrix = new THREE.Matrix4().multiplyMatrices(scaleMatrix, translationMatrix);

                instancedMesh_2.setMatrixAt(i, transformMatrix);
            }
            for (let i = 0; i < instanceCount; i++) {
                const rotationMatrix = new THREE.Matrix4().makeRotationY(Math.PI);
                const translationMatrix = new THREE.Matrix4().makeTranslation(0, i * 3.015, 0);
                const transformMatrix = new THREE.Matrix4().multiplyMatrices(rotationMatrix, translationMatrix);

                instancedMesh_3.setMatrixAt(i, transformMatrix);
            }
            scene.add(instancedMesh, instancedMesh_1, instancedMesh_2, instancedMesh_3);
        }
    });
}

Bro Thank you for answering, but truly speaking I dont have this much experience with Threejs so that I can understand it. even though Ill try, but right now, i have tried using instancing the mesh, and It kinda worked, I am getting 60 fps, to do that I have join the geometry in blender which have same material and separate geometry witch have more that one material and remodeled my building such way that any objects have only and only one material, cause I cant find solution for multiple material. now its creating shading issue , you can check code and shading problem in my previous reply
Thank you

Negative scaling is not officially supported. Zero scaling on an axis is also not officially supported. They may “work” but can lead to lighting issues.

Are you scaling the geometry? or the mesh.scale? if mesh.scale, you could try scaling the geometry itself instead.

Thank you, I tried scaling with geometry and it kinda worked, though I need to clone geometry to work … here is the code, is this best way to do it ?

            try {
                const floorGLTF = await load_gltf('assets/b2.glb');
                floorGLTF.traverse((child) = > {
                    if (child instanceof THREE.Mesh) {
                        const geometry = child.geometry;
                        const geometry_1 = geometry.clone();
                        const material = child.material;

                        const instanceCount = 12;

                        const instancedMesh = new THREE.InstancedMesh(geometry, material, instanceCount);
                        const instancedMesh_3 = new THREE.InstancedMesh(geometry, material, instanceCount);
                        for (let i = 0; i < instanceCount; i++) {
                            const transformMatrix = new THREE.Matrix4().makeTranslation(0, i * 3.015, 0);
                            instancedMesh.setMatrixAt(i, transformMatrix);
                        }

                        for (let i = 0; i < instanceCount; i++) {
                            const rotationMatrix = new THREE.Matrix4().makeRotationY(Math.PI);
                            const translationMatrix = new THREE.Matrix4().makeTranslation(0, i * 3.015, 0);
                            const transformMatrix = new THREE.Matrix4().multiplyMatrices(rotationMatrix, translationMatrix);
                            instancedMesh_3.setMatrixAt(i, transformMatrix);
                        }

                        geometry_1.scale(-1, 1, 1);
                        geometry_1.computeVertexNormals();

                        const instancedMesh_1 = new THREE.InstancedMesh(geometry_1, material, instanceCount);
                        const instancedMesh_2 = new THREE.InstancedMesh(geometry_1, material, instanceCount);

                        for (let i = 0; i < instanceCount; i++) {
                            const transformMatrix = new THREE.Matrix4().makeTranslation(0, i * 3.015, 0);
                            instancedMesh_1.setMatrixAt(i, transformMatrix);
                        }

                        for (let i = 0; i < instanceCount; i++) {
                            const rotationMatrix = new THREE.Matrix4().makeRotationY(Math.PI);
                            const translationMatrix = new THREE.Matrix4().makeTranslation(0, i * 3.015, 0);
                            const transformMatrix = new THREE.Matrix4().multiplyMatrices(rotationMatrix, translationMatrix);
                            instancedMesh_2.setMatrixAt(i, transformMatrix);
                        }
                        scene.add(instancedMesh, instancedMesh_1, instancedMesh_2, instancedMesh_3);
                    }
                });
            } catch (error) {
                console.error("Error loading GLTF:", error);
            }