CFrame.Lerp inaccuracies

Hello there,
I wanted to recreate the CFrame.Lerp function just out of curiosity. However, my function has slightly different rotation values compared to the original function.

Here’s my code:

local function cfLerp(start:CFrame, goal:CFrame, alpha:number):CFrame
	local pos = start.Position:Lerp(goal.Position, alpha)
	
	local rightVec = start.RightVector:Lerp(goal.RightVector, alpha)
	local upVec = start.UpVector:Lerp(goal.UpVector, alpha)
	local lookVec = -start.LookVector:Lerp(goal.LookVector, alpha)

	--[[return CFrame.new(
		pos.X, pos.Y, pos.Z,
		rightVec.X, upVec.X, lookVec.X,
		rightVec.Y, upVec.Y, lookVec.Y,
		rightVec.Z, upVec.Z, lookVec.Z
	)]]
	return CFrame.fromMatrix(pos, rightVec, upVec, lookVec)
end


--testing
local cf1 = CFrame.new(100.5448456, 50.5571957, -1.7499962, -0.906307757, -0.42261827, 0, -0.42261827, 0.906307757, 0, 0, 0, -1)
local cf2 = CFrame.new(58.0448418, 24.0571918, -88.25, -0.683012545, -0.183012679, -0.707106769, -0.258819044, 0.965925813, 0, 0.683012664, 0.183012709, -0.70710665)
print(cf1:Lerp(cf2, 0.891) == cfLerp(cf1, cf2, 0.891))
print(cf1:Lerp(cf2, 0.891))
print(cfLerp(cf1, cf2, 0.891))

I was told that these are internal inaccuracies, but they are pretty big sometimes. Does anyone have any idea why this is happening?

Linearly interpolating individual values, or vectors will not properly rotate them like you may think. This is a common misconception, and unfortunately the solution is a bit confusing. You can look into something called “slerp” if you want to learn more about it. For a more direct answer to your question, I think what you’ll want to do is convert the starting and destination CFrames to quaternions, then interpolate between the quaternions, and then convert back to cframe. This is what the Roblox engine does internally I believe.

1 Like

Thank you for your answer, I wonder why I never heard about Slerp :thinking:
This code produces the same result as CFrame.Lerp:

local function cfLerp(start:CFrame, goal:CFrame, alpha:number):CFrame
	local pos = start.Position:Lerp(goal.Position, alpha)
	
	local axis, theta = (start:Inverse() * goal):ToAxisAngle()
	local rot = CFrame.fromAxisAngle(axis, theta * alpha) * start

	return CFrame.new(pos.X, pos.Y, pos.Z, select(4, rot:GetComponents()))
end
1 Like