View Matrix

I don’t understand why the View Matrix is calculated different in MonoGame and DIrectX.

In DirectX the D3DXMatrixLookAtRH function is calculated like this:

And in MonoGame:

        public static void CreateLookAt(ref Vector3 cameraPosition, ref Vector3 cameraTarget, ref Vector3 cameraUpVector, out Matrix result)
        {
            var vector = Vector3.Normalize(cameraPosition - cameraTarget);
            var vector2 = Vector3.Normalize(Vector3.Cross(cameraUpVector, vector));
            var vector3 = Vector3.Cross(vector, vector2);
            result.M11 = vector2.X;
            result.M12 = vector3.X;
            result.M13 = vector.X;
            result.M14 = 0f;
            result.M21 = vector2.Y;
            result.M22 = vector3.Y;
            result.M23 = vector.Y;
            result.M24 = 0f;
            result.M31 = vector2.Z;
            result.M32 = vector3.Z;
            result.M33 = vector.Z;
            result.M34 = 0f;
            result.M41 = -Vector3.Dot(vector2, cameraPosition);
            result.M42 = -Vector3.Dot(vector3, cameraPosition);
            result.M43 = -Vector3.Dot(vector, cameraPosition);
            result.M44 = 1f;
        }

Why is there a minus sign in the matrix elements 41, 42, 43?

if i remember right gl is left handed and dx is right handed.

Though i was just looking at this earlier wondering if its correct.

camera - target is to camera not to target that’s reversed from what you would expect.

vector 2 3 assignments appear reversed, if you look at any of the matrix properties forward back up ect… set gets its 3 2 1 for the order of assignment.
vector is the z axis
vector2 is the y axis
vector3 is the x axis

but when i checked the math, it seemed to come out ok.

How i don’t know, though i didn’t fully test it. Though im suspicious as well.

I don’t understand what this means, sorry.

Switch x and y and it is right.

The calculation is exactly the same. Only difference are the minus signs in the matrix elements 41, 42, 43. Why are they necessary?

probably because camera - target doesn’t give a forward direction it gives a backwards direction the average person doesn’t expect to look backwards when facing forwards lol.

so at the end -x - y - z thats my guess unless there is a real reason that the whole thing is switched up.

Remember in spritebatch negative y is up so i dunno it might have been done on purpose maybe something im not seeing i actually started rewriting these matrixs then i thought if this is really messed up i don’t even want to …

i been testing matrix stuff all day and im more confused then when i started.

So you say because of the direction of the camera - target vector, there have to be minus signs? Why there aren’t any minus signs then in the DirectX D3DXMatrixLookAtRH function? There also the same camera - target vector is used…

just a guess but if you switched it to

targetpos - camerapos i don’t think you would need them. ya its weird to cause 41 42 and 43 are the positions of the camera the translation vector. Like i said i haven’t tested it. You could make a static method real quick and test it.

Actually i just tested it and it seems right kinda i don’t know i dont think the 41 42 43 are supposed to be getting dot’ed at all either. the dx page says they do though i dont see why you would move the cameras position.
I have been getting strange results that might be explained by just that.

I think m41 42 and 43 are supposed to be cameraPos. x y z

so i dunno how it works exactly like that.

sent
pos {X:0 Y:0 Z:0}
forward {X:0 Y:0 Z:-1}
up {X:0 Y:1 Z:0}

creating look at as is
you get backwards right down

returned
pos {X:0 Y:0 Z:0}
forward {X:0 Y:0 Z:-1}
right {X:1 Y:0 Z:0}
up {X:0 Y:1 Z:0}

creating test look at with all th changes was a mess
ya the order was right, had to look again, that version would be all messed up

returned
pos {X:0 Y:0 Z:0}
forward {X:0 Y:0 Z:1}
right {X:0 Y:-1 Z:0}
up {X:1 Y:0 Z:0}

creating test look at
with just the target and position switch and signs removed you get forward left down

returned
pos {X:0 Y:0 Z:0}
forward {X:0 Y:0 Z:1}
right {X:-1 Y:0 Z:0}
up {X:0 Y:1 Z:0}

uh oh … im not sure the regular version is right its altering the position then again i think the view matrix is only supposed to be rotational humm i forget i cant remember if the camera position is supposed to be part of the view matrix translation or not.

woops fudged that position test here we go.

send
pos {X:1 Y:1 Z:1}
forward {X:0 Y:0 Z:-1}
up {X:0 Y:1 Z:0}

creating look at

returned
pos {X:-0.4472136 Y:-0.3651484 Z:-1.632993}
forward {X:0.4472136 Y:0.3651484 Z:-0.8164966}
right {X:0.8944272 Y:-0.1825742 Z:0.4082483}
up {X:0 Y:0.9128709 Z:0.4082483}

creating test look at my posted version below only change is t - c

returned
pos {X:-0.4472136 Y:0.3651484 Z:-1.632993}
forward {X:-0.4472136 Y:0.3651484 Z:0.8164966}
right {X:-0.8944272 Y:-0.1825742 Z:-0.4082483}
up {X:0 Y:0.912871 Z:-0.4082483}

dx Lh

zaxis = normal(cameraTarget - cameraPosition)
xaxis = normal(cross(cameraUpVector, zaxis))
yaxis = cross(zaxis, xaxis)

xaxis.x yaxis.x zaxis.x 0
xaxis.y yaxis.y zaxis.y 0
xaxis.z yaxis.z zaxis.z 0
-dot(xaxis, cameraPosition) -dot(yaxis, cameraPosition) -dot(zaxis, cameraPosition) 1

dx Rh // eye is the target

zaxis = normal(Eye - At)
xaxis = normal(cross(Up, zaxis))
yaxis = cross(zaxis, xaxis)

xaxis.x yaxis.x zaxis.x 0
xaxis.y yaxis.y zaxis.y 0
xaxis.z yaxis.z zaxis.z 0
dot(xaxis, eye) dot(yaxis, eye) dot(zaxis, eye) 1

        public static Matrix TestCreateLookAt(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector)
        {
            Matrix matrix;
            TestCreateLookAt(ref cameraPosition, ref cameraTarget, ref cameraUpVector, out matrix);
            return matrix;
        }
        // is this wrong wth ?
        public static void TestCreateLookAt(ref Vector3 cameraPosition, ref Vector3 cameraTarget, ref Vector3 cameraUpVector, out Matrix result)
        {
            // whys this like this here.
            //    var vector = Vector3.Normalize(cameraPosition - cameraTarget);
            //    var vector2 = Vector3.Normalize(Vector3.Cross(cameraUpVector, vector));
            //    var vector3 = Vector3.Cross(vector, vector2);

            // shouldn't it be target - position
            var forwardZaxis = Vector3.Normalize(cameraTarget - cameraPosition);// vector Z
            var rightYaxis = Vector3.Normalize(Vector3.Cross(cameraUpVector, forwardZaxis)); // vector2 Y
            var upXaxis = Vector3.Cross(forwardZaxis, rightYaxis); // vector3 X

            result.M11 = rightYaxis.X;
            result.M12 = upXaxis.X;
            result.M13 = forwardZaxis.X;
            result.M14 = 0f;

            result.M21 = rightYaxis.Y;
            result.M22 = upXaxis.Y;
            result.M23 = forwardZaxis.Y;
            result.M24 = 0f;

            result.M31 = rightYaxis.Z;
            result.M32 = upXaxis.Z;
            result.M33 = forwardZaxis.Z;
            result.M34 = 0f;

            // needed a negative here ?, because the right up forward is inverted, 
            // because from target was used ?  beats me.
            result.M41 = -Vector3.Dot(rightYaxis, cameraPosition);
            result.M42 = -Vector3.Dot(upXaxis, cameraPosition);
            result.M43 = -Vector3.Dot(forwardZaxis, cameraPosition);
            result.M44 = 1f;
        }

@willmotil
Sorry it’s too dificult for me to understand what you have done exactly. But I think I have found the solution. Hope it helps.

The original question was about the difference between the documentation of the D3DXMatrixLookAtRH function and the implementation of this function in MonoGame.

I now think that the DirectX documentation is just wrong! The MonoGame implementation is correct.

There are two operations needed to transform from WorldSpace to ViewSpace:

1.Given the camera rotation, one must apply the inverse of this rotation. In this case the rotation matrix is orthogonal, so the inverse is equivalent to the transpose of the original rotation matrix. In the method below, the rotation matrix is the one containing the inverse rotation, this means the matrix has already been transposed. I hope it is clear what I mean.

2.Given the WorldSpace position of the camera, one must apply the inverse of this translation. The inverse translation matrix is given by using the negative of the position vector.
The Rotation matrix below is Since the XAxis, YAxis and ZAxis vectors are normalized.

I wrote the following method to check this. It gives the same result as the MonoGame Matrix.CreateLookAt method:

private static Matrix CalculateLookAt(Vector3 Position, Vector3 Target, Vector3 UpVector)
 {
     var ZAxis = Vector3.Normalize(Position - Target);
     var XAxis = Vector3.Normalize(Vector3.Cross(UpVector, ZAxis));
     var YAxis = Vector3.Cross(ZAxis, XAxis);
 
     //calculate rotation
     Matrix Rotation = new Matrix();
 
     Rotation.M11 = XAxis.X;
     Rotation.M12 = YAxis.X;
     Rotation.M13 = ZAxis.X;
     Rotation.M14 = 0f;
     Rotation.M21 = XAxis.Y;
     Rotation.M22 = YAxis.Y;
     Rotation.M23 = ZAxis.Y;
     Rotation.M24 = 0f;
     Rotation.M31 = XAxis.Z;
     Rotation.M32 = YAxis.Z;
     Rotation.M33 = ZAxis.Z;
     Rotation.M34 = 0f;
     Rotation.M41 = 0f;
     Rotation.M42 = 0f;
     Rotation.M43 = 0f;
     Rotation.M44 = 1f;
 
     //calculate translation
     Matrix Translation = new Matrix();
 
     Translation.M11 = 1f;
     Translation.M12 = 0f;
     Translation.M13 = 0f;
     Translation.M14 = 0f;
     Translation.M21 = 0f;
     Translation.M22 = 1f;
     Translation.M23 = 0f;
     Translation.M24 = 0f;
     Translation.M31 = 0f;
     Translation.M32 = 0f;
     Translation.M33 = 1f;
     Translation.M34 = 0f;
     Translation.M41 = -Position.X;
     Translation.M42 = -Position.Y;
     Translation.M43 = -Position.Z;
     Translation.M44 = 1f;
 
     return Rotation * Translation;
 }

Actually there is more then one way to do it. Im sure that’s right for dx.

2.Given the WorldSpace position of the camera, one must apply the
inverse of this translation. The inverse translation matrix is given by
using the negative of the position vector.

besides 1). having nothing to do with 2). which is m41 42 43

you hardly need to perform a dot on all 3 elements to get the negative of the position if that is the case isn’t it easier to simply say -m.Translation;

I don’t see how or why anyone would want to alter the translation elements themselves at all. Point is why even add it. Well its for the transform on the vertexs. Using it as a actual cameraPosition is probably improper.
That would mean reassigning the translation vector every pass as a rule or getting corrupt positional data. As well as storing the actual position else where.
If you don’t want to keep those values for actual translation accurate on the next pass then why bother even placing them into the matrix its far easier to transform each vertex and then add the -vpos you saved. Or just drop in the negative position into the matrx at the end; the rotations are only 3 x 3 anyways the w1 m44 is just for this positional stuff but i always thought it was a waste.

It’s not like its the only way to do it, just saying. Its the way its been taught to be done. Because then you can just use it for the transform on the vertexs i guess without a extra step of - CameraPosition even though that would be far more efficient.

As you can see that positional data is indeed corrupted as show previously from simply assigning the translation vector then retrieving it.

So its kinda misleading to even have a getter on the matrix.Translation property makes you think you get back what you put in, or that you can just m.Translation += motion which you cant;

setter
cameraPosition {X:1 Y:1 Z:1}

creating look (cameraPosition, Vector3.Forward, Vector3.Up) at

getter
m.Translation {X:-0.4472136 Y:-0.3651484 Z:-1.632993}

No thats not your camera position anymore.