Quaternions

It’s been a while in the writing, but I’ve finally gotten round to writing something about quaternions. I’m afraid it gets a bit mathematical! I started from rotations and worked my way towards quaternions. This doesn’t make for the neatest mathematics (which would be to start with quaternions and show that they represent rotations) but hopefully is more conceptual.

http://loopspace.mathforge.org/discussion/17/quaternions

(Comments/discussion should take place here.)

There’s an implementation of quaternionic algebra in my library: http://www.math.ntnu.no/~stacey/code/CodeaLibrary.

My brain just melted! :slight_smile:

Seriously, the article contains a lot of info, just need to take it one step at a time so I can digest it all (is there such a thing as indigestion of the brain?)

But thanks for sharing all the same.

Great piece and I understand it right up to when you start with the formulas…

While the maths fundamentals are great, what about some more simplified how to apply this stuff in a program?

Eg, I have a camera defined as camera(eye, lookat, up) let’s assume eye is the origin (0,0,0) as that’s simple to resolve, so effectively I have a space oriented based on lookat being the horizontal (notionally z axis) and up being the y axis.

So from these 2 vec3 vectors, lookat and up how can I use quaternions to apply rotation of this space either “horizontally” rotating lookat around up, or “vertically” by rotating lookat and up around the notional line orthogonal to both.

.@spacemonkey That’s the point about quaternions being an encoding scheme for rotations. You need to break down your problem into two pieces:

  1. How do I figure out the rotation involved?
  2. How do I encode that into quaternions? (And possibly short-circuit some of the computations.)

The first is pure geometry, the second is pure algebra. (Well, neither is completely pure …) Knowing about quaternions won’t help you figure out the rotations that you need. However, knowing that you will use quaternions might help you avoid unnecessary steps. So in figuring out the rotations you only need to know the axis and angle, not the full matrix.

In the situation you describe, you’re thinking of two families of rotations: one with axis “up” and one with axis “other”. Given what you’ve said, “up” is (0,1,0) and “other” is (1,0,0). So the quaternions you want are:

(cos(?/2),0,sin(?/2),0)
(cos(?/2),sin(?/2),0,0)

Then you just need some code that applies these quaternions to the vectors. This is in my library. So I would write:

q = Quaternion(cos(?/2),0,sin(?/2),0)
x = vec3(0,0,1)
x = x^q

(except that I’d use a function called Quaternion.Rotation to translate an angle-axis description of a rotation to a quaternion.)

Thanks, I think I’ve got it…

I took the 3dLab and tweaked it in there with your Quaternion class… basically had the following camera settings in setup and draw:

    --setup
    camPos = vec3(0,0,-300)
    camDir = vec3(0,0,1)
    camUp = vec3(0,1,0)
    
    --draw
    camera(camPos.x, camPos.y, camPos.z,camDir.x+camPos.x,camDir.y+camPos.y,camDir.z+camPos.z,camUp.x,camUp.y,camUp.z)

And then the guts of it is the touched function:

function touched(touch)
    if touch.state == MOVING then
        --get the horizon for vertical rotation
        q = Quaternion.Rotation(math.rad(90),camUp)
        horizontal = camDir^q
        --rotate camPos around vertical
        q = Quaternion.Rotation(math.rad(touch.deltaX/3),camUp)
        camDir = camDir^q
        --rotate camPos and camUp around horizontal
        q = Quaternion.Rotation(math.rad(touch.deltaY/3), horizontal)
        camDir = camDir^q
        camUp = camUp^q
    end
end

And I think it’s behaving reasonably sensibly, if you rotate to the side, then up and down you get the scene rolling over etc…

Hi @Andrew_Stacey,

Had to write and thank you for the article. Must admit most of it wooooshed over the top of my head, but I will persevere with it until I can at least appreciate the principles. Must admit matrix maths make me shudder, old time ‘A-leveler’.

I’d like to compliment you on your new website and thank you for the Codea support articles - good subject matter and well presented. Please continue with this and, if possible provide links to print pdf’s on all your articles - always useful to read on the pad or the Kindle.

Finally thanks for providing your library in a good accessible form.

I’ll be watching your site with interest.

Bri_G

:smiley:

My brain is overloading !

Wow I missed the fact you do Maths. I have a developing interest in loop groups we should do stuff together one of these days :slight_smile:

Just a note to @Bri_G - if you browse the web using Chrome you can print any webpage to a PDF file (with links intact) by choosing the print option and changing the destination to “Save as PDF”

OK, so in a copy of the 3d lab I’ve put a parameter for camera type where 1 is a free floating camera (imagine a camera in zero gravity not attached to anything) and 2 is a camera on a tripod, ie up down, left right, but always vertically oriented.

It includes @Andrew_Stacey’s quaternion library and I think the rotation behaviour is right.

You can grab it here. Rotate the camera by swiping on the screen.

https://gist.github.com/sp4cemonkey/4943400

.@Bri_G Good idea. I’ve uploaded a PDF and an ePub version (the ePub version uses MathML for the mathematics so may not display on all ereaders. iBooks is actually okay for this but I don’t think it works on a Kindle.)

.@corneliuhoffman I just googled your name. Google helpfully suggested where to split your username into first name and surname. If it got it right then it might be worth letting you know that this academic year then I’m on sabbatical a little bit down the M40. As well as loop groups, it would be interesting to discuss using Codea in lecturing.

I’ve been the unofficial “resident mathematician” here for a bit - nice to know that there are some others around.

.@spacemonkey I’ll need to ponder your code. There’s elements there that worry me, but I need to think carefully about what it does to see if any of those worries realise.

@all I guess this post was a “What are Quaternions?” but I didn’t cover “Why should one use them?”. I’ll try to add that soon.

Hi @TechDojo,

Thanks for the feedback, I have an installed printer driver which generates pdf’s from any document (including web pages). But, unfortunately, it does just that so you get all the web page - graphics adds etc. You can select the required text and print that, but is poorly formatted and not easy reading. If you check out Andrew’s tutorial on matrices, pdf format, you get a very polished article which is easy to read - probably due to Andrew’s skills with TEX and his academic reporting skills. That’s why I requested the same option on all his tutorials etc.

Thanks for the feedback tho - I have Chrome installed but didn’t think to try it.

Bri_G

:smiley:

.@spacemonkey Ah, I was right to worry. Try the following: zoom out a bit (set FieldOfView to about 80); then draw little squares on the screen with your finger: up a bit, right a bit, down a bit, left a bit (back to where you started). If you do it enough times you should rotate the entire picture by any angle you want.

What you are running into here is that you can’t keep track of rotations by simply keeping a running record of the rotation about each axis separately. Rotations about different axes don’t commute. You need to keep a “running total” of the complete rotation. If you look in my view library, you’ll see that there is a baseRotation which does this and is updated by touch events (the function singleTouch does this).

.@Andrew_Stacey yes, that happens in camera mode 1, but I think this is correct at least conceptually, although likely not in my code.

Camera mode 1 is to represent a free floating camera in zero gravity, if I rotate the camera up, and then from it’s new facing rotate right this would have the effect of spinning the view because my sideways rotation isn’t around the x,z plane, it’s around the sphere with the sideways rotation being the plane I am now elevated to.

So for instance, if I rotated the camera up 90 degrees, so it’s now on it’s back pointing at the sky, then I rotate right 90 degress, the camera is now on it’s side pointing to the right, then if I rotate it “down” 90 degress it is now on it’s side pointing at the original scene. Rotations are always relative to the cameras current orientation and there is no concept of gravity or a true up down.

Camera mode 1 is what I was working on getting for a space game concept I’m playing with.

Camera mode 2 is like a camera on a tripod, so left right is always rotating on the x,z plane and up down just points it up or down, this is more the camera behaviour you would look for in a fps style game. So in mode 2 90 degrees up is the camera on it’s back pointing at the sky, then right 90 degrees is the camera still on it’s back pointing at the sky but spun 90 degrees. Then camera down 90 degrees would have the camera back upright but pointing to the right of the original view.

I think I actually understood quaternions - once. I’ve long since forgotten everything other than “this is the way to do 3d rotation without gimbal lock”, and that if/when I need to do it again, I’ll need to go do a refresher. They can be hard to wrap your head around at first, and I’m not sure I ever really did so without a pinch of voodoo thrown in.

Thank you for posting this, Andrew - it looks heinously complete. And a double-thank-you for posting code. :slight_smile:

(I’m working on a “How to use it”, will post after the weekend)

I most often think of rotation of an item like this. Maybe it can be of help for someone? The ship has a position, direction and up, just like the camera input. Then the different turn, roll, yaw rotations can be described like this:


-- 0 Ship

-- Use this function to perform your initial setup
function setup()
    displayMode(FULLSCREEN)
    pos = vec3(0,0,100)
    dir = vec3(0,1,0)
    up = vec3(0,0,1)
end

function mult(m, v) -- matrix multiplication
    return vec3(
        m[1]*v.x+m[2]*v.y+m[3]*v.z,
        m[5]*v.x+m[6]*v.y+m[7]*v.z,
        m[9]*v.x+m[10]*v.y+m[11]*v.z
    )
end

function turn(d)
    local m = matrix():rotate(d, up.x,up.y,up.z)
    dir = mult(m,dir):normalize()
end
function roll(d)
    local m = matrix():rotate(d, dir.x,dir.y,dir.z)
    up = mult(m,up):normalize()
end
function yaw(d)
    local right = dir:cross(up)
    local m = matrix():rotate(d, right.x,right.y,right.z)
    dir = mult(m,dir):normalize()
    up = mult(m, up):normalize()
end

-- This function gets called once every frame
function draw()
    background(40, 40, 50)
    perspective(80)
    local lookAt = pos + dir
    camera(pos.x,pos.y,pos.z,
           lookAt.x, lookAt.y, lookAt.z,
           up.x, up.y, up.z)
    sprite("Cargo Bot:Pack Crazy",0,0,1000)
end

function touched(touch)
    if touch.state == MOVING then
        if math.abs(touch.deltaX) > math.abs(touch.deltaY) then
            turn(-touch.deltaX*.5)
        else
            yaw(touch.deltaY*.5)
        end
    end
end

But maybe there is a value to try using quats again…

The “How to use it” is now posted. Same link as before, the new stuff starts at http://loopspace.mathforge.org/discussion/17/quaternions/?Focus=46#Comment_46. The PDF and ePub versions have similarly been updated.

As is probably to be expected, in writing this I spotted lots of optimisations/issues with my Quaternion library so it’s gone through a few updates.

.@tnlogy Your matrix-times-vector function is the wrong way around. Remember that Codea uses matrices the opposite way to everyone else in that it should be v M not M v. However, as you’re rotating the camera rather than the object, you want the inverse rotation which (for a rotation) is the transpose so your multiplication is “right” for this case. You should also divide through by m[16] for a general function.

More mathematically, I think that your code is limiting you somewhat. You avoid the issue of commutation of rotations by forcing your touch data into being either horizontal or vertical so it is impossible to both a turn and a yaw simultaneously. Secondly, your storage of the rotations is as vectors (the up and dir) so if you have anything that needs to know the rotation you’d need to convert back to a matrix. The advantage of your code is that you only update the vectors when the rotation updates, but if the rotation is continually changing anyway (say, for an “in flight” game) then there’s no actual gain there.

Yeah, it’s probably not the most mathematically correct way to do it. Strange though, I followed the examples from wikipedia with M*v for multiplication. I’ve used quaternions before, but it was quite some years ago, so maybe it’s time to take out some old university books from the shelf, even though my unorthodox way of doing it has been working for a while. :slight_smile:

But mostly I just wrote it to make an example of how you can think if you want to move by changing the three vectors to the camera-function.