OpenGL Programming/GLStart/Tut4
Tutorial 4: Translation, Rotation, and Scaling
editThis tutorial will teach you how to manipulate objects in 3D space. Since we haven’t covered 3D drawing yet, we will manipulate the 2D primitives we created in the previous tutorial.
The Projection and Model Transformations
editIf you remember from the second tutorial, we created a function called Resize() that handles the resizing of the window. I'm not sure if the code within that function was explained well, so lets go over it again. Here is the whole code from the function Resize():
void Resize(int width, int height) { //set viewport glViewport(0,0,(GLsizei)width,(GLsizei)height); glMatrixMode(GL_PROJECTION);//change matrix mode to projection glLoadIdentity();//load identity matrix //set perspective gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,1.0f,1000.0f); glMatrixMode(GL_MODELVIEW);//change matrix mode to modelview glLoadIdentity();//load identity matrix }
The first line, the glViewport() function, determines what amount of the window that OpenGL draws on. We set it here to the whole window.
The interesting thing here to note is the glMatrixMode() function. This function takes as one parameter the matrix we want to edit. I am going to save the discussion of matrices for a while (perhaps for a different tutorial or a simple article). But basically the first call to glMatrixMode() with the parameter GL_PROJECTION affects how the user views the OpenGL scene. So in the line after that I put in glLoadIdentity() to set the Projection matrix to the identity matrix. Then in the next line I used the gluPerspective() function, which allows for a realistic view of a scene, since an object close to the viewer is large and the same object far from the viewer is small. The parameters for gluPerspective() was the angle of the view, the aspect ratio (width to height), the nearest z position the viewer can see and the farthest z position the viewer can see.
After that line, I changed the matrix mode again to GL_MODELVIEW, which affects what is called the Model View matrix. When editing the Model View matrix, you are only affecting the objects in the scene, not the actual camera that is pointed at the 3D scene. So any changes you make to the Model View matrix will affect just the objects. For example, if you are editing the Model View matrix and you use the glRotatef() function (I’ll cover this more later in the tutorial) to do a rotation operation, the result will be the object itself rotating, not the camera. To better remember it just remember that the camera is affected by projection and the objects are affected by model view.
Since the last two lines in the Resize() function is changing the matrix to Model View and then loading the identity matrix, any matrix manipulations such as rotation or translation will affect only the objects.
Translation
editIf you remember from the previous tutorials, in the Render() function we created, one of the first lines was a call to glTranslatef() which was then filled with the parameters to move the object 4 units away from the viewer on the z-axis.
If you notice in the Render() function code, after the glClear() function call, the identity matrix is loaded with glLoadIdentity() and then a translation operation occurs with the glTranslatef() function:
//clear color and depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity();//load identity matrix glTranslatef(0.0f,0.0f,-4.0f);//move forward 4 units
Because of the identity matrix loading every time the Render() function is called, the translation that occurs after that stays the same. Now lets say you were to take out the glLoadIdentity() function in the Render() function. Go ahead and do that, compile and run the program. What happened? Well, if all you saw was the colored shape for like a millisecond and then it started zooming away at a fast pace, then the object (the colored shape) was being translated at a constant rate without stopping. Since we set the translation operation to move the object away from the viewer 4 units, every time the Render() function was called (which could be hundreds of times a second depending on the speed of your computer), the object moved away 4 units. But with the call to glLoadIdentity() at the beginning of the Render() function, the object will only be translated just 4 units away.
So far all we have done is translate along the z-axis. The glTranslatef() function takes as three parameters the x, y, and z units, in that order, to translate the object. Lets do some examples with the other axis.
Translating along the X – axis
editThe first example I will explain will show an animation of a polygon moving back and forth along the x – axis. For this example I am using the “polygon” project files from the third tutorial about primitives.
The way I will do this animation is by first declaring two global variables. The first variable called “xTrans” is of type float and I initially set it to 0.0f. The “xTrans” variable is going to keep track of the amount to translate along the x-axis. The second variable is called “xDir” and is of type integer and is initially set to 1. The “xDir” variable will determine which direction the polygon will go on the x-axis. If the value is 1, then the polygon will move right along the x-axis. If the value is 0, then the polygon will move left along the x-axis:
float xTrans = 0.0f;//keeps track of X translation int xDir = 1;//1-right,0-left
Now going to the Render() function, right after the glTranslatef() function call that moves the object 4 units on the x-axis away from the user, we need first to check which direction that the polygon is moving. For that we create an IF statement that checks the value of the “xDir” variable and sees if it has the value of 1, meaning the polygon is moving to the right. Also within that IF statement, we also need to check to make sure that the polygon doesn’t move offscreen by checking that the “xTrans” variable is less than 2.0f, which is how many units to the right that is visible to the user. If both of these statements are true, then we increment the “xTrans” variable by a value of 0.1f. If the statements aren’t true, that means that the polygon has gone all the way to the right of the window and needs to reverse direction. We do that by setting the “xDir” variable to 0, meaning that the polygon will move to the left on the x-axis:
//checking if polygon can move right along x-axis if(xDir==1 && xTrans<2.0f) { xTrans+=0.1f;//increment xTrans } else//polygon has reached the right edge { xDir=0;//change direction }
Now we do the same thing over again, except we check for a value of 0 for the “xDir” variable and make sure that the “xTrans” variable is greater than –2.0f, which is the left side of the window. If the statements are correct, then we decrement (make less) the value in “xTrans” by –0.1f. If the statements are false, we set “xDir” to 1 so that the polygon starts moving to the right along the x-axis:
//checking if polygon can move left along x-axis if(xDir==0 && xTrans>-2.0f) { xTrans-=0.1f;//decrement xTrans } else//polygon has reached the left edge { xDir=1;//change direction }
After all that we need to actually perform the translation. After both IF statements, create a call to glTranslatef() and put in for the first parameter the “xTrans” variable and leave the last two parameters as 0.0f:
//translate along the x-axis glTranslatef(xTrans,0.0f,0.0f);
Now when you compile and run the program, you should get a polygon that is moving fast to the right and left of the window.
Following is the whole Render() function, followed by a sample output of this example. To see the entire code of this program, open the “xTranslate” project folder in the downloadable files:
void Render() { //clear color and depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity();//load identity matrix glTranslatef(0.0f,0.0f,-4.0f);//move forward 4 units //checking if polygon can move right along x-axis if(xDir==1 && xTrans<2.0f) { xTrans+=0.1f;//increment xTrans } else//polygon has reached the right edge { xDir=0;//change direction } //checking if polygon can move left along x-axis if(xDir==0 && xTrans>-2.0f) { xTrans-=0.1f;//decrement xTrans } else//polygon has reached the left edge { xDir=1;//change direction } //translate along the x-axis glTranslatef(xTrans,0.0f,0.0f); glColor3f(0.0f,0.0f,1.0f); //blue color glBegin(GL_POLYGON);//begin drawing of polygon glVertex3f(-0.5f,0.5f,0.0f);//first vertex glVertex3f(0.5f,0.5f,0.0f);//second vertex glVertex3f(1.0f,0.0f,0.0f);//third vertex glVertex3f(0.5f,-0.5f,0.0f);//fourth vertex glVertex3f(-0.5f,-0.5f,0.0f);//fifth vertex glVertex3f(-1.0f,0.0f,0.0f);//sixth vertex glEnd();//end drawing of polygon }
Translating along the y and z-axis can follow a similar process as shown above. Just make sure you know that when translating along the z-axis, moving along it in the positive direction will cause the object to get closer to you. So moving it along the negative direction will cause the object to get farther away from yo
Rotation
editRotation of an object involves moving an object around an axis. So if you were to rotate an object on the x-axis, then the object will rotate in a circle around the x-axis. This is the same with the other axis:
To achieve these rotations, we use the glRotatef() function which takes as the first parameter the angle at which to rotate the object. The second, third, and fourth parameters are the x, y, and z axis, respectively, on which to rotate the object. For example, to rotate an object clockwise along the x-axis 90 degrees, you put in 90.0f for the first parameter in the glRotatef() function, then put in 1.0f in the second parameter to indicate you want to rotate along the x-axis, and then leave the last two parameters as 0.0f:
glRotatef(90.0f,1.0f,0.0f,0.0f);
Rotating along the Y axis
editFor this example I am about to show you, we are going to take “polygon” example from the third tutorial and rotate the polygon along the y-axis. So open up the “polygon” example and try to follow along.
Just like the transformation example, we first need to declare a global variable called “yRot” which is of type float and initially set to 0.0f. This variable will hold the rotation angle:
float yRot = 0.0f;//variable to hold y rotation angle
Then in the Render() function, right after the standard translation call, we call the glRotatef() function with the first parameter being the “yRot” variable. The parameter is 0.0f indicating we are not rotating along the x-axis. The third parameter being 1.0f indicating we are rotating along the y-axis. Then the last parameter being 0.0f to indicate we do not want to rotate along the z-axis:
//rotate along the y-axis glRotatef(yRot,0.0f,1.0f,0.0f);
At the end of the Render() function, we increment the “yRot” variable by 0.1f so that the polygon rotates at a constant rate:
yRot+=0.1f;//increment the yRot variable
Now compile and run the program and you should see your polygon rotating along the y-axis.
Following is the code for the whole Render() function, followed by a sample output. To see the whole code, please refer to the “yRotation” project files included in the downloadable files:
void Render() { //clear color and depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity();//load identity matrix glTranslatef(0.0f,0.0f,-4.0f);//move forward 4 units //rotate along the y-axis glRotatef(yRot,0.0f,1.0f,0.0f); glColor3f(0.0f,0.0f,1.0f); //blue color glBegin(GL_POLYGON);//begin drawing of polygon glVertex3f(-0.5f,0.5f,0.0f);//first vertex glVertex3f(0.5f,0.5f,0.0f);//second vertex glVertex3f(1.0f,0.0f,0.0f);//third vertex glVertex3f(0.5f,-0.5f,0.0f);//fourth vertex glVertex3f(-0.5f,-0.5f,0.0f);//fifth vertex glVertex3f(-1.0f,0.0f,0.0f);//sixth vertex glEnd();//end drawing of polygon yRot+=0.1f;//increment the yRot variable }
Scaling
editScaling allows you to change the size of an object by however much you want it. To perform scaling you need to use the OpenGL function glScalef() which takes as parameters the x, y, and z scale of the object. When putting in the parameters, you need to remember that the parameters in glScalef() are multiplied by the original size of the object. For example, if you have a square drawn that is 1 unit wide and 1 unit high, to make the object two times the original size you would use 2.0f in the x and y parameters of the glScalef() function to indicate that you want to double the size of the square:
In the diagram above I kept the z parameter to 1.0f to indicate not to change the depth of the square. Even though there really is no depth in this 2D square, you should still get into the habit of doing this, especially when we get into 3D drawing.
Scaling Example
editFor this example, I am going to use the “triangle” example from the third tutorial. We will modify the triangle program by showing an animation of the two triangles on the screen enlarging, almost filling up the screen, and then shrinking to the point you can’t see the triangles anymore. We will have this animation loop on its own.
The first thing I did to modify this program is declare three global variables. The first variable called “xScale” which is of type float and initially set to 0.0f will hold the scaling factor on the x-axis. The second variable called “yScale”, again of type float and set to 0.0f, holds the scaling factor in the y-axis. The third variable called “enlarge” is of type bool and initially set to “true”. This variable controls whether the triangles should enlarge (grow bigger) or shrink (get smaller). If this variable is set to true, the triangles enlarge, and false if they shrink:
float xScale = 0.01f;//x scale factor float yScale = 0.01f;//y scale factor bool enlarge = true;//true - enlarge, false - shrink
In the Render() function, right after the glColor3f() function call, we need to check if the triangles can be enlarged. We first create an IF statement that checks if the Boolean variable “enlarge” is true and makes sure that the “xScale” variable is less than 2.0 so that the triangles don’t enlarge more than double their size. If these statements are true, we then increment the “xScale” and “yScale” variables by 0.01f to allow for enlargement of the triangles. If the statements aren’t true, then we set the variable “enlarge” to false meaning we should start shrinking the triangles:
//check if triangles can be enlarged if(enlarge == true && xScale < 2.0f) { xScale += 0.01f;//increment x scale yScale += 0.01f;//increment y scale } else { enlarge = false;//start shrinking }
After this we create another IF statement, but this time we check if the triangles can be shrunk. We check to make sure that the variable “enlarge” is set to false and that the “xScale” variable is greater than 0.0 so that the triangles don’t shrink on a scale of a negative scale. If both of these statements are true, then we decrement the “xScale” and “yScale” variables by 0.01f to allow shrinkage of the triangles. If the statements are not true, then we set the variable “enlarge” to true, meaning the triangles need to start enlarging:
//check if triangles can be shrunk if(enlarge == false && xScale > 0.0f) { xScale -= 0.01f;//decrement x scale yScale -= 0.01f;//decrement y scale } else { enlarge = true;//start enlarging }
The last thing we need to do after all this is do the actual scaling. We make a call to the glScalef() function with the first parameter being the “xScale” variable and the “yScale” variable being put in the second parameter. We keep the third parameter as 1.0f to indicate we are not scaling on the z-axis:
//scale triangles at a constant rate glScalef(xScale,yScale,1.0f);
Following is the whole Render() function code followed by a sample output. If you want to view the whole code, please refer to the “xyScale” project folder on the downloadable files:
void Render() { //clear color and depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity();//load identity matrix glTranslatef(0.0f,0.0f,-4.0f);//move forward 4 units glColor3f(0.0f,0.0f,1.0f); //blue color //check if triangles can be enlarged if(enlarge == true && xScale < 2.0f) { xScale += 0.01f;//increment x scale yScale += 0.01f;//increment y scale } else { enlarge = false;//start shrinking } //check if triangles can be shrunk if(enlarge == false && xScale > 0.0f) { xScale -= 0.01f;//decrement x scale yScale -= 0.01f;//decrement y scale } else { enlarge = true;//start enlarging } //scale triangles at a constant rate glScalef(xScale,yScale,1.0f); glBegin(GL_TRIANGLES);//start drawing triangles glVertex3f(-1.0f,-0.25f,0.0f);//triangle one first vertex glVertex3f(-0.5f,-0.25f,0.0f);//triangle one second vertex glVertex3f(-0.75f,0.25f,0.0f);//triangle one third vertex //drawing a new triangle glVertex3f(0.5f,-0.25f,0.0f);//triangle two first vertex glVertex3f(1.0f,-0.25f,0.0f);//triangle two second vertex glVertex3f(0.75f,0.25f,0.0f);//triangle two third vertex glEnd();//end drawing of triangles }
Custom Transformation and Rotation function
editThe last thing I would like to cover before we finish this tutorial is how to create a custom function to handle rotation and translation. I won’t be putting scaling in this function, but feel free to do that yourself if you feel like it.
The function I created is called “Transform” and has the return type of void. There are six parameters that are passed in which are all of type float. The first three parameters are the x,y, and z values of where to translate the object. They are called “xTrans”, “yTrans”, and “zTrans”. The last three parameters handle the x,y, and z values for rotation. They are called “xRot”, “yRot”, and “zRot”:
//custom function for transformations void Transform(float xTrans, float yTrans, float zTrans, float xRot, float yRot, float zRot) {
So within the function, we will first take care of the translation. All you have to do is make one call to the glTranslatef() function, filling it in with the first three parameters of the Transform() function:
//translate object glTranslatef(xTrans, yTrans, zTrans);
Now we will take care of the rotation. For this we make three separate calls to glRotatef() filling the first parameter of each with the parameters passed to the Translate() function. We then fill the rest of the parameters of glRotatef() with a value of either 1.0f or 0.0f, depending on which variable we are rotating on:
//rotate along x-axis glRotatef(xRot,1.0f,0.0f,0.0f); //rotate along y-axis glRotatef(yRot,0.0f,1.0f,0.0f); //rotate along z-axis glRotatef(zRot,0.0f,0.0f,1.0f);
And that is all there is to the function. Here is the whole function listing. I also created an example program that uses this custom function. Look for it on the downloadable project files called “customTrans”. I have also listed here the call to the Transform() function I did in this example program and a sample output:
//custom function for transformations void Transform(float xTrans, float yTrans, float zTrans, float xRot, float yRot, float zRot) { //translate object glTranslatef(xTrans, yTrans, zTrans); //rotate along x-axis glRotatef(xRot,1.0f,0.0f,0.0f); //rotate along y-axis glRotatef(yRot,0.0f,1.0f,0.0f); //rotate along z-axis glRotatef(zRot,0.0f,0.0f,1.0f); }
//translate to upper - left corner and //rotate 45 degrees along the x,y, and z axis Transform(-1.0f,1.0f,0.0f,45.0f,45.0f,45.0f);