requestAnimFrame无法提供恒定的帧速率,但我的物理引擎需要它

时间:2012-10-23 18:36:30

标签: javascript box2d webgl requestanimationframe

我使用Box2D和WebGL。 Box2D需要一个恒定的帧速率(它的“世界”更新的时间步长)。

function update(time) {//update of box2d world
     world.Step(
           1/60   // 1 / frame-rate
        ,  3      //velocity iterations
        ,  8       //position iterations
     );

但我已经读过如下定义的requestAnimFrame是正确的方法。

     requestAnimFrame = (function() {
     return window.requestAnimationFrame ||
     window.webkitRequestAnimationFrame ||
     window.mozRequestAnimationFrame ||
     window.oRequestAnimationFrame ||
     window.msRequestAnimationFrame ||
     function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
       window.setTimeout(callback, 1000/60);
     };
})();

requestAnimFrame没有给我一个恒定的帧速率,所以我的Box2D的变量是不同步的。

有解决方法吗?

[编辑]

John的(Cutch)解决方案在实施时如下所示:

function interpolate(dt) {
    var t = dt/time_step;
    body_coordinates = (1-t) * body_coordinates + t * next_body_coordinates;
}

var physicsDt = 0;
function tick() {
    var time_now = new Date().getTime();
    var dt = time_now - last_time; //Note that last_time is initialized priorly
    last_time = time_now;
    physicsDt += dt;
    clear_the_screen();
    requestAnimFrame(tick);
    drawEverything();
    if(physicsDt >= time_step) {
        update();
        physicsDt -= time_step;
    }
    interpolate(dt);
}

请注意,我的物理更新功能会注意next_attribue已设置。 而且,在此之前调用物理update,以使物理世界保持领先1帧。

结果

动画相当流畅,除非那些时候我能看到一些非常糟糕的跳跃和随机出现的微动作。

我认为解决方案中没有解决以下问题:

----> 1) dt可能会大于time_step:这会使dt/time_step大于1,这会破坏插值方程。

dt始终大于time_step时,问题会增加。 是否有可能克服时间间隔变得大于time_step的问题?

我的意思是,即使我们将世界保持在渲染前一帧,如果时间间隔始终大于time_step,那么“前方”帧也不会需要很长时间。

----> 2)想象dt小于time_step 1 ms。然后,世界不会更新一次。现在进行插值并找到近似位置(它应该落后1毫秒)。

让我们说下次dttime_step之间没有区别。

现在,考虑到dttime_step相等,没有进行插值。那么,下一个绘制的是世界上的“前方”框架,对吗?(使用这些方程式,t = 1

但准确地说,渲染的世界应该是之前的1ms。 我的意思是,它落后于世界框架的1ms不应该消失。 但是使用t = 1,绘制物理世界框架并忘记1ms。

我对代码或上述两点错了吗?

我请你澄清这些问题。

[编辑]

我在this评论中向there网页的作者询问了一种有效绘制多种形状的方法。

我学会了这样做: 我通过为每个形状保留单独的缓冲区并在初始化期间仅调用bufferDatacreateBufferbindBuffer一次来保存bufferData次调用。

每次刷新屏幕时,我都必须遍历所有形状和 绑定所需形状的缓冲区后,我必须调用enableVertexAttribArrayvertexAttribPointer(使用bindBuffer)。

我的形状不随时间变化。 它们有很多种(如多边形,圆形,三角形),从头到尾都有。

2 个答案:

答案 0 :(得分:6)

您必须将物理模拟步进时序与渲染vsync时序分离。最简单的解决方案是:

frameCallback(dt) {
  physicsDt += dt;
  if (physicsDt > 16) {
    stepPhysics();
    physicsDt -= 16;
  }
  renderWorld();
  requestAnimFrame(frameCallback);
}

这里最大的问题是,有时你将使用过时的物理世界进行渲染,例如,如果physicsDt为15,则不会发生模拟更新,但是你的对象在那个时间点几乎会移动整个帧。您可以通过将物理1帧保持在渲染之前并在渲染器中线性插入对象位置来解决此问题。类似的东西:

var t = dt/16.0;
framePosition = (1-t) * previousFramePositions + (t) * nextFramePositions;

即使您的渲染与物理模拟不同步,您的对象也会平滑移动。如果您有任何问题,请告诉我。

约翰

答案 1 :(得分:1)

requestAnimFrame并不是为了保证恒定的帧速率 - 它的设计使得浏览器只对它实际绘制的帧进行计算。

如果您想要/必须计算未绘制的帧,那么就不可能了。