【问题标题】:Completely wrong value for matrix multiplication with System.Numerics使用 System.Numerics 进行矩阵乘法的值完全错误
【发布时间】:2017-01-30 10:40:09
【问题描述】:

尽管可通过 NuGet 获得的 System.Numerics.Vectors 库有自己的视图和投影矩阵函数,但我想自己实现它,只使用向量和矩阵结构。

不幸的是,当我将目标向量与(正确的)视图矩阵相乘时,我已经得到了一个完全错误的结果。我正在使用右手坐标系和以下说明

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);

具有以下功能

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);
    }
}

在我上面的示例中,预期的视图矩阵是

vMatrix4 确实具有相同的值。将vMatrix4targetVector4 相乘应该会产生<1, 0.894427, -4.91935, 1>,但Visual Studio 会报告<1, -0.8944272, -0.4472136, 5.472136>

我的问题是,库在计算结果时是否存在数值问题,是否存在数据类型不匹配,或者我是否使用 Vector4.Transform 错误地期望它返回 ViewMatrix * TargetVector


编辑

使用以下自定义扩展方法时

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
    );
}

调用targetVector4.ApplyMatrix(targetVector4) 会产生正确的结果。这意味着在内部 Vector4.Transform 似乎做了一些非常出乎意料的事情。

【问题讨论】:

    标签: c# matrix numerics system.numerics


    【解决方案1】:

    我使用 dotPeek 反编译了 System.Numerics 库并偶然发现了这个:

    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));
    }
    

    此方法意味着库将向量解释为行向量并从右侧乘以矩阵,而不是“通常”的方式将矩阵乘以列向量的左侧。要解决此问题,您有两种选择:

    • 编写一个自定义方法,按您需要的顺序将向量与矩阵相乘。
    • 在应用转换之前转置矩阵,这可能会产生轻微的开销。

    【讨论】:

    • Vector4.cs,第 315-329 行。不需要反编译器,这东西是开源的。
    • @JeroenMostert 哦,感谢您提供链接。我只是偶然发现了没有在 GitHub 上实现的签名文件。
    • 它也在文档中:“对于矩阵转换,Vector2、Vector3 和 Vector4 实例表示为行:向量 v 由矩阵 M 与 vM 相乘进行转换。”见docs.microsoft.com/en-us/dotnet/api/…
    猜你喜欢
    • 2013-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多