如何从旋转矩阵中获得轴角?

时间:2012-04-16 10:34:05

标签: opengl quaternions euler-angles rotational-matrices

我需要从openGL旋转矩阵中获取一些数据。我需要获得等效的欧拉角(已经做过),等效的四元数(做了它,但只是从互联网上复制)和等效的轴角。

我不知道是否可以将旋转矩阵表示为围绕特定向量的特定角度的单个旋转。这些是等价的吗?如果是,我怎么能从另一个中获得一个?

另外,我想更好地理解四元数的含义,以及旋转矩阵的内部。我应该去哪里了解这个?

2 个答案:

答案 0 :(得分:1)

是的,任何旋转矩阵/单位四元数相当于围绕单个轴的旋转。如果我们将此轴n和角度theta称为,则此轮换的四元数为:

[n * sin(theta / 2) cos(theta / 2)]

要在四元数的acos元素上重建此使用w,以获取theta / 2。获得theta后,您可以将xyz组件与sin(theta / 2)分开来重建轴。

答案 1 :(得分:0)

这是一个将3x3矩阵转换为轴角度的函数(使用quatention,因此可能是绕过该步骤的更有效的方法)。

void axis_angle_from_mat3(float r_axis[3], float *r_angle, float mat[3][3])
{
    float q[4];

    /* -------------------------------------------------------------------- */
    /* matrix to quaternion */
    double tr, s;
    float tmat[3][3];

    /* work on a copy */
    memcpy(tmat, mat, sizeof(tmat));

    /* normalize the matrix */
    int i;
    for (i = 0; i < 3; i++) {
        float d = (tmat[i][0] * tmat[i][0] + tmat[i][1] * tmat[i][1] + tmat[i][2] * tmat[i][2]);

        if (d > 1.0e-35f) {
            d = sqrtf(d);
            tmat[i][0] /= d;
            tmat[i][1] /= d;
            tmat[i][2] /= d;
        }
        else {
            tmat[i][0] = 0.0f;
            tmat[i][1] = 0.0f;
            tmat[i][2] = 0.0f;
            d = 0.0f;
        }
    }


    tr = 0.25 * (double)(1.0f + tmat[0][0] + tmat[1][1] + tmat[2][2]);

    if (tr > (double)1e-4f) {
        s = sqrt(tr);
        q[0] = (float)s;
        s = 1.0 / (4.0 * s);
        q[1] = (float)((double)(tmat[1][2] - tmat[2][1]) * s);
        q[2] = (float)((double)(tmat[2][0] - tmat[0][2]) * s);
        q[3] = (float)((double)(tmat[0][1] - tmat[1][0]) * s);
    }
    else {
        if (tmat[0][0] > tmat[1][1] && tmat[0][0] > tmat[2][2]) {
            s = 2.0f * sqrtf(1.0f + tmat[0][0] - tmat[1][1] - tmat[2][2]);
            q[1] = (float)(0.25 * s);

            s = 1.0 / s;
            q[0] = (float)((double)(tmat[1][2] - tmat[2][1]) * s);
            q[2] = (float)((double)(tmat[1][0] + tmat[0][1]) * s);
            q[3] = (float)((double)(tmat[2][0] + tmat[0][2]) * s);
        }
        else if (tmat[1][1] > tmat[2][2]) {
            s = 2.0f * sqrtf(1.0f + tmat[1][1] - tmat[0][0] - tmat[2][2]);
            q[2] = (float)(0.25 * s);

            s = 1.0 / s;
            q[0] = (float)((double)(tmat[2][0] - tmat[0][2]) * s);
            q[1] = (float)((double)(tmat[1][0] + tmat[0][1]) * s);
            q[3] = (float)((double)(tmat[2][1] + tmat[1][2]) * s);
        }
        else {
            s = 2.0f * sqrtf(1.0f + tmat[2][2] - tmat[0][0] - tmat[1][1]);
            q[3] = (float)(0.25 * s);

            s = 1.0 / s;
            q[0] = (float)((double)(tmat[0][1] - tmat[1][0]) * s);
            q[1] = (float)((double)(tmat[2][0] + tmat[0][2]) * s);
            q[2] = (float)((double)(tmat[2][1] + tmat[1][2]) * s);
        }
    }


    /* normalize the quat */
    float len;
    len = sqrtf(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
    if (len != 0.0f) {
        q[0] /= len;
        q[1] /= len;
        q[2] /= len;
        q[3] /= len;
    }
    else {
        q[1] = 1.0f;
        q[0] = q[2] = q[3] = 0.0f;
    }


    /* -------------------------------------------------------------------- */
    /* quaternion to axis angle */

    float ha, si;

    ha = acosf(q[0]);
    si = sinf(ha);

    *r_angle = ha * 2;

    if (fabsf(si) < FLT_EPSILON)
        si = 1.0f;

    r_axis[0] = q[1] / si;
    r_axis[1] = q[2] / si;
    r_axis[2] = q[3] / si;
}