0
101 Jun 18, 2006 at 15:20

I’m trying to write a very simple quaternion camera that looks from its location when you move the mouse. Nothing fancy. Apparently, I’m not understanding something about quaternion rotation correctly, because I just get jiggle, and, without renormalization, the quaternion becomes unnormalized faster than I would expect (after < 30 frames, |q|=\~0). I would really like a picture explaining a simple quaternion camera system because the math/programming is surely trivial if I understand the problem I’m trying to solve.

Anyway, this is what I think I want to be doing:

1. Find rotation about vertical(x-axis) and horizontal(about y-axis) based on mouse movement.
2. Combine these using quaternion multiplication to get a quaternion that represents the change in rotation.
3. Multiply that quaternion by the “view” quaternion from the last frame and its conjugate.
4. Feed Quaternion to OpenGL.

And here is my function.

//passive mouse motion
void passiveMotion(int mx,int my)
{
cout << qview.w << ":";
cout << qview.x << ":";
cout << qview.y << ":";
cout << qview.z << ":::";
cout << length(qview) << "\n";

phi = (mx - mxo)/msensitivity;
theta = (my - myo)/msensitivity;
mxo = mx;
myo = my;

quaternion qhorz;
quaternion qvert;
quaternion qmove;
quaternion qtemp;

qhorz.w = cos(theta/2);
qhorz.x = 0*sin(theta/2);
qhorz.y = 1*sin(theta/2);
qhorz.z = 0*sin(theta/2);

qvert.w = cos(phi/2);
qvert.x = 1*sin(phi/2);
qvert.y = 0*sin(phi/2);
qvert.z = 0*sin(phi/2);

qtemp = qview;
qmove = mult(mult(qvert, qhorz),conjugate(qvert));

qview = mult(mult(qtemp, qmove), conjugate(qtemp));
qview = normalize(qview);

glutPostRedisplay();
}


…And how I tell OpenGL to rotate:

glRotatef(qview.w, qview.x, qview.y, qview.z);


-Tobey

#### 5 Replies

0
167 Jun 18, 2006 at 18:26

First off, the w component of the quaternion doesn’t represent an angle, so you need to do some conversion before passing it to glRotatef (which accepts an axis-angle pair). The proper angle is 2 * acos(w), and the axis should be the (x, y, z) normalized (remember since the whole quaternion (x, y, z, w) is normalized, the (x, y, z) part will not necessarily be normalized).

Second, the way you compose the quaternions is incorrect. You should just be multiplying qvert * qhorz, the conjugate should not appear. (Although I think that qhorz * qvert may be what you actually want.) It looks like you’ve gotten confused about that: the composition of two rotations is just the product of their quaternions. You only need the conjugate when you want to apply a rotation to a *vector*. Then you convert the vector to a quat with a w-component of 0, and apply the v’ = q * v * q\~ formula. But you won’t need this formula, since you convert the quat to axis-angle and let OpenGL do the work of applying it to vectors.

0
101 Jun 18, 2006 at 18:53

This is the code for my 3D editor:

void Orbit(float DeltaRight, float DeltaUp){
// Keep the rotations in the order RightAxis, UpAxis, or the error seems to build up in a strange way.

// Orbit a' la 3dstudio:
cMatrix Orientation = _Orientation.Matrix();
_Orientation = _Orientation*cQuaternion(_Right*Orientation, -DeltaUp);
_Orientation = _Orientation*cQuaternion(_Up, -DeltaRight);
//    _Orientation = _Orientation*cQuaternion(_Up*Orientation, -DeltaRight); // For trackball. Comment out the last line to try it.
}


It has a quaternion as it’s orientation, but it uses a fixed lookat point. Similar enough, I think.

Some explaination:
This ia a method of the camera class.
_Orientation is a quaternion representing what you think it does.
Orientation is _Orientation converted to a matrix. (To transform _Right.)
_Right is the Right axis of the camera. (Wich is the horizontal axis I want to rotate it around.)
The constructor of cQuaternion takes an axis and an angle.

The commented code line is an alternative way to do the vertical rotation. (Trackball view. I don’t like that. And it’s not sutiable for an FPS. Would work for a flight/space simulator.)

For your axis and angle conversion, you need something like this:

cVector cQuaternion::Axis(){
Normalize();

float sin_angle = sqrt(1.0 - w*w);
if(fabs(sin_angle) < 0.0005)
sin_angle = 1;

cVector Axis(x, y, z);
return Axis/sin_angle;
}

float cQuaternion::Angle(){
Normalize();
return acos(w)*2;
}

0
101 Jun 18, 2006 at 19:04

Thanks guys, that helps a bunch. BTW, is it true that some of the “Flipcoders” from back in the day immigrated here?

0
167 Jun 18, 2006 at 19:40

Yep, a bunch of us are around ;)

0
101 Jun 19, 2006 at 13:53

Cause’, I’m a Flipcoder, yes I am!