Physics interpolation gives me periodic bumps and jumps

Started by
8 comments, last by firmware_dude 8 years, 11 months ago

Hi,

I am trying to use interpolation in my physics game so that I can use a fixed timestep to update my Box2d physics world.

I have followed the same steps as in this article.

What I notice in my Android game is that my interpolation alpha (or fraction or accumulatorRatio) varies with this pattern as frames progress:

0.13
0.22
0.31
0.45
0.49
0.62
0.81
0.94
0.03
0.1

The above is an illustration but the point is that it goes from 0 to 1 and cycles back to 0 periodically. I believe this is due to the game frame rate being slight faster than the physics update interval.

Every time the interpolation alpha cycles around, it gives me a jitter (or jump).

Here is a log file: http://pastebin.com/3Yb7nVxX

The two columns are (i) interpolation alpha (ii) physics step count

The "jumps" happen roughly in lines 262 to 266 in the log.

Here is the code: http://pastebin.com/vWC7yNGM

What is causing these periodic "jumps"?

Thank you

(I have been stuck with this problem since such a long time. I would be happy to give a humble tip out of gratitude if this problem gets fixed :) )

Advertisement
Can you post some more code? The places update and renderCircle get called, and all places that change any of the deltas/accumulators. Also, is everything called from one thread?

Can you post some more code? The places update and renderCircle get called, and all places that change any of the deltas/accumulators. Also, is everything called from one thread?

Yes all of this is in one thread.

Here is more code: http://pastebin.com/PSZrntZP

I've omitted the obvious stuff like constructors, etc.

(PS, GameWorld.timeDelta is 1/60.0f. Sorry I forgot to include that line.)

Please ask me if something is unclear or if you need to see more code.

hmkay, so what i see is some points in the timeline where update() does multiple physics-steps in one call. however the renderer is called for every update(), not for every physics-step. that means that whenever update() does more or less than one physics-step, there will be a visual jump or hang of a at most a few frames - because compared to the rendertime, the simulationtime has sped up/down for a moment. or put differently, the renderer sometimes skips a position the player was in and maybe sometimes even draws it twice. now i wouldn't consider that a bug really, it is just how this type of gameloop works. i can think of a few ways to lessen the visual jumps. consider using multiple past states and an own accumulator for the renderer. then have the renderer run like 1-2 frames in the past compared to the physics, but with the same accumulator-method. you would then interpolate between the last two states, or if the physics did 2 tics this "round" you can use the two states before that. i use something like that, and have physics @ 50hz and rendering @ 60hz, the renderer has its own accumulator - the rendertime and simtime have a jittering difference between them, but the visuals are smooth. if the times drift apart, i just add/substract a little bit to/of the renderers accumulator to adjust, this isn't noticeable visually. for that to work i store like the last 3 states or so for the renderer to interpolate between. another, a bit simpler, way would be storing multiple past states with their game-time, and then call the renderer like render(realTimeNow()-someOffsetToThePast). while similar to the above, render and sim hz aren't decoupled this way. this would only work if you make sure the time accumulated for the sim-steps equals the real time, or at least does not differ wildly. (excuse the non-formatting, browser doesn't like gamedev.net it seems)
renderOffset = -20ms;
now = currentSimTime;
timeCorrectionFactor = 0.01f;

renderTime = timestamp() + renderShift;
if(abs(renderTime - now) > 0 ) { 
    renderShift = ((renderShift * (1.0f-timeCorrectionFactor)) + ((now-timestamp()) * timeCorrectionFactor) );
    renderTime = timestamp() + renderShift;
}
render(renderTime+renderOffset);
something like this can be used to smoothly adjust renderTime so it does not drift too far from simTime.
the negative renderOffset makes sure that we render a point in time that has some valid states and the renderer is not ahead of the sim.

Thanks for you reply, but I'm afraid I couldn't understand quite a bit of it. :(

As for the renderer being called multiple times with the same physics state, this can happen when the renderer is much faster than the physics rate. But the point here is that the interpolation should take care of not "hang"ing. Say, the interpolation alpha is 0.1, 0.4, 0.7 for the same physics state across 3 successive frames, without any physics world updatess. Even then, the renderer is drawing different things. Each thing that the renderer draws is interpolated between the same two physics states, but at different alphas. I think this is very appropriate and the only way to go about it, since we have no other information between the two physics states.

In your explanantion, I don't understand what "simulation time" and "render time" are. As I understand, there is only one time. This is the render time. The physics world may be updated a little less in which we store that left over time in an accumulator.

Can you please explain?

First part:

Well yes, but still the simulation may have stepped multiple times in one update(), and the renderer only interpolates between last and current state. So there will be one or more states that do not get displayed. Lets say update() does 3 physics steps, 1,2,3 - then the renderer will after that interpolate between 2 and 3, but what happens from 1 to 2 will not be rendered. Kind of a frameskip.

Example, imagine player moving with speed of 1:

realtime simulationframe renderedframe visual_playerpos shouldbe_playerpos

0 0 0-0 0 0

1 - - - 1

2 1,2 1-2 2 2

3 3 2-3 3 3

4 - - - 4

5 - - - 5

6 4,5,6 5-6 6 6

7 7 6-7 7 7

So this means that even though with this method the player is at the correct position at the right time, sometimes the frames that show how he got there will be skipped.

This will make it look a little jumpy/jittery,especially with fast movement.

You can check this by looking at the time between rendered frames - idealy this would be near constant, but it probably won't be. in the above example 2 frames will stay longer on screen than the others.

Second part:

Simulation time would be the current time of the physics simulation or the current state, rendertime would be the time the renderer actually displays. With the loop as you have it now, rendertime is always <= simtime but >= simtime-timeDelta, and should be almost exactly timeDelta behind realtime. with a fixed timestep like yours simulation time is always a multiple of timeDelta.

So by that definition you have 3 times already, sim, render and real-time. Even if all three just describe points on the same timescale.

realtime would be "now".

simtime would be whatever is left in the accumulator behind realtime.

rendertime would be timeDelta behind realtime.

Hm,yes, a little confusing maybe, i'm not the greatest explainer.

Anyway you got this basically: http://gafferongames.com/game-physics/fix-your-timestep/ (good read btw, and generally the way to go)

Now you could go one step further and decouple some more by having the renderer run at it's own fixed speed to get rid of that last bit of jitter in time between frames.
BTW, have you checked if the jitter is also noticeable when running fullscreen with vsync active?

Also, do you sleep() somewhere if stuff happens too fast/system is too powerfull? Can also help to stabilize framerate.

Why is there a minimum applied here?

physicsDelta += Math.min(delta, 0.25f);

This doesn't seem like it's the correct interpolation fraction:

delta / GameWorld.timeDelta

delta should be your physicsDelta, but it seems to be the time since your last frame.

First part:

Well yes, but still the simulation may have stepped multiple times in one update(), and the renderer only interpolates between last and current state. So there will be one or more states that do not get displayed. Lets say update() does 3 physics steps, 1,2,3 - then the renderer will after that interpolate between 2 and 3, but what happens from 1 to 2 will not be rendered. Kind of a frameskip.

Example, imagine player moving with speed of 1:

realtime simulationframe renderedframe visual_playerpos shouldbe_playerpos

0 0 0-0 0 0

1 - - - 1

2 1,2 1-2 2 2

3 3 2-3 3 3

4 - - - 4

5 - - - 5

6 4,5,6 5-6 6 6

7 7 6-7 7 7

So this means that even though with this method the player is at the correct position at the right time, sometimes the frames that show how he got there will be skipped.

This will make it look a little jumpy/jittery,especially with fast movement.

You can check this by looking at the time between rendered frames - idealy this would be near constant, but it probably won't be. in the above example 2 frames will stay longer on screen than the others.

Second part:

Simulation time would be the current time of the physics simulation or the current state, rendertime would be the time the renderer actually displays. With the loop as you have it now, rendertime is always <= simtime but >= simtime-timeDelta, and should be almost exactly timeDelta behind realtime. with a fixed timestep like yours simulation time is always a multiple of timeDelta.

So by that definition you have 3 times already, sim, render and real-time. Even if all three just describe points on the same timescale.

realtime would be "now".

simtime would be whatever is left in the accumulator behind realtime.

rendertime would be timeDelta behind realtime.

Hm,yes, a little confusing maybe, i'm not the greatest explainer.

Anyway you got this basically: http://gafferongames.com/game-physics/fix-your-timestep/ (good read btw, and generally the way to go)

Now you could go one step further and decouple some more by having the renderer run at it's own fixed speed to get rid of that last bit of jitter in time between frames.
BTW, have you checked if the jitter is also noticeable when running fullscreen with vsync active?

Also, do you sleep() somewhere if stuff happens too fast/system is too powerfull? Can also help to stabilize framerate.

Thanks for the detailed explanation. I'll give it a shot.

Why is there a minimum applied here?


physicsDelta += Math.min(delta, 0.25f);

This doesn't seem like it's the correct interpolation fraction:


delta / GameWorld.timeDelta

delta should be your physicsDelta, but it seems to be the time since your last frame.

The minimum is applied to prevent a "spiral of death". On slow devices, if the FPS goes too low and low enough to require multiple physics world updates per frame, the multiple physics updates might make it even slower. To prevent this, the min is capping the number of physics updates.

delta is physicsDelta. physicsDelta which is returned by update() is passed to the render function.

This topic is closed to new replies.

Advertisement