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)+
math.sin(math.pi*phi/360)*math.sin(math.pi*theta/360)*math.sin(math.pi*psi/360),
math.sin(math.pi*phi/360)*math.cos(math.pi*theta/360)*math.cos(math.pi*psi/360)-
math.cos(math.pi*phi/360)*math.sin(math.pi*theta/360)*math.sin(math.pi*psi/360),
math.cos(math.pi*phi/360)*math.sin(math.pi*theta/360)*math.cos(math.pi*psi/360)+
math.sin(math.pi*phi/360)*math.cos(math.pi*theta/360)*math.sin(math.pi*psi/360),
math.cos(math.pi*phi/360)*math.cos(math.pi*theta/360)*math.sin(math.pi*psi/360)-
math.sin(math.pi*phi/360)*math.sin(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],
r[0]*q[1]+r[1]*q[0]-r[2]*q[3]+r[3]*q[2],
r[0]*q[2]+r[1]*q[3]+r[2]*q[0]-r[3]*q[1],
r[0]*q[3]-r[1]*q[2]+r[2]*q[1]+r[3]*q[0]]
def unity_euler_to_quaternion(x,y,z):
return quaternion_mult(quaternion_mult(euler_to_quaternion(0,y,0),
euler_to_quaternion(x,0,0)),
euler_to_quaternion(0,0,z))
Now when I do a unity_euler_to_quaternion(45,45,45), the answers match.
Whew!