OpenGL quaternions

Started by
8 comments, last by Misantes 9 years, 10 months ago

Hello,

I'm playing around with creating a space sim, and ran into the usual gimbal lock when using euler angles to rotate the camera around.

And, I've gone nearly blind reading about quaternions and trying to apply them to a Camera. I'm utterly failing to actually understand them (apparently, that's rather normal), but worse, I'm failing to implement them properly either. Normally, I would post the code I've written, but, especially since I'm stuck on the generalities rather than the specifics(and I've tried numerous approaches to this), it may be less distracting to refrain from posting it.

My camera is a 3rd person camera (currently using glm::lookAt as my view matrix) that follows behind the ship in whatever direction the ship heads. Controlling via euler angles mostly works via this method, minus the gimbal lock. I'm currently rotating the first value "location" of glm::lookAt round the second value "position" using basic trigonometry before feeding it into that function and setting it as my view matrix (this may be an entirely dumb way to be going about it, but it's a moot point since I won't be using this method:)).

In trying to get away from using euler angles, what I feel like should work(and isn't), is to (after cutting out all of my trigonometry) translate my view matrix to the position of the ship, rotate it using a quaternion, then translate it back along the same direction(which should now put the camera in the rotated position around the ship, if my brain is doing this correctly).

I've also tried using this http://www.gamedev.net/page/resources/_/technical/math-and-physics/a-simple-quaternion-based-camera-r1997

but adapting it to a third person camera by translating before and after the rotation to keep the camera at a distance.

I've hit several roadblocks. So, first question I suppose is, am I going about this entirely wrong by still trying to use glm::lookAt, and if so, what should I do instead?

Secondly, is that so many examples I've come across on this forum and others, convert euler angles to a quaternion to rotate. I guess my next question is whether this is going to result in the same problems (since it's still using euler angles to decide the rotation) and if so, what ought I be doing instead? Most other examples of quaternion rotation I have found all rotate to a desired end point (which I don't have, I just want to rotate the view a few degrees from it's current position) or match one quaternion's rotation to another's, etc.

Additionally (and this is also a two part question), do I want to be applying these rotations to the view matrix, or beforehand to the "direction" value of the lookAt function (assuming I should still use this), and if so, will my attempt at translating to the ship, rotating, then translating back work, or am I thinking about this incorrectly?

And lastly, I've come across several comments in various threads about quaternions being unnecessary to solve gimbal lock. Some have suggested the use of axis-angles or various other methods. Are these simply lone disagreeable opinions or is this something I should devote time looking into as an alternative?

My apologies for the many, many questions, and for the fact that this question has been asked before. I'm just a little overwhelmed by it at the moment, and every forum and tutorial I read seems to give rather conflicting information on how to go about implementing this. Feel free to answer any small part of this (I've obviously asked several questions), and as always, if you know of a solid resource with reliable information, I'm happy to simply be pointed in that direction instead. Also, I've gone light on specifics, so if there's any pertinent information I've left out, I'll gladly supply it.

Thanks in advance for any advice, I genuinely appreciate it :)

Beginner here <- please take any opinions with grain of salt

Advertisement

To better understand how Quaternions work I would recommend learning how complex numbers can be used to represent rotation in 2D.

http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/complex/transforms/

Complex numbers are kinda like the 2D equivalent of Quaternions. The same basic operations can be done with complex numbers as can be done with Quaternions only they are much simpler to comprehend. Once you understand how to manipulate 2D using complex numbers.

My current game project Platform RPG

When I tried to implement quaternions, my biggest problem was learning how to pronounce the word. My second biggest problem was, after reading all the math, and nodding my head, I sat down to write a simple example, and still had no idea.

http://www.idevgames.com/forums/thread-3021.html

This was the best example I found.

Add this: http://www.raywenderlich.com/12667/how-to-rotate-a-3d-object-using-touches-with-opengl

and you're on your way. It is still hard, but you'll get there. This is one of those light bulb things. I look at these now and I'm thinking "Well of course. I remember all this. It's easy." But it's only easy after you figure it out. And it wasn't a slow process. I fought with it, and then all of a sudden... Ah Hah!!!

You'll get there.

I think, therefore I am. I think? - "George Carlin"
My Website: Indie Game Programming

My Twitter: https://twitter.com/indieprogram

My Book: http://amzn.com/1305076532

Great smile.png a huge thanks to both of you! Those both look like well explained tutorials. I'll spend some quality time with those tonight and let you know how it's going.

Sadly, my background is not in math (English lit >.< ). I had taken a second year of calculus in college and decided that it wasn't for me. Now, one of my biggest obstacles in learning to program has been having to go back and relearn everything I've forgotten (it's been about 10 years since college). It's been a bit of a process on top of learning to program as well. In hindsight I probably ought to have stuck with the sciences, but hindsight is 20/20 and all that tongue.png

Thanks again! smile.png

Beginner here <- please take any opinions with grain of salt

Huh.

So, after several hours of reading I feel like I might have at least a theoretical grasp of what I'm doing. And, yet I still feel pretty ignorant of what I'm doing. But, that's o.k. I think tongue.png

So, I'm successfully rotating my view matrix by a quaternion, however, the quaternion is based off angles I've calculated with basic trigonometry (in order to rotate things, I alter the direction vector the quaternion uses beforehand). So, I'm a little uncertain if that's simply the way it's done, or if I've simply done nothing but add some needless code tongue.png I haven't implemented this method completely enough (right now I'm just rotating along the x and z axis) to test it against gimbal lock, so I'm uncertain if that's still a problem. I'll spend some time this weekend and see if things are working.

Feel free to let me know if I'm completely heading down a dead-end path in my approach here(reading over this, I'm starting to feel is likely), but otherwise, I truly grateful to both of you for your input and help smile.png

Cheers,

Beginner here <- please take any opinions with grain of salt

If you are still dragging three angles around and keep rebuilding your rotation from scratch by doing three rotation in a fixed order, then yes, you probably achieved nothing but tons of head ache and overhead.

The key to avoiding gimbal lock isn't using magical fairy dus.. quaternions, but to stop using Euler angles. Do not store angles, period. Any order of rotation is lost if you just wildly sum up angles. When something rotates, apply the rotation directly to the matrix or quaternion. When using matrices, multiply either from the left or right, depending on whether you rotate around a global or local axis.

It doesn't matter if you store your rotation as quaternion or matrix. They are completely equivalent and just two ways to represent the same thing. Which one is "better" depends on what you do with it. For a single camera, neither memory nor performance is an issue, so matrices are usually a lot more intuitive. If you need to store a huge number of rotations or need to concatenate lots of them per frame (skeletal animations for example), then quaternions are probably a better choice.

f@dzhttp://festini.device-zero.de

just want to add that to solve your problem you do not necessarily need quaternions. You can work with 4x4 matrices as well.. just, as suggested, never store angles, always work on the underlying matrix.

So if you want to "pitch" your spaceship, do not change the "pitch angle".. create a rotation matrix around the X axis and multiply that by your current matrix.. you have now pitched your ship.

Stefano Casillo
TWITTER: [twitter]KunosStefano[/twitter]
AssettoCorsa - netKar PRO - Kunos Simulazioni

You're absolutely right. I just finished adapting the code and it still suffers from gimbal lock. I've read your post several times and sadly don't feel I quite understand what you're advising.

On the one hand, I completely understand (well, obviously not completely ) why the euler angles are still problematic. I was wary when I began as it seemed like I was basically utilizing quaternions to represent the euler angles. Even being as weak in mathematics as I am, I can kind of understand why this isn't working.

I've tried several methods, but ended up using a pretty straight-forward conversion of Angles to quaternions.

Embarrassingly (mostly because in hindsight, I'm soooo doing this wrong), here is a simplified version of what I was doing:


{
    glm::vec3 eulerAngles(angleInY, angleInX, angleInZ);
    quateyMcQuaternion = glm::quat(eulerAngles);
}

and then multiplying that against my view matrix. It works basically like you'd think it would. Essentially as though I were just using Euler angles.

The key to avoiding gimbal lock isn't using magical fairy dus.. quaternions, but to stop using Euler angles. Do not store angles, period. Any order of rotation is lost if you just wildly sum up angles. When something rotates, apply the rotation directly to the matrix or quaternion. When using matrices, multiply either from the left or right, depending on whether you rotate around a global or local axis.

If you have the time and don't mind elaborating, I'm not quite following you. If you don't mind, I'm going to break down where I'm struggling here.

"stop using Euler angles. "

I would love to stop using Euler Angles. Where I struggle is figuring out how to rotate from point A to point B without calculating that distance via angles. Unless you mean that it's O.K to calculate that as long as you do the appropriate things afterward (if this is the case, I guess what I need to know is the process after that).

"Do not store angles. Any order of rotation is lost if you just wildly sum up angles. When something rotates, apply the rotation directly to the matrix or quaternion."

I think I'm missing your point here. At render time, I'm multiplying the ViewMatrix by the quaternion. Well, I'm converting the quaternion to a matrix first and then multiplying (plus some translating and such, depending on the object). If I don't store the angle then it reverts back to the default view since no angles are being fed into the quaternion. In hindsight, I can try playing with this idea as I think I see what you're getting at, maybe (in the meantime I'll work on this and see what happens). I may be thinking about this wrong, but if I set my view matrix to the new position and reset all the angles, instead of rotating by a large angle at each draw, I can rotate by a series of small ones and maybe not have the same issues(?). Right now the View Matrix isn't changing each draw cycle, it stays the same and only the rotation it's multiplied by is changed.

And, I understand the order of multiplication, I'm fairly certain.

Edit**

@Trienco

So, after reading kunos comment, I've perhaps realized I wasn't understanding quaternions at all. So, I'm heading back to the drawing board to work things out. Feel free to ignore all the above questions unless they're still pertinent and just call me an idiot smile.png

Anyhow, whether or not you have the time to respond, I appreciate your input and patience. I'm really not quite as dense as it may seem here, I'm just really new to opengl and in a little over my head.

Edit*

@Kunos

"So if you want to "pitch" your spaceship, do not change the "pitch angle".. create a rotation matrix around the X axis and multiply that by your current matrix.. you have now pitched your ship."

This actually makes a little sense to me, for once. I'll definitely need to go back and figure a lot of things out. But, a quaternion is just an axis plus rotation as the W value, correct? Oddly enough, unless I'm still completely misunderstanding things still, this kind of sheds a lot of light that numerous tutorials haven't done for me. To use a bad analogy, it's like having a flashlight tied perpendicularly to a bar(the flashlight just representing the view) and holding the bar out in front of you, but instead of turning the bar left and right, you're rolling it in your hands. And then of course your have two more bars at the other axes? If this is the case i have been thinking about this all wrong (I've been thinking about rotating axes, rather than rotating along axes, which is totally different and didn't exactly make sense until this moment).

Basically, to look up and down, I can use a quaternion with the x axis as the x,y,z(since the view is looking down the z) with the w being the rotation, and when multiplied by the view matrix it should rotate the view up and down. To look left/right, I'd use the Y axis, and Z for a roll type rotation. I'll totally have to hit the drawing board again, but for the first time since starting this,I think a lightbulb just went off in my head >.<. Eugghhluckh, now I have to figure out how to find my axes. Are they always like 1,0,0 or what have you since the view is always rotated(I'm confusing world and local space here, but the view is always set to 0,0,0 looking down the z axis, right?), or does this need to be recalculated each time since it's not offset?

And lastly, is doing multiple rotations costly(i.e if you're pressing up and left, does multiplying the view by up then left give bad results) or should I be trying to calculate a different axis to represent the two directions? The former would certainly be simpler, but I'm happy to bang out the math behind the latter if it's needed. Obviously, two is more than one tongue.png But, if it's a small calculation I'm not above a little unnecessary code tongue.png

Thank you!

**Last edit, I promise.

Alright, I've implemented the advice both of you have given and it seems to be working(at least along a single axis tongue.png). I had a momentary setback initially because I wasn't clearing the angle each cycle(like triento suggested I should), but restructuring the code a bit solved that , but introduced a weird flickering, that after some troubleshooting I've sorted that out. In hindsight, using multiple quaternions to handle multiple axes seems way unnecessary. So, now all I have to do is change the controls so they alter the rotation axis so I can rotate towards different axes and I should be all set!

I just wanted to give one last thank you to you both for your advice. Quaternions don't seem to be nearly as un-intuitive as I was led to believe, nor as difficult to implement. Just, all the tutorials I came across seemed to do a poor job of actually explaining the fundamental idea of what was going on (or, they did a great job but hadn't dumbed it down to my level).

Anyhow, all seems to be in working order, feel free to add any advice I may have overlooked thus far, but thanks again, and cheers smile.png

**real last edit.

Eh, after implementing controls to change the axis, I now have "up" issues. But, I'll try to sort that out on my own.

Actually, "up" seems to be fine. I'm somehow not taking into account what I've already rotated. So, if I say, look to my left 90 degrees, then look up, I'm rotating the world as though I were still looking from my original position, but since I'm not, the rotation looks wrong. I'll suss it out eventually, but if you have any ideas what I'm doing wrong there, I'm all ears smile.png

***seriously, I won't edit this again >.<

Alright, everything is sorted out, the funny behavior above was fixed by multiplying the view matrix by the rotation and the view matrix itself. So, everything is fine and dandy and working wonderfully. I'm rotating in all directions without any gimbal lock. I still have to figure out how all that fits in with velocity and position and such, but that I won't update here as it's entirely another subject and I should be able to work all that out. My genuine gratitude to you both as you as your help led me to actually understand what quaternions actually were. Also, my apologies for typing out the whole messy process here. I figure at the very least it may help someone else learn from my sad mistakes smile.png

Beginner here <- please take any opinions with grain of salt

As long as you keep scaling out of it, the only thing to really understand is what each column of your matrix means: right, up, forward and position (assuming your model is created with x/y/z as right/up/forward). You shouldn't need to figure out any axis (like "what direction is up"), since they are right there in your matrix. Understanding what these columns (ie. vectors) are usually makes the difference between "I want my object to face point B, how do I figure out the axis and rotation to make it point that way" and "I'll just set the matrix to have it point that way" (generally this also involves embracing the cross product, since this will give you the third vector when you already know two, like "right" when you already know "up" and "forward").

The other important thing is that it makes a huge difference whether you multiply your matrix from the right or left, because it means the new transformation either happens before or after every transformation you applied so far. This also neatly translates to global vs. local. In other words, whether you want to rotate around your current "right" or the worlds "east", you always create a rotation matrix around "x" (1,0,0). The only difference is multiplying it from the left or right.

But since you keep talking about your view matrix, please keep in mind that the view matrix is usually the inverse of your imaginary camera's transformation. If the camera moves up, your view matrix will transform everything down. If it rotates left, the matrix rotates everything right. The easiest way is to handle the camera like any other object and only invert the matrix to get the view matrix (again, keep scaling out of it and inversion is trivial compared to a generic matrix inverse).

Visually it helps to stick one pencil in your ear, one in your mouth and imagine a third sticking out of your head (I'd advice against actually doing so). Those three pencils and the location of your head are exactly what a transformation matrix is.

f@dzhttp://festini.device-zero.de

Trienco,

If you don't mind me picking your brain a little longer I have a followup question that relates to your answer, but is kind of a separate subject (mods, feel free to let me know, I'm happy to make a new subject instead).

Bear in mind I'm still quite new to this. But, moving to quaternions has messed up the way I have been doing this vis a vis the "camera." Previously, I would use glm::LookAt and then use trig functions to rotate around and move the camera position and "center" position, (I should note, since I'm uncertain if this is standard nomenclature or not, my current setup is:


glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;

Now, anything applied to ModelMatrix before this will apply to "world space," correct? And, then the ViewMatrix ought to apply to the local space, or camera space. So, now that I'm using quaternions to rotate the view space around, I'm uncertain how to go about moving the camera around(or the world around, however you want to define it).

Applying translations to the game objects in the ModelMatrix works great. However, I'm having difficulty getting them to move in the correct direction (what is alluded to in your above response, I think). Since I'm trying to apply them to the world space, but the View Matrix is offset by the quaternion passed to it, things move via the world coordinates rather than against the direction the view is facing. So, if I want the camera to move "forward" I ought to move the world negatively along the z-axis, but I need to alter that so they're moving along the "camera" z-axis and not the world z-axis.

Trying to translate anything in the ViewMatrix doesn't result in anything.

Now, what I've tried so far is multiplying the modelMatrix by the same rotation Matrix that I'm multiplying the ViewMatrix by, then translating it by whatever coordinates, lets say just a bit along the z axis. This works wonderfully as well. When I try to move forward, all the objects come toward the camera space. But, now of course all the objects in the game are rotating with me. So, and here is where I'm struggling, I'm having difficulty rotating things back to where they were after translating them. After translating the objects, I'm trying to multiply the ModelMatrix by the conjugate of the quaternion I'm using in the Rotation Matrix. So, I make another Matrix but pass in the conjugate instead. But, this results in odd behavior, things rotating not quite right.

To get the conjugate I'm just doing:


glm::quat inversequat(-originalQuat.w, originalQuat.x, originalQuat.y, originalQuat.z).

or, even
glm::quat inversequat(originalQuat.w, -originalQuat.x, -originalQuat.y, -originalQuat.z).

Which, is maybe completely wrong, but I struggle with math, so in my head it was a viable althernative :P

It's likely I'm making this more difficult than it is, or just doing it wrong from the get go. Or, perhaps, I'm going about this the right way, but messing something little up in the process.

Anyhow, if you or anyone have any suggestions, I'd be grateful, and I appreciate the time you've given thus far. And, as usual, my apologies tongue.png This probably ought be be a new thread, but since it was along the same lines as my original question I thought I'd leave it be.

edit*

and how embarrassing, it was a typo that was preventing me from translating in the view matrix. Translating along the z-axis (or any other axis) works correctly there. No need for conjugates or anything, just a straight up rotate then translate.

Alright, I've made myself look silly enough and this thread has gone on too long, so thanks everyone for your help and patience and I'll do my best to keep any future questions more concise and less confusing. Also, I completely forgot to give rep to everyone (I always forget >.<), so rep all around.

Cheers,

Misantes smile.png

Beginner here <- please take any opinions with grain of salt

This topic is closed to new replies.

Advertisement