基于 Tait-Bryan 角计算四元数

5

我正在使用SharpDX编写一个相机,并借助四元数进行旋转。

  • 相机旋转由俯仰(X轴旋转)、偏航(Y轴旋转)和滚转(Z轴旋转)设置,这被称为“Tiat-Bryan”角(这不是欧拉角,欧拉角具有XYX旋转,而不是XYZ)。
  • 我使用的是左手坐标系:X+向右,Y+向上,Z+向下屏幕。
  • 如果我绕X轴旋转(“俯仰”),正值使相机看向下方。如果我绕Y轴旋转(“偏航”),正值使相机看向左侧。如果我绕Z轴旋转(“翻滚”),相机会顺时针翻滚。

我已经能够从四元数中获取俯仰、偏航和滚转,以下是C#代码:

public static class QuaternionExtensions
{        
    public static Vector3 GetPitchYawRoll(this Quaternion q)
    {
        return new Vector3(q.GetPitch(), q.GetYaw(), q.GetRoll());
    }

    public static float GetPitch(this Quaternion q)
    {
        return q.GetK().GetPitch();
    }

    public static float GetYaw(this Quaternion q)
    {
        return q.GetK().GetYaw();
    }

    public static float GetRoll(this Quaternion q)
    {
        // This is M12 * M22 of rotation matrix
        float xx = q.X * q.X;
        float xy = q.X * q.Y;
        float zz = q.Z * q.Z;
        float wz = q.W * q.Z;
        return (float)Math.Atan2(2f * (xy - wz), 1f - 2f * (xx + zz));
    }

    public static Vector3 GetK(this Quaternion q)
    {
        float xz = q.X * q.Z;
        float wy = q.W * q.Y;
        float yz = q.Y * q.Z;
        float wx = q.W * q.X;
        float xx = q.X * q.X;
        float yy = q.Y * q.Y;
        return new Vector3(
            2f * (xz - wy),
            2f * (yz + wx),
            1f - 2f * (xx + yy));
    }

}

public static class Vector3Extensions
{
    public static float GetPitch(this Vector3 v)
    {
        return (float)-Math.Atan2(v.Y, Math.Sqrt(v.X * v.X + v.Z * v.Z));
    }

    public static float GetYaw(this Vector3 v)
    {
        return (float)-Math.Atan2(v.X, v.Z);
    }
}

我不知道另一种方法。如何通过指定俯仰角、偏航角和滚动角来获取四元数(例如从GetPitchYawRoll返回的向量3中返回的内容)?

2个回答

3

Kryzon来自BlitzBasic.com论坛(失效链接),他在这里帮助了我:

internal static class QuaternionExtensions
{
    internal static Vector3 GetYawPitchRollVector(this Quaternion q)
    {
        return new Vector3(q.GetYaw(), q.GetPitch(), q.GetRoll());
    }

    private static float GetYaw(this Quaternion q)
    {
        float x2 = q.X * q.X;
        float y2 = q.Y * q.Y;
        return (float)Math.Atan2(2f * q.Y * q.W - 2f * q.Z * q.X, 1f - 2f * y2 - 2f * x2);
    }

    private static float GetPitch(this Quaternion q)
    {
        return (float)-Math.Asin(2f * q.Z * q.Y + 2f * q.X * q.W);
    }

    private static float GetRoll(this Quaternion q)
    {
        float x2 = q.X * q.X;
        float z2 = q.Z * q.Z;
        return (float)-Math.Atan2(2f * q.Z * q.W - 2f * q.Y * q.X, 1f - 2f * z2 - 2f * x2);
    }
}

internal static class Vector3Extensions
{
    internal static Quaternion GetYawPitchRollQuaternion(this Vector3 v)
    {
        float c1 = (float)Math.Cos(-v.Z / 2.0);
        float c2 = (float)Math.Cos(-v.Y / 2.0);
        float c3 = (float)Math.Cos( v.X / 2.0);
        float c1c2 = c1 * c2;
        float s1 = (float)Math.Sin(-v.Z / 2.0);
        float s2 = (float)Math.Sin(-v.Y / 2.0);
        float s3 = (float)Math.Sin( v.X / 2.0);
        float s1s2 = s1 * s2;

        return new Quaternion(
            c1 * s2 * c3 - s1 * c2 * s3,
            c1c2 * s3 + s1s2 * c3,
            s1 * c2 * c3 + c1 * s2 * s3,
            c1c2 * c3 - s1s2 * s3);
    }
}

请注意,BlitzBasic.com链接已失效,因为域名似乎已过期。 - Warpspace
我需要先从“Heading”(v.Y)中减去*/2*,使其在0处水平。这几乎肯定是由于Tait-Bryan角和更正式的欧拉角之间存在轻微差异所致。 - Warpspace
@Warpspace 感谢您告诉我关于死链的事情(BlitzBasic 也是如此)。不幸的是,我找不到它的存档版本。 - Ray

0

4
称Tait-Bryan角为“欧拉角”是具有误导性的,因为这些值被解释的方式不同。你确定你的信息源不是维基百科所分类的“经典”或“适当”的欧拉角吗?我查看了那段代码以检查它期望哪些值——由于变量名称和无法解释的 EulGetOrd 函数,我发现它很难理解 :(. - Ray
Ken Shoemake涵盖了所有轴旋转的可能变体。代码的所有说明可以在他的文章中找到: http://thehuwaldtfamily.org/jtrl/math/Shoemake,%20Euler%20Angle%20Conversion,%20Graphic's%20Gems%20IV.pdf - minorlogic
1
在查看了代码之后,我必须同意@Ray Koopa的观点。正如指出的那样,“欧拉角表示法”有不止一种可能的变体。该代码没有参数来区分适当的,卡丹,'XYZ','ZYX'等表示法。 - ntg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接