Question

I am writing a camera with SharpDX and rotate it with the help of a quaternion.

  • The camera rotation is set with pitch (X rotation), yaw (Y rotation) and roll (Z rotation), so called "Tiat-Bryan" angles (these are not Euler angles which would have an XYX rotation, and not XYZ).
  • I am using a left-handed coordinate system: X+ is rightwards, Y+ is upwards, Z+ is downwards the screen.
  • If I rotate around X ("pitching"), positive values make the camera look downwards. If I rotate around Y ("yawing"), positive values make the camera look leftwards. If I rotate around Z ("rolling"), the camera rolls clockwise.

I was already able to get pitch, yaw and roll from the quaternion, s. the following C# code:

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);
    }
}

I don't know the other way round. How to get the quaternion by specifying pitch, yaw and roll (e.g. what's returned in the Vector3 from GetPitchYawRoll)?

Was it helpful?

Solution

Kryzon from the BlitzBasic.com forum (dead link) helped me out here:

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);
    }
}

OTHER TIPS

This all often called Euler Angles,

http://en.wikipedia.org/wiki/Euler_angles

All variants of conversion can be found at https://github.com/erich666/GraphicsGems/blob/master/gemsiv/euler_angle/EulerAngles.c

Originally written by Ken Shoemake, 1993

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top