15
\$\begingroup\$

This is a very standard way of doing a fixed timestep game loop, where you just accumulate time from the previous frame and consume it in discrete steps with the physics engine:

    while ( accumulator >= dt )
    {
        previousState = currentState;
        integrate( currentState, t, dt );
        t += dt;
        accumulator -= dt;
    }

A lot of articles suggest that with the residual lag we should do an interpolation between the previous update and the current one, BUT it does not make any sense to me!

If you have residual lag it means that you still have some time to catch and you should EXTRAPOLATE the current state. Why would you ever do an interpolation with the previous state?

\$\endgroup\$
4
  • \$\begingroup\$ You are correct. To get interpolation, you would have to loop while(accumulator >=0) so that the frame time t+accumulator is between integration times t-dt with state previousState and t with state currentState. Upon external input you will need to re-compute the step or restart the integration with the changed parameters. \$\endgroup\$ Dec 14, 2020 at 16:51
  • \$\begingroup\$ Are you sure there aren't any demons hidden in this? I did not see this solution recommended anywhere and trust me I read a lot of articles..It seems promising at first tbf \$\endgroup\$
    – Gavriil
    Dec 14, 2020 at 18:37
  • \$\begingroup\$ The present time t+acc is between the past t-dt and the future t. There might not be truth to the future state as events may still happen, but the interpolation at the present time is the truth within the given accuracy. I do not develop game engines, so I'm not sure if there is not a deeply philosophical counter argument. \$\endgroup\$ Dec 14, 2020 at 19:24
  • \$\begingroup\$ Also, the argumentation likely also depends on the relation of dt to the frame rate. If there are several frames per integration step, likely only for higher order integration methods, interpolation is really better than extrapolation, there might be noticeable jumps from one extrapolation arc to the next. In the converse direction, several integration steps per frame, which happens for lower order methods, there will be not much difference between the approaches. \$\endgroup\$ Dec 14, 2020 at 19:27

3 Answers 3

32
\$\begingroup\$

Some games do indeed extrapolate.

An advantage of extrapolation is that you can do it with just the single most recent state and a rate of change (like velocity, angular velocity, etc.) rather than two complete states. So it is simpler to implement and compute.

The main disadvantage of extrapolation is that it's a prediction about the future, and like most such predictions, it can be wrong.

Say the player has just started changing directions or slowing to a stop, but their current velocity is still pointing in their old travel direction. With extrapolation, they'll see themselves overshoot, then snap back to a different course when the next simulation step factors in the change in velocity, resulting in a jerky appearance that can look a bit like a bad framerate or network lag (even when both those systems are running smoothly).

Since you're generally not running a full simulation step for the extrapolated state, systems like collision detection don't run on these in-between frames. So your extrapolated velocity might take you partway into a wall or floor, or over-extend a physics joint beyond its correct range of motion, before the next simulation step correctly resolves this situation.

These artifacts usually aren't game-breaking, but they can be quite unsightly.

In contrast, by interpolating two previous simulation steps, we have a strong guarantee that we'll have continuous motion: the end of one interpolated interval is guaranteed to match the start of the next one, unlike with extrapolation. And because we're blending between two known-good states, the chances we do something ridiculous in-between like pass through a solid object or rip apart a physics joint are greatly reduced.

The cost is that the state we're displaying is technically a tiny fraction of a second older.

But "older than what?" is the question. The player's perception of what constitutes "now" in the game is informed by the visible, audible, and tactile feedback we present. If the numbers inside the computer memory representing the most recent state we simulated are technically a tiny fraction of a second ahead of the displayed state, it's difficult for the player to observe that directly.

The one place it can be perceived is in input latency. The player knows "I pressed this button now" so if it takes a few frames for visible/audible/tactile reactions to that button press, that can reveal the time mismatch.

Fortunately, as I explain in more depth in this answer, this input latency is often much less than you might expect, and by eagerly presenting fresh input feedback immediately, even on an interpolated frame, we can shrink the perceived latency down to the resolution of our display framerate.

So we can make a game that feels just as responsive as one that's extrapolating, but without the juddery errors of mis-prediction, and that's why we usually prefer to interpolate.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Great answer, thank you! Now I understand the trade off, but what if we do another update if we have residual lag and interpolate with that? \$\endgroup\$
    – Gavriil
    Dec 14, 2020 at 18:36
  • \$\begingroup\$ That's an option too, but generally more expensive both in implementation complexity and runtime workload. \$\endgroup\$
    – DMGregory
    Dec 14, 2020 at 18:38
  • 2
    \$\begingroup\$ And then there's the difference between actual input latency (which, when you add up all of the little latencies on the way, can be quite large) and perceived latency. Brains work with latency all the time, and are very good at latency hiding - as long as the latency is predictable. Jerkiness is bad, keeping things smooth even at the cost of higher average latency is usually worthwhile. Indeed, brains are so used to latency that you can trick them to think reaction happened before the action that caused it (e.g. button press -> light up). \$\endgroup\$
    – Luaan
    Dec 15, 2020 at 10:02
0
\$\begingroup\$

Interpolation with the last two known simulation states also makes little sense to me. You do want some smoothing to gloss over the situation where your simulation and render "frame time" are not full dividers. Some frames will get either an extra simulation tick or one less.

IMHO the sensible solutions are:

Use simulation values unfiltered.

If your simulation dt is sufficiently small this may actually be an option. This means the simulation is "as close" as possible to the rendered frame as possible. This only works well when your simulation frame rate is high. This becomes a real problem if your simulation frame rate is slower than your render frame rate.

Extrapolate simulated values.

You can extrapolate the simulated values and will try to replicated the missing time. The down side to this is if done blindly you will get additional stutter as the extrapolated values update. (Temporal moiré and extrapolation inaccuracy.)

Interpolate view values with simulation values.

This is almost what you alluded to, but not the last two values of the simulation, but the last value of the view and the current simulation. The last view value is not the last simulation as that contains all previous interpolations. This results is a smooth motion, but drags by somewhat of a frame.

Interpolate view values with extrapolated simulation values.

This takes both previous approaches, you take the last view values and interpolate them with extrapolated simulation values. This soothes the motion but also tries to compensate for lagging a frame behind.


If you can guarantee high tick rate of the simulation and preferably the rendering, I would not bother with interpolation as it just introduced more noise as necessary. But in my experience this is never a given. I have reasonably good experience with the two last options. But as DMGregory writes, latency generally is not so much of an issue, the smoothness of the motion is what you tend to notice.

\$\endgroup\$
0
\$\begingroup\$

The code you posted looks a lot like Glenn Fiedler explaining why you should interpolate. Let me add additional clarification to his writing:

Any remainder in the accumulator is effectively a measure of just how much more time is required before another whole physics step can be taken. For example, a remainder of dt/2 means that we are currently halfway between the current physics step and the next.

After you've produced your new currentState and your accumulator is greater than zero but less than dt, you have a state that is in the future.

If a car was travelling at 10 m/s, you may have simulated 0.02 s of it's movement, but you only wanted 0.01666 s of it's movement to match your 60 fps render speed.

We can use this remainder value to get a blending factor between the previous and current physics state simply by dividing by dt. This gives an alpha value in the range [0,1] which is used to perform a linear interpolation between the two physics states to get the current state to render.

Use interpolation to determine the current state as a point between the past and the future states. Note how previousState is overwritten each time in the accumulator loop so it's the state right before the last time we ran a simulate step.


I think some confusion may come from assuming you could pass accumulator into integrate(currentState, t, dt) instead of dt, but that's covered in the "Variable delta time" section of Fiedler's post:

The problem is that the behavior of your physics simulation depends on the delta time you pass in. The effect could be subtle as your game having a slightly different “feel” depending on framerate or it could be as extreme as your spring simulation exploding to infinity, fast moving objects tunneling through walls and players falling through the floor!

Possibly, you could solve this by changing your simulation to handle variable delta time values but Fielder is doubtful:

One thing is for certain though and that is that it’s utterly unrealistic to expect your simulation to correctly handle any delta time passed into it. To understand why, consider what would happen if you passed in 1/10th of a second as delta time? How about one second? 10 seconds? 100? Eventually you’ll find a breaking point.

Likely, you'd have to introduce a lot of iterations into your logic. It's easy to imagine it working for any delta time if there's only one object, but when you have 10 balls dropped in a pile, how do you handle updating each one of them with any size delta time? You'd have to run many small step iterations to ensure balls that would collide actually collide and then you've just replicated the post's final solution.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .