OpenGL ES Tutorial for AndroidPart IIITransformations
January 1st, 2010 by PerErik BergmanAndroid,
Embedded
Last tutorial was about building your polygons. this tutorial is all about transformations, how to move the polygons around. I will continue this tutorial from where the previous ended so you can use that source code or make a copy of it.
The previous tutorial details how to create a polygon. This tutorial describes how to move and transform a polygon. The previous tutorials in this tutorial are based on this principle)
I am not going to bore you with a lot of mathematics but I believe it is important to know that when OpenGL render a mesh it multiplies all vertices with a matrix. all the transformations you do are about manipulating the vertices in different ways by modifying
This matrix. you can think of the matrix as a paper and that you never move the pen before you start to draw. you always draw in the center. but by doing a translation on the matrix you are moving the paper and also the center. A rotation is like rotating
The paper around the center. and a scale is a bit harder to visualize with the paper view but it is like changing the unit size regarding to how you translate your meshes. usually you talk about transformations according to the mesh not the world, but it is
Still important to know about.
Here, I'm not going to talk about some algorithms to get tired of you, but I believe it is important to know the matrix multiplication principle when OpenGL shows the mesh. All moving transformations are obtained by multiplication of matrices in different ways. You can think that the matrix is a piece of paper. Before you start painting, you did not move the pen, and the pen was positioned in the center of the paper. Matrix transformation is equivalent to moving paper (and center point) rotation, which can be seen as paper rotating around the center point. Scaling is more difficult to understand. It can be seen as changing the grid size of the grid paper.
Coordinate System
OpenGL uses a so called righthanded coordinate system. A system is called righthanded if you look from the positive end towards the origin of the axis the counterclockwise rotation is considered to be a positive rotation. OpenGL uses a coordinate system called the Right Hand coordinate system. The end of the coordinate axis is in the positive direction relative to the origin, and the clockwise direction is regarded as the positive rotation. When you have started up your view and haven't applied any transformations the axis are aligned like this: The xaxis goes from left to right, the yaxis comes from the bottom and goes up and the zaxis is moving from the back of the screen towards the front Of the screen. When the OpenGL view is started and no movement transformation is applied, the orientation of each axis is: the X axis is left to right, the Y axis is down to top, and the Z axis is from back to front. As shown in the right figure 

Translate
Public abstract void
GlTranslatef (float x, float y, float z) // OpenGL docs.
A translations added to the matrix makes the mesh appear as it has been moved. translations are made along the axis and with no rotation added the axis are in there default state. translation affects all the vertices in a polygon the same amount over
Same axis. Translations are simply additions and subtractions to a current value. The image to the right shows a translation in 2 dimensions
Moving is the addition of the matrix. It looks like the grid is moving, and moving is added in the direction of the three axes in the current state. Moving Causes all vertices on the polygon to add an equal offset on the coaxial side. Because the moving direction has positive and negative values, you can also consider the moving operation to add or subtract the current value. The right figure shows a 2D movement.
The start point is {x:2, y: 1} we like to go to {x: 1, y: 3} so we add {x: 3, y: 2 }.
A simple addition: {x:2, y: 1} + {x: 3, y: 2 }={ x:2 + 3, y: 1 + 2 }={ x: 1, y: 3 }.
In 3 dimensions we do the same, if we are located at position: {x: 1, y: 1, z: 0} and we like to move 3 units into the screen we add {x: 0, y: 0, z:3} and end up at: {x: 1, y: 1, z:3 }.
In the last tutorial we moved the square 4 units into the screen just to be able to see the square. what we did was that we added {x: 0, y: 0, z:4} to the current position. this is the code we used for the translation:
In the previous tutorial, point the square to four units inside the screen, which is equivalent to moving the current position {x: 0, y: 0, z:4.
// Translates 4 units into the screen.
Gl. gltranslatef (0, 0,4); OpenGL docs.
If you do several translations after each other the order of the movement is along the X, Y and Z axis, in that order. on translate the order isn't so important but when we do a rotation it's really important.
When multiple moves are performed, the final result is the same regardless of the order of the moves. The Movement is irrelevant to the order. But the rotation is related.
It can be quite tricky to remember how the axis is aligned. fortunate there is a good trick to remember the direction of the axis. hold your left hand like the photo below. the point on each finger represents the positive ction on one axis. your thumb
Is Yaxis, index finger is Xaxis and your middle finger wowould represent the zaxis. when I first started with 3D programming I actually wrote the letters, x, y and z on my fingers
It is very tricky to remember the alignment of the coordinate axes (I don't think this is a difficult task, including the principle of matrix addition and multiplication, which has been fully learned in high school .) Here, the author provides a fast way to remember, as shown in, extending your left hand, palm toward the body, forefinger direction for the X axis, thumb direction for the Y axis, middle finger direction for the Z axis, when I first started learning 3D programming, I often wrote X, Y, and Z on my fingers.
Rotate
Public abstract void
Glrotatef (float angle, float X, float y, float Z) // OpenGL docs.
Rotating is what it sounds like. you add a rotation to the matrix making it appears like the mesh are rotated. with no translation before the rotation is around the origo. the x, y and z values defines the vector to rotate around. the angle value is Number of degrees to rotate. Rotation can be seen as the rotation of the Grid. If it is not moved, it is considered to be rotated by the origin. The X, Y, and Z values define the center of rotation, and the Rotation Unit is the angle (not the radian ). 

If you remember these three things you will manage rotation quite easy.
1. The rotation value are in degrees.
Most frameworks and math functions on computers use radians but OpenGL use degrees.
The rotation values of the frameworks and mathematical functions on many computers use radians, but OpenGL is different.
2. When doing several rotations the order are important.
If you like to restore a rotation you negate the angle or all the axis like this:
Glrotatef (angle, x, y, z) is restored with glrotatef (angle,X,y,z) or glrotatef (angle, x, y, z ).
If you want to restore it, You can convert it to the same negative corner or rotate it based on the opposite values of each axis. For example, if you want to restore glrotatef (angle, x, y, z), you can use glrotatef (angle, x, y, z) or glrotatef (angle,X, y,Z)
But if you do several rotations after each other like this:
For more information, see the following figure (the Green Arrow is the X axis direction, the Blue Arrow is the Y axis direction, and the Red Arrow is the Z axis direction)
Gl. glrotatef (90f, 1.0f, 0.0f, 0.0f); // OpenGL
Docs.
Gl. glrotatef (90f, 0.0f, 1.0f, 0.0f); // OpenGL
Docs.
Gl. glrotatef (90f, 0.0f, 0.0f, 1.0f); // OpenGL
Docs.
And want to restore the mesh to it's original position you can't just negate the angle like this:
Gl. glRotatef (90f,1.0f, 0.0f, 0.0f); // OpenGL
Docs.
Gl. glRotatef (90f, 0.0f,1.0f, 0.0f); // OpenGL
Docs.
Gl. glRotatef (90f, 0.0f, 0.0f,1.0f); // OpenGL
Docs.
You have to revert the order of the rotations as well like this:
Gl. glRotatef (90f, 0.0f, 0.0f,1.0f); // OpenGL
Docs.
Gl. glRotatef (90f, 0.0f,1.0f, 0.0f); // OpenGL
Docs.
Gl. glRotatef (90f,1.0f, 0.0f, 0.0f); // OpenGL
Docs.
The order of several rotations is important.
The order of rotation is very important !!
3. If you look from the positive end towards the origin of the axis the positive rotation is counterclockwise.
If you take the end point as the positive direction, the forward rotation is counterclockwise relative to the origin point.
If you take a penpencil in your hand, let the point be in the same direction as your thumb, as in the picture below, then aligns the penpencil with the xaxis. let the penpencil's point be aligned with the positive direction of the axis. your other fingers will now
Point in the positive direction of the rotation over that axis.
Translate & Rotate
Since both rotation and translations are made within each mesh own coordinate system it is important to remember that order you do the translation and rotation are very important.
When moving and rotating at the same time, remember the order of moving and rotating, which is important and related to the final result.
If you do a translation on the mesh first and then rotate it, the translation is made on the current state of the mesh coordinate system and then rotated at the new location.
For example, first translate and then rotate
If you first rotate and the move the mesh it will be moved accordingly to its own rotated coordinate system.
Result of first rotation and then Translation
Scale
Public abstract void
Glscalef (float X, float y, float Z )//
OpenGL docs.
Scaling is just as it sounds and it is possible to scale over each axis separately. scaling is the same as multiplying all vertexes with the same scalar. in the image below we scale with: Gl. glscalef (2f, 2f, 2f ). that means that we multiply all vertixes
With 2.
Zooming can be considered as a separate zooming in the direction of each axis. The result of scaling 2 is displayed.
Translate & Scale
The order of scaling and translating does matter. If you translate before scaling the transformation is intact. Like this example, first a translation of 2 units and then scale it by 0.5.
The order of moving and scaling is also important. The following figure shows the result of performing translation and then scaling.
Gl. gltranslatef (2, 0, 0); // OpenGL docs.
Gl. glscalef (0.5f, 0.5f, 0.5f); // OpenGL docs.
But if you scale before the translation you get a different result. since you scale the mesh coordinate system then do the translation you will not move the mesh the same amount as you wowould before the scaling. so if you first scale with 0.5 and then do a translation
Of 2 units the result will appear as a translation of 1 unit.
However, if you scale and then pan, you will get a different result. Because of scaling, the unit value of the grid is changed. If translation is performed, the balance is different. For example, the previous code first bloomed 0.5, then translated 2, and finally translated 1, as shown in
Gl. glScalef (0.5f, 0.5f, 0.5f); // OpenGL docs.
Gl. glTranslatef (2, 0, 0); // OpenGL docs.
Load Identity, push and pop matrix
When you translate, rotate or scaling you are not applying the transformation from the same preconditions, you are applying them to the previous transition. You need to be able to reset the position.
When performing translation, rotation, and scaling, if you do not want to apply preprocessing to restore the status before the transformation, you can reapply the current matrix to achieve the same effect.
GlLoadIdentity
Public abstract void
GlLoadIdentity ()//
OpenGL docs.
GlLoadIdentity replaces the current matrix with the identity matrix. It is the same as calling glLoadMatrix with the identity matrix:
GlLoadIdentity is used to replace the current matrix with the unit matrix, which is equivalent to the following matrix:
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
There are situations where you don't want to reset the model matrix, you rather want to go back to how it was just before your latest transformation.
If you don't want to enclose the model matrix, you just want to simply return to the matrix just before the transformation. You can use the following functions:
GlPushMatrix
Public abstract void
GlPushMatrix ()//
OpenGL docs.
GlPushMatrix makes a copy of the current matrix and put it on the stack. This means that when you do any kind of translations after glPushMatrix you are doing them on a copy.
The function is to generate a copy of the current matrix and push it into the stack. This means that any operation on the matrix after this is actually equivalent to the operation on the duplicate, without affecting the original matrix.
GlPopMatrix
Public abstract void
GlPopMatrix ()//
OpenGL docs.
To get back to the previous matrix you use the glpushmatrix command.
A good practice can be to have one glloadidentity In the begining of each frame and after that use glpushmatrix and glpopmatrix.
Restore to the matrix before glpushmatrix is used. We recommend that you use glloadidentity at the beginning of the frame, and then use glpushmatrix and glpopmatrix later.
Putting it all togetter
So to make something with this new knowlege let us do 3 squares call them a, B and c. scale them so that B is 50% smaller then A and C is 50% smaller then B. then let a rotate counterclockwise in the center of the screen. B shoshould rotate clockwise around
A And finaly C rotating clockwise around B and counterclockwise in a high speed around it's own center.
Here, we use three squares A, B, and C to simulate the sun, Earth, and moon. B is the result of a blooming 50%, and C is the result of B scaling 50%. A rotates counterclockwise. B turns clockwise around. C rotates clockwise around B and rotates clockwise at high speed.
Java code
 Public void onDrawFrame (GL10 gl ){
 // Clears the screen and depth buffer.
 Gl. glClear (GL10.GL _ COLOR_BUFFER_BIT  GL10.GL _ DEPTH_BUFFER_BIT );
 // Replace the current matrix with the identity matrix
 Gl. glLoadIdentity ();
 // Translates 10 units into the screen.
 Gl. glTranslatef (0, 0,10 );
 // SQUARE
 // Save the current matrix.
 Gl. glPushMatrix ();
 // Rotate square A counterclockwise.
 Gl. glRotatef (angle, 0, 0, 1 );
 // Draw square.
 Square. draw (gl );
 // Restore the last matrix.
 Gl. glPopMatrix ();
 // SQUARE B
 // Save the current matrix
 Gl. glPushMatrix ();
 // Rotate square B before moving it, making it rotate around.
 Gl. glRotatef (angle, 0, 0, 1 );
 // Move square B.
 Gl. glTranslatef (2, 0, 0 );
 // Scale it to 50% of Square
 Gl. glscalef (. 5f,. 5f,. 5f );
 // Draw square B.
 Square. Draw (GL );
 // Square C
 // Save the current matrix
 Gl. glPushMatrix ();
 // Make the rotation around B
 Gl. glRotatef (angle, 0, 0, 1 );
 Gl. glTranslatef (2, 0, 0 );
 // Scale it to 50% of square B
 Gl. glScalef (. 5f,. 5f,. 5f );
 // Rotate around it's own center.
 Gl. glRotatef (angle * 10, 0, 0, 1 );
 // Draw square C.
 Square. draw (gl );
 // Restore to the matrix as it was before C.
 Gl. glPopMatrix ();
 // Restore to the matrix as it was before B.
 Gl. glPopMatrix ();
 // Increse the angle.
 Angle ++;
 }
Public void onDrawFrame (GL10 gl) {// Clears the screen and depth buffer. gl. glClear (GL10.GL _ COLOR_BUFFER_BIT  GL10.GL _ DEPTH_BUFFER_BIT); // Replace the current matrix with the identity matrix gl. glLoadIdentity (); // Translates 10 units into the screen. gl. glTranslatef (0, 0,10); // square a // Save the current matrix. gl. glPushMatrix (); // Rotate square A counterclockwise. gl. glRotatef (angle, 0, 0, 1); // Draw square. square. draw (gl); // Restore the last matrix. gl. glPopMatrix (); // square B // Save the current matrix gl. glPushMatrix (); // Rotate square B before moving it, making it rotate around. gl. glRotatef (angle, 0, 0, 1); // Move square B. gl. glTranslatef (2, 0, 0); // Scale it to 50% of square A gl. glScalef (. 5f ,. 5f ,. 5f); // Draw square B. square. draw (gl); // square c // Save the current matrix gl. glPushMatrix (); // Make the rotation around B gl. glRotatef (angle, 0, 0, 1); gl. glTranslatef (2, 0, 0); // Scale it to 50% of square B gl. glScalef (. 5f ,. 5f ,. 5f); // Rotate around it's own center. gl. glRotatef (angle * 10, 0, 0, 1); // Draw square C. square. draw (gl); // Restore to the matrix as it was before C. gl. glPopMatrix (); // Restore to the matrix as it was before B. gl. glPopMatrix (); // Increse the angle. angle ++ ;}
And don't forget to add angel as a variable as well. Thanks Tim!
Public class OpenGLRenderer implements Renderer {
Private Square;
Private float angle; // Don't forget to add this.
...
References
The info used in this tutorial is collected from:
Android Developers
OpenGL ES 1.1 Reference Pages
You can download the source for this tutorial here:
Tutorial_Part_III
You can also checkout the code from:
Code.google.com
Previous tutorial:
OpenGL ES Tutorial for AndroidPart IIBuilding a polygon
Next tutorial:
OpenGL ES Tutorial for AndroidPart IVAdding colors
PerErik Bergman
Consultant at Jayway