26
\$\begingroup\$

I am a Game Developer and did not study Mathematics. So I only want to use Quaternions as a tool. And to be able to work with 3D rotation, it's necessary to use Quaternions (Or Matrixes, but let's stay at Quaternions here in this Question). I think it's important for many developers to use them. That's why I want to share my knowledge and hopefully fill the holes I have. Now....

So as far as I understood:

A Quaternion can describe 2 things:

  1. The current orientation of a 3d Object.
  2. The rotation transformation a Object could do. (rotationChange)

You can do with a Quaternion:

Multiplications:

  1. Quaternion endOrientation = Quaternion rotationChange * Quaternion currentOrientation;

    So for example: My 3D Object is turned 90° to left - and my rotation I multiply is a rotation 180° to right, in the end my 3D Object 90° is turned to right.

  2. Quaternion rotationChange = Quaternion endRotation * Quaternion.Inverse(startRotation);

    With this you get a rotationChange, which can be applied to another Orientation.

  3. Vector3 endPostion = Quaternion rotationChange * Vector3 currentPosition;

    So for example: My 3D Object is on Position (0,0,0) and my rotation I multiply is a rotation 180° to right, my endposition is something like (0,-50,0) . Inside that Quaternion there is a Axis - and a rotation around that axis. You turn your point around that axis Y Degrees.

  4. Vector3 rotatedOffsetVector = Quaternion rotationChange * Vector3 currentOffsetVector ;

    For example: My start direction is showing UP - (0,1,0), and my rotation I multiply is a rotation 180° to right, my end direction is showing down. (0,-1,0)

Blending (Lerp and Slerp):

  1. Quaternion currentOrientation = Quaternion.Slerp(startOrientation, endOrientation , interpolator)

    if the interpolator is 1 : currentOrientation = endOrientation

    if the interpolator is 0 : currentOrientation = startOrientation

    Slerp interpolates more precise, Lerp interpolates more performant.

My Question(s):

Is everything I explained until now correct?

Is that "all" you can do with Quaternions? (obv. not)

What else can you do with them?

What are the Dot product and the Cross product between 2 Quaternions good for?

Edit:

Updated Question with some answers

\$\endgroup\$
10
  • \$\begingroup\$ Say you have not 2, but n different orientations (attitudes, poses, etc). Then you can average them using weights, effectively generalizing slerp/lerp. You can also convert a quaternion to a rotor, which is equivalent to applying an angular velocity for a certain amount of time to a rigid body. Hence you can describe angular velocity integration with quaternions also. You can also estimate how dissimilar two orientations are (compute the length of the arc spanned by the two quaternions on the hypersphere). \$\endgroup\$
    – teodron
    Nov 21, 2016 at 16:15
  • \$\begingroup\$ And yes, at a first glance, your rationale is correct (your understanding of quaternions is quite good for a non-technical person). This is inappropriate for a comment, but congrats! Not even the technically gifted know all quaternion uses, although they use them just as software engineering tools for a purpose. \$\endgroup\$
    – teodron
    Nov 21, 2016 at 16:17
  • 4
    \$\begingroup\$ "And to be able to work with 3D rotation, it's necessary to use Quaternions" I can't stress enough how false this sentence is. You can use Euler or Tait-Bryan angles for game developement, the only problem is the gimbal lock. If you want to be a game developer, you'll need math at one point, learn it. \$\endgroup\$
    – Bálint
    Nov 22, 2016 at 6:30
  • 1
    \$\begingroup\$ "game developer" and "not studying math" is an oxymoron. \$\endgroup\$ Nov 22, 2016 at 18:01
  • 2
    \$\begingroup\$ I appreciate what you're trying to do with the question, but the answers should be in an answer, not the question. Make a "summary" answer if you think it's worth collating them. \$\endgroup\$
    – Basic
    Nov 23, 2016 at 1:49

2 Answers 2

24
\$\begingroup\$

Multiplication

At least in terms of Unity's implementation of Quaternions, the multiplication order described in the question is not correct. This is important because 3D rotation is not commutative.

So, if I want to rotate an object by rotationChange starting from its currentOrientation I'd write it like this:

Quaternion newOrientation = rotationChange * currentOrientation;

(ie. Transformations stack up to the left - same as Unity's matrix convention. The rightmost rotation is applied first / at the "most local" end)

And if I wanted to transform a direction or offset vector by a rotation, I'd write it like this:

Vector3 rotatedOffsetVector = rotationChange * currentOffsetVector;

(Unity will generate a compile error if you do the opposite)

Blending

For most cases you can get away with Lerping rotations. That's because the angle used "under the hood" in a quaternion is half the angle of rotation, making it substantially closer to the linear approximation of Lerp than something like a Matrix (which in general will not Lerp well!). Check out around 40 minutes into this video for more explanation.

The one case when you really need Slerp is when you need consistent speed over time, like interpolating between keyframes on an animation timeline. For cases where you just care that an output is intermediate between two inputs (like blending layers of an animation) then usually Lerp serves quite well.

What Else?

The dot product of two unit quaternions gives the cosine of the angle between them, so you can use the dot product as a measure of similarity if you need to compare rotations. This is a little obscure though, so for more legible code I'd often use Quaternion.Angle(a, b) instead, which more clearly expresses that we're comparing angles, in familiar units (degrees).

These types of convenience methods that Unity provides for Quaternions are super useful. In almost every project I use a this one at least a few times:

Quaternion.LookRotation(Vector3 forward, Vector3 up)

This builds a quaternion that:

  • rotates the local z+ axis to point exactly along the forward vector argument
  • rotates the local y+ axis to point as close as possible to the up vector argument, if provided, or to (0, 1, 0) if omitted

The reason the "up" only gets "as close as possible" is that the system is overdetermined. Facing z+ to forward uses up two degrees of freedom (ie. yaw and pitch) so we have only one degree of freedom remaining (roll).

I find quite often I want something with the opposite exactness properties: I want local y+ to point exactly along up, and local z+ to get as close as possible to forward with the remaining freedom.

This comes up for example when trying to form a camera-relative coordinate frame for movement input: I want my local up direction to stay perpendicular to the floor or inclined surface normal, so my input doesn't try to tunnel the character into the terrain or levitate them off of it.

You might also get this if you want the turret housing of a tank to face a target, without peeling up off of the tank's body when aiming up/down.

We can build our own convenience function to do this, using LookRotation for the heavy lifting:

Quaternion TurretLookRotation(Vector3 approximateForward, Vector3 exactUp)
{
    Quaternion rotateZToUp = Quaternion.LookRotation(exactUp, -approximateForward);
    Quaternion rotateYToZ = Quaternion.Euler(90f, 0f, 0f);

    return rotateZToUp * rotateYToZ;
}

Here we first rotate local y+ to z+, and local z+ to y-.

Then we rotate the new z+ to our up direction (so the net result is local y+ points directly along exactUp), and the new y+ as close as possible to the negated forward direction (so the net result is local z+ points as close as possible along approximateForward)

Another handy convenience method is Quaternion.RotateTowards, which I often use like so:

Quaternion newRotation = Quaternion.RotateTowards(
                             oldRotation, 
                             targetRotation,
                             maxDegreesPerSecond * Time.deltaTime
                         );

This lets us close in on targetRotation at a consistent, controllable speed regardless of framerate - important for rotations that affect the outcome/fairness of gameplay mechanics (like turning a character's movement, or having a turret track-in on the player). Naively Lerping/Slerping in this situation can easily lead to cases where the movement gets snappier at high framerates, impacting game balance. (That's not to say these methods are wrong - there's ways to use them correctly without changing fairness, it just requires care. RotateTowards gives a convenient shortcut that takes care of this for us)

\$\endgroup\$
5
  • \$\begingroup\$ Tip: Add &t=40m to the end of the video URL so it'll hop straight there (optionally e.g. 40m5s). Quaternion dot products come in handy when dealing with spherical gameworlds too - or more broadly when orientating chunks of rotating spheres. \$\endgroup\$ Nov 22, 2016 at 2:41
  • \$\begingroup\$ @Luke Briggs: The spherical game world point sounds like it would be worth elaborating on in its own answer (especially with diagrams) if you'd be up for it. :) \$\endgroup\$
    – DMGregory
    Nov 22, 2016 at 2:43
  • \$\begingroup\$ Great idea - it's 3am here (so I think it would come out a little gibberish!) but I'd be happy to pull something together tomorrow (If I remember!) \$\endgroup\$ Nov 22, 2016 at 2:45
  • 1
    \$\begingroup\$ Edit: I've got a little carried away thinking about an answer so challenge accepted! I'll at least mark it as a rough cut so people can be aware of the late night burn that went into it :P \$\endgroup\$ Nov 22, 2016 at 2:52
  • \$\begingroup\$ There we go! I attempted to cover it in a graphical overview sense on the basis that your answer already covers the underlying functions really well. Time for some sleep I think! \$\endgroup\$ Nov 22, 2016 at 3:48
15
\$\begingroup\$

Where's the dot product used?

In Unity, one of the most common users of the dot product is whenever you check if two quaternions are equal via == or !=. Unity computes the dot product to check similarity rather than directly comparing the internal x,y,z,w values. It's worth keeping this one in mind as it makes the call more expensive than you might expect it to be.

We also use it in an interesting use case too..

Fun with quaternion dot products - Spherical worlds and orbitals

Simulations of entire planets and even entire solar systems are getting increasingly common. In order to pull this off in realtime, we need the quaternion dot product too. Lots of them. The quaternion dot product is very underused but it certainly has its uses - Let's take a look!

Firstly, we have a whole series of rotations to consider:

  1. (Optionally) The star around the galactic centre
  2. The planet around the star
  3. The tilt of the planet
  4. The spin of the planet
  5. The position of nearby grid cells (rotated about the planets core)*
  6. Multiple orbital planes

Combine them all together and you end up with a lot of complexity (and a lot of huge numbers!). When the viewer is standing on the surface of the planet, we don't want them to be hurtling at some crazy speed through our gameworld space. We'd actually rather they were stationary and somewhere near the origin - move the universe around the player instead.

Rotating planet

Importantly, in order for us to be able to get the planet's spin and tilt correct in this scenario, we need to axis lock the pole so it can only swing up/down on the image above (i.e. swinging "up" as the player travels north). That's where a quaternion dot product comes in. If we didn't use a dot product here and instead just multiplied the tilt too, this would happen:

Incorrectly tilted 'planets'

Notice how the poles of our orbiting 'planets' are always tilting towards the star. This isn't what happens in reality - the tilt is in a fixed direction.

Without going too far off topic, here's a quick summary:

  • On a sphere, an orientation also neatly describes a surface position.
  • We've got lots of rotations to combine together.
  • Describe everything as rotation; the position of the viewer, too. This helps boost performance as ultimately we end up doing less operations.
  • The angle between rotations (our dot product) then helps measure longitude and works particularly well at dealing with tilts.

By obtaining only the angle, we drop some of that unwanted rotation. At the same time we also ended up with a longitude measurement which is useful for navigation as well as the local climate.

* Planets are constructed from lots of grid cells. Only the nearby ones actually get displayed.

\$\endgroup\$
7
  • 2
    \$\begingroup\$ This does a great job setting the scene and motivating the problem, but I'm still a bit fuzzy on how the math of the quaternion dot product (ie. the scalar product dot(a, b) = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w as opposed to quaternion composition which we'd use to chain-together rotations) would help us solve this problem. I'd gladly upvote if you're able to elaborate on this a little later (I don't mean to keep you from your slerp...I mean sleep!) \$\endgroup\$
    – DMGregory
    Nov 22, 2016 at 3:55
  • \$\begingroup\$ @DmGregory the short answer is tilt is the odd one out; everything composes nicely except for that one (the planet would appear to wobble around its star otherwise). I'll (hopefully!) add some more context tomorrow! \$\endgroup\$ Nov 22, 2016 at 4:05
  • \$\begingroup\$ @DMGregory I've added some additional information (I couldn't sleep!) - hopefully that makes it clearer. \$\endgroup\$ Nov 22, 2016 at 4:36
  • 1
    \$\begingroup\$ Sorry if I'm being a bit dense, but after re-reading several times I still don't know how I'd use the dot product in a formula to achieve the transformation you're describing. Would you be able to add a little pseudocode laying out the operations you perform explicitly? \$\endgroup\$
    – DMGregory
    Nov 22, 2016 at 16:07
  • \$\begingroup\$ @DMGregory I'm not familiar with the quaternions but if this is rotations on a sphere, then this isn't rotation compositions. This is using spherical geometry with vectors to compute the normal vector for a "line" on the surface of a sphere AKA any circumference. Once again, the answer makes little sense nor does this question, but I believe they are using spherical geometry. \$\endgroup\$
    – user64742
    Nov 23, 2016 at 5:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .