7. OpenGL Rotation and Translation (Version 2.0)
Introduction
Placing an object into your scene, in a 3D OpenGL environment can often be quite the achievement when you have everything in place. But just placing an object in your scene can be a little boring, so to get some character movement, or object interaction, it is necessary to both translate and rotate objects, which OpenGL makes quite simple for us to do.
Behind the extremely simple OpenGL calls that we are going to use, lays some pretty intense mathematics which I am not going to go into during these tutorials. In short, it all revolves around matrices and there is a matrix known as a transformation matrix. By multiplying together different transformation matrices, you get your final transformation. Now don’t get transformation confused with translation though, a translation moves the position of an object while a transformation is a combination of translate and rotate.
Rotation
What is rotation? May be something you are asking yourself if you are totally new to computer graphics, or even new to maths. It’s a little hard to describe without a picture or even an animation, but picture an object sitting still, not moving at all. And then it turns on the spot. This turning on the spot is called rotation, and is measured by the number of degrees (or radians) you have turned from your starting position along any three axis in a 3D scene. With the three main axis being the X, Y and Z axis.
Translation
Now that you know what rotation is, comes the question as to what exactly is translation? Translation is movement along any of the three axis in a 3D scene, for example, moving something the left is a translation on the X axis if you are looking straight on. In OpenGL, the length of translation is called units, and a unit has no definitive length, which can be confusing, but bear with me. On OpenGL.org, the question as to what the length of a unit is has been asked, and the official response is “Whatever you want it to be”. Depending on your implementation of your scene, a unit can be a centimetre, metre or a kilometre (or if you work in Imperial measurements, inches, yards and miles), it’s entirely up to you. Unfortunately this does have its downside, if you ever find yourself having to merge several OpenGL applications, they may be using different measurement systems, and you may find things are the wrong size. Trust me, it can be a pain and there are times when I wish there was an unofficial standard. Personally I tend to use 1 unit is equal to 1 metre. In OpenGL, movements either up, left or towards the camera are negative values in respect to the origin. While movements down, right or back away from the camera are all positive in respect to the origin.
Code
Compared to Direct3D, it is actually a lot simpler to do rotation and translation in OpenGL; you just have to keep your head around your current states of your transformation matrices at all times. Let’s start off with the code for a cube from our last tutorial.
Keep in mind that the animation in this tutorial may not be the smoothest, and I will address as to why and how to fix it in the double buffering tutorial.
Now let’s get started. I’m going to aim for a cube that bounces up and down in our window, while rotating around on the spot. To do this, we are going to need three variables. The first, which is a Boolean called movingUp, is going to determine if our cube is moving up, or moving down. Next up we need something to store the location of our object along the y axis (up and down), I am going to create a float called yLocation, and initialize it to 0.0f. It is important that you initialize it to 0.0f as anything drawn is originally drawn at the location (0,0,0) unless you have called glTranslate before placing it. Finally I am going to use another float, this one called yRotationAngle to store the angle in degrees of rotation for our cube along the y axis. These variables are just examples for this tutorial, and so far have nothing to do with OpenGL itself.
bool movingUp = false; // Whether or not we are moving up or down float yLocation = 0.0f; // Keep track of our position on the y axis. float yRotationAngle = 0.0f; // The angle of rotation for our object
Next, jump down to your “main” method where we are going to need to add a new line which allows variables inside of the display method to be updated. Because we are using GLUT, GLUT likes us to place all of our variables to be updated into an “idle” method, which handles nothing but calculations, but this isn’t as efficient as it is led out to be. Whilst it is not written in bold anywhere, it is possible to use your “display” method as GLUT’s “idle” method, and if you read the GLUT API in detail, you can see that this is possible as rendering in the “idle” method is possible. But what does this mean? Well it means that not only are all our variables updated every time our “display” method is called as if they were in an “idle” method, but we can also get the maximum frame rate possible by utilizing the “idle” methods resources. It may not give too much of an improvement, but I like to push out every last frame possible on my system.
Anyway, enough talking, lets add this one line of code:
int main (int argc, char **argv) { … glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering glutIdleFunc(display); // Tell GLUT to use the method "display" as our idle method as well glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping … }
Good, now let’s get to updating our variables, so that we can then use them. We will do this at the end of our “display” method, just as a preference of mine, some people do all their processing at the start of their “display” method so that they can get their head around “these updates apply to this frame”, I look at it this way: “I have just done this, next I’m going to do this”. This code is quite basic, all we are doing is checking if the cube has gone below 3 units down or move above 3 units up, and toggling our Boolean variable to say if we need to move up or down, and then depending on this, we are either incrementing or decrementing our yLocation variable. I am also constantly increment my rotation angle, and when it gets above 360 degrees, or a full rotation, subtracting 360 degrees to reset it.
Just before the end of our “display” method, and after our glFlush method, place the lines:
if (movingUp) // If we are moving up yLocation -= 0.005f; // Move up along our yLocation else // Otherwise yLocation += 0.005f; // Move down along our yLocation if (yLocation < -3.0f) // If we have gone up too far movingUp = false; // Reverse our direction so we are moving down else if (yLocation > 3.0f) // Else if we have gone down too far movingUp = true; // Reverse our direction so we are moving up yRotationAngle += 0.005f; // Increment our rotation value if (yRotationAngle > 360.0f) // If we have rotated beyond 360 degrees (a full rotation) yRotationAngle -= 360.0f; // Subtract 360 degrees off of our rotation
Now we are ready to start rotating and translating our object. This is extremely complicated, so give me some time to build it up.
Go back into your display method, and look for the lines glTranslatef(0.0f, 0.0f, -5.0f); and glutWireCube(2.0f); and make some space.
void display (void) { … glTranslatef(0.0f, 0.0f, -5.0f); // Push eveything 5 units back into the scene, otherwise we won't see the primitive // Space for translation and rotation code glutWireCube(2.0f); // Render the primitive … }
Great, make sure you have plenty of room, because the next two lines… Wait, the next *two* lines? Yes, I’m not joking, it only takes two lines, one the translation and the other for our rotation.
The only thing you need to make sure of here is that you do these calls in the correct order. If you translate and then rotation, the object will move into position and then rotate on the spot. If you rotate first, and then translate, your object will translate to its new position, but will then rotate in a giant circle around its starting position. To get the movement we described, add the following lines into the space you created above:
glTranslatef(0.0f, yLocation, 0.0f); // Translate our object along the y axis glRotatef(yRotationAngle, 0.0f, 1.0f, 0.0f); // Rotate our object around the y axis
In OpenGL, this is all it takes to making your object rotate on the spot, and move, in this case up and down. It’s quite simple; the only tricky thing is keeping track of all your objects when you start working with fairly large complex scenes. If you try adding several objects in at the moment and moving them separately, you may have some trouble as OpenGL works on states and each translation and rotation is added to the previous. For getting around this, look at my tutorial on popping and pushing matrices J
If you have any questions, as usual you can always contact me at swiftless@gmail.com
Tutorial Code
#include <GL/glew.h> // Include the GLEW header file #include <GL/glut.h> // Include the GLUT header file bool* keyStates = new bool[256]; // Create an array of boolean values of length 256 (0-255) bool movingUp = false; // Whether or not we are moving up or down float yLocation = 0.0f; // Keep track of our position on the y axis. float yRotationAngle = 0.0f; // The angle of rotation for our object void keyOperations (void) { if (keyStates[GLUT_KEY_LEFT]) { // If the left arrow key has been pressed // Perform left arrow key operations } } void display (void) { keyOperations(); glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // Clear the background of our window to red glClear(GL_COLOR_BUFFER_BIT); //Clear the colour buffer (more buffers later on) glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations glTranslatef(0.0f, 0.0f, -5.0f); // Push eveything 5 units back into the scene, otherwise we won't see the primitive glTranslatef(0.0f, yLocation, 0.0f); // Translate our object along the y axis glRotatef(yRotationAngle, 0.0f, 1.0f, 0.0f); // Rotate our object around the y axis glutWireCube(2.0f); // Render the primitive glFlush(); // Flush the OpenGL buffers to the window if (movingUp) // If we are moving up yLocation -= 0.005f; // Move up along our yLocation else // Otherwise yLocation += 0.005f; // Move down along our yLocation if (yLocation < -3.0f) // If we have gone up too far movingUp = false; // Reverse our direction so we are moving down else if (yLocation > 3.0f) // Else if we have gone down too far movingUp = true; // Reverse our direction so we are moving up yRotationAngle += 0.005f; // Increment our rotation value if (yRotationAngle > 360.0f) // If we have rotated beyond 360 degrees (a full rotation) yRotationAngle -= 360.0f; // Subtract 360 degrees off of our rotation } void reshape (int width, int height) { glViewport(0, 0, (GLsizei)width, (GLsizei)height); // Set our viewport to the size of our window glMatrixMode(GL_PROJECTION); // Switch to the projection matrix so that we can manipulate how our scene is viewed glLoadIdentity(); // Reset the projection matrix to the identity matrix so that we don't get any artifacts (cleaning up) gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0); // Set the Field of view angle (in degrees), the aspect ratio of our window, and the new and far planes glMatrixMode(GL_MODELVIEW); // Switch back to the model view matrix, so that we can start drawing shapes correctly } void keyPressed (unsigned char key, int x, int y) { keyStates[key] = true; // Set the state of the current key to pressed } void keyUp (unsigned char key, int x, int y) { keyStates[key] = false; // Set the state of the current key to not pressed } int main (int argc, char **argv) { glutInit(&argc, argv); // Initialize GLUT glutInitDisplayMode (GLUT_SINGLE); // Set up a basic display buffer (only single buffered for now) glutInitWindowSize (500, 500); // Set the width and height of the window glutInitWindowPosition (100, 100); // Set the position of the window glutCreateWindow ("Your first OpenGL Window"); // Set the title for the window glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering glutIdleFunc(display); // Tell GLUT to use the method "display" as our idle method as well glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events glutMainLoop(); // Enter GLUT's main loop }
Awesome website man
Quick question, how do you make the translation in a way it doesn’t leave a trace behind it of where the object as passed? trying to do a space invader ship but I can’t delete the trace and it just ends up as a block of color 😡
When using freeGLUT and GLEW I need to add glutPostRedisplay(); somewhere to make the mainloop actually loop and not freeze until there is a mouse click. Very strange but I had the same problem before when I first did this tutorial last year.
Ahha, I figured it by myself, cuz the yRotationAngle I set was 0.005f as what ur code shows, but it seems too slow to be recognized. So i changed it to 1.0f, and it starts to rotate faster. But seems all other ppl are doing good in 0.005f. So is this because of my computer is too slow or something? I bought it 2 years ago, don’t think it’s older than ur tutorial 🙂 Anyway, everything runs well so far.
Great tutorial!
hi, I just have exactly the same code, but the shape doens’t do any rotation and translate. It seems the frame is automatically updating forever, so the shape is flashing( along with the display() ‘s updating ) . Can u explain this for me please?
how to change the translation & rotation?
so i can translation anywhere and rotatin any axis
example like the earth rotates around the sun
thanks before
How set the speed of translation?
because the translation for the example above is too fast
thanks b4
hi!!
I’m beginner in Open Gl and your program helped a lot…
Can you pls explain how did u figure out the limit of y axis.. i.e why it has to be between -3 to 3…. How did u get this 3??
Thank you..
Hi Rahul,
While it is possible to work it out mathematically, I just did it by hand.
To work it out, you need the field of view, the near and far clipping planes and the z axis of the object. There is plenty of information online about perspective projections, especially in some ray tracer tutorials.
Cheers,
Swiftless
Hi.
Can you suggest a (preferably freeware) software to save opengl screen as video?Do these kind of softwares have a general name?
Hmm, why do all the tutorials after this have differently formatted code compared to this, I was following the tuts, but 8 and up are written different, not updated?
Hey Solus,
I originally wrote most of these tutorials about 4 to 5 years ago and have been going through and rewriting many of them. The different tutorials after tutorial 8 are all the old ones which I am yet to update.
Cheers,
Swiftless
interesting…………..
Great tutorials! I’m learning a bit of openGL and C++ at the same time :).
A quick question: I created a simple cube (e.g. at pos [x,y,z]). I then rotate the cube let’s say along the z-axis by D degrees. Is there a way to get the new position of the cube [x2,y2,z2]?
Hi Alex,
That’s great to hear.
Unfortunately there is no quick way to read back the new position of the cube straight out. If you could imagine, you would need to be able to identify every shape and their position, which would be updated every time the modelview matrix is modified prior to it being rendered.
You can however read back the modelview matrix after you apply your transformation, and this will give you current state of the modelview matrix including the translations and rotations required to get to the current position.
Note however you will only get back the final values of all of your translations and rotations combined, if you want a list of transformations along the way, you will need to read back the modelview matrix every time you make a change such as a translation or rotation.
On the other hand, if you are using picking in OpenGL, you can convert your mouse coordinates to 3D coordinates in the screen.
Cheers,
Swiftless
Thanks for the quick reply! Could you just point me to the right code for retrieving the modelview matrix and then the code for applying this to my point [x,y,z] to get [x2,y2,z2]?
I had a brief look at the API but I couldn’t seem to find the right functions. Is there another library for doing the maths (i.e. for applying a transformation matrix to a point)?
i think ‘movingUp’ is used the wrong way. the positive y-axis points upwards, so you should increase ‘yLocation’ if ‘movingUp’ is true. you can see this because when the program starts the cube moves up though ‘movingUp’ is initialized with ‘false’.
this looks very easy and simple. I tried checking my CPU after running this.. its really using all the CPU cycles but it sure runs smooth. can you please eloborate the arguments of this function: glRotatef(yRotationAngle, 0.0f, 1.0f, 0.0f); what do these values actually stand for?
Hi Imrn,
In any glRotate method, here are the parameters: glRotate*(angle, x_axis, y_axis, z_axis).
The angle is the angle of rotation you wish to rotate by.
The last three values decide which axis we are going to rotate around. A value of 0 means we are not going to rotate around this axis, and a value of 1 means we are going to.
Cheers,
Swiftless
This program runs smoothly, but greatly overworks the processor. I recommend replacing glutIdleFunc(display); with glutTimerFunc(delay, timer, 0); where delay is an unsigned int and timer is a void function:
//millisecond delay between frames
unsigned int delay= 16;
void timer(int value){
//update the display
glutPostRedisplay();
//the timer function replaces idle AND is placed here
glutTimerFunc(delay, timer, 0);
}
This does alter the frame rate to a known value of 62.5 fps based on this formula: fps = 1000/(delay), where the delay is in milliseconds. This means rates need to be adjusted, so for the smoothest translation I use:
yLocation -= 0.005f * (float)delay * 3.f;
yLocation += 0.005f * (float)delay * 3.f;
yRotationAngle += 0.005f * (float)delay * 3.f;
I know this seems ridiculous, but my processor ran at 40-60% at first, now it’s around 10-15%. This is just a suggestion.
P.S. I recommend keeping delay below 40, due to a lag appearance. It is said movies run at a minimum of 24fps. I tried a delay of 1ms and even that helps the processor greatly. Also, I posted this on a GLSL tutorial earlier, but I imagine it’s more relevant here.
Hey Mitch,
Usually in computer graphics we aim to get the maximum framerate possible, which is why I use both the display and the idle function to do my rendering.
It’s also not recommended to use timers, especially the glutTimerFunc because it is not guaranteed to be called at the specified interval, it could be delayed even longer, not to mention if you have a system which renders even slower than your interval, some people have had problems with glutTimerFunc chewing up their CPU even more.
It is true that movies and television transmissions are at a rate of generally 24 frames per second, but GTA San Andreas tried capping their frame rate to 25, and I could tell the difference. I was glad when GTA IV went back to unlimited frames per second. The more frames per second, the smoother your playback.
When you get further into computer graphics and possibly game development, you will find yourself wanting to consume as much as everything as possible at a time (CPU, GPU, RAM), so that your users have the smoothest experience possible and you aren’t stuck waiting on anything.
It was a good idea you had, unfortunately it’s not really practical, which is why you won’t see many people using glutTimerFunc.
Cheers,
Swiftless
Around 25 fps is about how fast human brain processes images. However it is not enough in case of computer generated images, since we also require a motion blur in every frame.
Films captured with cameras are blurred, thus leading to smooth playback even at eye’s refresh rate.
Because of all that you noticed a difference in GTA.
However it’s also completely pointless to render above screen refresh rate – usually 60Hz – which is also enough to provide brain with enough frames per single “eye refresh” to create blur.
Hi MatMar,
While you have a valid point that the human brain won’t notice a difference with framrates above approx. 24 fps, there is a subconscious interaction with the brain. I believe it was the movie Fight Club where a 25th frame was added every now and then with a picture of Bambi and people would suddenly burst into tears.
Films have actual blur recorded from fast movement of a camera, it’s a natural effect just as chromatic abberation or depth of field. Which requires faking in a virtual scene.
However stating it is completely pointless to render above screen refresh rate is extremely misinformed. Maybe not from a rendering perspective unless you want extremely high quality motion blur that spans more than 60 frames, plus many rendering techniques require multiple passes. But definitely from an input and algorithmic point of view. Many algorithms can be optimized by spreading them over several frames, a clear example of this is sorting, where you sort a set of data over time and get a more accurate result each time. Sorting is a major issue in computer graphics, how else do you render translucent objects correctly? Or perform correct culling? The faster you do this, the more accurate the result each visible frame.
Then you have the input point of view, where compared to a 30fps scene, a games running at 60fps has twice the accuracy with a mouse. This point alone is a major factor in competitive gaming. Therefore if you are running at 120fps and your mouse input is linked to your rendering, you’re going to get double the accuracy and responsiveness again. You can offload input to another thread or even another core, but you still have to sync the two and you will get higher responsiveness in your scene.
These are just two examples. Try to add in physics calculations, audio, artificial intelligence or anything else you need to put in your application, when you still need it to run at, at least 60fps.
Cheers,
Swiftless
Hey BrainStorm,
Yes, you can simplify any if statement from:
if (expression) {
}
to:
expression ? outcome : else_outcome;
However, there are a few people who come here knowing little if not nothing about C++, and I don’t want to confuse them further.
Cheers,
Swiftless
correction:
movingUp = (!yLocation3.0f);
Code on ‘display’ method could be simplified to:
yLocation += (movingUp?-.005f:.005f);
movingUp = (!yLocation3.0f);
yRotationAngle+=(yRotationAngle>360.0f?-360.0f:.005f);
I’ve created a lil’ project with 2 .cpp files and its respective headers with the content of these tuts so far, maybe you can use it.
http://pastebin.com/DX4XKbuF
Feel free of course to change whatever you want 🙂
4 thumbs up 😉
wow……….this is nice program………………keep it up