Geometric algebra is great if you want to extend things to higher dimensions, or spaces with different metric signatures (the 4-dimensional spacetime for example). But quaternions should not be discounted just because they fit into a generalisation - they have many quite special properties all of their own.
I always felt like quaternions weren't a perfect fit for 3d rotation, and when I finally learned about bivectors my faith in the elegance of mathematics was restored :D
I liked this article: https://marctenbosch.com/quaternions/
Yikes! This is the sort of thing that scares people away from complex numbers from a young age.
No really, unit quaternions are nice because it's pretty clear they belong to a sphere, therefore you can fairly easily extend geometric methods on the usual sphere to perform filtering, averaging, interpolation etc on rotations.
With GA it's not so obvious.
I've read linear and geometric algebra and it's amazing for linear operations. Unfortunately it gets more complex when you want to use it for nonlinear operations (such as translation) - now you need to use CGA or PGA or something. Still, seems like it would be amazing for computer graphics, physical simulation, etc. especially if someone can figure out a good strategy for efficient compilation (e.g. representing a multivector with exactly the minimal number of non-zero entries, with non-zero basis elements tracked at compile time).
Game engines don't need much if anything from GA (and I've written about using GA decades ago for software, and did it for some time, before realizing that trading that elegance for worse performance was not worth it).
Here's [1] one example from the godfather of GA demonstrating how poorly GA performs
[1] https://webspace.science.uu.nl/~kreve101/asci/GAraytracer.pd...
> The main cause for the lower performance of GA is the Gaigen’s soft typing of the geometric algebra objects at compile-time: all types of objects (scalars, vectors, bivectors, trivectors, rotors and so on) are represented by a single data type in Gaigen. When a product or operation has to be computed, Gaigen first checks the grade usage of the argument(s) and then acts accordingly. This conditional step between function call and actual computation is largely responsible for the drop in performance.
So, I blame the library. Writing out the 3-4 datatypes for 3D GA by hand as real types costs some development effort but should give equivalent performance.
Two heartbeats later, we're bearish on Q's.
Complex numbers (2-dimensional): No more ordering
Quaternions (4-dimensional): No more commutative multiplication
Octonions (8-dimensional): No more associative multiplication
Sedenions (16-dimensional): We lose the alternative property, i.e., with octonions its still true that x(xy) = (xx)y but with sedenions it is not.
We can continue this process indefinitely, although I know nothing of the characteristics of trigintaduonions or what comes later. I suspect that it might be the loss of the flexible identity next, a(ba) = (ab)a but I don't know for sure.
https://nautil.us/blog/the-strange-numbers-that-birthed-mode...
"Place your phone face-up on a flat surface, for example. Spin it 90 degrees to the left, and then flip it away from you. Note which way the camera points.
Returning to the original position, flip it away from you first and then turn it to the left second. See how the camera points to the right instead?
This initially alarming property, known as non-commutativity, turns out to be a feature the quaternions share with reality "
Real numbers (1-dimensional): No more algebraic closure [1]
though as you suggest we do gain a nice ordering where "nice" means "compatible with operations". BTW, you can always impose some ordering on all of these sets. It is the orderings' compatibility with arithmetic that we lose going from real numbers to complex numbers.
This can be generalized a little. Real numbers fail to be algebraically closed because some real polynomials lack real solutions, e.g. xx=-2. Complex numbers "fix" this problem since there every n degree polynomial has exactly n roots (counting multiplicity). In a sense, quaternions overdo this, e.g. xx=-2 has an infinite number of solutions (exercise for the reader). However, since quaternion multiplication is not commutative, polynomials as traditionally understood are ill-defined. For example, axx, xax and xxa are different degree two quaternion "polynomials". Ugh.
[1]: https://en.wikipedia.org/wiki/Algebraically_closed_field
[1] https://projecteuclid.org/journals/pacific-journal-of-mathem...
Normed means that each number has associated nonnegative real number that describes the "size" of the number, also |xy| <= |x||y|. Number having a size (a norm) is very useful concept.
Sedions and the rest are less useful because they are not normed algebras.
[0] https://www.cs.cas.cz/portal/AlgoMath/AlgebraicStructures/St...
https://en.m.wikipedia.org/wiki/Cayley%E2%80%93Dickson_const...
w + xi + yj + zk
Where w, x, y, and z are real and i^2 = j^2 = k^2 = -1 and ij = k, ji = -k, jk = i, kj = -i, ki = j, ik = -j.
Being u = (x, y, z) = xi + yj + zk a unitary vector, it is possible rotate any vector q by an angle theta around u by doing:
pqp'
where p = cos(theta/2) + sin(theta/2)u and p' = cos(theta/2) - sin(theta/2)u .
I'm gonna guess "hypercomplex" means "involves imaginary numbers" due to the rest of this.
> Where w, x, y, and z are real and i^2 = j^2 = k^2 = -1 and ij = k, ji = -k, jk = i, kj = -i, ki = j, ik = -j.
Why even use different letters for i, j, and k, if they're all the same thing? Which thing appears to be i, as in, the square root of -1, that is to say, the most well-known and basic imaginary number, if I'm reading this right. As for the second part, I can't even begin to unravel the significance. I know it must not be, but it just seems like arbitrary rules added for... some unknown reason.
> Being u = (x, y, z) = xi + yj + zk a unitary vector, it is possible rotate any vector q by an angle theta around u by doing:
> pqp'
> where p = cos(theta/2) + sin(theta/2)u and p' = cos(theta/2) - sin(theta/2)u .
Oooooh kay... 1) Where'd w go? Is this one of those things where there's a (situationally-defined) constant in the formula but we just pretend it doesn't exist most of the time (until it comes time to actually use the math to, like, do anything real)? Would we need to bring it back in to apply the rest of this? 2) "u =" is just defining something, fine, but (x, y, z) doesn't seem to equal the thing after it at all—I suppose this is a shorthand function notation, though it seems really weird to me to use equality to relate that. Am I right, or is this something else?, 3) A quaternion is... a point, then? Since we're rotating around it? 4) I've got a feeling that theta needs a direction in this hypercomplex space but don't see where it's coming from. Somewhere "off screen", in this explanation? Or is it there but I'm not seeing it?
Why should -1 have three distinct imaginary roots? Well, why should it have one? Essentially, we just made up i, and we found out that the complex numbers had some really useful algebraic properties. The same is true of the quaternions.
But why not two imaginary roots, or four, or 17? Those turn out not to have nice algebraic properties. The only other thing out there are the octonions, with 7 imaginary roots.
>
>I'm gonna guess "hypercomplex" means "involves imaginary numbers" due to the rest of this.
Think it is right.
>>
>> Where w, x, y, and z are real and i^2 = j^2 = k^2 = -1 and ij = k, ji = -k, jk = i, kj = -i, ki = j, ik = -j.
>
>Why even use different letters for i, j, and k, if they're all the same thing? Which thing appears to be i, as in, the square root of -1, that is to say, the most well-known and basic imaginary number, if I'm reading this right.
They are not the same thing. All of these are equal to -1 when squared, but they are different when multiplied by any other of this set and multiplication between them is anti-commutative.
>As for the second part, I can't even begin to unravel the significance. I know it must not be, but it just seems like arbitrary rules added for... some unknown reason.
>
These are not added for unknown reasons. They specify rotations of unitary vectors of a canonical base around one another.
>> Being u = (x, y, z) = xi + yj + zk a unitary vector, it is possible rotate any vector q by an angle theta around u by doing:
>>
>> pqp'
>>
>> where p = cos(theta/2) + sin(theta/2)u and p' = cos(theta/2) - sin(theta/2)u .
>>
>Oooooh kay... 1) Where'd w go?
The coordinate 'w' is the real part. For the vector 'u', such coordinate is 0. For 'p' and 'p'', it is cos(theta/2).
>Is this one of those things where there's a (situationally-defined) constant in the formula but we just pretend it doesn't exist most of the time (until it comes time to actually use the math to, like, do anything real)?
No.
>Would we need to bring it back in to apply the rest of this?
No. Just follow the rules to multiply hypercomplex numbers and the rotation will occur.
>2) "u =" is just defining something, fine, but (x, y, z) doesn't seem to equal the thing after it at all—I suppose this is a shorthand function notation, though it seems really weird to me to use equality to relate that. Am I right, or is this something else?,
The notation '(x, y, z)' is just a shorter notation for "xi + yj + zk". For this problem, the vector 'u' defines the axis of rotation.
>3) A quaternion is... a point, then? Since we're rotating around it?
You can see a quaternion as a point in R^4 since it has 4 coordinates. Actually it describes an axis of rotation, by its imaginary part, and the angle of rotation.
>4) I've got a feeling that theta needs a direction in this hypercomplex space but don't see where it's coming from. Somewhere "off screen", in this explanation? Or is it there but I'm not seeing it?
>
The axis of rotation is actually the vector "u".
If you try to use a product of 3 rotation matrices that represent rotations around fixed axes to represent rotations in 3D, there will inevitably be gimbal lock because there cannot exist a covering map from a product of 3 circles to SO(3). (Gimbal lock happens at the points where the map locally fails to be a covering map)
SU(2) is the universal cover of SO(3). The unit quaternions are a faithful representation of SU(2). Conveniently, the double cover map from SU(2) to SO(3) is dead simple in this representation: a unit quaternion q in SU(2) act on a purely imaginary quaternion (thought of as a vector in R^3) by conjugation. This is the formula you wrote.
Since it is a covering map, there is no gimbal lock.
Edited to fix explanation.
There are a vanishingly small number of scenarios where lerp() + normalize() doesn't work perfectly well enough and it is drastically faster and SIMD-friendly. That used to be the case at least.
It works great if the chord is far away from the center (and also the angle is less than a half turn). If the chord goes through the center, it doesn't work at all.
It was essentially the same reasoning as for using `-ffast-math`. Yeah it's not "correct", but users can't tell the difference and it has a measurable performance benefit.
To be clear, I'm not questioning using slerp() at all, it definitely has a role to play. I'm just wondering about using it by default as implied by the post.
The amount that it matters depends on the angle between keys. Suggesting it rarely matters means you’re claiming nobody ever animates large angles, which I can confidently say I’ve seen plenty of counter-examples in game development.
I also wouldn’t be so sure that lerp+normalize is that much faster on today’s GPUs. Normalize takes a sqrt and reciprocal or divide, while slerp takes a few sin evaluations and a recip or divide. These special functions these days execute in a separate pipeline from linear (FMA) type instructions, and can be had for “free” as long as you can mix enough independent linear math in between the special functions. It used to be that sin() was very slow, but these days you can often mix a couple calls in without affecting your perf at all.
In practice, the former is often provably never going to happen, and the later doesn't actually matter from a qualitative standpoint: Quaternion interpolation will always look a bit weird over large angles, especially if there's a roll component to one of the orientations.
As far as having a constant interpolation speed: again, this is rarely perceptible, and truly linear transitions from one orientation to another looks janky so some amount of filtering happens anyways (an ease-in-ease-out for example).
But yeah, for very large interpolations, it can create a slight authoring/runtime skew. My rule of thumb used to be: use slerp() when the orientations can be at least 120 degrees apart, or when lerp() yields weird results. However, it's really rare to be interpolating over such a large orientation change, you almost always have a few keyframes or dead-reckoning syncs in between.
Again: This might be an outdated perspective though.
Quaternions also capture the spinorial character of rotations in three dimensions. For a three-dimensional object connected to its (fixed) surroundings by slack strings or bands, the strings or bands can be untangled after two complete turns about some fixed axis from an initial untangled state. Algebraically, the quaternion describing such a rotation changes from a scalar +1 (initially), through (scalar + pseudovector) values to scalar −1 (at one full turn), through (scalar + pseudovector) values back to scalar +1 (at two full turns). This cycle repeats every 2 turns. After 2n turns (integer n > 0), without any intermediate untangling attempts, the strings/bands can be partially untangled back to the 2(n − 1) turns state with each application of the same procedure used in untangling from 2 turns to 0 turns. Applying the same procedure n times will take a 2n-tangled object back to the untangled or 0 turn state. The untangling process also removes any rotation-generated twisting about the strings/bands themselves. Simple 3D mechanical models can be used to demonstrate these facts.
https://twitter.com/johncarlosbaez/status/146720413326244659...
The convention in that article is known as “Hamilton's quaternions”. There’s another incompatible one usually called “JPL quaternions”, for some American Jet Propulsion Laboratory.
Authors of 99% articles about the quaternions, and software libraries implementing quaternions, are assuming exactly one of these conventions, and almost never mention which one that is.
More info there: https://fzheng.me/2017/11/12/quaternion_conventions_en/
https://arxiv.org/abs/1711.02508
This is actually a great source of frustration in the robotics state estimation community, as you have the influential University of Minnesota using JPL, and most other universities using Hamiltonian.
Here’s a Math SE answer on why: https://math.stackexchange.com/questions/1784166/why-are-the..., here’s a more ELI5 discussion: https://www.reddit.com/r/math/comments/9urjyx/why_there_is_n.... I especially like this very intuitive comment in that thread : https://www.reddit.com/r/math/comments/9urjyx/comment/e96j4l...
Left as an exercise to reader: prove that no such n-tuples exist for n>4.
It depends on how you define "such" n-tuples. The octonions (n = 8) aren't associative, but they have all the other relevant properties.
Would be more amusing if "they eluded a lecturer" or "a lecturer once elided them".
("alluded to them").
What is slerp?
A interesting read is this letter [0] written the following day, where he explains his ideas in his own words. It's a great (and accessible!) look into his thought process.
[0] https://www.maths.tcd.ie/pub/HistMath/People/Hamilton/QLette...
Anyway it's a great writeup, even though I know the material it was a good read.
At first it really bothered me that quaternions were a black box to me, yet so easy to use in so many contexts. What kind of magic was happening? I had to know.
My math is terrible. I spent actual days revisiting YouTube videos explaining quaternions but my limited brain cells were failing to properly grasp it. I had to know though, I’d go crazy writing so much code that depends on something I couldn’t understand.
It took a long time. This would have been very helpful. Quaternions are very cool, and well worth reading about if you do anything in the digital 3d world.
I think I can still hear my brain sizzling as I tried to absorb this stuff.
[1] https://www.geometrictools.com/Documentation/Documentation.h...
[2] https://www.geometrictools.com/Documentation/LinearAlgebraic...
Check out a demo https://observablehq.com/@enkimute/animated-orbits
Join the discord https://discord.gg/vGY6pPk.
Enki (the guy behind bivector) also gave a talk on GA at SIGGRAPH 2019 https://www.youtube.com/watch?v=tX4H_ctggYo
I know that I am not coming in with same perspective as a lot of the other software engineers/graphics developers, but I started learning about quaternions freshman year of college. For aerospace engineering quaternions are used almost exclusively for spacecraft attitude determination and control. For a very dynamic system they are very useful.
There are some limitations that I have seen, and I have seen Rodrigues parameters (or modified Rodrigues parameters) used instead which are mentioned near the end of the article.
http://www.tutis.ca/Rotate/7quaternions.htm
One thing I didn't see in the OP's linked article is how to transform a quaternion into a different coordinate system defined by another quaternion. For example, suppose you have a spaceship in a game at an arbitrary orientation defined by Q1 which is a rotation away from a conventional "unrotated" orientation, and you want to input say 5 degrees of yaw to the right. Well, you just construct a quaternion Q2 that is 5 degrees yaw to the right from this conventional unrotated orientation, and you can use quaternion conjugation to transform Q2 into the orientation of Q1 where it can then be applied to the spaceship's orientation. OP's link mentioned quaternion conjugation, but didn't really mention what it was good for. It is described in section IV of the 7quaternions link.
I was able to learn enough from this to then use quaternion matrix transformations extensively in my startup to incredible effect.
Today, I have zero recollection of what they were or why it was in my graphics class. Heck I barely remember anything from that class today, which is ironical considering it was a required class for me to graduate that semester.
I wish my job had atleast a little more to do withwhat I learnt in college.
(I'm a web developer, and I'd like to get closer to the machine.)
There was at one point a debate in physics about whether it was better to use quaternions or linear algebra for physics. With i, j, and k being the unit vectors, and the real numbers representing time. In the end, of course, linear algebra won. But we still use i, j, and k as the unit vectors. And in electrical engineering where i wound up hijacked for current, they use j as the square root of -1.
Update: It’s due to incompatibility with the Dark Reader extension, which causes MathJax to find unexpected nulls in `classList`
Let's remove Quaternions from every 3D Engine (2018) - https://news.ycombinator.com/item?id=29512302
First, let's ask what it means to rotate around an axis. If you consider rotation of a vector v in a plane A around the unit normal n, you can decompose v as follows:
v = proj_n(v) + proj_A(v) = vn + vA
Now a 90 degree rotation of v about n is
rot_n(90,v) = vn + n >< vA = vn + vR = (v•n)n + v >< n
So if we consider v rotated by an angle S around n, we can infer that vn is preserved and the image rot_n(S,vA) can be projected onto vA and vR according to the usual cosine rule for projection:
rot_n(S, vA) = cos(S)vA + cos(s-90)vR = cos(S)vA + sin(S)vR
Likewise,
rot_n(S, v) = cos(S)v + sin(S)rot_n(90, v) = cos(S)v + sin(S)((v•n)n + v >< n)
Okay, now what's wrong with this formula? What if we want to combine rotations? We're stuck, because this "multiplication" isn't associative. So we can turn this into a linear transformation and use the matrix representation, which gives a nine-dimensional algebra, although with symmetry you can make it five-dimensional. (The fifth dimension comes from the requirement that both the determinant and the L2 norm of the matrix coefficients are constant, 1 and 3 respectively.)
But we have perhaps heard about the quaternions, which are associative. And we might have noticed that Im(v*n), where * denotes quaternion multiplication and Im() discards the real part, is equal to v >< n, viewed as vectors. But multiplication under Im() isn't associative either. But we can also define Im() another way, using the apostrophe for conjugation:
Im(v*n) = (v*n - (v*n)')/2 = (v*n - n*v)/2
since (v*n)' = n'*v' = (-n)*(-v) = n*v for pure imaginary v,n, where we recall that conjugation distributes over products of quaternions at the cost of reversing the order.
Now what if we try to construct the rotation again:
rot_n(S,v) = cos(S)v + sin(S)(v*n - n*v)/2 = (v*(cos(S)+ sin(S)n) - (-cos(S) + sin(S))*v)/2 = (v*q(n,S) + q(n,S)'*v)/2
Here q(n,S) = cos(S) + sin(S)n is a perfectly reasonable quaternion, and we call it a rotation quaternion. This is now almost associative but not quite. If we combine rotations q and p, we end up with terms like q'*v*p. How annoying. But it feels like we're getting closer, so let's see if we can learn anything else about these "sandwich" products.
Geometrically, if we rotate the vector forwards and backwards, we should get the same vector. But q(n,-S) = q(n,S)', so we must find (we drop the arguments since there is only one q and we are sick of parentheses):
((v*q + q'*v)*q' + q*(v*q + q'*v))/4 = (v*q*q' + q*q'*v + q'*v*q' + q*v*q)/4 = v
Now it's not so hard to verify that q*q' = q'*q = 1, so we can subtract v/2 from both sides and clear the denominator to find the curious-looking lemma:
q*v*q + q'*v*q' = 2v
Now (q'*v*q')' = q*v'*q = -q*v*q, the last equality being established because v is imaginary. Therefore:
q*v*q - (q*v*q)' = 2Im(q*v*q) = 2v
so Im(qvq) = v, for arbitrary rotation vectors q.
Evidently a multiplication by q on the right undoes a multiplication by q on the left, at least when we are talking about imaginary parts, which we were interested in already. So what happens when we take q'*v*q — the missing sandwich in the above equation? This should give us two rotations in the same direction, since switching sides inverts the rotation and conjugation inverts it again to leave us with the same rotation. But what's the use of writing
rot_n(2S, v) = Im(q'*v*q)?
Just try it:
Im(q'*v*q) = (q'*v*q - (q'*v*q)')/2 = (q'*v*q - q'*v'*q)/2 = (q'*v*q + q'*v*q)/2 = q'*v*q!
So we now have a formula where everything is in one product. It turns out that the ugly "sandwiches" (we also call this conjugation, which can be confusing) are useful after all. (Explicitly verifying that q'vq = rot_n(2S, v) is left as an exercise for the reader. See hint [1]. ) Now if we consider a rotation by a second quaternion p, we find:
p'*q'*v*q*p = (q*p)'*v*(q*p)
by inverting the distributive property for conjugation. So we only need to take one product to combine rotations. Finally, an associative structure!
There is only one sticking point left. Evidently q(n,S) corresponds to the transformation rot_n(2S,v). So in order to get rot_n(S, v) we need:
Q(n,S) = q(n,S/2)
But there is one more thing. What happens when S = 360? We all know that a rotation of 360 degrees doesn't change the geometry. So shouldn't we have Q(n,360) = 1? But in fact:
Q(n,360) = q(n,180) = cos(180) + sin(180)n = -1!
Of course (-1)'v(-1) = v.
Evidently Q has a period of 720 degrees under rotations S rather than the usual 360, i.e., Q is a spinor. This is sometimes called a "double cover".
In fact, we could also represent rotations and positions with the Pauli spin matrices and complex 2-vectors respectively, although (as in quantum mechanics) we pick up a meaningless global phase, and translation is weird.
[1]: Consider various ways of writing parentheses on the expression Im(q*q'*v*q*q), and show that q(n,S)*q(n,S) = q(n,2S).