Major camera stuttering when lerping to an object (delta time issue)

Greetings. I am attempting to make the Camera lerp to an object that it is tracking.

The object is controlled locally and uses BodyVelocity for it to move for the sake of example.

This is my code (very sloppy/pseudo code, please forgive):

Code
-- Please excuse the messy code
local RS = game:GetService("RunService");

local Goal = script.Goal;
local Camera = workspace.CurrentCamera;

Goal.Parent = workspace;

local CONST = 0.016666666666666667;

RS:BindToRenderStep("Test", Enum.RenderPriority.Camera.Value + 1, function (dt)
	local delta = dt/CONST; -- This just make it work with variable framerates if using an unlocker, does not afffect jittering 	
	
	local p = Goal.Position + Vector3.new(1,3,2);
	local final = CFrame.new(p, Goal.Position);
	
	Camera.CameraType = Enum.CameraType.Scriptable;
	
	local speed = 0.5;
	
	local rot = Camera.CFrame.Rotation;
	Camera.CFrame = Camera.CFrame:Lerp(final, speed * delta); -- Introduces jitter, with or without delta
	--Camera.CFrame = final; -- Does not have jitter
end)

Letā€™s take a look at what this causes to happen:

Apologies for low quality, had to downscale it so it would fit here. As I speed the object up you will notice itā€™s jittering. But the object itself is fine, itā€™s actually the camera that is jittering.

Normally Iā€™d assume the solution would be to add delta time in but thatā€™s what Iā€™m doing.

Camera.CFrame = Camera.CFrame:Lerp(New, Speed * Delta)

On top of that, itā€™s in :BindToRenderStep() loop set to Enum.RenderPriority.Camera.Value + 1. So Iā€™m not really sure what else I could be doing here to fix the stuttering. Iā€™ve even tried using heartbeat delta, stepped delta, adding them together, etc, and it only very minutely improves the stuttering.

I should also mention that lerp() is important here. Itā€™s not very apparent since this example shows the object moving at a custom speed but when itā€™s attached to an object that moves around at varying speeds like a player or vehicle, it improves the look and feel.

So what could I be doing wrong here and what could I do to fix it? Iā€™m pretty stumped. I should also note that Iā€™m not a novice (proof) so Iā€™m unsure if Iā€™m having a major brain fart or if this is actually a complex issue, Iā€™m guessing the former.


Here is the repro place:
delta correcting HELP.rbxl (1.1 MB)

there is a nice topic about this:

maybe you can get some answers from that. For what Iā€™ve read, a roblox moderator explains why it jitters and how to get the best result.

I wont get a better answer than in those topics so I hope that helps

I checked again your code and I have a question, why the need of using deltaTime? why not just .2 or .1?

I appreciate your willingness to help but if you donā€™t understand what delta time is for then you wonā€™t be able to help answer my question. Delta time is supposed to correct stuttering by measuring the variation in the amount of time it takes to render each frame and adjusting the value based on that to keep it steady.

The issue is I canā€™t figure out how to implement it properly in this case.

1 Like

Iā€™m not sureā€¦

Assuming that you took care of the issues explained in Manage physical states carefully, I would make sure that the lerp value never exceeds 1. Also CFrame.new(vector3, vector3) is deprecated and can cause jittering by itself especially under vertical angles. I also had some CFrame interpolation jittering issues before that I was never able to solve though. Hope you find out, good luck!

Yes I know what deltaTime is but I was questioning its use. Although Iā€™ve somewhat understood why you are make use of it.
Anyway, my apologies if I just bothered.

1 Like

Itā€™s not immediately obvious, since the position of the goal is already updated every time step. So to just track the object, the dt would not be neccessary. But from what I see, the idea is that the camera is smoothed/delayed a bit to make changes in velocity etc more obvious to the human eye. Without the dt, a variable framerate would cause this smoothing to happen slower or faster. By correcting for that in this way, the slight smoothing effect takes equally long on all devices. If I understand correctly.

2 Likes

Which also leads me to believe this formula is not entirely what you intendedā€¦

Iā€™m not great at this but, what if you tried something along the lines of
speed ^ (1/delta)

That way, the interpolation ratio is always between 0 and 1. When the framerate is high, it is closer to 0. When the framerate is low, itā€™s closer to 1. Should work out the math to make sure you get the right formula to always have the same ā€˜latencyā€™ for the smoothing.

Edit: maybe itā€™s better along the lines of:
1 - speed ^ (1+dt)

But thatā€™s still not it. Too tired today :slight_smile: sorry, good luck!

So I canā€™t stand not knowing the answer to math problems. The correct interpolation factor is I think given by:
1- const / (dt + const)

In your case you want a framerate of 60 to have an interpolation factor of .5. So then your const should be .01666 ish.

I may be wrong, but I think now the ā€˜latencyā€™ of the camera smoothing is always constant, no matter the amount of lag or super fast fps. Didnā€™t test, no warranty. :3

Unfortunately still jitters. 1 - speed ^ dt jitters slightly less but the faster the camera moves the more jittering.

Ok, very curious.

I opened your place, and hereā€™s my theory:

For whatever reason, the bodyVelocity you use has a slight jitter. If you use your smoothing, the jitter becomes visible. This is actually what you wanted, the smoothing working correctly.

When I anchor the part, and introduce at the start of your BindToRenderStep the line:
Goal.CFrame = Goal.CFrame + Vector3.new(3,0,4) * dt;

Then Iā€™m sure it moves correctly and smoothly. Then using the line:
Camera.CFrame = Camera.CFrame:Lerp(final, 1 - CONST / (dt + CONST));

It seems to remove the jitter. Strange but, meh, thatā€™s my best guess!

Note that if you move the camera along with the jitter of the bodyvelocity, the jitter is hidden. Itā€™s so small that the background looks smooth. Again, itā€™s just my best theory atm!

2 Likes

Interesting, for the most part this seems to resolve the issue except when the part is traveling at high speeds.

Unfortunately, my use case requires the use of body velocity or linear velocity yet these both stutter. I will need to try and find a solution to thisā€¦

There are so many threads on devforum regarding lerped camera stutter, once you get past the phase of using physics dt instead of render dt, camera stutter is mostly fixed, however, it still happens on high velocities, I had been struggling to fix it, but that simple formula fixed it up completely, thank you so much.

2 Likes

Yes. And one more thing I would mention is that itā€™s really impossible to have both a non jittery lerped camera and an object that uses physics to move, bottom line. Itā€™s either one or the otherā€¦

Well, you can have that, you can move the object with physics, then make sure you are using physics timestep for your camera, than make sure you process delta with a proper formula above

There is no perfect solution unfortunately. But there are mitigations.

Your suggestion will still result in jitter.

You have three options:

  1. Object jitter, camera smooth
  2. Object smooth, camera jitter
  3. Object smooth, camera smooth, but both have to be lerped using renderstepped

Using physics for both camera and object 1 - CONST / (dt + CONST) will still result in jitter at higher speeds, likely due to physics precision and the issue of having to combine the deltas for both renderstepped and physics.

The solution is to pick which option best fits the need or avoid using lerping.

Well, depends on what you consider ā€œhigher speedsā€, my vehicles move up to 80 studs per second and they are fine.

I use above approach for running camera update script, according to which the new delta would be:

dt = lastPhysicsDeltaTime + lastFramesDeltaTime

and then I lerp with that dt (delta) the following way:

Camera.CFrame = Camera.CFrame:Lerp(targetCamCFrame,1 - CONST / (dt + CONST))
1 Like

Iā€™ll give this a shot and see what happens.