Instancing + moving objects - algorithm?

Hello everyone,
I continue my “3d project”.

My goal is to display a CHANGING forest with MOVING animals.

Something like “50k plants” + “100 animals”

My only way yet to successfully display something at 60 fps was to use “points”

naive Algorithm :

Animals = list of points…
Tree = list of points…

engine loop:
animals are moving : animal.X++ ,animal.Y++ …
tree are growing (having more points) and so on

refresh draw loop

  • delete all points
  • foeach animal / tree : redraw all points

It works but it’s miles away the minimum quality i am aiming for,


So i tried objects instancing instead of using points

  1. For some reasons, depending on camera angle, everything become invisible

  2. I have moving & changing objects, my algorithm is now

  • init instances once with maximum count
  • loop : change all positionMatrice according to animal positions, and set exact draw range

Initial draw is good; but refresh is not performant (4 fps vs 60 with points)


// the exact code portion

// called once, init 2 instances

    initInstances()
    {
        for (let i = 0 ; i < this.geometry.length ; i++) {

            this.geometry[i] = new THREE.OctahedronGeometry( 0.35, 0);
            this.material[i] = new THREE.MeshBasicMaterial( { map: this.textures[i]  } );
            this.matrix[i] = new THREE.Matrix4();

            this.mesh[i] = new THREE.InstancedMesh( this.geometry[i], this.material[i], this.maxCount[i] );
            this.mesh[i].instanceMatrix.setUsage( THREE.DynamicDrawUsage ); // will be updated every frame
            this.mesh[i].userData = { "animalInstance" : true };
            this.objRender3D.scene.add(this.mesh[i]);
        }


    }

// call in refresh loop : moving all instance position according to simulation x,y


    drawInstances() 
    {

        const scale = 0.9;
        var xPlayer = this.objRender3D.objMonde.objPlayer.positionX;
        var yPlayer = this.objRender3D.objMonde.objPlayer.positionY;    
        var seuil =  64;  

        for (let h = 0 ; h < this.geometry.length ; h++) {


            let indiceMatrice=0;
            for (var i = 0 ; i < this.objRender3D.objMonde.objFaune.tabAnimals.length ; ++i) {  
                
                var objAnimal = this.objRender3D.objMonde.objFaune.tabAnimals[i];
                
                var posVegX = objAnimal.positionX;
                var posVegY = objAnimal.positionY;
    
                if ( 
                    Math.abs( posVegX - xPlayer ) <= seuil  &&
                    Math.abs( posVegY - yPlayer ) <= seuil 
                )
                {

                    var posX = Math.floor(objAnimal.positionX);
                    var posY = Math.floor(objAnimal.positionY);
                    var posZ = this.objRender3D.objMonde.objTerrain.tabTuiles[posX][posY].altitude * 55.0 + 5.0 + ( 0.4* 1);

                    for (var o = 0 ; o < objAnimal.objModRender.listeOrganes.length ; o++) {

                        if (objAnimal.objModRender.listeOrganes[o].texture != h)
                            continue;

                        for (var j = 0 ; j < objAnimal.objModRender.listeOrganes[o].listeVertices.length ; ++j) {            
                        
                            this.matrix[h].setPosition( posX + scale * objAnimal.objModRender.listeOrganes[o].listeVertices[j].x + objAnimal.objModRender.listeOrganes[o].organX,
                                                posZ + scale * objAnimal.objModRender.listeOrganes[o].listeVertices[j].z + objAnimal.objModRender.listeOrganes[o].organY, 
                                                posY + scale * objAnimal.objModRender.listeOrganes[o].listeVertices[j].y + objAnimal.objModRender.listeOrganes[o].organZ
                                                );

        
                            this.mesh[h].setMatrixAt( indiceMatrice, this.matrix[h] );                   
                            ++indiceMatrice;
                        }
                    }
                }
            }

            this.mesh[h].count = indiceMatrice;
            this.mesh[h].instanceMatrix.needsUpdate = true;

          //  console.log("Draw H" + h + " = " + indiceMatrice + " points ");

        }
        
    }


Thanks for any help & advice !

Hi!
Maybe this post will be helpful somehow: Displaying 500 Basic Textured Models Performantly? (InstancedMesh or another method) - #13 by prisoner849

Thanks for reply and example.

If i understand well, the code is correct.
I expected that “instance” would support 2 more zeros. like displaying 100k and not only 1000.

for infos, here’s my current scene

  • to display trees i use POINTS SYSTEM & sprite texture → but its ugly i want to replace them with some procgen-models
  • the “animals” (lol, if we can tell) are represented by a list of “spheres”…

Well as you see i have multiple thousand of points, and i am looking to replace them with more complex objects :frowning:


I am not sure yet if i must try to drop points, and try to optimise instancing
or forget instancing and try to get the best of “points”

for the moment instancing doesnt seems the solution, it doenst display the quantity i thought.