Search code examples
camerarotationquaternionssharpdx

Quaternion from Tait-Bryan angles


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)?


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