Verifying Euler to Quaternion calculations

I’m new to Quaternions so to learn more about them I decided to read up on various resources and try to construct my own elementary Quaternion library in python and then validate it with results from Unity.

So what I do is create a new project and place a cube in space. I then add to that cube a new script that simply copies its transform.rotation to a public variable that I call “myQuaternion”. A snippet of the innards of the script are:

public Quaternion myQuaternion;

void Update() {
  myQuaternion = transform.rotation;

So that seems to be working as expected, I can inspect the cube, change its rotation and get different values in myQuaternion. Great.

So now based on this wikipedia section: Conversion between quaternions and Euler angles - Wikipedia

I create this python function:

def euler_to_quaternion(phi,theta,psi):
    return [math.cos(math.pi*phi/360)*math.cos(math.pi*theta/360)*math.cos(math.pi*psi/360)+

So now I run some tests:

For rotation (45,0,0):

  • Unity: w=0.9238795325112867
    x=0.3826834323650898 y=0.0 z=0.0
  • Python: w=0.9238795325112867
    x=0.3826834323650898 y=0.0 z=0.0

Looks good, they match

For rotation (0,45,0):

  • Unity: w=0.9238795325112867 x=0.0
    y=0.3826834323650898 z=0.0
  • Python:
    w=0.9238795325112867 x=0.0
    y=0.3826834323650898 z=0.0

Again, a match

For rotation (0,0,45):

  • Unity: w=0.9238795325112867 x=0.0 y=0.0 z=0.3826834323650898
  • Python: w=0.9238795325112867 x=0.0 y=0.0 z=0.3826834323650898

Again, a match

So here’s the problem:

For rotation (0,45,45):

  • Unity: w=0.8535533905932737 x=0.14644660940672624 y=0.3535533905932738 z=0.3535533905932738
  • Python: w=0.8535533905932737 x=-0.14644660940672624 y=0.3535533905932738 z=0.3535533905932738

So the polarity for the value of x is different, not good…

For rotation (45,45,0):

  • Unity: w= 0.8446231986207332 x=0.46193976625564337 y=0.1913417161825449 z=0.1913417161825449
  • Python: w= 0.8446231986207332 x= 0.1913417161825449 y=0.46193976625564337 z= 0.1913417161825449

Now things look even more confusing, any idea what is going on?

Update: Doing more research and working through this issue, I’ve discovered that order of operations is very important when doing rotations. I found that I needed to break the rotations into 3 separate quaternions and then multiply them in a specific order, here’s some additional python code:

def quaternion_mult(q,r):
    return [r[0]*q[0]-r[1]*q[1]-r[2]*q[2]-r[3]*q[3],

def unity_euler_to_quaternion(x,y,z):
    return quaternion_mult(quaternion_mult(euler_to_quaternion(0,y,0),

Now when I do a unity_euler_to_quaternion(45,45,45), the answers match.


Well, as you figured out yourself the order in which you apply the rotations matters. There is not just one valid euler angles representation. Unity uses the order Z-X-Y around worldspace axes. To express this combination by rotations around localspace axes you just need to reverse the order. So it’s the same as Y-X-Z around the localspace axes.

I haven’t looked at the conversion used on the wikipedia page, but i guess it’s X-Y-Z or Z-Y-X so you need a different order of your rotations. That means the combination of all those sin / cos will look different.

You shouldn’t use your “euler_to_quaternion” method inside your “unity_euler_to_quaternion” method. You do way to many calculations that way. You should first implement the AngleAxis method. I don’t really know python so here’s a C# example:

public static Quaternion AngleAxis(float aAngle, float aX, float aY, float aZ)
    float s = Mathf.Sin(aAngle/2f);
    return new Quaternion(Mathf.Cos(aAngle/2f), aX*s, aY*s, aZ*s);

This method should create a quaternion rotation of “aAngle” radians around the normalized axis defined by (aX,aY,aZ)

To create your 3 rotations you would simply use:

AngleAxis(x, 1f,0, 0 );
AngleAxis(y, 0, 1f,0 );
AngleAxis(z, 0, 0, 1f);

Finally multiply them in the right order. Keep in mind that you can calculate the combined result in one go just like the example on the wikipedia page. However i would recommend to cache each sin / cos value in a local variable. At the moment inside your “euler_to_quaternion” method you calculate each sin and cos value 4 times.