Unexpected behaviour using look_at

Godot Version

4.5.1

Question

I have a 3D object (a 3D vector): VisVec in the image below. I want its tip to always face the camera.

I have attempted to do this by ensuring the tip is facing along the -Z axis in its scene, and placing it as such in the game scene, and then every frame calling look_at in the process function, passing in the camera position. Link to the look_at documentation.

extends Node3D

func _process(_delta: float) -> void:
	var camera = get_viewport().get_camera_3d()
	if (camera):
		self.look_at(camera.position)

This video shows that the tip of the cone is not always facing the camera. Why is this?

Try

get_camera_3d().global_position

instead of

get_camera_3d().position

The global position is the actual position in world coordinates, the position variable is the position in the local reference frame, so probably zero.

I did try that before posting, assuming you mean this:

extends Node3D

func _process(_delta: float) -> void:
	var camera = get_viewport().get_camera_3d()
	if (camera):
		self.look_at(camera.global_position)

Here’s a video of me orbiting it again, using global_position instead (I added more to the scene to give a more obvious visual reference frame for how it is rotating).

1 Like

If you had to rotate your VisVec for it to point -Z then it’s rotation will be reset by look_at, you could add a Node3D in between to keep the child’s rotation at -Z while the parent node can use look_at

1 Like

This fixed it! Yes, you are right, I did a -90 degree rotation about the x-axis in the scene to get it facing the -Z axis. I’ve instead moved them to a container node and applied that rotation to the container.


Just to add to @gertkeno’s answer, I believe this is the implementation in the source code. No attempt to preserve any rotations is made (which seems very obvious in hindsight, thank you again!)

Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) {
#ifdef MATH_CHECKS
	ERR_FAIL_COND_V_MSG(p_target.is_zero_approx(), Basis(), "The target vector can't be zero.");
	ERR_FAIL_COND_V_MSG(p_up.is_zero_approx(), Basis(), "The up vector can't be zero.");
#endif
	Vector3 v_z = p_target.normalized();
	if (!p_use_model_front) {
		v_z = -v_z;
	}
	Vector3 v_x = p_up.cross(v_z);
	if (v_x.is_zero_approx()) {
		WARN_PRINT("Target and up vectors are colinear. This is not advised as it may cause unwanted rotation around local Z axis.");
		v_x = p_up.get_any_perpendicular(); // Vectors are almost parallel.
	}
	v_x.normalize();
	Vector3 v_y = v_z.cross(v_x);

	Basis basis;
	basis.set_columns(v_x, v_y, v_z);
	return basis;
}