Notes Of A Three.js Beginner: Euler Angles vs. Quaternions
I was pretty happy with how quickly I was able to get a static 3D visualization on screen with the three.js library. My first project to turn the static display into an interactive color picker also went smoothly, giving me a great deal of self confidence for proceeding to the next challenge: adding an animation. And this was where three.js put me in my place reminding me I'm still only a beginner in both 3D graphics and JavaScript.
Before we get to details on how I fell flat on my face, to be fair three.js animation system is optimized for controlling animations created using content creation tools such as Blender. In this respect, it is much like Unity 3D. In both of these tools, programmatically generated animations are not the priority. In fact there weren't any examples for me to follow in the manual. I hunted around online and found DISCOVER three.js, which proclaimed itself as "The Missing Manual for three.js". The final chapter (so far) of this online book talks about animations. This chapter had an ominous note on animation rotations:
As we mentioned back in the chapter on transformations, quaternions are a bit harder to work with than Euler angles, so, to avoid becoming bamboozled, we’ll ignore rotations and focus on position and scale for now.
This is worrisome, because my goal is to animate the 256 colors between two color model layouts. From the current layout of a HSV cylinder, to a RGB cube. This required dealing with rotations and just as the warning predicted that's what kicked my butt.
The first source confusion is between Euler angles and quaternions when dealing with three.js 3D object properties. Object3D.rotation
is an object representing Euler angles, so trying to use QuaternionKeyframeTrack
to animate object rotation resulted in a lot of runtime errors because the data types didn't match. This problem I blame on JavaScript in general and not three.js specifically. In a strongly typed language like C there would be an error indicating I've confused my types. In JavaScript I only see errors at runtime, in this case one of these two:
- When the debug console complains "NaN error" it probably meant I've accidentally used Euler angles when quaternions are expected. Both of those data types have fields called x, y, and z. Quaterions have a fourth numeric field named w, while Euler angles have a string indicating order. Trying to use an Euler angle as quaternion would result in the order string trying to fit in w, which is not a number hence the NaN error.
- When the debug console complains "THREE.Quaternion: .setFromEuler() encountered an unknown order:" it means I've done the reverse and accidentally used Quaternion when Euler angles are expected. This one is fortunately a bit more obvious: numeric value w is not a string and does not dictate an order.
Getting this sorted out was annoying, but this headache was nothing compared to my next problem: using QuaternionKeyframeTrack
to animate object rotations.
[Code for this project is publicly available on GitHub]