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.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 )
// 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.
Quaternion.CreateFromAxisAngle( rotationAxis, axisAngle ));
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 !!!
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.
*= 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.