Hacker News new | past | comments | ask | show | jobs | submit login

While they might be theoretically pleasing, I've had trouble seeing the appeal of quaternions for 3D graphics. Recently I was working on some 3D-rendering code from scratch for a project, and I looked into using quaternions for rotation, only to scratch my head at how fiddly they were to apply to vectors. (Also, many resources talking about them focus on their abstract properties at the expense of actual examples, which is annoying when I'm just trying to implement them.)

I had a much simpler time just using rotation matrices for everything. They're not much more difficult to compose, they're trivial to apply to vectors, and they can be easily understood in terms of their row and column vectors. (For my project in particular, I really enjoyed the property of easily knowing which octants the basis vectors are mapped to.)

Where are the practical areas where quaternions shine? Are they just useful for the slerp operations that everyone points at, or are there other situations where they're better than rotation matrices?




Quaternions are automatically orthogonal, whereas matrices can shear and therefore may accumulate floating point distortions under repeated opreations.[0]

Think of matrices as computational instructions, which are straightforward but lossy, while quaternions are the canonical "lossless representations".

The sweet spot for using quaternions is to use them as intermediate representations of rotation operations, then "compile" them down to a matrix once you are gonna apply it to a vector.

    ijkl_as_matrix = {
        (ll+ii)-(jj+kk), (ij+ji)-(lk+kl), (ki+ik)+(lj+jl),
        (ij+ji)+(lk+kl), (ll+jj)-(kk+ii), (jk+kj)-(li+il),
        (ki+ik)-(lj+jl), (jk+kj)+(li+il), (ll+kk)-(ii+jj),
    };
(Keep in mind that unnormalized quaternion has uniform scaling, so if your quats are unnormalized then your matrix will also apply a scale factor of dot(q,q), so you should divide your final vertex coords by that factor.)

---

[0] For an example, see https://old.reddit.com/gdd8op


Does leaving them unnormalized affect their numerical accuracy? A few sources [0] [1] suggest renormalizing often, but I'm not sure whether it's just dogmatic or if it's actually necessary. It definitely seems less involved than re-orthogonalizing matrices, in any case.

(Luckily, for my project, I'm not particularly worried about error, since the only thing being rotated frequently is the camera, and microscopic scaling and shearing won't affect the result much. I measured the error in the basis vectors over time to make sure, and it just seems to be O(sqrt(t)) random-walk noise, not anything that compounds on itself.)

[0] https://www.tobynorris.com/work/prog/csharp/quatview/help/or...

[1] https://stackoverflow.com/a/12934750


As I mentioned, unnormalized quaternions just adds an extra uniform scaling, so it’s just a matter of dividing the final vertex by qq* to remove the scaling.

EDIT: I guess if you use a shitty overzealously reduced version of the quaternion formula then you absolutely need to normalize it constantly, because the reduced formula assumed three degrees of freedom (completely defeating the purpose of using quats in the first place), normalization is then to solve a problem that you caused. But if you use a proper formula then my recommendation is actually that you never normalize your quats, instead only un-scale your vertex at the end.


I get what you mean, but it seems misleading to cite floating point issues with matrices and call quats lossless. Matrices are not inherently imprecise, they have floating point error when you use floating point numbers to represent them, and the same is absolutely true for quats too.

A better word than lossless is perhaps ‘overspecified’ when referring to a 3x3 matrix being used to represent a rotation or orientation. A 3x3 matrix has redundant information in that case (however a matrix is more general and more powerful than a quat). But axis-angle is 4D like a quat too, and more intuitive than a quaternion. Actually normalized-axis-angle (with no scaling) can beat normalized quats, because axis-angle can be a 3D value and quats cannot. Same goes for Euler angles too. In general, if your quats are implemented with floats then applying quat transforms will introduce unwanted floating point scaling that may accumulate under repeated operations (and if you compile to a matrix first then you also have the matrix problem you mentioned).


1. In this application, floating point drift affect matrices far more than quats.

Drifts in quats results in a drifted value of rotation + uniform scaling, but will never introduce deformation.

Drifts in matrices may result in total mutilation of your coordinates.

The overdetermined nature of matrices with respect to orthogonal transforms means that you lose information about which values constitute the authoritative state, whereas it is by definition impossible for a quat not to be orthogonal even when perturbed with significant error.

As an analogy, think of matrices as retained-mode GUI while quats are immediate-mode.

2. Axis angle is just the logarithm of [unit]quats (non-unit quat adds an additional scalar to the axis-angle components).

If you want to compose rotations sequentially, you'll still need to take the exponential of axis-angle to turn it into quats.

You can author initial state in axis angles as an authoritative declaration of what you meant for the orientation to be, but composing them still invariably requires you to un-logarithm them back to quats, hence what I said about "intermediate representation"

3. I said compile to a matrix at the very end when transforming the final vertices, entirely sidestepping the problem of repeated operations since you're only "baking" it for the final transformation onto vertices.


As someone not at all really familiar with maths/3D stuff, your comment gives me some relief that should I want to do any for hobby projects, I can use quaternions to avoid accumulating floating point errors.


Quaternions can be useful in robotics when you're trying to perform trajectory planning or any kind of rotation control. Quaternions provide a continuous space where every point represents a rotation, unlike rotation matrices, which exist in a much harder space to explore since most matrices do not represent pure rotations. If you have algorithms for example trying slerp between rotations, or find a path from one rotation to another under some constraint, or sample rotations near the current rotation, then the space of quaternions is a much more practical space to work in.


Houdini is all quaternions under the hood— purportedly to avoid gimbal lock. Houdini’s thing generally is doing things the hard way if there’s any possibility it could lead to a better outcome. Unfortunately, without a (fortunately open source at the free-level) plugin, doing things like rotating a bunch of objects on their own local axes means wrangling the quaternions directly in code.

If you’re interested in seeing their approach, here’s the repo:

https://github.com/toadstorm/MOPS


I found quaternions much more convenient to work with when writing some 3d graphics software for a college course a long time ago.

It's mostly for the same reasons everyone else mentioned, simplifying interpolation and avoiding gimbal lock. I also found the actual operations much easier to implement. I never developed a good mental model of what they actually are, but tried not to let that bother me too much.


For 3D graphics I think the main issue quaternions helps with is for interpolations. Depending on your use case that may or may not be important to you.


Imagine you are creating a 3D flight or space simulator. You would need to use the quaternion system to avoid your camera/craft from experiencing gimbal lock.

It can have other uses for things like inverse kinematics too.


Never having done anything aviation simulation related I've never properly understood this.

At least for a simple camera I've done that with something that resembled a spherical coordinate system (although maybe not following the usual conventions) and it didn't experience gimbal lock. Effectively the axes rotate with you. It's also very intuitive to work with the code, although probably not very efficient compared to other approaches (but hey it's a camera it's not like there's 10k of them). Once you have the camera orientation you can derive a corresponding rotation matrix to apply to the scene geometry.

I guess what I'm trying to ask is if there's any reason other than efficiency not to do something like that when modeling craft and the like?


How do you represent the viewports roll angle?


Suppose unit X is viewport direction and unit Y is viewport horizontal. Since the axes follow you with each rotation the viewport direction is always the X unit vector even after rotation. So the viewport roll angle is a rotation about X.

Future user control inputs (ie a 4th rotation step) happen in this rotated space, and then you normalize the result so you only ever need 3 instead of 4 rotations.

It's been several years since I did this so hopefully I didn't mess up the explanation. Again I realize it isn't computationally efficient but there's only the one camera and it let me have code that was minimal and easy to reason about. Presumably if I were simulating 10k airplanes or whatever this wouldn't be a good idea.




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: