Converting Quaternion to Euler Angle
Filed under Mathematics
Overview
Conceptually, there are many ways to represent rotation in 3D game programming, the most common being Quaternion, Euler Angle, Matrix and Axis Angle. A lot of resources can be found on the web on each representation.
However, there are not so many “concrete” implementation on how to convert from quaternion to euler angle. This article will focus solely on this matter. It will discuss on what you need to know and give a working example code in XNA.
Convention
One problem in Euler angle is there are possibly more than one way to represent a rotation. Euler angle can be defined as three numbers (ex, ey, ez) whereby each number represent a rotation relative to its axis.
Problem arises when we need to construct a rotation matrix based on the Euler angle. We can construct a rotation matrix of an axis, i.e.:

Derivation
To create a single rotation matrix, we need to know the order of multiplication. In XNA, the order is Z-X-Y, so the rotation matrix will be:
![]()



Your engine may adopt a different convention; thus it will lead to a different result.
From, the result above, we can get ex:

also ey:

and finally ez:

Using .NET reflection, we can know how XNA derive matrix from quaternion Q=(qx,qy,qz,qw) and thus we have all the information we need:
![]()
![]()
![]()
![]()
![]()
and finally, we can get the euler angle ex, ey, ez:
![]()


Special Case
There is a problem when ex=90 or ex=-90degree. Since cos(ex) will be 0, so M12 = M22 = M31 = M33 = 0 and yields ey and ez undefined. To handle this, let see how XNA converts Euler Angle to Quaternion:

When ex=90:

it yields

If we choose ez=0, then we get ey = 2 * atan2(qy, qw)
Similarly, when ex=-90:

it yields

If we choose ez=0, then we get ey = 2 * atan2(qy, qw).
Implementation So Far
Based on the theory above, we can write a function to convert from Quaternion to Euler Angle. This method guarantees if you transform point P to P’ by using quaternion Q, you can also apply rotation using euler angle E to transform P to P’.
(NOTE: This method does not guarantee that EulerToQuaternion(E) = Q).
{
Vector3 euler;
float sqx = q.X * q.X;
float sqy = q.Y * q.Y;
float sqz = q.Z * q.Z;
float sqw = q.W * q.W;
float unit = sqx + sqy + sqz + sqw;
float test = (q.X * q.W - q.Y * q.Z);
// Handle singularity
if (test > 0.4999999f * unit)
{
euler.X = MathHelper.PiOver2;
euler.Y = 2.0f * (float)System.Math.Atan2(q.Y, q.W);
euler.Z = 0;
}
else if (test < -0.4999999f * unit)
{
euler.X = -MathHelper.PiOver2;
euler.Y = 2.0f * (float)System.Math.Atan2(q.Y, q.W);
euler.Z = 0;
}
else
{
float ey_Y = 2 * (q.X * q.Z + q.Y * q.W);
float ey_X = 1 - 2 * (sqy + sqx);
float ez_Y = 2 * (q.X * q.Y + q.Z * q.W);
float ez_X = 1 - 2 * (sqx + sqz);
euler.X = (float)System.Math.Asin(2 * test);
euler.Y = (float)System.Math.Atan2(ey_Y, ey_X);
euler.Z = (float)System.Math.Atan2(ez_Y, ez_X);
}
// Convert to degrees
euler.X = MathHelper.ToDegrees(euler.X);
euler.Y = MathHelper.ToDegrees(euler.Y);
euler.Z = MathHelper.ToDegrees(euler.Z);
return euler;
}
Antipodal Quaternion
As I have mentioned above, the Euler angle E obtained by the method above does not guarantee that converting it back to quaternion will yield Q. Using the method above, it may yield its antipodal, i.e. -Q. It is important to note that Antipodal quaternions, Q and −Q, represent the same rotation.
Thus we need some way to ensure property EulerToQuaternion( QuaternionToEuler(Q) ) = Q. Unfortunately, I don’t know a good way to do this. I found a workaround by trying to add/subtract 360 from the Euler angle. If you find a better way, I would be happy to update this posting.
{
Vector3 euler;
float sqx = q.X * q.X;
float sqy = q.Y * q.Y;
float sqz = q.Z * q.Z;
float sqw = q.W * q.W;
float unit = sqx + sqy + sqz + sqw;
float test = (q.X * q.W - q.Y * q.Z);
// Handle singularity
if (test > 0.4999999f * unit)
{
euler.X = MathHelper.PiOver2;
euler.Y = 2.0f * (float)System.Math.Atan2(q.Y, q.W);
euler.Z = 0;
}
else if (test < -0.4999999f * unit)
{
euler.X = -MathHelper.PiOver2;
euler.Y = 2.0f * (float)System.Math.Atan2(q.Y, q.W);
euler.Z = 0;
}
else
{
float ey_Y = 2 * (q.X * q.Z + q.Y * q.W);
float ey_X = 1 - 2 * (sqy + sqx);
float ez_Y = 2 * (q.X * q.Y + q.Z * q.W);
float ez_X = 1 - 2 * (sqx + sqz);
euler.X = (float)System.Math.Asin(2 * test);
euler.Y = (float)System.Math.Atan2(ey_Y, ey_X);
euler.Z = (float)System.Math.Atan2(ez_Y, ez_X);
}
Quaternion qNeg = -q;
Quaternion qTest;
Quaternion.CreateFromYawPitchRoll(euler.Y, euler.X, euler.Z, out qTest);
if (ExMath.Equals(ref qNeg, ref qTest))
{
if (euler.X < 0)
euler.X += TwoPi;
else
euler.X -= TwoPi;
Quaternion.CreateFromYawPitchRoll(euler.Y, euler.X, euler.Z, out qTest);
if (ExMath.Equals(ref qNeg, ref qTest))
{
if (euler.Y < 0)
euler.Y += TwoPi;
else
euler.Y -= TwoPi;
}
}
// Convert to degrees
euler.X = MathHelper.ToDegrees(euler.X);
euler.Y = MathHelper.ToDegrees(euler.Y);
euler.Z = MathHelper.ToDegrees(euler.Z);
return euler;
}
Final Note
As a final note, the code above uses ExMath.Equals(ref Quaternion a, ref Quaternion b) which compares quaternion components with some epsilon:
{
return ((System.Math.Abs(a.X - b.X) < ExMath.Epsilon) &&
(System.Math.Abs(a.Y - b.Y) < ExMath.Epsilon) &&
(System.Math.Abs(a.Z - b.Z) < ExMath.Epsilon) &&
(System.Math.Abs(a.W - b.W) < ExMath.Epsilon));
}
Jan16








