Why doesn't my Basis.slerp() take the closest path?

V4.2.2

Question

Why doesn’t my Basis.slerp() take the closest path ?

To demonstrate, I made a example scene with a node that rotates from the back to the top. A rod is there to show the precise movement.

The code of the Sphere node is :

``````extends MeshInstance3D

var timer = 0
var top = true

var goal_basis: Basis

@onready var rot: Node3D = \$RotatingNode

func _process(delta: float) -> void:
timer -= delta
if timer <= 0:
timer = 3
if top:
top = false
print("Facing back")
goal_basis = Basis.looking_at(Vector3.BACK)
else:
top = true
print("Facing top")
goal_basis = Basis.looking_at(Vector3.UP, Vector3.LEFT) # Specify an "up" vector different than the direction
print(goal_basis)
rot.basis = Basis(rot.basis.slerp(goal_basis, 0.02))
``````

I can’t upload videos, so here’s some screenshots.

For the record, I also tried to use Quaternions, as by the doc :

For practical use, it’s enough to understand that pretty much their main use is doing a closest path interpolation.

To no avail : it behaved the same as the current scene. I used `get_rotation_quaternion()` on every basis to get those.

What happens if you use your timer as the weight?

``````var top_basis := Basis.looking_at(Vector3.UP, Vector3.LEFT)
var back_basis := Basis.looking_at(Vector3.BACK)

var clamped_timer := clampf(timer, 0, 1)
rot.basis = back_basis.slerp(top_basis, clamped_timer)
``````

I edited my function to try that.

``````func _process(delta: float) -> void:
timer -= delta
if timer <= 0:
timer = 1
if top:
top = false
print("Facing back")
goal_basis = Basis.looking_at(Vector3.BACK)
else:
top = true
print("Facing top")
goal_basis = Basis.looking_at(Vector3.UP, Vector3.LEFT) # Specify an "up" vector different than the direction
print(goal_basis)
#rot.basis = Basis(rot.basis.slerp(goal_basis, 0.02))
var clamped_timer: float = 1 - clampf(timer, 0, 1)
rot.basis = rot.basis.slerp(goal_basis, clamped_timer)
``````

But as expected, it only changes the timing of the movement : it becomes linear.

one key difference I should’ve pointed out is not using `rot.basis.slerp` I used `back_basis.slerp`.

Using lerp/slerp on a shifting starting point might cause issues.

Ohh, nice point. But I get the same result with :

``````@onready var rot: Node3D = \$RotatingNode
var previous_basis: Basis
var goal_basis: Basis

func _process(delta: float) -> void:
timer -= delta
if timer <= 0:
timer = 1
if top:
top = false
print("Facing back")
previous_basis = rot.basis
goal_basis = Basis.looking_at(Vector3.BACK)
else:
top = true
print("Facing top")
previous_basis = rot.basis
goal_basis = Basis.looking_at(Vector3.UP, Vector3.LEFT) # Specify an "up" vector different than the direction
print(goal_basis)
#rot.basis = Basis(rot.basis.slerp(goal_basis, 0.02))
var clamped_timer: float = 1 - clampf(timer, 0, 1)
rot.basis = previous_basis.slerp(goal_basis, clamped_timer)
``````

Huh, maybe it has to do with the `look_at` possibly rotating to face a different way that we can’t tell with the cylinders.

See if changing this line from `Vector3.LEFT` to `RIGHT`, `FORWARD`, or `BACK` results in a different rotation

``var top_basis := Basis.looking_at(Vector3.UP, Vector3.LEFT)``
1 Like
``````goal_basis = Basis.looking_at(Vector3.UP, Vector3.RIGHT)
``````

This makes the curve at the left side of the ball, while it was at the right side before.

``````goal_basis = Basis.looking_at(Vector3.UP, Vector3.FORWARD)
``````

This makes the movement perfect, exactly as wanted.

``````goal_basis = Basis.looking_at(Vector3.UP, Vector3.BACK)
``````

This makes an even more exaggerated curve on the left.

How can I interpret that ?
Later, those target basis will be completely dynamic, so I have to find an explanation to have a consistant behavior.

Remember you are also rotating the UP vector. Add another cylinder to represent that up axis, it should be more clear with that visual aid

1 Like

Thank you for your help ! It’s more clear now.

I still want to choose the direction of my `up` vector, without it affecting the main rotation.
But I think I can manage it with several layers of rotation in children.