• Create Account

How to Use a Quaternion as a Camera Orientation

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

4 replies to this topic

#1Saldan  Members

115
Like
0Likes
Like

Posted 20 May 2012 - 11:23 PM

So a while ago I was trying to figure out how to rotate objects using quaternions, and I eventually succeeded. Now, I'm trying to adapt the same system for use in my camera. I am coding in C++, using OpenGL and SFML. Here is how I rotate and translates objects at the moment:

	//Create change quaternions for each axis
Quaternion xOffset = Quaternion();
xOffset.FromAxisAngle(Rotation.x * m_TurnSpeed, 1.0, 0.0, 0.0);
Quaternion yOffset = Quaternion();
yOffset.FromAxisAngle(Rotation.y * m_TurnSpeed, 0.0, 1.0, 0.0);
Quaternion zOffset = Quaternion();
zOffset.FromAxisAngle(Rotation.z * m_TurnSpeed, 0.0, 0.0, 1.0);

//Multiply the change quats by the current quat
m_Orientation = yOffset * zOffset * xOffset * m_Orientation;

//Get matrix
m_Orientation.Normalize();
m_Orientation.RotationMatrix(m_RotationMatrix);

Translation = m_Orientation.MultVect(Translation);

m_Position.x += (Translation.x * m_MoveSpeed);
m_Position.y += (Translation.y * m_MoveSpeed);
m_Position.z += (Translation.z * m_MoveSpeed);


I apply m_RotationMatrix using glMultMatrix(), and m_Position using glTranslatef().

This works fine for objects rotating and translating about, but when I apply this to my camera, obviously things don't work. Now, I say obviously, because as far as I know, if I want to change the direction I'm "looking at" in OpenGL, I need to apply the reverse of the changes I'd apply to an object. However, I'm not quite sure how to do this, and all my attempts have failed.

Leaving everything exactly the same, I find rotations don't add up and translations are backwards or inverted. Inverting the input doesn't help.

I believe that using

m_Orientation = m_Orientation * yOffset * zOffset * xOffset;


Instead of the previous form is appropriate for the camera, and this is enough to fix all the rotation issues, but I simply can't manage to get translations to work after that. I can get close by adding the X and Y changes to the position normally and then subtracting the Z change from the position, but this still leads to wonky behavior when moving and rotating around the Z axis at the same time, and the object ends off spiraling away from the point it is supposed to be pointing towards.

So, any tips on how to adapt the above behavior for proper use as a camera?

Also, when applying a camera to OpenGL, to I need to use glPushMatrix() and glPopMatrix()? I ask because that helped some tests run better than others, but it doesn't seem to make sense to do so, since those are basically for pushing a new local space for transformations onto the stack (I'm horribly mangling my terminology, I'm sure).

Edited by Saldan, 20 May 2012 - 11:29 PM.

#2Matias Goldberg  Members

9042
Like
0Likes
Like

Posted 21 May 2012 - 12:04 AM

"Doing the reverse" means in this case m_TurnSpeed becomes "-m_TurnSpeed"
And, when translating, m_MoveSpeed is also negated "- m_MoveSpeed"

But don't change the order of the quaternion multiplcations, it's not the same. The idea behind the "camera matrix" (actually called view matrix) is that, if you can't the camera, move the whole world. This means if the camera is moved 2 units to the left, it's the same as if everything else moves 2 units to the right. Same with rotations, etc. You get the idea.

If you still don't get it working after negating those variables, the way you're extracting the translation part and then apply movement doesn't look quite right. Can't really tell without debugging the numbers.
If I'm not mistaken, that code should make the camera spin around the origin when trying to rotate, rather than rotating around it's center. But in the models you don't see it glitch like that because you normalize the matrix, and use glTranslate to let OpenGL perform the translation for you, so it miraculous works.

But in the camera, you don't have that, so if I'm not mistaken, you are unable to move it, right?

#3Saldan  Members

115
Like
0Likes
Like

Posted 21 May 2012 - 12:31 AM

"Doing the reverse" means in this case m_TurnSpeed becomes "-m_TurnSpeed"
And, when translating, m_MoveSpeed is also negated "- m_MoveSpeed"

But don't change the order of the quaternion multiplcations, it's not the same. The idea behind the "camera matrix" (actually called view matrix) is that, if you can't the camera, move the whole world. This means if the camera is moved 2 units to the left, it's the same as if everything else moves 2 units to the right. Same with rotations, etc. You get the idea.

See, I thought this was the case, but when I try that out (inverting m_TurnSpeed and m_MoveSpeed, leaving the order of the quaternion multiplication alone), what happens is the rotations are in the wrong direction, and they don't add up. So for example, if I turn to the right around the Y axis (by pressing the key meant to turn to the left), then try to rotate around the Z axis, I end up rotating around the global Z axis, not the camera's Z axis, i.e. I end up rotating around the camera's X axis (if it's a 90° turn). When I leave the values alone and reverse the multiplication order, rotation *appears* to work as expected (i.e. rotation around the Y axis, then around the Z axis, leads to a rotation around the new, post-Y-rotation Z axis and not the old one). It might not be, of course; if that's the case, then there's a deeper problem.

If you still don't get it working after negating those variables, the way you're extracting the translation part and then apply movement doesn't look quite right. Can't really tell without debugging the numbers.
If I'm not mistaken, that code should make the camera spin around the origin when trying to rotate, rather than rotating around it's center. But in the models you don't see it glitch like that because you normalize the matrix, and use glTranslate to let OpenGL perform the translation for you, so it miraculous works.

Hm, that could be a problem. Here is what MultVect() looks like:

sf::Vector3<float> Quaternion::MultVect(sf::Vector3<float> Vector)
{
//From http://www.idevgames.com/articles/quaternions
Quaternion VectorQuat = Quaternion();
VectorQuat.x = Vector.x;
VectorQuat.y = Vector.y;
VectorQuat.z = Vector.z;
VectorQuat.w = 0.0;
Quaternion Inverse = (*this);
Inverse.Invert();
Quaternion Result = Inverse * VectorQuat * (*this);
sf::Vector3<float> ResultVector;
ResultVector.x = Result.x;
ResultVector.y = Result.y;
ResultVector.z = Result.z;
return ResultVector;
}


I do use glTranslafef() when rendering the scene. Here is what that code looks like (it is called before anything else in the scene is rendered):

	//Rotate camera
glMultMatrixf(m_PlayerCamera.GetMatrix());
//Translate camera
sf::Vector3<float> CameraPos = m_PlayerCamera.GetPosition();
glTranslatef(CameraPos.x, CameraPos.y, CameraPos.z);


But in the camera, you don't have that, so if I'm not mistaken, you are unable to move it, right?

It actually moves quite smoothly, just in the wrong direction. If I do the following, I get near-perfect movement so long as I don't try to rotate around the camera's Z axis.

	//Create new quaternions for each axis
Quaternion xOffset = Quaternion();
xOffset.FromAxisAngle(Rotation.x * m_TurnSpeed, 1.0, 0.0, 0.0);
Quaternion yOffset = Quaternion();
yOffset.FromAxisAngle(Rotation.y * m_TurnSpeed, 0.0, 1.0, 0.0);
Quaternion zOffset = Quaternion();
zOffset.FromAxisAngle(Rotation.z * m_TurnSpeed, 0.0, 0.0, 1.0);
//Multiply the new quaternions into the orientation
m_Orientation = m_Orientation * yOffset * zOffset * xOffset;
//Get matrix
m_Orientation.Normalize();
m_Orientation.RotationMatrix(m_RotationMatrix);
///Translate camera
Translation = m_Orientation.MultVect(Translation);
m_Position.x += (Translation.x * m_MoveSpeed);
m_Position.y += (Translation.y * m_MoveSpeed);
m_Position.z -= (Translation.z * m_MoveSpeed);


Now, mathematically, I'm almost certain something isn't right here, since that's not what I'd expect the proper operations to look like.

Edited by Saldan, 21 May 2012 - 12:33 AM.

#4Saldan  Members

115
Like
1Likes
Like

Posted 21 May 2012 - 07:57 AM

Good news! I've solved the problem. The issue was that I should have been pushing a matrix created from the inverse of the Orientation (as well as reversing all the transformation data, i.e. the rotation and translation). What I do now is

	//Rotate camera
glMultMatrixf( m_PlayerCamera.GetInvertedMatrix() );

//Translate camera
sf::Vector3<float> CameraPos = m_PlayerCamera.GetPosition();
glTranslatef(CameraPos.x, CameraPos.y, CameraPos.z);


Where GetInvertedMatrix() looks like this:

GLfloat* Camera::GetInvertedMatrix()
{
//Get a new quaternion, the inverse of the Orientation
Quaternion InvertedOrientation = m_Orientation;
InvertedOrientation.Invert();

//Fill the matrix with the orientation
InvertedOrientation.RotationMatrix(m_RotationMatrix);

//Return that matrix
return m_RotationMatrix;
}


Once I do this, everything works as intended.

In case anybody in the future is looking for the way to Invert() a quaternion, here's my code:

void Quaternion::Invert()
{
float Length = 1.0 / (x*x + y*y + z*z + w*w);
x *= -Length;
y *= -Length;
z *= -Length;
w *= Length;
}


#5sjhalayka  Members

985
Like
0Likes
Like

Posted 21 May 2012 - 11:14 AM

In case anybody in the future is looking for the way to Invert() a quaternion, here's my code:

void Quaternion::Invert()
{
float Length = 1.0 / (x*x + y*y + z*z + w*w);
x *= -Length;
y *= -Length;
z *= -Length;
w *= Length;
}


Right on. Clever way to minimize the division ops. This is a handy cheatsheet for quaternion ops/functions (the Boost code is good too, or was, last time that I checked):
http://world.std.com...ools/tools.html

Edited by taby, 21 May 2012 - 11:16 AM.

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.