Hey everyone, sorry that it’s been a while. Work has been keeping me a little busy. Anyway I’ve been using a lot of Quaternions recently and as I know it’s something that a lot of people struggle with I thought I would do a quick post with some of the tips and tricks I’ve picked up.
What is a Quaternion?
First off let’s quickly cover what a quaternion is, if you clicked on this you probably already have a basic idea of what they are but I want to cover all the bases.
Quaternions are the way that most game engines and computer programs in general store 3d rotations, the reason they use these convoluted things instead of Euler angles is because of something called angle lock, I won’t go into details but lets just say that you definitely never want to have to deal with angle lock. While I’m not entirely sure how the underlying math works, I believe they get around this by using imaginary/complex numbers. Yes, those things you loathed in algebra, they actually have a use. Something to do with how they loop their values. Anyway 3blue1brown has some videos on it that will explain it much better then me if you really want to understand it:
For now all you need to know is that in unity Quaternions are represented by a 4d vector with the values xyzw, Never touch them, they are black magic. Unity provides plenty of functions so that we will never have to figure out how they work ourselves.
Order of Operations
In math class we learn that you carry out math operations from left to right just like reading, quaternions are the same right? Wrong! Quaternions multiply from right to left. And to top it off they’re are not commutative, they are associative. What that means is that unlike multiplication, addition, or subtraction, the order that you multiply things matter. Basically a * b is the same as b * a but rotation1 * rotation2 is different then rotation2 * rotation1. This means that you might have to try out several different combinations before you get the desired result, at least until you get the hang of it. I can’t wait until that happens to me.
Basic Operations
So now we can finally start getting into some of the code, first lets go over some basics.
World vs local
An easy way to keep track of the order of operations for quaternions is to think of it in terms of world and local rotations. When multiplying a quaternion the world rotation is on the left, and the local rotation is on the right, like this:
finalRotation = worldRotation * localRotation;
For example if you took the global rotation of an object and then multiplied it by the localRotation of a child of that object you would get the global rotation of that child object
childRotation = parentTransform.rotation * childTransform.localRotation;
You can also use this to add an offset to a rotation, for instance if you wanted to rotate an object around it’s local z axis you would just do something like this:
tranform.rotation = targetRotation * Quaternion.Euler(0, 0, 90);
Of course this also applies to global rotations, if you want to rotate an object in global space you would just do this:
tranform.rotation = Quaternion.Euler(0, 0, 90) * transform.rotation;
Adding and Subtracting
So you don’t just want to add quaternions? You want to subtract them too? Are you insane? If the answer is yes, welcome to the club and let’s continue.
To subtract one rotation from another you are going to need to get it’s inverse like so:
inverseRotation = Quaternion.Inverse(rotation);
Why unity couldn’t just add a .inverse to the struct so that you could do rotation.inverse I don’t know, it would make things a lot cleaner looking. (Unity plz fix).
One of the main uses for this is getting the rotation between two rotations. For example if you wanted to know the Quaternion that would get you from rotationA to rotationB you would do something like this:
FromAtoB = Quaternion.Inverse(rotationA) * rotationB
You would then get the rotation between them, this is most useful when you want to copy the offset of one thing to another thing, for example maybe you want to make two levers mirror each other and refuse to use local rotations for whatever reason.
offset = Quaternion.Inverse(lever1BaseRotation) * lever1Rotation
lever2Rotation = lever2BaseRotation * offset
Multiple rotations at once
So maybe you’re completely insane and you want to do multiple rotations at once in the same equation, maybe you’re storing local rotations with an offset or something in a scriptable object for and auto hand poser or something. The trick is to always keep the order of operations. So if you did something like this to store rotation3:
rotation3Offset = Quaternion.Inverse(rotation1) * Quaternion.Inverse(rotation2) * rotation3
to get back to rotation3 you would need to do this:
rotation3 = rotation1 * rotation2 * rotation3Offset
This can even work with predefined local offsets if you’re carful:
storedRotation = Quaternion.Inverse(parentRotation) * rotationToStore * Quaternion.Inverse(offset)
rotationToStore = parentRotation * storedRotation * offset
Sorry if none of that made sense, but I hope it helps someone. If I’m just crazy please send help.
Quaternions to Euler Angles and Back
Now as awesome as it would be to be able to understand Quaternions enough to be able to type them into the inspector of our scripts just off pure intuition, that’s not very realistic and we are going to need to occasionally convert to and from eulers and back.
The first thing we need to understand before starting this is Unity’s order of operations for Euler angles, which is first y, then x, then z. Just something to remember.
Converting from Euler to Quaternion is easy, just use the Quaternion.Euler() function, where it gets hard is when you start wanting Euler angles out of a Quaternion.
The easy way is to use rotation.eulerAngles like this:
Vector3 eulerAngles = transorm.rotation.eulerAngles
And this works just fine if you want to get the rotation of a quaternion around x, y, or z. It does get a little harder when you want to get the euler angle around a different axis or vector. Luckily for you I have a convoluted function that can figure out the Euler angle of a rotation around an axis (Kind of like projecting a vector on to a plane). I had to come up with it because DOTS mathematics does not have a function for getting the y rotation of a quaternion and I needed that for a movement script. You can find it somewhere in here. It will be in the math extensions class, along with some others for projecting vectors if you ever wondered about the math for that.
Conclusion
That’s all I have for you guys today, If you find any errors please put them in the comments so that others can see them and so that I can fix them later. If I come up with anything else I think will be useful I’ll add it here later. Until next time.