Limiting framerate in Three.js to increase performance, requestAnimationFrame?

30,554

Solution 1

What about something like this:

function animate() {

    setTimeout( function() {

        requestAnimationFrame( animate );

    }, 1000 / 30 );

    renderer.render();

}

Solution 2

This approach could also work, using the THREE.Clock to measure the delta.

let clock = new THREE.Clock();
let delta = 0;
// 30 fps
let interval = 1 / 30;

function update() {
  requestAnimationFrame(update);
  delta += clock.getDelta();

   if (delta  > interval) {
       // The draw or time dependent code are here
       render();

       delta = delta % interval;
   }
}

Solution 3

The amount of work your CPU and GPU needs to do depend on the workload and they set the upper limit of smooth framerate.

  • GPU works mostly linearly and can always push out the same count of polygons to screen.

  • However, if you have doubled the number of objects CPU must work harder to animate these all objects (matrix transformationsn and such). It depends on your world model and other work Javascript does how much extra overhead is given. Also the conditions like the number of visible objects is important.

For simple models where all polygons are on the screen always then it should pretty much follow the rule "half the framerate, double the objects". For 3D shooter like scenes this is definitely not the case.

Solution 4

I came across this article which gives two ways of solving the custom frame rate issue.

http://codetheory.in/controlling-the-frame-rate-with-requestanimationframe/

I think this way is more robust, as it will have a steady animation speed even on computers that do not render the canvas consistently at 60 fps. Below is an example

var now,delta,then = Date.now();
var interval = 1000/30;

function animate() {
    requestAnimationFrame (animate);
    now = Date.now();
    delta = now - then;
    //update time dependent animations here at 30 fps
    if (delta > interval) {
        sphereMesh.quaternion.multiplyQuaternions(autoRotationQuaternion, sphereMesh.quaternion);
        then = now - (delta % interval);
    }
    render();
}

Solution 5

The accepted answer has a problem and gives up to -10fps on slow computers compared to not limiting the fps, so for example without limiting 36pfs, with the accepted solution 26fps (for a 60fps, 1000/60 setTimeout).

Instead you can do this:

var dt=1000/60;
var timeTarget=0;
function render(){
  if(Date.now()>=timeTarget){

    gameLogic();
    renderer.render();

    timeTarget+=dt;
    if(Date.now()>=timeTarget){
      timeTarget=Date.now();
    }
  }
  requestAnimationFrame(render);
}

That way is not going to wait if its already behind.

Share:
30,554
Cory Gross
Author by

Cory Gross

Senior engineer at PlayStation

Updated on April 14, 2020

Comments

  • Cory Gross
    Cory Gross about 4 years

    I was thinking that for some projects I do 60fps is not totally needed. I figured I could have more objects and things that ran at 30fps if I could get it to run smoothly at that framerate. I figured if I edited the requestAnimationFrame shim inside of three.js I could limit it to 30 that way. But I was wondering if there was a better way to do this using three.js itself as provided. Also, will this give me the kind of performance increase I am thinking. Will I be able to render twice as many objects at 30fps as I will at 60? I know the difference between running things at 30 and 60, but will I be able to get it to run at a smooth constant 30fps?

    I generally use the WebGLRenderer, and fall back to Canvas if needed except for projects that are targeting one specifically, and typically those are webgl shader projects.

  • Johan Fredrik Varen
    Johan Fredrik Varen over 2 years
    This works better than the accepted answer, at least for me. My sprite animations would pause for a few millis after each loop when using the setTimeout approach. This suggestion has no such negative effect, while still reducing CPU load equally well.
  • aelmosalamy
    aelmosalamy over 2 years
    I think this is how you are supposed to do it, smart and clean. I also don't feel very comfortable memory-wise with calling setTimeout multiple times in a second,. It could be totally fine but still.
  • Damien
    Damien about 2 years
    Interestingly enough mrdoob suggested setTimeout even though THREE.Clock already existed back in 2012. In any case, I'm also skeptical of the timeout and this answer is solid!