I'm writing an simple application based on openGL and qt. It creates primitive (triangle for example) which rotation user can change by using one of three spinboxes
(coordinates: x, y, z). The problem is local coordinate system. It changes and works properly for axes Y and Z but not for X. Local axis X always coincides with global axis X. So when i rotate my figure by 90' around Y then axes X and Z are parallel because local Z changed (as I expected) and local X didn't change. Here is fragment of my paintGL() function:
glRotatef(rot[0], 1.0, 0, 0);
glRotatef(rot[1], 0, 1.0, 0);
glRotatef(rot[2], 0, 0, 1.0);
You should use rotation matrix which lets you specify rotation axis and angles of rotation:
http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
Generally it is not a good idea to perform rotations with euler angles: the next problem you will come around is the 180 deg. flip in certain angle constellations. The problem is broadly known as gimbal lock with euler angles.
Related
so i'm trying to rotate the camera around a cube object , using the keyboard arrows to change the y angle and the x angle , i want a result like this : this video
Well now I want to move the camera in a circular motion around the shape, so I used
the sphere parametric equation to determine the camera's x, y, and z coordinates on the parameter of the sphere using the x angle and the y angle , this is the equation : sphere parametric equation
For this moment, the equation is as following:
x = radius*cos(y_angle)*cos(x_angle)
y = radius*cos(y_angle)*sin(x_angle)
z = radius*sin(y_angle)
so the code will be :
gluLookAt(x, y, z, 0, 0, 0, 0, 1, 0); // x,y and z that we calculated above
Now it is supposed to work without problems, but the camera dose rotate in a strange way that is not as it should, I think this has something to do with the up vector in the
glulookat() function because i'm using a constant up vector like
(0,1,0)
Anyway, I can't calculate the up vector correctly, so I want help in making a circular motion around the cube like the one that appeared in the video using the glulookat , i hope i made it clear ، Because as you have noticed, I can't explain well
In most 3D platform games, only rotation around the Y axis is needed since the player is always positioned upright.
However, for a 3D space game where the player needs to be rotated on all axises, what is the best way to represent the rotation?
I first tried using Euler angles:
glRotatef(anglex, 1.0f, 0.0f, 0.0f);
glRotatef(angley, 0.0f, 1.0f, 0.0f);
glRotatef(anglez, 0.0f, 0.0f, 1.0f);
The problem I had with this approach is that after each rotation, the axises change. For example, when anglex and angley are 0, anglez rotates the ship around its wings, however if anglex or angley are non zero, this is no longer true. I want anglez to always rotate around the wings, irrelevant of anglex and angley.
I read that quaternions can be used to exhibit this desired behavior however was unable to achieve it in practice.
I assume my issue is due to the fact that I am basically still using Euler angles, but am converting the rotation to its quaternion representation before usage.
struct quaternion q = eulerToQuaternion(anglex, angley, anglez);
struct matrix m = quaternionToMatrix(q);
glMultMatrix(&m);
However, if storing each X, Y, and Z angle directly is incorrect, how do I say "Rotate the ship around the wings (or any consistent axis) by 1 degree" when my rotation is stored as a quaternion?
Additionally, I want to be able to translate the model at the angle that it is rotated by. Say I have just a quaternion with q.x, q.y, q.z, and q.w, how can I move it?
Quaternions are very good way to represent rotations, because they are efficient, but I prefer to represent the full state "position and orientation" by 4x4 matrices.
So, imagine you have a 4x4 matrix for every object in the scene. Initially, when the object is unrotated and untraslated, this matrix is the identity matrix, this is what I will call "original state". Suppose, for instance, the nose of your ship points towards -z in its original state, so a rotation matrix that spin the ship along the z axis is:
Matrix4 around_z(radian angle) {
c = cos(angle);
s = sin(angle);
return Matrix4(c, -s, 0, 0,
s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
now, if your ship is anywhere in space and rotated to any direction, and lets call this state t, if you want to spin the ship around z axis for an angle amount as if it was on its "original state", it would be:
t = t * around_z(angle);
And when drawing with OpenGL, t is what you multiply for every vertex of that ship. This assumes you are using column vectors (as OpenGL does), and be aware that matrices in OpenGL are stored columns first.
Basically, your problem seems to be with the order you are applying your rotations. See, quaternions and matrices multiplication are non-commutative. So, if instead, you write:
t = around_z(angle) * t;
You will have the around_z rotation applied not to the "original state" z, but to global coordinate z, with the ship already affected by the initial transformation (roatated and translated). This is the same thing when you call the glRotate and glTranslate functions. The order they are called matters.
Being a little more specific for your problem: you have the absolute translation trans, and the rotation around its center rot. You would update each object in your scene with something like:
void update(quaternion delta_rot, vector delta_trans) {
rot = rot * delta_rot;
trans = trans + rot.apply(delta_trans);
}
Where delta_rot and delta_trans are both expressed in coordinates relative to the original state, so, if you want to propel your ship forward 0.5 units, your delta_trans would be (0, 0, -0.5). To draw, it would be something like:
void draw() {
// Apply the absolute translation first
glLoadIdentity();
glTranslatevf(&trans);
// Apply the absolute rotation last
struct matrix m = quaternionToMatrix(q);
glMultMatrix(&m);
// This sequence is equivalent to:
// final_vertex_position = translation_matrix * rotation_matrix * vertex;
// ... draw stuff
}
The order of the calls I choose by reading the manual for glTranslate and glMultMatrix, to guarantee the order the transformations are applied.
About rot.apply()
As explained at Wikipedia article Quaternions and spatial rotation, to apply a rotation described by quaternion q on a vector p, it would be rp = q * p * q^(-1), where rp is the newly rotated vector. If you have a working quaternion library implemented on your game, you should either already have this operation implemented, or should implement it now, because this is the core of using quaternions as rotations.
For instance, if you have a quaternion that describes a rotation of 90° around (0,0,1), if you apply it to (1,0,0), you will have the vector (0,1,0), i.e. you have the original vector rotated by the quaternion. This is equivalent to converting your quaternion to matrix, and doing a matrix to colum-vector multiplication (by matrix multiplication rules, it yields another column-vector, the rotated vector).
I'm trying to move around a cube centered at the origin using gluLookAt instead of performing the R*T transformation directly on the object. At first, I was not sure how to do this problem. Then I realized (after implementing half of a solution in circular coords) that I should try using a spherical coordinate system representation. I was able to write some code to do this, but I (the 'eye' of the camera) spin really quickly around the cube. Also, I notice that I'm moving a bit closer to the cube as well instead of holding a constant radius. When I use the Rotate*Translate method, it spins at a more proper rate with the same distance.
My approach for rotation was to use spherical coordinates, but I'm not sure if this is correct. I calculate the two angles, according to the diagram located on the Wikipedia page. I calculate the magnitude of the current point I'm at (distX, distY, distZ). We are initially in the XY plane looking at the negative Z plane). I'm also given two angles, one to specify the angle of rotation about the x-axis and one for the y-axis. These five values are calculated based on how my mouse clicks are made.
I calculate theta and phi based off of some trig and then finally the new position. These formulae can be found in that link earlier. The last step is to plug it into gluLookAt. Again, upon running this program, I am able to spin around the cube, but it is very fast and translates the camera as well. What am I doing wrong?
My code is listed below in case you want to reference it. The part referencing 'y' and 'up' are my attempts to calculate the rotation about the Z axis. The best way I can describe that rotation that is if you look at an object, imagine yourself the camera, and tilt your head left and right. I did not include it in my call to gluLookAt since I couldn't get that to work either.
EDIT: It now rotates at what appears to be the correct rate, but it does not rotate completely. By this, I mean that upon rotating, the "eye" moves towards the cube with an angle, but then will swivel back away, undoing the rotation. But this loops so the path taken by the eye looks similar to a bent infinity symbol.
void sceneTransformation(){
glLoadIdentity( );
//Using the R*T approach. Works flawlessly
//glTranslatef(-distX, distY, -distZ);
//glRotatef( anglex, 1.0, 0.0, 0.0 );
//glRotatef( angley, 0.0, 1.0, 0.0 );
GLdouble radx = anglex*PI/180.;
GLdouble rady = angley*PI/180.;
GLdouble l = sqrt(pow(distX, 2) + pow(distY, 2) + pow(distZ, 2));
GLdouble phi = atan(distY/distZ) + radx;
GLdouble theta = acos(distZ/l) + radx;
GLdouble deltaZ = l*sin(theta)*cos(phi);
GLdouble deltaY = l*sin(theta)*sin(phi);
GLdouble deltaX = l*cos(theta);
GLdouble ytheta = atan(distX/distY);
GLdouble y = sqrt(pow(distX,2) + pow(distY, 2));
GLdouble yangle = PI/2.-ytheta-rady;
GLdouble up_y = y*sin(yangle);
GLdouble up_x = y*cos(yangle);
gluLookAt((distX - deltaX), (distY - deltaY), (distZ - deltaZ), 0, 0, 0, 0,1,0);
}
The math functions like sin(), cos(), and friends expect input in radians, not degrees. It looks like you're assuming degrees, so you're spinning roughly 57.3 times (exactly: 360 / (2 * pi)) faster than intended.
You need to make your movement timebased, this means to multiply the rotation angle with the delta between the beginning last and current frame.
angle += 90 * timeDelta; // Rotate by 90 degree each second
There is a fine solution to your problem. take a bit of investigation, but I think it will meet your needs.
Quaternions
Some code for it
Good for transforming vector to rotation and backwards
I have camera class that hold her orientation via euler angles and position. Something like that:
float m_x;
float m_y;
float m_z;
Vector4 m_pos;
And i want to move this camera free over the space.
When user move mouse up-down camera must rotate around x axis in her own coordinate system. But i want to hold only this three angles and position and nothing more.
So algorithm looks like this:
Find camera local system axis (u, v, n)
Rotate around u axis on angle alpha
Find angles around (1, 0, 0), (0, 1, 0), (0, 0, 1) that answer to rotation on angle alpha around u axis
Add them to m_x, m_y, m_z
Question is: how can i calculate rotation angles in default coordinate system (i mean in (1, 0, 0), (0, 1, 0) and (0, 0, 1)) that answer to rotation angles in local camera coordinate system?
Or may be better solution exists for this problem?
I'm answering the concise question from your comment:
how to calculate rotation in one coordinate system that answer to rotation in another coordinate system?
You can transform rotations between coordinate systems by applying a suitable transformation matrix. This in turn can be computed form the euler angles, see the Wikipedia section on conversion formulae.
Depending on your application, you might or might not have to take translations into account as well. As I understand your question, you can concentrate on the rotation part of each transformation.
I am working on an application that has similar functionality to MotionBuilder in its viewport interactions. It has three buttons:
Button 1 rotates the viewport around X and Y depending on X/Y mouse drags.
Button 2 translates the viewport around X and Y depending on X/Y mouse drags.
Button 3 "zooms" the viewport by translating along Z.
The code is simple:
glTranslatef(posX,posY,posZ);
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
Now, the problem is that if I translate first, the translation will be correct but the rotation then follows the world axis. I've also tried rotating first:
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
glTranslatef(posX,posY,posZ);
^ the rotation works, but the translation works according to world axis.
My question is, how can I do both so I achieve the translation from code snippet one and the rotation from code snippet 2.
EDIT
I drew this rather crude image to illustrate what I mean by world and local rotations/translations. I need the camera to rotate and translate around its local axis.
http://i45.tinypic.com/2lnu3rs.jpg
Ok, the image makes things a bit clearer.
If you were just talking about an object, then your first code snippet would be fine, but for the camera it's quite different.
Since there's technically no object as a 'camera' in opengl, what you're doing when building a camera is just moving everything by the inverse of how you're moving the camera. I.e. you don't move the camera up by +1 on the Y axis, you just move the world by -1 on the y axis, which achieves the same visual effect of having a camera.
Imagine you have a camera at position (Cx, Cy, Cz), and it has x/y rotation angles (CRx, CRy). If this were just a regular object, and not the camera, you would transform this by:
glTranslate(Cx, Cy, Cz);
glRotate(CRx, 1, 0, 0);
glRotate(CRy, 0, 1, 0);
But because this is the camera, we need to do the inverse of this operation instead (we just want to move the world by (-Cx, -Cy, and -Cz) to emulate the moving of a 'camera'. To invert the matrix, you just have to do the opposite of each individual transform, and do them in reverse order.
glRotate(-CRy, 0, 1, 0);
glRotate(-CRx, 1, 0, 0);
glTranslate(-Cx, -Cy, -Cz);
I think this will give you the kind of camera you're mentioning in your image.
I suggest that you bite the apple and implement a camera class that stores the current state of the camera (position, view direction, up vector, right vector) and manipulate that state according to your control scheme. Then you can set up the projection matrix using gluLookAt(). Then, the order of operations becomes unimportant. Here is an example:
Let camPos be the current position of the camera, camView its view direction, camUp the up vector and camRight the right vector.
To translate the camera by moveDelta, simply add moveDelta to camPos. Rotation is a bit more difficult, but if you understand quaternions you'll be able to understand it quickly.
First you need to create a quaternion for each of your two rotations. I assume that your horizontal rotation is always about the positive Z axis (which points at the "ceiling" if you will). Let hQuat be the quaternion representing the horizontal rotation. I further assume that you want to rotate the camera about its right axis for your vertical rotation (creating a pitch effect). For this, you must apply the horizontal rotation to the camera's current angle. The result is the rotation axis for your vertical rotation hQuat. The total rotation quaternion is then rQuat = hQuat * vQuat. Then you apply rQuat to the camera's view direction, up, and right vectors.
Quat hRot(rotX, 0, 0, 1); // creates a quaternion that rotates by angle rotX about the positive Z axis
Vec3f vAxis = hRot * camRight; // applies hRot to the camera's right vector
Quat vRot(rotY, vAxis); // creates a quaternion that rotates by angle rotY about the rotated camera's right vector
Quat rQuat = hRot * vRot; // creates the total rotation
camUp = rQuat * camUp;
camRight = rQuat * camRight;
camView = rQuat * camView;
Hope this helps you solve your problem.
glRotate always works around the origin. If you do:
glPushMatrix();
glTranslated(x,y,z);
glRotated(theta,1,0,0);
glTranslated(-x,-y,-z);
drawObject();
glPopMatrix();
Then the 'object' is rotate around (x,y,z) instead of the origin, because you moved (x,y,z) to the origin, did the rotation, and then pushed (x,y,z) back where it started.
However, I don't think that's going to be enough to get the effect you're describing. If you always want transformations to be done with respect to the current frame of reference, then you need to keep track of the transformation matrix yourself. This why people use Quaternion based cameras.