godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
88.74k stars 20.12k forks source link

Incorrect lerp usage with deltatime for camera smoothing (and probably other things too) #93115

Open mse-k opened 2 months ago

mse-k commented 2 months ago

Tested versions

System information

N/A

Issue description

The function that interpolates the camera position to the position it should be following isnt properly calculated with the frame delta, causing any usage of camera smoothing to be frame rate dependent (and more jittery that it needs to be!). Currently it calculates the % to move by multiplying the delta time and the smoothing speed, but this is easily seen to be wrong; imagine if the delta time was >1, that would mean the camera would go all the way over its target, which should not be possible at all! The correct way to do this is using an exponential function, which adds up correctly over different frame rates and will never go above 1. Something like

            real_t c = 1 - pow(0.7, position_smoothing_speed * delta);
            smoothed_camera_pos = ((camera_pos - smoothed_camera_pos) * c) + smoothed_camera_pos;

would fix the issue, 0.7 can be any number between 0 and 1 though.

Steps to reproduce

Compare the motion of the camera between two different fpses (say, 15 and 120) and notice how even if they have the same speed, they move completely differently.

Minimal reproduction project (MRP)

N/A (i based this issue on the c++ code, not actually making it in a project)

Calinou commented 2 months ago
clayjohn commented 2 months ago

CC @lawnjelly @rburing

F3der1co commented 2 months ago

Also see Freya Holmérs talk on this topic https://youtu.be/LSNQuFEDOyQ?si=gBw9Sx5xxtMr_WVZ&t=2990 , there she suggest using an exponential decay function for smoothing lerps, which is much faster to calculate as it omits the pow() image

MrEgggga commented 2 months ago

This has come up in a game i'm working on -- the camera lurches forward a bit every time a frame takes too long.

Here's a somewhat-minimal reproduction project. The square here is in free fall, with the camera following behind it. Every frame there is a 1% chance to do some slow calculation -- when this happens the camera gets significantly closer to the square (& sometimes ahead) when it should be staying behind. (The computation might not be slow enough to make a noticeable difference for people with fast computers.)

minimal_reproduction.zip

MrEgggga commented 2 months ago

I think this would work -- essentially the same as what mse-k suggested in the original message but with exp instead of pow w/ base of 0.7:

            real_t c = 1 - exp(-position_smoothing_speed * delta);
            smoothed_camera_pos = ((camera_pos - smoothed_camera_pos) * c) + smoothed_camera_pos;

With this, the camera will move the same as before (but framerate-independent) for the same position_smoothing_speed.