代码之家  ›  专栏  ›  技术社区  ›  Mark Ingram

矩阵乘法

  •  3
  • Mark Ingram  · 技术社区  · 14 年前

    这两段伪代码有什么区别?

    // Multiplying a matrix by the difference between each frame
    float difference = current - previous; // Time since previous frame
    float angle = difference / 500;
    matrix rotation;
    rotation.RotateX(angle);
    rotation.RotateY(angle);
    worldMatrix *= rotation; // Note multiply
    
    // Multiplying a matrix by the difference between current and start
    float difference = current - start; // Time since first frame
    float angle = difference / 500;
    matrix rotation;
    rotation.RotateX(angle);
    rotation.RotateY(angle);
    worldMatrix = rotation; // Note assignment
    

    每段代码之间只有很小的差异,但会导致很大的视觉差异。输入如下:

    第1帧:旋转=1弧度
    worldMatrix*=旋转;
    第2帧:旋转=1弧度
    worldMatrix*=旋转;
    等。。。

    第1帧:旋转=1弧度
    worldMatrix=旋转;
    第2帧:旋转=2弧度
    worldMatrix=旋转;
    等。。。

    2 回复  |  直到 14 年前
        1
  •  8
  •   comingstorm    14 年前

    实际上,结果 应该 与众不同(即使你不考虑累积误差,如上所述)。原因是顺序在旋转中很重要:绕X旋转,然后绕Y旋转,这与绕Y旋转,然后绕X旋转不同。

    在矩阵表示法中(据我了解您的设置),您有以下理想行为:

    let angle = (end - start)/500
      Rx = rotate.RotateX(angle)
      Ry = rotate.RotateY(angle)
    
    then, foreach frame in (0..500):
    cumulative: Rc = Rx * Ry * Rx * Ry * ... * Rx * Ry
                   = (Rx * Ry)^frame
    assignment: Ra = Rx * Rx * ... * Ry * Ry * ....
                   = (Rx)^frame * (Ry)^frame
    

    关于伪代码的一些注释:这里的约定是我们从左到右乘矩阵(这意味着点是行向量)。另外,如果不清楚, (matrix)^N 是矩阵求幂:乘法 N 的副本 (matrix) 依次在一起。

    对于累积情况,OQ从一个单位矩阵开始,乘以小的旋转。 Rx Ry 连续的;你的 rotation 等于我的 (Rx*Ry) . 然后乘以 worldMatrix 乘以这个矩阵;这意味着对于任何给定的帧, 世界旅行社 = initial_worldMatrix * (Rx*Ry)^frame .

    对于赋值情况,计算出 frame * total_angle/total_frames . 这相当于旋转 total_angle/total_frames 按顺序 frame 时间,这一点很重要,因为这些小旋转与累积情况中使用的小旋转完全相同。因此,在分配的情况下,您的代码正在计算 (Rx)^frame * (Ry)^frame ,并每次将worldMatrix重置为该值。


    重点是这些是 不同的矩阵 即使有了完美的数学,他们也应该看起来与众不同。

    你应该选择哪一个取决于你想要什么样的行为。累积版本将非常接近围绕X和Y轴之间的对角线轴旋转;分配版本的作用类似于旋转框架。

    如果您确实想要累积行为,有比将最多500个矩阵相乘更好的方法(如上所述,您的矩阵将由于浮点错误而漂移)。具体来说,可以围绕Z轴旋转45度,然后围绕X轴旋转frame/500,然后围绕Z轴旋转-45度,以获得类似效果。


    阐述这两种情况的区别:

    在累积的情况下,你在x上旋转一点,然后在y上旋转一点,重复很多次。如果旋转很小,两个小旋转组合的结果将是围绕 一些 轴(如果它们不是那么小,轴可能不完全在两者之间,但它仍然是一个特定的旋转)。关键是,如果你重复这对旋转,结果将是在那个轴上,越来越多的旋转,不管它是什么。

    在作业的例子中,你做的是关于x的所有旋转,然后是关于y的所有旋转。这使得旋转变大,这就有了区别。可视化差异的一种方法是想象一组坐标轴:大X轴旋转会使原始Y轴偏离直线,因此Y轴旋转的应用方式不同。

    在数学术语中,之所以存在如此大的差异,是因为一般来说,旋转不是交换的:换句话说,顺序很重要。注意,小的旋转是 大约 可交换的(当旋转角度接近零时, Rx * Ry Ry * Rx 四次接近零——把角度切成两半,可以减少4倍的差异,但是当你把所有的小旋转组合成两个大的旋转时,你做了很多的重新排序,这就产生了巨大的差异。即使每个交换( Rx*Ry -gt; Ry*Rx )效果很小,迁移n RX “S到一边”实际上是一种泡沫类型:你需要O(n^2)交换来完成它……

        2
  •  2
  •   FrustratedWithFormsDesigner    14 年前

    区别似乎是第一个样本 改变 当前世界矩阵由旋转矩阵构成。第二个样本 替换 世界矩阵由旋转矩阵构成。如果在此之前没有对世界矩阵应用其他操作,您可能看不到任何区别,但是如果在此之前应用了任何操作,则第二个代码示例将丢弃这些操作。

    问题是,你是否希望你对世界矩阵的改变是累积的?第一个代码示例会给您一个累积的效果,第二个不会。