为啥 Matrix.MutiplyMV 顺时针旋转向量?

Posted

技术标签:

【中文标题】为啥 Matrix.MutiplyMV 顺时针旋转向量?【英文标题】:Why is Matrix.MutiplyMV rotating vectors clockwise?为什么 Matrix.MutiplyMV 顺时针旋转向量? 【发布时间】:2012-07-12 00:55:12 【问题描述】:

我很困惑为什么 android 的矩阵类的 multiplyMV 方法似乎是顺时针旋转我的矢量坐标,而我认为它是逆时针旋转的。

在这段代码中,pos是向量坐标,设置为,矩阵会将坐标向量绕Z轴旋转-45度。我希望 象限中的结果向量坐标,即 。但相反,它将坐标向相反方向旋转, 象限,即

Matrix4 mtxRot = Matrix4.InitRotateEulerXYZ(0.0f, 0.0f, -45f);
pos.Set(0.0f, 5.0f ,0.0f);
mtxRot.TransformCoordVec(pos);

这是我的 Matrix4.InitRotateEulerXYZ

public static Matrix4 InitRotateEulerXYZ(float x, float y, float z)

    Matrix4 rotMatrix = new Matrix4();

    /*  XYZ = | cz*cy,   sz*cx + cz*sy*sx,  sz*sx - cz*sy*cx |
              | -sz*cy,  cz*cx - sz*sy*sx,  cz*sx + sz*sy*cx |
              | sy,     -cy*sx,             cy*cx            |  */

    // Convert from degrees to radians
    x = MathHelper.DegreesToRadians(x);
    y = MathHelper.DegreesToRadians(y);
    z = MathHelper.DegreesToRadians(z);

    rotMatrix.GetArray()[0] =  MathHelper.Cos(z) * MathHelper.Cos(y);
    rotMatrix.GetArray()[1] = -MathHelper.Sin(z) * MathHelper.Cos(y);
    rotMatrix.GetArray()[2] =  MathHelper.Sin(y);

    rotMatrix.GetArray()[4] =  (MathHelper.Sin(z) * MathHelper.Cos(x)) + (MathHelper.Cos(z) * MathHelper.Sin(y) * MathHelper.Sin(x));
    rotMatrix.GetArray()[5] =  (MathHelper.Cos(z) * MathHelper.Cos(x)) - (MathHelper.Sin(z) * MathHelper.Sin(y) * MathHelper.Sin(x));
    rotMatrix.GetArray()[6] = -(MathHelper.Cos(y) * MathHelper.Sin(x));

    rotMatrix.GetArray()[8 ] = (MathHelper.Sin(z) * MathHelper.Sin(x)) - (MathHelper.Cos(z) * MathHelper.Sin(y) * MathHelper.Cos(x));
    rotMatrix.GetArray()[9 ] = (MathHelper.Cos(z) * MathHelper.Sin(x)) + (MathHelper.Sin(z) * MathHelper.Sin(y) * MathHelper.Cos(x));
    rotMatrix.GetArray()[10] =  MathHelper.Cos(y) * MathHelper.Cos(x);

    return rotMatrix;

这是我的 Matrix4.TransformCoordVec 方法

public Vector3 TransformCoordVec(Vector3 vec3)

    Matrix4.inVec[0] = vec3.X;
    Matrix4.inVec[1] = vec3.Y;
    Matrix4.inVec[2] = vec3.Z;
    Matrix4.inVec[3] = 1.0f; // homogeneousCoord

    Matrix.multiplyMV(Matrix4.outVec, 0, this.matrix, 0, Matrix4.inVec, 0);     

    vec3.X = Matrix4.outVec[0]; vec3.Y = Matrix4.outVec[1]; vec3.Z = Matrix4.outVec[2];     
    return vec3;

非常感谢任何帮助!

已修复

InitRotateEulerXYZ 和我的四元数 ToMatrix() 方法需要转置以便逆时针旋转正角度。以下是修正后的方法。

四元数.ToMatrix

/**Converts a quanternion to its equivilant matrix form**/
public Matrix4 ToMatrix()
       
    // First, lets check if we need to re-normalize our quaternion
    if(normalRegenerationCount <= 1000)
    
        Normalize();
    

    float x2 = x * x;
    float y2 = y * y;
    float z2 = z * z;
    float xy = x * y;
    float xz = x * z;
    float yz = y * z;
    float wx = w * x;
    float wy = w * y;
    float wz = w * z;

    Matrix4 result = new Matrix4();

    // This calculation would be a lot more complicated for non-unit length quaternions
    // Note: The constructor of Matrix4 expects the Matrix in column-major format like expected by
    //   OpenGL
    result.Set_11(1.0f - (2.0f * (y2 + z2)));
    result.Set_12(2.0f * (xy + wz)); 
    result.Set_13(2.0f * (xz - wy));        
    result.Set_14(0.0f);        

    result.Set_21(2.0f * (xy - wz));  
    result.Set_22(1.0f - (2.0f * (x2 + z2)));
    result.Set_23(2.0f * (yz + wx));
    result.Set_24(0.0f); 

    result.Set_31(2.0f * (xz + wy)); 
    result.Set_32(2.0f * (yz - wx)); 
    result.Set_33(1.0f - (2.0f * (x2 + y2))); 
    result.Set_34(0.0f);

    result.Set_41(0.0f);
    result.Set_42(0.0f);        
    result.Set_43(0.0f);        
    result.Set_44(1.0f);

    return result;

Matrix.InitRotateEulerXYZ

public static Matrix4 InitRotateEulerXYZ(float x, float y, float z)

    Matrix4 rotMatrix = new Matrix4();

    // Convert from degrees to radians
    x = MathHelper.DegreesToRadians(x);
    y = MathHelper.DegreesToRadians(y);
    z = MathHelper.DegreesToRadians(z);

    rotMatrix.matrix[0] =  MathHelper.Cos(z) * MathHelper.Cos(y);
    rotMatrix.matrix[1] = (MathHelper.Sin(z) * MathHelper.Cos(x)) + (MathHelper.Cos(z) * MathHelper.Sin(y) * MathHelper.Sin(x)); 
    rotMatrix.matrix[2] = (MathHelper.Sin(z) * MathHelper.Sin(x)) - (MathHelper.Cos(z) * MathHelper.Sin(y) * MathHelper.Cos(x));

    rotMatrix.matrix[4] =  -MathHelper.Cos(y) * MathHelper.Sin(z);
    rotMatrix.matrix[5] =  (MathHelper.Cos(z) * MathHelper.Cos(x)) - (MathHelper.Sin(z) * MathHelper.Sin(y) * MathHelper.Sin(x));
    rotMatrix.matrix[6] =  (MathHelper.Cos(z) * MathHelper.Sin(x)) + (MathHelper.Sin(z) * MathHelper.Sin(y) * MathHelper.Cos(x)); 

    rotMatrix.matrix[8 ] =  MathHelper.Sin(y); 
    rotMatrix.matrix[9 ] = -(MathHelper.Cos(y) * MathHelper.Sin(x));
    rotMatrix.matrix[10] =  MathHelper.Cos(y) * MathHelper.Cos(x);

    return rotMatrix;

【问题讨论】:

尝试聚合对其中一个 rotateM 方法的调用序列以生成最终的旋转矩阵,并注意与您的方法相比的差异(如果有的话)。无论哪种方式都在此处发布新代码。 尝试乘以这个矩阵的转置,看看是否能解决问题。 【参考方案1】:

通过将 X 和 Y 设置为 InitRotateEulerXYZ 中的参数并乘以 (0, 5, 0, 0) 来简化您创建的矩阵:

| cos(θ) sin(θ)    0     0 | | 0 |   | 5*sin(θ) |
|-sin(θ) cos(θ)    0     0 | | 5 |   | 5*cos(θ) |
|    0     0       1     0 | | 0 | = |     0    |
|    0     0       0     0 | | 0 |   |     0    |

代入θ=-π/4,得到的向量由下式给出:

| 5*sin(-π/4) |   | -5*sin(π/4) |
| 5*cos(-π/4) |   |  5*cos(π/4) |
|      0      | = |       0     |
|      0      |   |       0     |

这正是您所看到的答案。所以,我建议这不是 Android 矩阵乘法例程的问题。您应该重新检查矩阵乘法的推导,或者简单地选择其中一个already derived at another site 并相应地调整您的代码。

或者,您可以使用内置的setRotateEulerM 方法并让系统为您生成它。您只需确保矩阵乘法的顺序与您的意图一致,因为有多种方法可以生成这些矩阵。

要考虑的另一件事是,使用 Euler 旋转矩阵通常是一种较差的广义旋转方法,因为它们会受到诸如万向节锁之类的影响。您可能需要考虑Axis/Angle methods 之一,例如Rodrigues' method,或使用quaternions。如果您更愿意使用其中之一,Android 库似乎有 native implementations of axis/angle routines。

【讨论】:

谢谢,你让我开心!我的推导不正确。我想它们适用于左手坐标系。我很高兴android方法没有问题。事实上,我也使用四元数和角轴。事实上,当我注意到我的四元数矢量旋转与我的四元数生成的旋转矩阵旋转的角度相反时,我遇到了这个问题。

以上是关于为啥 Matrix.MutiplyMV 顺时针旋转向量?的主要内容,如果未能解决你的问题,请参考以下文章

Leecode-48:旋转图像(矩阵顺时针旋转90度)

用mplayer的时候,视频被顺时针旋转了90°,怎么办?

求顺时针旋转变换对应的矩阵

将下面矩阵分别按顺时针90度,逆时针90度,和旋转180度,打印出来

css中怎样在右下角为基点顺时针旋转90度?

顺时针或逆时针旋转网页