I want to rotate an object to look at a given direction. So I thought:

var rotation = Matrix.CreateLookAt( Vector3.Zero, direction, Vector3.Up );

Unfortunately this does not give me the expected result. It even gives me NaNs in the matrix when direction == Vector3.Up I am doing something wrong here. Where am I mistaken?

Thank you,
Georg

PS: I guess what I want is what Unity’s Transform.LookAt does. Matrix.CreateLookAt is not the same, right?

Hi again,
what I did now, is to write my own LookAt function:

It does not handle all cases where this might fail. But what it does is basically:

Project the forward and direction Vector to the X-Z plane.

Calculate the angle between them.

Rotate the forward vector about that angle.

Calculate the rotation axis as cross product of rotated forward and direction vector.

Calculate angle between them.

Rotate the whole thing about the rotation axis.

public static Quaternion LookAt( Vector3 origin, Vector3 target, Vector3 forward, Vector3 up )
{
// Calculate the direction vector.
var direction = Vector3.Normalize( target - origin );
// Calculate the angle between direction and forward on XZ.
var xzAngle = MathF.Acos(Vector2.Dot(
new Vector2( Vector3.Forward.X, Vector3.Forward.Z),
new Vector2( direction.X, direction.Z)));
// Rotate about up.
var rotationY = float.IsNaN( xzAngle )
? Quaternion.Identity
: Quaternion.CreateFromAxisAngle( Vector3.Up, xzAngle );
// Get rotation axis.
var rotatedForward = Vector3.Transform( Vector3.Forward, rotationY );
var rotationAxis = Vector3.Cross( rotatedForward, direction );
// If both vectors are almost equal, the area between them is almost zero.
if( rotationAxis.Length() < 0.0001f )
return rotationY;
// Normalize the rotation axis.
rotationAxis = Vector3.Normalize( rotationAxis );
// Get angle between rotated forward axis and direction vector.
var axisAngle = MathF.Acos( Vector3.Dot( rotatedForward, direction ) );
// Concatenate rotation about Y axis and following rotation about cross product of direction and rotated forward.
return Quaternion.Concatenate(
rotationY,
Quaternion.CreateFromAxisAngle( rotationAxis, axisAngle ));
}

That’s because you also use Vector3.Up as the up-vector (3rd parameter). Direction and up vector can’t be the same.

Otherwise Matrix.CreateLookAt should work just fine for creating a rotation matrix as long as the position is set to Vector3.Zero, which it is in your case. I think the problem is somewhere else.

Oh yeah, CreateLookAt is for creating a view matrix, I just got that mixed up in my head. What you want to use is Matrix.CreateWorld. The parameters should be the same.

You will get the same problem with CreateWorld as you will with CreateLookAt that markus stated.
You will also get gimble lock on the opposite pole aka Vector3.Down or the negative of up when the forward matches it.

If the create world or create lookat parameters for forward and up are the same or inverse of each other you will get gimble lock.
^^^ The above is a exact statement it means exactly what is written you can repeat it to others confidently.

So from the above you may come to the conclusion that you need a way to ensure that the Forward is never evaluated to be equal or inverse of the Up they must in a sense move away from each other but never to far away as to be at opposite directions.
.
.
Oh ok , how do you do that !!!
.
.

Use quaternions.

Change the way you rotate the matrices to be similar to quaternions.

I will only address 2 as i figured out how to do it with 2 on my own.
For 1 well find a tutorial prolly not to easy to find.

Typically you have camera position and a direction its pointing in the world this is insufficient to create a full orientation matrix which is what CreateWorld does.

CreateLookAt is the inverse of CreateWorld which turns a world space orientation matrix representing the camera into a view matrix.

The structure of a matrix a right handed one that we use in monogame has some meaning to its rows and columns that are associated to saying Vector3.Up or Right or Forward ect
That meaning is in relation to the apparent motion that a rotation axis affects.

11,12,13,14 // X axis = Up -Down.
21,22,23,24 // Y axis = -Left Right
31,32,33,34 // Z axis = -Forward Backwards
41,42,43,44 // position and W

In order to avoid gimble lock a quaternion by its nature has a defined axial rotation order built into it.
In order to do that with a matrix you must use axis angle and define a axial rotation order for the matrix multiplications of your own camera matrix.

Because this sounds terribly non intuitive in words its easier to give a example.

lets say you have a world matrix that represents were our camera is in the world. C

we wish to add some series of rotations to that matrix and not define a vector3 up .
the way we can do this is to use axis angle to create a axis of rotation on each of the vectors of the current world matrix C

var x = Matrix.CreateAxisAngle( C.Right, angleClockWiseOrCounterClockwise);
var y = Matrix.CreateAxisAngle( C.Up, angleClockWiseOrCounterClockwise);
var z = Matrix.CreateAxisAngle( C.Forward, angleClockWiseOrCounterClockwise);

we can chain these so that the rotation is cummulative to the previous axial rotation in one motion.

C = Matrix.CreateWorld(pos, targetOrForwardDirection, up);
// the prevention of gimble lock for applied rotations on all axis simultaneously.
C
*= Matrix.CreateAxisAngle( C.Right, radialAngleClockWiseOrCounterClockwiseOfTurningUpToAdd) // pitch up down
* Matrix.CreateAxisAngle( C.Up, radialAngleClockWiseOrCounterClockwiseOfTurningRightToAdd) // turn right left
* Matrix.CreateAxisAngle( C.Forward, radialAngleClockWiseOrCounterClockwiseOfRollingToAdd) // roll clockwise or anti clockwise
;

We can then take the values for postion and forward and up from the C matrix and give them to CreateLookAt or Define the view matrix as the inverse of C.

The axial rotation order can be tweeked as well to alter the behavior as this sort of chained rotation is like a free floating camera by that i mean there is absolutely no sense of what system direction up right or down means as far as the camera itself is concerned now. It’s rotations are completely decoupled from the system axis.
This also means somewhat similar as to real life torque can impart motion conserving un-intutive rotational changes.

Unlike a quaternion you can pick the rotation order here.
However that also means you must maintain it at all times.

This particular implementation has one main drawback, because there really is no sense of up which is what we want, that also means there is no sense of a horizon and that’s because on earth we got gravity which the above doesn’t account for in any way.