限制Three.js中的帧速率以提高性能,requestAnimationFrame?

问题描述 投票:0回答:7

我在想,对于我做的一些项目来说,60fps 并不是完全需要的。我想如果我能让它以 30 fps 的帧速率平稳运行,我就可以拥有更多以 30 fps 运行的对象和事物。我想如果我在 Three.js 中编辑 requestAnimationFrame shim 就可以将其限制为 30。但我想知道是否有更好的方法使用提供的 Three.js 本身来做到这一点。另外,这会给我带来我想要的性能提升吗?我能够以 30 fps 渲染两倍于 60 fps 渲染的对象吗?我知道以 30 fps 和 60 fps 运行的区别,但是我能够让它以平稳恒定的 30fps 运行吗?

我通常使用 WebGLRenderer,如果需要的话会回退到 Canvas,除了专门针对某一特定目标的项目(通常是 webgl 着色器项目)。

javascript html canvas three.js requestanimationframe
7个回答
83
投票

这样的事情怎么样:

function animate() {

    setTimeout( function() {

        requestAnimationFrame( animate );

    }, 1000 / 30 );

    renderer.render();

}

28
投票

这种方法也可行,使用 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;
   }
}

6
投票

CPU 和 GPU 需要完成的工作量取决于工作负载,它们设置了平滑帧速率的上限。

  • GPU 主要以线性方式工作,并且始终可以将相同数量的多边形推送到屏幕上。

  • 但是,如果对象数量加倍,CPU 必须更加努力地为这些所有对象设置动画(矩阵变换等)。这取决于你的世界模型和 Javascript 所做的其他工作,给出了多少额外的开销。此外,像可见物体的数量这样的条件也很重要。

对于所有多边形始终在屏幕上的简单模型,它应该遵循“帧速率减半,对象加倍”的规则。对于 3D 射击游戏这样的场景来说,情况绝对不是这样。


6
投票

我发现这篇文章提供了两种解决自定义帧速率问题的方法。

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

我认为这种方式更稳健,因为即使在不能以 60 fps 一致渲染画布的计算机上,它也会有稳定的动画速度。下面是一个例子

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

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


0
投票

就我而言,使用 setTimeout 会立即降低 FPS。即使具有 50 等高 fps,由于逻辑原因,它也会降低到 30。 我在这个小提琴上找到了一个适合我的工作解决方案: https://jsfiddle.net/m1erickson/CtsY3/

我将

Date.now()
更改为
performance.now()
//我们不需要完整日期

 gameTimeUpdate() {
    this.deltaTime = (performance.now() - this.time)/1000;
    requestAnimationFrame(this.gameTimeUpdate);

    const now = performance.now();
    const elapsed = now - this.lastUpdateTime;
    if (elapsed < fpsInterval) return;
    //skip if too big FPS
    stats.update(); // -> ThreeJs stats will update with changes FPS
    this.lastUpdateTime = now - (elapsed % fpsInterval);    //fpsInterval = 30
    this.time = performance.now();
    
© www.soinside.com 2019 - 2024. All rights reserved.