6

Even though the System.Numerics.Vectors library that is obtainable via NuGet has its own functions for view and projection matrices I wanted to implement it myself and just use the vector and matrix structures.

Unfortunately I am already getting a totally wrong result when multiplying my target vector with the (correct) view matrix. I am using a right hand coordinate system and the following instructions

var cameraVector4 = new Vector4(0, 4, 2, 1);
var focusVector4 = new Vector4(0, 0, 0, 1);
var vMatrix4 = LookAt(cameraVector4, focusVector4, new Vector4(0, 1.0f, 0, 0));
var targetVector4 = new Vector4(1, 0, -1, 1);
var targetViewVector4 = Vector4.Transform(targetVector4, vMatrix4);

with the following functions

private static Matrix4x4 LookAt(Vector4 cameraVector4, Vector4 focusVector4, Vector4 upVector4) {
    if (cameraVector4 == focusVector4) return Matrix4x4.Identity;
    var z = Vector4.Normalize(cameraVector4 - focusVector4);
    var x = Vector4.Normalize(upVector4.Cross(z));
    var y = Vector4.Normalize(z.Cross(x));

    return new Matrix4x4(
        x.X, x.Y, x.Z, -Vector4.Dot(x, cameraVector4),
        y.X, y.Y, y.Z, -Vector4.Dot(y, cameraVector4),
        z.X, z.Y, z.Z, -Vector4.Dot(z, cameraVector4),
        0, 0, 0, 1);
}

public static class Vector4Extensions {
    public static Vector4 Cross(this Vector4 self, Vector4 vector4) {
        return new Vector4(
            self.Y * vector4.Z - self.Z * vector4.Y,
            self.Z * vector4.X - self.X * vector4.Z,
            self.X * vector4.Y - self.Y * vector4.X,
            0);
    }
}

In my sample above the expected view matrix is

and vMatrix4 does indeed have the same value. Multiplying vMatrix4 to targetVector4 however should yield <1, 0.894427, -4.91935, 1> but Visual Studio reports <1, -0.8944272, -0.4472136, 5.472136>.

My question is, whether the library is having numerical issues computing the result, whether I am having data type mismatches or whether I am using Vector4.Transform incorrectly expecting it to return ViewMatrix * TargetVector.


EDIT

When using the following custom extension method

public static Vector4 ApplyMatrix(this Vector4 self, Matrix4x4 matrix) {
    return new Vector4(
        matrix.M11 * self.X + matrix.M12 * self.Y + matrix.M13 * self.Z + matrix.M14 * self.W,
        matrix.M21 * self.X + matrix.M22 * self.Y + matrix.M23 * self.Z + matrix.M24 * self.W,
        matrix.M31 * self.X + matrix.M32 * self.Y + matrix.M33 * self.Z + matrix.M34 * self.W,
        matrix.M41 * self.X + matrix.M42 * self.Y + matrix.M43 * self.Z + matrix.M44 * self.W
    );
}

the call targetVector4.ApplyMatrix(targetVector4) yields the correct result. This means internally Vector4.Transform seems to doing something really unexpected.

Christian Ivicevic
  • 10,071
  • 7
  • 39
  • 74

1 Answers1

5

I have decompiled the System.Numerics library using dotPeek and stumbled upon this:

public static Vector4 Transform(Vector4 vector, Matrix4x4 matrix) {
    return
        new Vector4(
            (float)
            (vector.X * (double) matrix.M11 +
             vector.Y * (double) matrix.M21 +
             vector.Z * (double) matrix.M31 +
             vector.W * (double) matrix.M41),
            (float)
            (vector.X * (double) matrix.M12 +
             vector.Y * (double) matrix.M22 +
             vector.Z * (double) matrix.M32 +
             vector.W * (double) matrix.M42),
            (float)
            (vector.X * (double) matrix.M13 +
             vector.Y * (double) matrix.M23 +
             vector.Z * (double) matrix.M33 +
             vector.W * (double) matrix.M43),
            (float)
            (vector.X * (double) matrix.M14 +
             vector.Y * (double) matrix.M24 +
             vector.Z * (double) matrix.M34 +
             vector.W * (double) matrix.M44));
}

This method implies that the library is interpreting the vectors as row vectors and multiplying the matrix from the right instead of the "usual" way of multiplying a matrix to the left of a column vector. To fix this issue you have two options:

  • Write a custom method to multiply vectors with matrices in the order you need.
  • Transpose the matrix before applying the transformation which might be a slight overhead.
Christian Ivicevic
  • 10,071
  • 7
  • 39
  • 74
  • [Vector4.cs](https://github.com/dotnet/corefx/blob/master/src/System.Numerics.Vectors/src/System/Numerics/Vector4.cs), lines 315-329. No need for a decompiler, this stuff is open source. – Jeroen Mostert Jan 30 '17 at 16:08
  • @JeroenMostert Oh thank you for providing the link. I have only stumbled upon signature files without implementation on GitHub. – Christian Ivicevic Jan 30 '17 at 16:10
  • It's also in the documentation: "For matrix transformations, the Vector2, Vector3, and Vector4 instances are represented as rows: a vector v is transformed by a matrix M with vM multiplication." See https://learn.microsoft.com/en-us/dotnet/api/system.numerics.vector3?view=netframework-4.8. – Andre Kampling Apr 24 '20 at 06:39