在Three.js中限制帧率以提高性能,使用requestAnimationFrame?

40
我在考虑对于某些项目,不一定需要60帧每秒的运行速度。如果我可以让物体和其他事物以30帧每秒的速度流畅运行,我觉得我可以拥有更多这样的元素。我想如果我编辑three.js中的requestAnimationFrame函数,限制帧率到30fps可能是一种方式。但我想知道是否有更好的方法在使用提供的three.js本身时做到这点。此外,这样做能否给我想要的性能提升?如果我将帧率从60降至30,能否使我以两倍的速度呈现出同样数量的物体?我知道在30fps和60fps下的运行差异,但我能否使它以流畅恒定的30fps运行?
我通常使用WebGLRenderer,并在必要时回退到Canvas,除非针对特定项目,通常是WebGL着色器项目。
6个回答

82

这样的话怎么样:

function animate() {

    setTimeout( function() {

        requestAnimationFrame( animate );

    }, 1000 / 30 );

    renderer.render();

}

有什么理由考虑这个答案而不是其他提到外部时钟的答案吗? - Kalnode

27

使用THREE.Clock来测量时间间隔,这种方法也可以起作用。

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;
   }
}

2
这个方法对我来说比被接受的答案更有效。当使用setTimeout方法时,我的精灵动画在每次循环后会暂停几毫秒。这个建议没有这样的负面影响,同时同样有效地减少了CPU负载。 - Johan Fredrik Varen
我认为这就是你应该做的,聪明而干净。另外,我对于在一秒钟内多次调用setTimeout不太舒服,可能完全没问题,但还是有点担心内存方面的问题。 - aelmosalamy
1
有趣的是,即使在2012年已经存在THREE.Clock,mrdoob仍然建议使用setTimeout。无论如何,我也对timeout持怀疑态度,这个答案很可靠! - Damien
这个解决方案也非常适用于限制重计算(例如:游戏物理)。如果您喜欢无上限的FPS,可以在间隔内计算物理,将渲染函数调用放在if条件之外,然后使用间隔增量插值3D对象。示例:https://twitter.com/varunprime/status/1591274302686445568 - doppler

6
我看到了这篇文章,它提供了两种解决自定义帧率问题的方法。
我认为这种方式更加稳健,即使在不以60 fps渲染画布的计算机上也可以保持稳定的动画速度。以下是一个示例: http://codetheory.in/controlling-the-frame-rate-with-requestanimationframe/
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();
}

6
你的CPU和GPU需要处理的工作量取决于任务负载,并设置了平稳帧速率的上限。
GPU主要是线性工作,始终可以将相同数量的多边形推送到屏幕上。
然而,如果你增加了对象数量,CPU必须更努力地对所有这些对象进行动画处理(矩阵转换等)。它取决于你的世界模型和Javascript的其他工作,在可见对象数方面也很重要。
对于简单模型,其中所有多边形始终在屏幕上,那么它应遵循“帧速率减半,对象翻倍”的规则。但对于像3D射击游戏这样的场景,情况肯定不是这样。

4

接受的答案存在问题,与不限制fps相比,在运行速度较慢的计算机上会导致最高10fps的降低。因此,例如在没有限制36pfs的情况下,使用接受的解决方案只能达到26fps(针对60fps,1000/60 setTimeout)。

相反,您可以采取以下方法:

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);
}

那种方法不会等待,如果它已经落后了。

2

之前的回答似乎忽略了requestAnimationFrame的设计意图,从而导致了一些不必要的调用。requestAnimationFrame接受一个回调函数作为参数,该函数又以高精度时间戳作为其参数。因此,您知道当前时间,不需要调用Date.now()或任何其他变体,因为您已经有时间了。所需的只是基本算术:

var frameLengthMS = 1000/60;//60 fps
var previousTime = 0;

function render(timestamp){
  if(timestamp - previousTime > frameLengthMS){
   /* your rendering logic goes here */
       drawSomething();
   /* * * * */
    previousTime = timestamp;
  }
  requestAnimationFrame(render);
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接