Quaternion woes

Started by
4 comments, last by Zakwayda 13 years, 3 months ago
Like everyone else in the world, judging from the number of quaternion threads in this section, ive been *trying* to implement a quaternion camera. And i have been largely successful, except for one annoying effect i cannot seem to get rid of.

Here is my Camera class (the relevant parts anyway)

const Vector3 xAxis(1.0f, 0.0f, 0.0f);
const Vector3 yAxis(0.0f, 1.0f, 0.0f);
const Vector3 zAxis(0.0f, 0.0f, 1.0f);

const Quaternion Xaxis(1.0f, 0.0f, 0.0f, 0.0f);
const Quaternion Yaxis(0.0f, 1.0f, 0.0f, 0.0f);
const Quaternion Zaxis(0.0f, 0.0f, 1.0f, 0.0f);

Camera::Camera(Vector3 position)
{
Position = position;
Rotation = Quaternion(0.0f, 0.0f, 0.0f, 1.0f);

Velocity = Pitch = Heading = 0.0f;
}

void Camera::Move(float speed)
{
Vector3 direction = Rotation * Vector3(0.0f, 0.0f, speed);
Position += direction;
}

void Camera::Strafe(float speed)
{
Vector3 direction = Rotation * Vector3(speed, 0.0f, 0.0f);
Position += direction;
}

void Camera::Look()
{
// Form rotation quaternion //
Quaternion pitch;
pitch.FromAxis(xAxis, -Pitch);

Quaternion heading;
heading.FromAxis(yAxis, Heading);

Rotation = pitch * heading;

// Apply quaternion //
float RotationMatrix[16];
Rotation.GetMatrix(RotationMatrix);
glMultMatrixf(RotationMatrix);

// Translate position //
glTranslatef(-Position.x, -Position.y, Position.z);
}


Pitch refers to the rotation about the x axis, while heading refers to the y rotation. This code works when im simply looking around (not changing my position), so i believe my GetMatrix and Rotation quaternion setup is correct. However, problems start occuring when i do move. If i rotate around the x-axis (pitch), the movement distorts considerably. If i am turned 90 degrees on the y axis and 45 on the x axis, strafing actually causes my Position.y to change! Also, if i simply call Move, the camera moves towards the wrong center.

But if my Rotation matrix is right, then that means that the problem lies here:

Vector3 direction = Rotation * Vector3(0.0f, 0.0f, speed);
Position += direction;


(and same for the strafing function)

So here is the code for vector rotation using a quaternion:

Vector3 Quaternion::operator* (const Vector3& v)
{
Quaternion vecQuat, resQuat;
vecQuat.x = v.x;
vecQuat.y = v.y;
vecQuat.z = v.z;
vecQuat.w = 0.0f;

resQuat = vecQuat * GetConjugate();
resQuat = *this * resQuat;

return Vector3(resQuat.x, resQuat.y, resQuat.z);
}


As far as I can tell there is nothing wrong with that as well.

What gives?
Advertisement
I suppose you use Camera::Look() to setup your view matrix? My guess would be that this part is where your problem is. Also, if you want a 6-DoF camera, you shouldn't be keeping pitch and heading separately - accumulate all orientation changes in your Rotation quaternion instead.

Of course, it depends on how you setup the rest of your transforms, but I would do something like this:


void Camera::Look()
{
glLoadIdentity();

glTranslatef(-Position.x, -Position.y, -Position.z);

// Apply quaternion //
float RotationMatrix[16];
Rotation.Inverse().GetMatrix(RotationMatrix);
glMultMatrixf(RotationMatrix);
}
This code worked !:

Quaternion pitch;
pitch.FromAxis(xAxis, Pitch);

Quaternion heading;
heading.FromAxis(yAxis, -Heading);

Rotation = heading * pitch;

// Apply quaternion //
float RotationMatrix[16];
Rotation.GetConjugate().GetMatrix(RotationMatrix);
glMultMatrixf(RotationMatrix);

glTranslatef(-Position.x, -Position.y, -Position.z);;


I have a few questions about why though:

Why do you need to get the inverse (conjugate in this case) of the Rotation quaternion before getting its matrix? I have never seen this before.
Why did you put glTranslatef before the view transform?

Thanks
Well, the order of transformations is pretty much arbitrary. However, the results differ depending on the order you apply rotation, scale and translation. You have to be aware of that and pick the order that yields your desired result. I usually use scale, rotate, translate (SRT).

In any case, the reason why you have to use the inverse of the rotation is the following. In order to transform your view, what you want to be doing is translate all objects by the inverse of the view transformation. The inverse of a product of matricies is the reversed product of their inverses, so (SRT)^-1 = T^1R^1S^1. The inverse of a translation is simply its negation. The inverse of a rotation represented by a quaternion is its inverse (or if the quaternion is normalized, the conjugate).

Well, the order of transformations is pretty much arbitrary. However, the results differ depending on the order you apply rotation, scale and translation. You have to be aware of that and pick the order that yields your desired result. I usually use scale, rotate, translate (SRT).

In any case, the reason why you have to use the inverse of the rotation is the following. In order to transform your view, what you want to be doing is translate all objects by the inverse of the view transformation. The inverse of a product of matricies is the reversed product of their inverses, so (SRT)^-1 = T^1R^1S^1. The inverse of a translation is simply its negation. The inverse of a rotation represented by a quaternion is its inverse (or if the quaternion is normalized, the conjugate).


Oh wow, did not think of it that way.

That makes sense. I guess before when i was applying the incorrect quaternion i was unknowingly compensating for it by changing random things in my other functions. All is back to normal though.

Thanks again :D
The quaternion is conjugated because Camera::Look() is (presumably) intended to set up a view matrix, which is typically the inverse of the model/object/world matrix for the same object. The inverting of the rotation and translation and the changing of the transform order (translate first, then rotate) has the desired effect of creating the inverse transform. [Edit: As kloffy said.]

Also, I think it's worth noting that the use of quaternions in this context is more or less pointless. (Maybe there's more to it than what you posted, but as far as the code excerpts shown are concerned at least, the use of quaternions doesn't gain you anything.)

This topic is closed to new replies.

Advertisement