四元数旋转在 y/z 旋转下工作正常,但在我添加 x 旋转时变得混乱

Quaternion rotation works fine with y/z rotation but gets messed up when I add x rotation

所以我最近一直在学习四元数,并决定自己实现。我试图让它变得简单,但我仍然无法查明我的错误。 x/y/z 轴旋转本身可以正常工作,y/z 旋转也可以,但是第二次我将 x 轴添加到任何其他轴时,我得到了一个奇怪的拉伸输出。我将在下面附上旋转的重要代码:(警告我对 cpp 很陌生)。

这是我描述四元数的方式(据我所知,因为它们是单位四元数,不需要虚数):

struct Quaternion {
    float w, x, y, z;
};

四元数的乘法规则:

Quaternion operator* (Quaternion n, Quaternion p) {
    Quaternion o;

    // implements quaternion multiplication rules:
    o.w = n.w * p.w - n.x * p.x - n.y * p.y - n.z * p.z;
    o.x = n.w * p.x + n.x * p.w + n.y * p.z - n.z * p.y;
    o.y = n.w * p.y - n.x * p.z + n.y * p.w + n.z * p.x;
    o.z = n.w * p.z + n.x * p.y - n.y * p.x + n.z * p.w;

    return o;
}

生成旋转四元数以将总旋转乘以:

Quaternion rotate(float w, float x, float y, float z) {
    Quaternion n;

    n.w = cosf(w/2);
    n.x = x * sinf(w/2);
    n.y = y * sinf(w/2);
    n.z = z * sinf(w/2); 

    return n;
}

最后,将四元数转换为 x/y/z 位置的矩阵计算:

inline vector<float> quaternion_matrix(Quaternion total, vector<float> vec) {
    float x = vec[0], y = vec[1], z = vec[2];

    // implementation of 3x3 quaternion rotation matrix:
    vec[0] = (1 - 2 * pow(total.y, 2) - 2 * pow(total.z, 2))*x + (2 * total.x * total.y - 2 * total.w * total.z)*y + (2 * total.x * total.z + 2 * total.w * total.y)*z;
    vec[1] = (2 * total.x * total.y + 2 * total.w * total.z)*x + (1 - 2 * pow(total.x, 2) - 2 * pow(total.z, 2))*y + (2 * total.y * total.z + 2 * total.w * total.x)*z;
    vec[2] = (2 * total.x * total.z - 2 * total.w * total.y)*x + (2 * total.y * total.z - 2 * total.w * total.x)*y + (1 - 2 * pow(total.x, 2) - 2 * pow(total.y, 2))*z;

    return vec;
}

差不多就这些了(我还有一个规范化函数来处理浮点数错误),我将所有对象四元数初始化为:w = 1, x = 0, y = 0, z = 0。我旋转一个使用如下表达式的四元数:

obj.rotation = rotate(angle, x-axis, y-axis, z-axis) * obj.rotation

其中 obj.rotation 是对象的总四元数旋转值。

如果有人知道出了什么问题或之前也遇到过这个问题,我将非常感谢我能在这个问题上得到的任何帮助。谢谢

编辑:将总和乘以这些四元数输出预期的旋转:

rotate(angle,1,0,0)
rotate(angle,0,1,0)
rotate(angle,0,0,1)
rotate(angle,0,1,1)

但是,诸如此类的任何旋转都会使模型出现奇怪的拉伸:

rotate(angle,1,1,0)
rotate(angle,1,0,1)

EDIT2:这是我用来规范化四元数的规范化函数:

Quaternion normalize(Quaternion n, double tolerance) {
    // adds all squares of quaternion values, if normalized, total will be 1:
    double total = pow(n.w, 2) + pow(n.x, 2) + pow(n.y, 2) + pow(n.z, 2);
    if (total > 1 + tolerance || total < 1 - tolerance) {
        // normalizes value of quaternion if it exceeds a certain tolerance value:
        n.w /= (float) sqrt(total);
        n.x /= (float) sqrt(total);
        n.y /= (float) sqrt(total);
        n.z /= (float) sqrt(total);
    }
    return n;
}

正如评论中指出的,您没有正确初始化四元数。

以下四元数不是旋转:

rotate(angle,0,1,1)
rotate(angle,1,1,0)
rotate(angle,1,0,1)

原因是轴没有标准化,例如向量 (0,1,1) 没有标准化。还要确保你的角度以弧度为单位。

要按顺序实现两个旋转,您需要两个基本旋转的四元数乘积。每个基本旋转由轴和角度指定。但是在你的代码中你没有确保你有一个轴的单位向量(方向向量)。

做如下修改

Quaternion rotate(float w, float x, float y, float z) {
    Quaternion n;
    float f = 1/sqrtf(x*x+y*y+z*z)

    n.w = cosf(w/2);
    n.x = f * x * sinf(w/2);
    n.y = f * y * sinf(w/2);
    n.z = f * z * sinf(w/2); 

    return n;
}

然后如下使用

Quaternion n = rotate(angle1,1,0,0) * rotate(angle2,0,1,0)

用于 angle1x 轴的组合旋转,以及 angle2y 的组合旋转-轴。