Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Three.js crashing on 100 cube animation

in my scene I have 4 point-lights 3 of those attached on the camera and about 100 to 300 cubes. I have many categories of cubes, each between 100 - 300. Only 1 category of cubes may appear in my scene at any time based on user menu selection.

(category with 100 cubes) renderer.info:

memory: 
  Objectgeometries: 2
  programs: 3
  textures: 100
render: 
  calls: 203
  faces: 1360
  points: 0
  vertices: 4080

In a loop I generate my cubes for each category like this:

var materials = [   
    backgroundMaterial,
    backgroundMaterial,
    backgroundMaterial,
    backgroundMaterial,
    productMaterial,
    backgroundMaterial
];

var cubeMaterial = new THREE.MeshFaceMaterial( materials );

var object3D = new THREE.Mesh( geometryBox, cubeMaterial );

The material backgroundMaterial is defined out of the loop once;

var backgroundMaterial = new THREE.MeshPhongMaterial({
    color: this.options.models.boxColor,
    specular: this.options.models.boxSpecular,
    //emissive : 0xefefef,
    //side: THREE.DoubleSide,
    overdraw: false,
    transparent: false,
    metal:true,
    shininess: this.options.models.boxShininess,
    reflectivity: this.options.models.boxReflectivity,
    fog:false
});

and the productMaterial each time inside the loop since the texture is different for every cube.

var productMaterial = new THREE.MeshBasicMaterial({
    map: productModelTexture,
    color: that.options.models.boxColor,
    specular: that.options.models.boxSpecular,
    //emissive : 0xefefef, 
    side: THREE.FrontSide,
    overdraw: false,
    transparent: false,
    metal:true,
    shininess: that.options.models.textureShininess,
    reflectivity: that.options.models.textureReflectivity,
    opacity: 1,
    fog:false
});

Also I do not add the meshes into the scene at this point, and they are set to visible = false

after that I push my cubes into an object of arrays, each array inside that object is a category of cubes with length between 100 - 300.

When my application starts I run an animation like the one bellow which brings a category of cubes into the scene.

helix : function( category ) {

    if ( this.models[category] && this.models[category].length > 0 ) {              

        TWEEN.removeAll();

        new TWEEN.Tween( this.camera.position ).to( {x:0,y:0,z:90000}, 1000 ).easing( TWEEN.Easing.Exponential.InOut ).start(); 
        new TWEEN.Tween( this.camera.rotation ).to( {x:0,y:0,z:0}, 1000 ).easing( TWEEN.Easing.Exponential.InOut ).start();             

        this.models.reset( category );

        for ( var i in this.models[category] ) {

            var model = this.models[category][i];
                model.visible = true;
                this.scene.add( model );

            new TWEEN.Tween( model.position ).to({
                x: model.helix.position.x,
                y: model.helix.position.y,
                z: model.helix.position.z
            }, randBtwn( 1000, 3000 ) ).easing( TWEEN.Easing.Exponential.InOut ).delay( 1001 ).start();

            new TWEEN.Tween( model.rotation ).to( {
                x: model.helix.rotation.x,
                y: model.helix.rotation.y,
                z: model.helix.rotation.z
            }, randBtwn( 1000, 3000 ) ).easing( TWEEN.Easing.Exponential.InOut ).delay( 1001 ).onComplete(function(){

            }).start();
        }

    }

}.bind( that )

Furthermore you will notice another function call inside helix, this one: this.models.reset( category );

Which is the code below and is essentially reseting the objects position and sets them to visible = false and finally removing them from the scene.

reset : function( category, callback ) {

    for ( var j in this.models ) {

        if ( this.models[j] instanceof Array && this.models[j].length > 0 && category !== j ) {

            for ( var i in this.models[j] ) {

                var model = this.models[j][i];
                    model.visible = true;

                new TWEEN.Tween( model.position ).to({
                    x: model.outside.position.x,
                    y: model.outside.position.y,
                    z: model.outside.position.z
                }, 1000 ).easing( TWEEN.Easing.Exponential.InOut ).start();

                new TWEEN.Tween( model.rotation ).to( {
                    x: model.outside.rotation.x,
                    y: model.outside.rotation.y,
                    z: model.outside.rotation.z
                }, 1000 ).easing( TWEEN.Easing.Exponential.InOut ).onComplete(function ( m ){

                    m.visible = false;
                    this.scene.remove( m );

                    if ( callback ) {
                        callback();
                    }

                }.bind( that, model )).start();
            }

        }

    }

}.bind( that )  

In a pc everything works smooth and I'm running with 36 fps. My gpu is a new nvidia GTX (I don't know if 36 is acceptable though).

The issue is that when I try to run my application on my nexus 5 with latest chrome I get a huge fps loss between the transition of cubes going outside the scene and the other cubes coming in. Most of the times this results in a chrome crash... Other than that if I don't change category and no animations are being played it works OK in my mobile.

PS: I cannot merge the geometries since each mesh has to move on each own upon user selection.(If I'm not mistaken atleast)

What could be the reason for this performance drop/crash, and how would you approach a similar scenario where you move 200 cubes outside the scene and another 200 inside the scene? Are there any tips I should take under consideration, keeping in mind that I'm still new to three.js.

Any other source required that might be the reason please let me know and I will update my question.

like image 734
0x_Anakin Avatar asked Dec 23 '14 11:12

0x_Anakin


1 Answers

first of all, 36 fps is acceptable. As a rule of thumb i use 25 fps to be the bare minimum.

now for the problem. a nexus 5 has a much slower gpu than your pc. since shaders are passed directly to the gpu, speed matters. its the same issue when you try to play crysis on a budget pc, the gpu just isnt powerful enough to handle all the data fast enough.

it MAY be a solution to add all the cube geometries to one single mesh, perhaps with a THREE.MeshFaceMaterial to apply the different materials to each cube. a single mesh with 100 geometries is processed quicker than 100 meshes with 1 geometry. but as i said. it may also be this doesnt solve anyting at all, it's more of a hail mary.

EDIT: Adding the geometries in a single mesh and detecting what geometry was clicked.

I thought about it some more, and it IS possible to detect the clicked geometry. it's not pretty but it might give you some ideas about how to do it pretty. simply add another property to the faces of the geometry.

var addGeometry = function(baseGeometry, addedGeometry){
    for(i in addedGeometry.faces){
        addedGeometry.faces[i].parent = addedGeometry;
    }
    baseGeometry.add(addedGeometry);
}

then, when raycasting, you don't call the object property, but the face.parent property, it should contain the clicked geometry, which you can manipulate as you please.

but again, i dont know how this is gonna work performance wise. Worth a try though.

something else you could try is using webworkers.

like image 61
Kevin Kuyl Avatar answered Oct 04 '22 11:10

Kevin Kuyl