What is the correct way of using Lerp with deltaTime correction?

Hi everyone, I hope you are all fine!

I’m creating a small Arcade Flight simulator, and I’m using Lerp to do some rotations, for example:

let pitchAngle = BABYLON.Angle.FromDegrees(40).radians();
pitch = BABYLON.Scalar.Lerp(pitch, pitchAngle * Math.sign(verticalInput), 0.2)

But I was concerned about how to make the Lerp work correctly regardless of the framerate, so I started using scene.getAnimationRatio() to do it:

let animationRatio = scene.getAnimationRatio()

let pitchAngle = BABYLON.Angle.FromDegrees(40).radians()
            pitch = BABYLON.Scalar.Lerp(pitch, pitchAngle * Math.sign(verticalInput), 0.2 * animationRatio)

But after searching the internet, I found apparently more complex ways to solve the problem, as in this Unity article, so I was in doubt if I’m using Lerp correctly in this case.

Apparently using scene.getAnimationRatio works fine, but if possible, I’d like an opinion from the more experienced veterans on the subject, please.

Thank you very much in advance for your help.

Relying on Babylon Animations is the simplest as you would need a system similar to this as a pseudo code example:

const start = pitch;
const target = BABYLON.Angle.FromDegrees(40).radians() * Math.sign(verticalInput);
const animationDuration = 2000;

let time = 0;
scene.onBeforeRender = () => {
  time += engine.getDeltaTime();

  let gradient = time / animationDuration;
  
// should clamp the gradient between 0 and 1 and prevent the animation to continue once done

  BABYLON.Scalar.Lerp(start, target, gradient)
}
1 Like

@sebavan thank you very much for answering… so from what I understand from yours if I don’t want the animation to last a specific time, but just increase x% by frame until it reaches the target, I can use the animation ratio…

And, if I want it to last a certain time, so I don’t have to do all that process you illustrated, I could use Babylon Animations.

Please correct me if I’m wrong.

Grateful

even without a specific timing, you would need some kind of accumulation from 0 to 1 with a given start and end unfortunately the animation ratio would need to be accumulated in this case

Hello @sebavan ,

I believe I couldn’t explain my question very well, however, I did some experiments here and I believe I got a satisfactory result. Basically, what I was wondering was if, by multiplying the Lerp target by the animationRatio, Lerp would work proportionally according to the framerate.

As I’m passing the variable itself as Lerp’s start in each iteration, in this case, it ends up being cumulative (causes that effect of being slower at the end, but I believe it’s not a problem for the game I’m making at the moment).

That is if I’m at 60fps (animationRatio = 1), and my Lerp has a target of 0.1, my variable will be modified 10% at each iteration of Lerp. However, if my framerate drops to 30fps, my animationRatio is 2. That is, on the next iteration, Lerp will modify my variable by 20% (0.1 * 2).

Below are the results of the experiments I did.

First, I started by performing the Lerp of the x position of this cone by multiplying the target by the animationRatio (the average FPS here is 140, without stuttering):

const cone =  BABYLON.MeshBuilder.CreateCylinder("cone", {height: 3,diameterTop: 0, diameterBottom: 3, diameter: 1, tessellation: 10, subdivisions: 10}, scene)

// On render loop...
let target = 0.08 * scene.getAnimationRatio()
cone.position.x = BABYLON.Scalar.Lerp(cone.position.x, 50 * direction, target)

Then I made some changes to the code to lower the FPS (I left the cone with 3500 tesselations and 3500 subdivisions). This dropped the FPS to around 30/40, but with a lot of stuttering. Even so, the cone movement happens at the same speed as when the FPS was 140 (which is the result I’m looking for):

const cone =  BABYLON.MeshBuilder.CreateCylinder("cone", {height: 3,diameterTop: 0, diameterBottom: 3, diameter: 1, tessellation: 3500, subdivisions: 3500}, scene)

// On render loop...
let target = 0.08 * scene.getAnimationRatio()
cone.position.x = BABYLON.Scalar.Lerp(cone.position.x, 50 * direction, target)

After that, I reset the cone to 10 tesselations/subdivisions and removed the animationRatio and reduced the Lerp target from 0.08 to 0.04 (with the FPS rate at 140 by default, the animationRatio is close to 0.5, so I halved the value to maintain an approximate speed). With this, the movement was close to the first result:

const cone =  BABYLON.MeshBuilder.CreateCylinder("cone", {height: 3,diameterTop: 0, diameterBottom: 3, diameter: 1, tessellation: 10, subdivisions: 10}, scene)

// On render loop...
let target = 0.04
cone.position.x = BABYLON.Scalar.Lerp(cone.position.x, 50 * direction, target)

And finally, still, without the animationRatio and with the Lerp target at 0.04, I increased the cone again to 3500 tesselations/subdivisions. With that, the cone was much slower, as the FPS drops back to 30/40 and as I’m not using animationRatio, the distance traveled is not compensated by the drop in frame rate.

const cone =  BABYLON.MeshBuilder.CreateCylinder("cone", {height: 3,diameterTop: 0, diameterBottom: 3, diameter: 1, tessellation: 3500, subdivisions: 3500}, scene)

// On render loop...
let target = 0.04
cone.position.x = BABYLON.Scalar.Lerp(cone.position.x, 50 * direction, target)

So basically I was able to use animationRatio to make Lerp work proportionally to the frame rate. Not sure if this is the correct way, but it seems to work fine.

One question that stuck in my mind was:

  • Is it wrong to use the variable itself inside Lerp’s start? Or is this practice not recommended?

If you have any more information on this, I would be grateful to understand better.

Thank you very much for your willingness to help and teach.

What you do in this case is basically moving by x% from current to target so you will get closer and closer but never reach it :slight_smile: it is not wrong and done a lot as it kind of decelerate nicely. This is exactly what the Unity post was highlighting.

1 Like

Awesome @sebavan thank you very much for your help