Archived

This topic is now archived and is closed to further replies.

Need Some Freelook Help w/ Quaternions Plz Help

This topic is 5400 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

I have some questions on quaternions and gluLookAt(): If I have a rotation matrix, I change it to a quaternion, and then change it back, is this doing anything to prevent gimbal''s lock? When I have two rotation matrices, one for rotating around the y axis and one for rotating around the x axis (yaw and pitch, no roll), I convert them both to quaternions. But then how do I combine them so to speak? I tried using the approach on NeHe''s game tutorial #7, however all I got was a bunch of sparatic and uncontrollable (and completely locked on one axis, free on the other), rotation. Is the unit vector that gluLookAt() takes for the "up" vector just supposed to be perpendicular to the line created between the viewing and the position? My computer graphics teacher told me that no, its just supposed to be 0, 1, 0 for 99% of all cases. I tried to explain that if you want the camera to rotate then you rotate the up vector, but he has a degree from MIT so I think he knows what he''s talking about. Once I have my unit vectors telling my camera which way its x axis, z axis, and y axis are, I have a relatively simple method for moving. If they move forward, I just add the z axis vector to the position. If they move backwards, I just subtract the z axis vector (my camera is looking down the z axis). If they strafe, I add the x axis and so on. The problem with this is that eventually my coordinates get very manipulated and twisted. It gets to the point where when I press left, I start going up! What''s the deal with this? Perhaps if I post my code it can become more clear, but any help I can get would be, well, very helpful.

Nobody knows?

Share on other sites
quote:
If I have a rotation matrix, I change it to a quaternion, and then change it back, is this doing anything to prevent gimbal's lock?

No. If you want the benefits of quaternions, you have to work in quaternions. If you want to avoid gimbal lock, you can store a rotation matrix, instead of the euler angles you originally used to create it.

quote:
Is the unit vector that gluLookAt() takes for the "up" vector just supposed to be perpendicular to the line created between the viewing and the position?

It's supposed to be straight up, but it doesn't much matter. Internally, it performs a Gramm-Schmidt orthonormalization of the upvector, resulting in the second type you mentioned. Since it'll go ahead and compute that for you, there's no real need for you to.

How appropriate. You fight like a cow.

[edited by - sneftel on May 3, 2003 11:09:41 PM]

Share on other sites
The way to avoid gimble lock is to store your rotations as a matrix (not create a new matrix each frame). Alternatively, you can store it as a quaternion, which has the same effect. The only way it doesn''t work is if you store it as 3 angles.

As far as gluLookAt goes, if you always use the up vector (0,1,0) as up, you will have problems if you try to look at something that is, well, straight up. In that special case, your up vector will be the same as your center vector, and glulookat will not be happy. You can get around this by either keeping track of what the up vector should really be ((right vector) cross (forward vector)), or just not letting the camera rotate all the way up. Or, don''t use glulookat. See, if you are already storing your rotations as a matrix, you can just call glLoadMatrix() to set the orientation, you will have no need to call gluLookAt.

Its confusing but hang in there! Check out the openGL camera code at www.flipcode.com/cotd, for an example of a camera that uses matrices to store position and orientation.

Share on other sites
Ok so if I have two quarternions, one storing my yaw rotation and one storing my pitch rotation, how do I combine them into one more final rotation? I''m still very confused about the up vector, I think I will just try to make it so they can''t look all the way up.

Share on other sites
thats the funny thing with quaternions: if you want to combine the two rotation represented by the two, you can just multiply them, like you would with a matrix. the method of multiplying is differnet ofcource, but you can look that up in the tutorials section.

Share on other sites
Where can I find the "method" of multiplying them? I used the one NeHe has and it worked somewhat but on one axis the vector wouldn''t keep its position.

Share on other sites
here is a method that multiplies quaternion:

    void MultiplyQuat(Quaternion * ptrQuat1, Quaternion * ptrQuat2, Quaternion * ptrResult){	ptrResult->x = (ptrQuat1->w * ptrQuat2->x) + (ptrQuat1->x * ptrQuat2->w) + (ptrQuat1->y * ptrQuat2->z) - (ptrQuat1->z * ptrQuat2->y);	ptrResult->y = (ptrQuat1->w * ptrQuat2->y) + (ptrQuat1->y * ptrQuat2->w) + (ptrQuat1->z * ptrQuat2->x) - (ptrQuat1->x * ptrQuat2->z);	ptrResult->z = (ptrQuat1->w * ptrQuat2->z) + (ptrQuat1->z * ptrQuat2->w) + (ptrQuat1->x * ptrQuat2->y) - (ptrQuat1->y * ptrQuat2->x);	ptrResult->w = (ptrQuat1->w * ptrQuat2->w) - (ptrQuat1->x * ptrQuat2->x) - (ptrQuat1->y * ptrQuat2->y) - (ptrQuat1->z * ptrQuat2->z);}

it assumes a structure like so:

        typedef struct{	float x, y, z, w;} Quaternion;

i hate to state the obvious, but your quaternions need to have been created properly for you to see the desired result after multiplying them. in other words, if u still have a problem after implementing the code above (or equivalent code), the problem is probably in how u are creating them.

[edited by - jorgander on May 5, 2003 11:30:08 AM]

Share on other sites
I remember trying to implement them but I ran into a problem. My pitch rotations of the view vector would work, but every frame my yaw rotations would reset. Does anybody know why this is happening? Thanks

Share on other sites
CHA CHING! THANK YOU SO MUCH, that quaternion multiplication code made it work for me. I cannot thank you enough. I''ve been stressing over this forever. It turns out the function I used is some wierd crap. It looks like this:

pQuatResult->x = pQuat2->x*pQuat1->x
-pQuat2->y*pQuat1->y
-pQuat2->z*pQuat1->z
-pQuat2->w*pQuat1->w;

pQuatResult->y = pQuat2->x*pQuat1->y
+pQuat2->y*pQuat1->x
+pQuat2->z*pQuat1->w
-pQuat2->w*pQuat1->z;

pQuatResult->z = pQuat2->x*pQuat1->z
-pQuat2->y*pQuat1->w
+pQuat2->z*pQuat1->x
+pQuat2->w*pQuat1->y;

pQuatResult->w = pQuat2->x*pQuat1->w
+pQuat2->y*pQuat1->z
-pQuat2->z*pQuat1->y
+pQuat2->w*pQuat1->x;

I dont know what that is for but it sure doesn''t work for what I''m doing. Thanks again man.

Share on other sites
DANGIT! I''m still having problems with my camera''s x axis, z axis, and y axis vectors getting messed up. Here''s my code, if anybody could advise me I''d be very appreciative.

void CCamera::UpdateByMouse(int nChangeX, int nChangeY) {
CVector vecs;
CMatrixMath mats;
CQuaternionMath quats;
m_fYaw = nChangeX / m_fSensitivity;
m_fPitch = nChangeY / m_fSensitivity;
float rotMat[16];
float rotMatPitch[16];
Vector temp;
Quat quatYaw;
Quat quatPitch;
mats.MakeRotationMatrix(0, m_fYaw, rotMat);
mats.MakeRotationMatrix(-1, m_fPitch, rotMatPitch);
quats.RotMatToQuat(&quatYaw, rotMat);
quats.RotMatToQuat(&quatPitch, rotMatPitch);
quats.Normalize(&quatYaw);
quats.Normalize(&quatPitch);
quats.MultiplyQuats(&quatYaw, &quatPitch, &finalQuat);
quats.Normalize(&finalQuat);
quats.QuatToRotMat(&finalQuat, rotMat);

mats.MatrixByVector(rotMat, &camZAxis, &camZAxis);
mats.MatrixByVector(rotMat, &camXAxis, &camXAxis);
mats.MatrixByVector(rotMat, &camYAxis, &camYAxis);

camViewing.x = camPosition.x + camZAxis.x;
camViewing.y = camPosition.y + camZAxis.y;
camViewing.z = camPosition.z + camZAxis.z;
return;
}

void CCamera::Move(int nStepsForward, int nStepsSide, float speed) {
if(nStepsForward>0) {
for(int i=0; i camPosition.x += camZAxis.x * speed;
camPosition.y += camZAxis.y * speed;
camPosition.z += camZAxis.z * speed;
}
}
else if(nStepsForward<0) {
for(int i=0; i<-1*nStepsForward; i++) {
camPosition.x -= camZAxis.x * speed;
camPosition.y -= camZAxis.y * speed;
camPosition.z -= camZAxis.z * speed;
}
}
if(nStepsSide>0) {
for(int i=0; i camPosition.x += camXAxis.x * speed;
camPosition.y += camXAxis.y * speed;
camPosition.z += camXAxis.z * speed;
}
}
else if(nStepsSide<0) {
for(int i=0; i<-1*nStepsSide; i++) {
camPosition.x -= camXAxis.x * speed;
camPosition.y -= camXAxis.y * speed;
camPosition.z -= camXAxis.z * speed;
}
}
return;
}

Share on other sites
It seems to me that the problem might be my Vector by Matrix function. When I multiply a 3d vector by a 4x4 matrix, I end up with some extra "change" which I just ignore. What am I supposed to do with that change? Thanks Here''s my matrix by vector function:

float CMatrixMath::MatrixByVector(float *pMat, Vector *pVec, Vector *pResult) {
if(pMat==NULL || pVec==NULL || pResult==NULL)
return -1;
else {
float change; // we will return the change
pResult->x = pMat[0]*pVec->x + pMat[1]*pVec->y + pMat[2]*pVec->z + pMat[3];
pResult->y = pMat[4]*pVec->x + pMat[5]*pVec->y + pMat[6]*pVec->z + pMat[7];
pResult->z = pMat[8]*pVec->x + pMat[9]*pVec->y + pMat[10]*pVec->z + pMat[11];
change = pMat[12]*pVec->x + pMat[13]*pVec->y + pMat[14]*pVec->z + pMat[15];
return change;
}
return -1;
}

Share on other sites
with your UpdateByMouse() function, why do u make rotation matrices and then make quaternions from the matrices? do u need the matrices for something else? if you''re just making the matrices so u can make the quaternions from them, u can skip the matrices and just make the quaternions from the start. also, you shouldn''t have to normalize a quaternion you have just made if it represents an euler angle (which in this case it does).

if u multiply two unit quaternions, the result will be a unit quaternion. due to the drifting of floating point values, the quaternion would cease to be a unit quaternion after *many* multiplications. since you only multiply once, you probably don''t have to worry about normalizing.

in your matrixbyvector function, your need to switch row-column ordering. i.e. for the x component multiply by 0 4 8 and 12, not 0 1 2 and 3, etc.

Share on other sites
Ok I did that but I''m still having problems with my camera''s unit vectors for the x, y, and z axis are still getting messed up. By messed up I mean that sometimes when I move left the camera will move up. Definatly not cool. Here''s my new matrix by vector function just to make sure I did it right.

float CMatrixMath::MatrixByVector(float *pMat, Vector *pVec, Vector *pResult) {
if(pMat==NULL || pVec==NULL || pResult==NULL)
return -1;
else {
float change; // we will return the change
pResult->x = pMat[0]*pVec->x + pMat[4]*pVec->y + pMat[8]*pVec->z + pMat[12];
pResult->y = pMat[1]*pVec->x + pMat[5]*pVec->y + pMat[9]*pVec->z + pMat[13];
pResult->z = pMat[2]*pVec->x + pMat[6]*pVec->y + pMat[10]*pVec->z + pMat[14];
change = pMat[3]*pVec->x + pMat[7]*pVec->y + pMat[11]*pVec->z + pMat[15];
return change;
}
return -1;
}

Share on other sites
And about the matrix creation and quaternion normalization, I will worry about that after I get it working I guess. How would I create a quaternion "from scratch" that would do the same thing as a rotation matrix?

Share on other sites
i dont know if this is your only problem, but i notice in updatebymouse() you''re passing the same variable (or the address of it) to the matrixbyvector function as the second and third parameters. but, in the matrixbyvector function the first things you do is set the x component of the vector, then you use the same x component in the y and z calculations. it''s not the same parameter in the matrixbyvector formal parameter list, but since you passed in the address of it the same variable, it will update the same memory. a solution would to pass by value or cache the vector''s values before doing the multiplication.

you set the x component on the left side of the ''='':
pResult->x = pMat[0]*pVec->x + pMat[1]*pVec->y + pMat[2]*pVec->z + pMat[3];

then u use the new value here on the left side of the ''='', since pResult and pVec point to the same thing:
pResult->y = pMat[4]*pVec->x + pMat[5]*pVec->y + pMat[6]*pVec->z + pMat[7];
pResult->z = pMat[8]*pVec->x + pMat[9]*pVec->y + pMat[10]*pVec->z + pMat[11];

Share on other sites
Vector tempVec;
mats.MatrixByVector(rotMat, &camZAxis, &tempVec);
camZAxis = tempVec;
mats.MatrixByVector(rotMat, &camXAxis, &tempVec);
camXAxis = tempVec;
mats.MatrixByVector(rotMat, &camYAxis, &tempVec);
camYAxis = tempVec;

Ok I changed it to look like that but now my freelook is even more messed up!

Perhaps if I give a better description of the problem you can help me out. When I don''t move at all and just move the mouse, the vectors change perfectly and smoothly. However, after I move to a new location, it gets a little wierd.

When I move straight ahead and turn around, my y axis viewing vector rotation gets inverted. This could potentially have something to do with my position vector being negative on an axis instead of positive.

When I strafe to the right, slowly my position begins to lower itself (the y value in the position vector gets decreased). This is not correct. When I strafe to the left, my position vector raises itself.

After my camera has raised and lowered itself a lot do to strafing, the viewing vector rotation goes completely wack.

Thanks for all your help, I will be overjoyed once I get this freakin thing working.

Share on other sites
step through your code with a debugger and see what''s happening to the vectors when u are moving. i.e. see why the the y vector gets inverted when you''re position is negative, etc. there could be many things affecting your vectors and the only way to tell for sure is to step through and see exactly when and how it is happening.

Share on other sites
Ok I will try but it seems to me like its too much work and that I don''t know enough about how the math actually works in order for me to find the problem. Do you have any sample code I could see of how to do this? I would greatly appreciate it, thanks

Share on other sites
if u don''t understand the math, i''m sure there are plenty of tutorials around that will explain more than u ever wanted to know about rotating things. for starters here is an excellent article on gamasutra about using quaternions with lots of sample code:

http://www.gamasutra.com/features/19980703/quaternions_01.htm

Share on other sites
I read the gamsutra article, but alas I still don''t understand why my camera controls are so wierd. Perhaps this will give someone some insight into my endless woe''s of 3d rotations:

When I do just straight matrix multiplication with no quaternions, I run into a problem:

When my camera''s yaw is < 180 (or > 180, not quite sure), the viewing vector translation works fine by multiplying it by the product of my yaw rotation matrix and my pitch rotation matrix.

HOWEVER, once my yaw angle flips to being not less than 180 (or to being 180, just the opposite of before), my viewing vector pitch calculations are inverted.

WHY ON EARTH would this happen? Is this supposed to happen? Man this is confusing...