Quaternion over-rotation problem?
so i''ve been following some tutorials trying to make a quaternion-based system. I''m testing it out on my camera, but i''m getting a weird problem.
I set the ''a'' and ''d'' keys to be my rotate "left" and "right" respectively, each rotating around the camera vUp 5.0 degrees or -5.0 degrees respectively.
What i do is, i initially have the quaternion to be the unit quaternion (or as i do it, the vUp axis rotated by 0.0 radians).
Then, i take the axis and angle i want to rotate by, turn that into a quaternion using the following formula:
angle = angle * (M_PI / 180.0);
Quaternion result;
result.a = cosf(angle / 2.0);
result.v[0] = vector[0] * sinf(angle / 2.0);
result.v[1] = vector[1] * sinf(angle / 2.0);
result.v[2] = vector[2] * sinf(angle / 2.0);
then, take the current quaternion,
current = result * current;
and then construct a matrix out of this.
the reason I want to construct the matrix is that eventually i want to use SLERP
Anyway, when i hit the keys, they do infact rotate the camera, but on consecutive hits, it either over-rotates or rotates the wrong way in respect to rhe key (aka i hit ''d'' and it rotates left).
help is very much appreciated
What does your quaterion operator * do?
Why do you need a matrix to slerp?
I''ve found the following code in various places around the web, and modified it slightly such that the results match the D3DX functions. You could optimize the generation of return values to get a slight speed boost when using the MSVC.NET 7.1 compiler.
Why do you need a matrix to slerp?
I''ve found the following code in various places around the web, and modified it slightly such that the results match the D3DX functions. You could optimize the generation of return values to get a slight speed boost when using the MSVC.NET 7.1 compiler.
EQuat operator *(const EQuat &q) { EQuat ret; EFloat A, B, C, D, E, F, G, H; A = (q.w + q.x)*(w + x); B = (q.z - q.y)*(y - z); C = (q.w - q.x)*(y + z); D = (q.y + q.z)*(w - x); E = (q.x + q.z)*(x + y); F = (q.x - q.z)*(x - y); G = (q.w + q.y)*(w - z); H = (q.w - q.y)*(w + z); ret.w = B + (-E - F + G + H) / 2.0f; ret.x = A - (E + F + G + H) / 2.0f; ret.y = C + (E - F + G - H) / 2.0f; ret.z = D + (E - F - G + H) / 2.0f; return ret; } EQuat SLerp(EQuat &q, EFloat ratio) { EQuat ret; EFloat omega, cosom, sinom, scale0, scale1; // calc cosine cosom = x * q.x + y * q.y + z * q.z + w * q.w; // adjust signs (if necessary) if ( cosom < 0.0 ) { cosom = -cosom; scale1 = -1.0f; } else { scale1 = 1.0f; } if ( (1.0 - cosom) > 0.0001f/*DELTA*/ ) // Must define delta. { // standard case (slerp) omega = EACOS(cosom); sinom = ESIN(omega); scale0 = ESIN((1.0f - ratio) * omega) / sinom; scale1 *= ESIN(ratio * omega) / sinom; } else { // "from" and "to" quaternions are very close // ... so we can do a linear interpolation scale0 = 1.0f - ratio; scale1 *= ratio; } // calculate final values ret.x = (EFloat) (scale0 * x + scale1 * q.x); ret.y = (EFloat) (scale0 * y + scale1 * q.y); ret.z = (EFloat) (scale0 * z + scale1 * q.z); ret.w = (EFloat) (scale0 * w + scale1 * q.w); return ret; }
Again, you don''t need a matrix to use SLERP - that''s one of the points of using quaternions.
What do you mean by ''over-rotate''? Do you mean it rotates too far?
Also, make sure you''re multiplying in the right order, i.e. current * result vs. result * current. Which one is right depends on how you have your quat mult function set up.
The problem could also be happening at the stage where you convert from your quaternion to a matrix.
Also, remember that you need to transpose the matrix to get your view matrix. If you forget to do this, your camera will probably rotate in the wrong direction.
What do you mean by ''over-rotate''? Do you mean it rotates too far?
Also, make sure you''re multiplying in the right order, i.e. current * result vs. result * current. Which one is right depends on how you have your quat mult function set up.
The problem could also be happening at the stage where you convert from your quaternion to a matrix.
Also, remember that you need to transpose the matrix to get your view matrix. If you forget to do this, your camera will probably rotate in the wrong direction.
the quaternion is of this form:
float a (the "real" value)
Vec3f v (the 3 values cooresponding to i,j,k)
General answers to questions you guys raised:
I don''t rotate along the wrong axis, so i know the matrix is set in the right direction.
jyk:
I will try not using the matrix-form instead.
In any case, I used the tutorials under Quaternions from gamedev, so i''ll copy what they have, which is essentially exactly what i do:
quat1 * quat2
where quat1 = {w1, x1, y1, z1}
and quat2 = {w2, x2, y2, z2}
w=w1w2 - x1x2 - y1y2 - z1z2
x = w1x2 + x1w2 + y1z2 - z1y2
y = w1y2 + y1w2 + z1x2 - x1z2
z = w1z2 + z1w2 + x1y2 - y1x2
float a (the "real" value)
Vec3f v (the 3 values cooresponding to i,j,k)
General answers to questions you guys raised:
I don''t rotate along the wrong axis, so i know the matrix is set in the right direction.
jyk:
I will try not using the matrix-form instead.
In any case, I used the tutorials under Quaternions from gamedev, so i''ll copy what they have, which is essentially exactly what i do:
quat1 * quat2
where quat1 = {w1, x1, y1, z1}
and quat2 = {w2, x2, y2, z2}
w=w1w2 - x1x2 - y1y2 - z1z2
x = w1x2 + x1w2 + y1z2 - z1y2
y = w1y2 + y1w2 + z1x2 - x1z2
z = w1z2 + z1w2 + x1y2 - y1x2
neglected to answer one question:
When i say over-rotate, i mean, lets say i need to go 5 degrees to the left, it will go 5 degrees initially, but the next time i do it, it will go, say, 7 degrees. also, when i hit the button to go right (AKA -5 degrees) it will sometimes keep going to the left a few clicks, then start going to the right, but not at equal amounts.
When i say over-rotate, i mean, lets say i need to go 5 degrees to the left, it will go 5 degrees initially, but the next time i do it, it will go, say, 7 degrees. also, when i hit the button to go right (AKA -5 degrees) it will sometimes keep going to the left a few clicks, then start going to the right, but not at equal amounts.
OK, so i removed the matrix, which was the problem, and now turning left and right works just fine.
However, looking up and down go slightly diagonally up or down, thus if i click up and down simultaniously a couple times, it looks like its moving on a jagged line.
the way i do my rotations is this:
unitize(rot_axis);
Quaternion rotation;
rotation = axis_to_quat(angle, rot_axis);
unitize_quat(rotation);
Quaternion vector;
vector.a = 0.0;
vector.v = direction; //direction is the camera "foreward"
vector = multiply_quat(multiply_quat(rotation, vector), conjugate(rotation));
direction = vector.v;
vector.v = up; //up is the camera up vector
vector = multiply_quat(multiply_quat(rotation, vector), conjugate(rotation));
up = vector.v;
However, looking up and down go slightly diagonally up or down, thus if i click up and down simultaniously a couple times, it looks like its moving on a jagged line.
the way i do my rotations is this:
unitize(rot_axis);
Quaternion rotation;
rotation = axis_to_quat(angle, rot_axis);
unitize_quat(rotation);
Quaternion vector;
vector.a = 0.0;
vector.v = direction; //direction is the camera "foreward"
vector = multiply_quat(multiply_quat(rotation, vector), conjugate(rotation));
direction = vector.v;
vector.v = up; //up is the camera up vector
vector = multiply_quat(multiply_quat(rotation, vector), conjugate(rotation));
up = vector.v;
Hm. I''ve never rotated a vector directly with a quat before (I use the matrix form), but it looks like you''re doing it correctly. When you rotate up and down, what are you using for the rotation axis? The cross product of the direction and up vectors?
You also may need to normalize your direction and up vectors occasionally, although I doubt that''s your problem...
You also may need to normalize your direction and up vectors occasionally, although I doubt that''s your problem...
You are mixing up your representation of the camera orientation. There is no need to store a forward and up vector. All you need to store is a single quaternion. The quaternion contains all the information about rotation about all three axes.
A simple way to generate the forward and up vectors you really want would be to just choose a starting forward vector (maybe (0,0,-1)) and a starting up vector (possibly (0,1,0)). These vectors would never actually be altered. You would use the quaternion on those vectors to get the current forward and up vectors. The quaternion is the only thing that needs to be updated. Also this quaternion is what would be stored between frames.
The code could look something like this:
read input
pick rotation to apply based on user input
current = rotation * current
forward = rotation * (0,0,-1) * conj(rotation)
up = rotation * (0,0,-1) * conj(rotation)
Actually now that I look at it this looks similiar to what you had before. But maybe if this works then it means the bug was in how you were building the matrix or something? Really building the matrix directly is just an alternative to generating the forward and up vectors and having opengl generate the matrix for you...
A simple way to generate the forward and up vectors you really want would be to just choose a starting forward vector (maybe (0,0,-1)) and a starting up vector (possibly (0,1,0)). These vectors would never actually be altered. You would use the quaternion on those vectors to get the current forward and up vectors. The quaternion is the only thing that needs to be updated. Also this quaternion is what would be stored between frames.
The code could look something like this:
read input
pick rotation to apply based on user input
current = rotation * current
forward = rotation * (0,0,-1) * conj(rotation)
up = rotation * (0,0,-1) * conj(rotation)
Actually now that I look at it this looks similiar to what you had before. But maybe if this works then it means the bug was in how you were building the matrix or something? Really building the matrix directly is just an alternative to generating the forward and up vectors and having opengl generate the matrix for you...
hrm, interesting, i will try implementing it that way anonymous.
Also, what you have is actually different than mine...in mine, i change the foreward and up vectors, in yours they are constant.
my question is, with this "constant" quaternion, what exactly do i do with that? in your code, you don''t do anything with it. Or, instead of
current = rotation * current
forward = rotation * (0,0,-1) * conj(rotation)
did you mean
forward = current * (0,0,-1) * conj(current)
Also, what you have is actually different than mine...in mine, i change the foreward and up vectors, in yours they are constant.
my question is, with this "constant" quaternion, what exactly do i do with that? in your code, you don''t do anything with it. Or, instead of
current = rotation * current
forward = rotation * (0,0,-1) * conj(rotation)
did you mean
forward = current * (0,0,-1) * conj(current)
I''m pretty sure he meant to use current (correct me if I''m wrong, anon).
The only tricky part I see is "pick rotation to apply based on user input." In order to construct this quaternion, you''ll need to already know your forward, up, and side vectors (they will be the axes you rotate around for roll, yaw, and pitch, respectively). So you may have to calculate them twice...or something :-|
In any case, it''s probably better to do it as anon has said, that is, start from scratch with (0, 0, 1), etc. each frame rather than doing it cumulatively. This will help you avoid precision errors.
The only tricky part I see is "pick rotation to apply based on user input." In order to construct this quaternion, you''ll need to already know your forward, up, and side vectors (they will be the axes you rotate around for roll, yaw, and pitch, respectively). So you may have to calculate them twice...or something :-|
In any case, it''s probably better to do it as anon has said, that is, start from scratch with (0, 0, 1), etc. each frame rather than doing it cumulatively. This will help you avoid precision errors.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement