5

I use Box2D with WebGL. Box2D demands a constant frame rate (time steps for it's "world" updates).

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

But I've read that requestAnimFrame defined as below is the right way to go.

     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 doesn't give me a constant frame rate and so my Box2D's variables are going unsynchronized.

Is there a fix to this?

[EDIT]

John's (Cutch) solution when implemented looks like this:

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

Note that my physics update function takes care that the next_attribues are set. And also, a physics update is called before this, to keep the physics world ahead by 1 frame.

The result

The animation is fairly smooth, except for those times when I can see some really bad jumps and random appearing micro-movements.

I thought the following issues were not addressed in the solution :

----> 1) dt may become larger than time_step : This would make dt/time_step greater than 1 which would ruin the interpolation equations.

When dt remains larger than time_step consistently, problems would increase. Is it possible to overcome the problem of the time gap becoming larger than time_step ?

I mean, even if we keep the world one frame ahead of the rendering, if time gaps consistently stay greater than time_step, it wouldn't take long pass that "ahead" frame.

----> 2) Imagine dt being lesser than time_step by 1 ms. Then, the world is not updated that one time. Now interpolation is done and the approximate position is found(1 ms behind where it should have been).

Lets say the next time no difference is seen between dt and time_step.

Now, no interpolation is done considering that dt and time_step are equal. So, the next that is drawn is the "ahead" frame in the world, right?(using those equations, with t = 1)

But accurately, the rendered world should be that 1ms behind which it was before. I mean, that 1ms by which it was behind the world frame should not vanish. But with t = 1, draws the physics world frame and forgets that 1ms.

Am I wrong about the code or the above 2 points?

I request you to clarify these issues.

[EDIT]

I asked the author of this webpage, for a way to efficiently draw many shapes, in the comments there.

I learnt to do it this way : I'm saving bufferData calls by keeping separate buffers for each shape and calling createBuffer, bindBuffer, bufferData only once during init.

Everytime I refresh the screen, I have to iterate over all the shapes and I have to call enableVertexAttribArray and vertexAttribPointer after binding the required shape's buffer(using bindBuffer).

My shapes don't change with time. There are just a variety of them (like polygons, circles, triangles) that stay from beginning to end.

batman
  • 5,022
  • 11
  • 52
  • 82

2 Answers2

6

You must decouple your physics simulation stepping timing from your render vsync timing. The easiest solution is to do this:

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

The biggest issue here is that sometimes you'll be rendering with an outdated physics world, for example, if physicsDt was 15 no simulation update will occur but your objects would have moved almost an entire frame by that point in time. You can work around this by keeping the physics 1 frame ahead of the rendering and linearly interpolating object positions in the renderer. Something like:

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

That way your objects move smoothly even if you're rendering is out of sync with your physics simulation. Let me know if you have any questions.

John

Cutch
  • 3,511
  • 1
  • 18
  • 12
  • I'm not sure how that code decouples the physics simulation. So, `frameCallBack` is the "update" function? It takes in difference in time of last call and current call as argument, and only after the current physics frame has lasted for 32 milliseconds, it updates the world. Am I correct? – batman Oct 24 '12 at 06:26
  • But what if both world update and screen refresh have to happen after comparable time intervals? Lets say my physics world waits 60 ms before an update. The 1st screen refresh happens at 59 ms. Physics world won't update as 60 ms haven't passed since last update. Next screen refresh? after another 59 ms. Now physics world updates. But when it should have updated after 60 ms, it now happens after 118 ms. Isn't that very inaccurate? – batman Oct 24 '12 at 06:30
  • Oh and I don't understand that 2nd snippet you put. Can you please explain? – batman Oct 24 '12 at 06:31
  • 1. frameCallback is the function you pass to requestAnimFrame. You keep track of how much time has gone by and after 16ms (your fixed physics timestep) you step your physics. You always draw. – Cutch Oct 24 '12 at 14:34
  • 2. The second code snippet is basic linear interpolation: http://en.wikipedia.org/wiki/Linear_interpolation – Cutch Oct 24 '12 at 14:34
  • I read up on it and finally was able to set up what you told me to. Please see the edit in the question. – batman Oct 28 '12 at 18:06
  • Edit #1) Yes it is possible that dt becomes greater than your physics time step, the likely cause is that the work you do per frame exceeds 16ms (the time slice requestAnimationFrame expects). At this point you need to simplify your simulation allowing you to run fast enough or throw away time (clamp dt at 16ms or something). – Cutch Oct 28 '12 at 18:11
  • Edit #2) I think you're interpolating with the wrong variable- you want interpolate based on physicsDt not dt. If your physicsDt is 15ms (1ms smaller than timeStep) your interpolation will be correct, you'll be 93% through the frame and the next tick() call will simulate 1 more physics frame and then you'll be 15ms through that one ((15ms+16ms)%16ms). – Cutch Oct 28 '12 at 18:15
  • Yes, edit #2 is solved. But, what do you mean by "throw away time"? Or "clamp dt at 16 ms"? I now, know that simplifying my WebGL simulation will solve the problem. But for reasons I can't seem to find out, I don't find a better way of drawing many shapes, other than how I've shown in another edit. So, please tell me what the other alternative you mentioned, means. Of course, it would be good if `dt` were to be clamped at 16ms consistently, but if my simulation is taking up more time than 16ms, that may not be possible. Sorry for so many questions ... – batman Oct 28 '12 at 18:39
  • By throwing away time, I mean simulating physics every 32ms instead of 16ms. This will let you use 1 animationFrame for rendering, 1 animationFrame for physics. – Cutch Oct 29 '12 at 01:06
1

requestAnimFrame isn't meant to guarantee a constant frame rate – it's designed so that the browser only does the calculations for frames it actually draws.

If you want/have to calculate frames that aren't drawn, then it isn't the way to go.

Rich Bradshaw
  • 71,795
  • 44
  • 182
  • 241