16 ms·
Fixing the iterative damping interpolation in video games
- chrisjj 2y agoNice, but ... > So there we have it, the perfect formula, frame rate agnostic , ... worth noting what happens on a frame time spike.
- ux 2y agoA frame time spike is covered by the overshooting point: it will basically land further down the curve, which means a point converging toward the B target.
- chrisjj 2y agoIn monoticity guaranteed even if the next frame is short?
- ux 2y agoAs the delta time converges to 0, the lerp coefficient is going to converge to 0 as well, meaning it won't move. Said differently, if time stops the interpolation as well. You may enter into the realm of floating point accuracy issues at some point with low values, but I'm curious of what specific scenario you have in mind here?
- chrisjj 2y agoOK, so that's confirmed Yes. Thanks.
- kibibu 2y agoAlternatively, you could fix your timestep, decoupling your physics from your display refresh entirely. https://gafferongames.com/post/fix_your_timestep/ https://gafferongames.com/post/fix_your_timestep/
- mort96 2y agoWhich is hard to do well and has its own set of drawbacks.
- nightowl_games 2y agoHard disagree. It's not hard to do well. The draw back is less than 1 frame of input latency. If your game uses a physics engine or is networked you are forced to use a constant update rate, ie: 30 or 60hz. The draw backs to linking the physics update rate to the monitor refresh rate are enormous and untenable in a networked game.
- mort96 2y agoI don't personally care much about a frame or two of latency (some do though, and for some genres that'll be a big deal); however I care about animation smoothness. If the monitor refresh rate is 144Hz and physics tick rate is 60Hz, you'll need some form of interpolation, which is hard to do well especially since the refresh rate isn't a multiple of tick rate.
- magicalhippo 2y ago> you'll need some form of interpolation, which is hard to do well Due to non-constant accelerations?
- kibibu 2y agoSome games use a 120hz physics sim even if they're capped to 60hz display refresh.
- nightowl_games 2y agoAn 'animation' tick can be (and should be) disconnected from the physics 'tick'. Physics/game logic 'ticks' should use a constant time so the simulation is more predictable and stable, a 'animation tick' can be exactly what the monitor refresh rate is and thus doesnt need 'interpolation between ticks'.
- markisus 2y agoUnfortunately you cannot extend this approach much beyond this simple example if you are writing a physics simulation. There is no known closed form solution, for example, for the famous three body problem, so a some sort of numerical integration is absolutely required. At that point, you should just decouple the numerical integration timestep from the monitor refresh rate, or mandate a refresh rate that will be acceptable to the player.
- mort96 2y agoYou can do that, but it has serious drawbacks. You can: * Run physics at such a high rate that sampling it at 30 or 60 or 144 or 260 hertz is fine, like running physics at 1kHz. This is appropriate for certain kinds of very realistic physics sim style games, but usually undesirable due to performance. * Run physics at some fixed, sane rate (somewhere between 20 and 100 Hz probably), and then do rendering separately, interpolating between positions for refresh rates higher than the physics tick rate. This can be hard to make feel good on refresh rates which aren't clean multiples or divisors of the tick rate, but ensures that physics behaves identically independent of frame rate. * Couple rendering and physics and run at a fixed frame rate (60 FPS). This will work okay for many players but a whole lot of people will be angry at you that your game is choppy on their fancy high refresh monitor, and if you don't change monitor mode to change the monitor's refresh rate, a 144Hz screen will sometimes refresh twice and sometimes thrice per game frame, meaning nothing will ever seem smooth even by 60 FPS standards. * Couple physics and frame rate but allow the delta time to vary (maybe running physics 2 or 3 times per frame if the frame rate is really low). I'd say this produces the best results for any given refresh rate, and it's relatively simple to implement, but it causes physics to behave slightly differently with different frame rates, and it's prone to bugs like the one discussed in this article. It's not an easy decision, there is no good rule of thumb. I tend to prefer the last option I listed for my games, but I'm aware that it has drawbacks. But "just decouple physics from rendering" is certainly not the straightforward rule of thumb you seem to suggest it is. (EDIT since this post seemed more negative than I meant: running at a fixed time step is a good solution and very appropriate for many games, I'm not saying your suggestion is bad)
- 2y ago
- jprete 2y agoI think the real problem with the OP's straw-proposal interpolation method is that it's stateless and doesn't need to be. I would instead record the start position, then interpolate along a function F(t) to the end position. F(0)=0, F(1)=1, F'(0)=F'(1)=0 just so the ends aren't jerky. The curve can even overshoot 1 to give the animation some bounce. I'm sure somewhere on the Internet somebody's written down some good choices of curve; hopefully someone else knows what to search for. (I don't know what to do if another change interrupts the first but it's a rare case and can probably be handled imperfectly.)
- v21 2y ago> (I don't know what to do if another change interrupts the first but it's a rare case and can probably be handled imperfectly.) This kind of lerp trick, while imperfect, is useful in exactly these sort of situations. It allows you to smooth movement even if the target point is changing at at arbitrary intervals (note too that it works okay generalised to multiple dimensions). And the statelessness is very useful too - I don't think the feel is great and it's not very controllable, but being able to just add some damping to the movement without having to track animation states or anything like that is super useful.
- 10000truths 2y ago> I don't know what to do if another change interrupts the first but it's a rare case and can probably be handled imperfectly. Extract the position (p0) and velocity (v0) vectors at the moment of interruption, and derive a new function F(t) that meets the constraints {F(0)=p0, F'(0)=v0, F(1)=p1, F'(1)=0}.
- jayd16 2y agoWouldn't you just want to sample a normalized curve and apply that? What is the use case where you want to use this iterative call, care about frame accuracy, but can't afford curve data? With the explicit curve you have more control in the exact damping, too.
- meindnoch 2y agoExponential decay has the special property that it can be calculated from the current state with no additional data needed. With other kinds of interpolation curves, you need to additionally store the time the animation started.
- jayd16 2y agoSure, but when is that relevant?
- manmtstream 2y agoWhen the end state changes over time, or there is no defined duration.
- spoiler 2y agoStated dabbling with game dev very recently so I'm curious about this! Couldn't we get the delta, or for some animations use modulo arithmetic? Maybe mix in some gradient noise into the animation to make it seem organic to break the looping.
- meindnoch 2y agoIt would be illuminating if you showed an example of what you mean by using modulo arithmetic, or getting the delta; otherwise we can't comment on your idea.
- Ono-Sendai 2y agoProposed solution is too expensive with ln and exp calls.
- ux 2y agoOnly the exp call happens in the processing loop. The ln is in the conversion function, it's not supposed to land in the code.
- ncruces 2y agoNow write the formula in terms of expm1 for numerical stability, and express the constant in terms of “half life” measured in seconds to make it intuitive instead of magical.
- deleted 2y ago[deleted]
- ncruces 2y agoSeriously guys, don't write: a = lerp(a, B, 1.0 - exp(-delta * RATE2)) Write: a = lerp(a, B, -expm1(-delta * RATE2)) This is precisely the situation where you really want to use expm1: https://www.johndcook.com/blog/cpp_expm1/ https://www.johndcook.com/blog/cpp_expm1/ And (as pointed in the above) if you're worried about the slowness of it, just Taylor expand it to x+x²/2. Finally, unless division is too costly, do: a = lerp(a, B, -expm1(delta / -T)) Where T is the “time constant” (which you can intuitively express in time units, like seconds): https://en.wikipedia.org/wiki/Exponential_smoothing#Time_constant https://en.wikipedia.org/wiki/Exponential_smoothing#Time_con... It's not the half-life (sorry) but it's still a lot more intuitive as a parameter.
- ux 2y agoThat's some very good recommendations you made here. I'm adding all 3 at the end, thank you.
- BoingBoomTschak 2y agoDon't most modern video games run a physics thread at fixed rate in addition to the rendering one? I remember Quake 3 still having that issue, but that was in '99...
- nightowl_games 2y agoGame dev here. Hardly any serious game developer uses lerp like that. I prefer a quadratic friction when I write custom friction, ie: a constant force away from velocity, and simply ensure it doesn't go past the zero point. More controllable. Box2Ds friction uses something like v = v * 1/(1+damping*delta_time), which is a good method as well. Keep in mind it's best practice to run your game logic at a constant update rate and render an interpolated state between the current and previous game logic frame, meaning that if your game logic is frame rate dependent, you can still run it smoothly on any monitor.
- 05 2y ago> run your game logic at a constant update rate and render an interpolated state between the current and previous game logic frame But if graphics frame rate is higher you won’t be able to interpolate until the next logic frame is ready, - wouldn’t that increase latency?
- marcosdumay 2y agoIt will keep latency constant, at right the amount you tested the game with.
- nightowl_games 2y agoTechnically, 'yes', but with huge caveats. You will still show player movement at the same 'latency', just _less than a full simulation frames worth_. The benefits _far_ outweigh the costs of the other options. Games look _horrible_ if you render the physics at a different frame rate than the rest of the game. Like, horridly bad.
- stephc_int13 2y agoConstant update rate is a must, and can even be tweaked to minimize input latency if you are using a custom engine. Using two frames to interpolate at render time is adding one frame of latency. It is possible to do it on a single frame, extrapolating instead of interpolating, but you have to use a simple but uncommon trick to make it robust. The latency gain is not a full frame but some sizeable fraction of a frame, I think it is worth it for some games.
- omoikane 2y agoAll the frame rates in this post appears to refer to physics frame rates, but Godot makes the distinction between physics and display frame rates, and have separate callbacks for each: https://docs.godotengine.org/en/stable/tutorials/best_practices/godot_notifications.html#process-vs-physics-process-vs-input https://docs.godotengine.org/en/stable/tutorials/best_practi... Because physics frame rate is fixed (configured under project settings), I suspect developers would be able to get away with using the common lerp() formula and not having to worry about varying (display) frame rates.
- PhoenixFlame101 2y agoWhile this is true for simpler games, the author mentions the possibility where we aren't able to hit the target of 60 FPS or whatever, and we get physics spikes on slower computers.
- refulgentis 2y agoI was bit confused how a formula that depends on a constant frame rate works with variable refresh rate. Then the author commented that you don't need to calculate the quantity that depends on frame rate in code. https://news.ycombinator.com/item?id=40401211 https://news.ycombinator.com/item?id=40401211 I'm really lost, but alas have ~0 experience with 3D about 15 years into a career. Ignoring variable frame rate: 1. Doesn't the frame rate depend on the display hardware? 2. Can you know the frame rate at runtime? Variable frame rate: 3. What quantity do you use for frame rate for variable frame rates?
- ux 2y ago1. depends if you're in the physics main loop or not; in Godot for example the physics main loop is based on the project configuration, not correlated with the display (I could have expanded on that in the article) 2. You can know the targeted frame rate, you can also infer it by averaging the delta times at runtime (not reliable but maybe more accurate on a slow machine?) 3. In my graph, I did pick random delta between 15 and 120 fps IIRC; the point was to simulate what could roughly happen with random lag spikes