【问题标题】:Multiplying matrices乘法矩阵
【发布时间】:2010-11-09 22:15:03
【问题描述】:

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

// 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 弧度
世界矩阵 *= 旋转;
第 2 帧:旋转 = 1 弧度
世界矩阵 *= 旋转;
等等……

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

【问题讨论】:

    标签: math opengl 3d matrix direct3d


    【解决方案1】:

    实际上,结果应该有所不同(即使您不考虑累积误差,如上所述)。原因是顺序在旋转中很重要:先先绕 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 的副本按顺序相乘。

    对于累积的情况,您的 OQ 从单位矩阵开始,将其乘以连续的小旋转 Rx 和 Ry;你的rotation 等于我的(Rx*Ry)。然后它将worldMatrix 乘以该矩阵多次;这意味着对于任何给定的帧,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 旋转一点,重复很多次。如果旋转很小,则组合两个小旋转的结果将是围绕 some 轴的小旋转(如果它们不是那么小,则轴可能不在两者之间,但是它仍然是一个特定的轮换)。关键是,如果你重复那对旋转,结果将是在那个轴上越来越多的旋转,不管它是什么。

    在分配情况下,您先围绕 X 进行所有旋转,然后围绕 Y 进行所有旋转。这会使旋转变大,这会产生影响。一种可视化差异的方法是想象一组坐标轴:大的 X 旋转会将原始 Y 轴旋转到线外,因此 Y 旋转的应用方式不同。

    用数学术语来说,差异如此之大的原因在于,一般来说,旋转不是可交换的:换句话说,顺序很重要。请注意,小旋转是近似可交换的(随着旋转角度接近零,Rx * Ry 和 Ry * Rx 之间的差值二次方接近零 - 将角度减半可将差值减少 4 倍),但是当你将所有的小旋转组合成两个大的旋转时,你做了很多重新排序,它会产生巨大的差异。即使每个单独的交换(Rx * Ry -> Ry * Rx)只有很小的影响,将 N 个 Rx 迁移到一侧实际上是一种冒泡排序:你需要 O(N^2) 交换到做吧....

    【讨论】:

    • 累积 = 轴旋转 & 赋值 = 旋转云台正是我所看到的,感谢您准确理解我在原始问题中试图提出的问题。你能解释一下累积/分配伪代码吗?我试图理解为什么数学如此不同。谢谢!
    【解决方案2】:

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

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

    【讨论】:

    • 一般来说,作业版比累积版更可取。如果将 500 个旋转矩阵相乘,由于浮点问题,结果会趋于漂移——结果可能最终看起来越来越不像旋转矩阵...
    • 第一个例子应该演示累积效应——但它实际上并不起作用。我得到的旋转与第二个例子完全不同。
    猜你喜欢
    • 2018-04-11
    • 2017-03-11
    • 2013-12-23
    • 2014-09-19
    相关资源
    最近更新 更多