Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Completely wrong value for matrix multiplication with System.Numerics

Tags:

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.

like image 358
Christian Ivicevic Avatar asked Jan 30 '17 10:01

Christian Ivicevic


People also ask

When multiplication of matrix is not possible?

You can only multiply two matrices if their dimensions are compatible , which means the number of columns in the first matrix is the same as the number of rows in the second matrix.

What are the rules for matrix multiplication?

To perform matrix multiplication, the first matrix must have the same number of columns as the second matrix has rows. The number of rows of the resulting matrix equals the number of rows of the first matrix, and the number of columns of the resulting matrix equals the number of columns of the second matrix.

Does order matter in matrix multiplication?

Matrix multiplication is not commutative In other words, in matrix multiplication, the order in which two matrices are multiplied matters!

How do you solve matrix problems with multiplication?

How to multiply two given matrices? To multiply one matrix with another, we need to check first, if the number of columns of the first matrix is equal to the number of rows of the second matrix. Now multiply each element of the column of the first matrix with each element of rows of the second matrix and add them all.


1 Answers

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.
like image 63
Christian Ivicevic Avatar answered Sep 23 '22 19:09

Christian Ivicevic