Scaling
If we wanted to re-size a shape, we could change the vertex positions to make a bigger shape. However, OpenGL offers the function glScalef which will use a matrix to re-size input shapes along the x, y, and z axes. The following example will only use the x and y axes for a two-dimensional rectangle:
The function glMatrixMode will accept the enumerations GL_PROJECTION, GL_MODELVIEW, and two others. OpenGL uses two matrices to modify shapes: a ModelView matrix and a Projection matrix. The projection matrix is mostly used to help display the shape, especially three dimensional shapes. For this example, it maps the shape's points to their pixel positions (similar to the coordinates used in SDL).
The function glLoadIdentity requires no arguments, but will set the specified matrix (modelview or projection) to the identity matrix. Any additional functions like gluOrtho2D or glScalef will multiply this identity matrix by an additional matrix. The result will be the new matrix since any matrix multiplied by the identity matrix is the same matrix (hence the name identity matrix).
For this example, the screen is mapped from -4 on the left to 4 on the right as well as -3 on the bottom to 3 on the top. The specified shape is a 2x2 square in the center, yet the final picture shows a 6x4 rectangle. Before the projection matrix maps the shape points to their correct pixel positions, the modelview matrix is applied, which in this case scales horizontally by 3 and by 2 along the y-axis.
As a demonstration of the math, here is a diagram which multiples a shape point by the modelview matrix:
This modelview matrix would be the result of the glScalef operation. Note that the example above does scale along the z-axis by 1. If the modelview matrix were not "scaled", the 3 and 2 would instead be 1's and the modelview matrix would be the identity matrix. Also, notice that the point consists of an x and y (which are given by glVertex2f), a 0 for z (since its two dimensional), and an additional 1 at the end. This extra 1 will prove useful later.
Previous examples used the identity matrix as the modelview matrix, which meant the input points were mapped according to the projection matrix. This example will scale each point to produce a different shape, then apply the project matrix to the new shape. This intermediate step allows us to make easy changes to a shape with any number of points since OpenGL will automatically apply the transformation to every vertex.
Rotating
OpenGL also allows us to rotate shapes before they are displayed on screen. This transformation is the most complex mathematically, especially in three dimensions. It requires that we specify an angle (in degrees) and an axis to rotate around. For two-dimensional rotation, we can use the z-axis.
The z-axis in OpenGL is perpendicular to the screen. If you imagine a pole running straight through your monitor, that would represent the z-axis. you could turn the monitor clockwise or counter-clockwise around this pole by a certain number of degrees. For this example, the drawn square has a similar pole, which is specified by the z-axis (or vector <0,0,1>):
/*thanks to tohtml.com for syntax highlighting*/ #include <SDL/SDL.h> #include <SDL/SDL_opengl.h> #include <stdio.h> SDL_Surface* screen; SDL_Event input; int loop = 1; /*degrees of rotation*/ float angle = 0; int main(int argc, char** argv) { SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/ screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL); /*set clear color to dark red*/ glClearColor(0.5, 0, 0, 1.0); glClear(GL_COLOR_BUFFER_BIT); /*set origin to center, use 4:3 ratio*/ glMatrixMode(GL_PROJECTION); glLoadIdentity(); /*start with identity matrix*/ gluOrtho2D(-4, 4, -3, 3); glMatrixMode(GL_MODELVIEW); /*for transforming shapes*/ while (loop) { /*glClear(GL_COLOR_BUFFER_BIT); /*clear screen*/ while (SDL_PollEvent(&input)) { if (input.type == SDL_QUIT) loop = 0; } /*setup modelview matrix to rotate shape*/ glLoadIdentity(); glRotatef(angle, 0, 0, 1); /*along z-axis*/ /*print rotation for current frame*/ printf("drawing shape at %g degrees\n", angle); /*draw quadrilateral*/ glBegin(GL_QUADS); /*multi-colored square*/ glColor3f(1, 1, 0); glVertex2f(-1, -1); glColor3f(1, 0, 0); glVertex2f(-1, 1); glColor3f(0, 1, 0); glVertex2f(1, 1); glColor3f(0, 0, 1); glVertex2f(1, -1); glEnd(); angle += 1; /*adjust angle for next frame*/ SDL_GL_SwapBuffers(); SDL_Delay(20); /*wait 20ms*/ } /*perform final commands then exit*/ SDL_Quit(); /*close SDL*/ fflush(stdout); /*update stdout*/ return 0; } | ||
prompt/stdout: drawing shape at 0 degrees drawing shape at 1 degrees drawing shape at 2 degrees drawing shape at 3 degrees drawing shape at 4 degrees drawing shape at 5 degrees drawing shape at 6 degrees drawing shape at 7 degrees drawing shape at 8 degrees drawing shape at 9 degrees drawing shape at 10 degrees drawing shape at 11 degrees drawing shape at 12 degrees drawing shape at 13 degrees drawing shape at 14 degrees drawing shape at 15 degrees drawing shape at 16 degrees drawing shape at 17 degrees drawing shape at 18 degrees drawing shape at 19 degrees drawing shape at 20 degrees drawing shape at 21 degrees drawing shape at 22 degrees drawing shape at 23 degrees drawing shape at 24 degrees drawing shape at 25 degrees | ||
output inside window:
|
The function glRotatef uses the inputs angle, x, y, and z (in that order). The angle is in degrees and can be negative to rotate the opposite way. The x, y, and z make up the axis or vector we wish to rotate about. The length of the input vector does not affect the rotation, so we could use <0,0,5>. However, if we change the vector to <0,0,-1>, we have also changed the direction, which would reverse the way the square is rotated.
Notice that the glClear call is commented out. This is to show the rotation of the square over time. Feel free to un-comment this line when running the example. For this example, the square starts out aligned to the x and y axes, then rotates counter-clockwise until the program is ended. The picture above should show a rotation from 0 to 25 degrees, with each new square drawn on top of the previous.
While OpenGL does allow is to rotate around any axis we input, the following diagram only shows rotations around the z-axis (or two-dimensional rotations):
The alpha symbol represents the angle we wish to rotate by. If we wanted to rotate 45 degrees, to make the square look like a diamond, the new point would have an x of 0 and a y of root 2. Basically, the upper-right corner would be moved to the center and moved up slightly. Since a similar process is done for every corner, the end result is a rotation of 45 degrees.
Translating
The final transformation is referred to as translation, which means moving or shifting the shape. This requires the function glTranslatef, which requires an x, y, and z input. These float variables will specify how much to move the shape in each direction. They can be negative to indicate a movement in the opposite direction.
For two-dimensional shapes, we only need to use x and y inputs and pass 0 to z. The following example will use translation with scaling and rotating:
/*thanks to tohtml.com for syntax highlighting*/ #include <SDL/SDL.h> #include <SDL/SDL_opengl.h> #include <stdio.h> SDL_Surface* screen; SDL_Event input; int loop = 1; /*scale variables*/ float scaleX = 2, scaleY = 1.5; /*rotation variable*/ float angle = 0; /*translation variables*/ float shiftX = 1, shiftY = 1; int main(int argc, char** argv) { SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/ screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL); /*print scaling variables*/ printf("will scale shape %g times on the x-axis\n", scaleX); printf("and %g times on the y-axis\n", scaleY); /*print translating variables*/ printf("will move shape to (%g,%g)\n", shiftX, shiftY); /*set clear color to dark red*/ glClearColor(0.5, 0, 0, 1.0); glClear(GL_COLOR_BUFFER_BIT); /*set origin to center, use 4:3 ratio*/ glMatrixMode(GL_PROJECTION); glLoadIdentity(); /*start with identity matrix*/ gluOrtho2D(-4, 4, -3, 3); glMatrixMode(GL_MODELVIEW); /*for transforming shapes*/ while (loop) { /*glClear(GL_COLOR_BUFFER_BIT); /*clear screen*/ while (SDL_PollEvent(&input)) { if (input.type == SDL_QUIT) loop = 0; } /*setup modelview matrix*/ glLoadIdentity(); glTranslatef(shiftX, shiftY, 0); /*move shape*/ glRotatef(angle, 0, 0, 5); /*along z-axis*/ glScalef(scaleX, scaleY, 1); /*re-size shape*/ /*print rotation for current frame*/ printf("drawing shape at %g degrees\n", angle); /*draw quadrilateral*/ glBegin(GL_QUADS); /*multi-colored square*/ glColor3f(1, 1, 0); glVertex2f(-1, -1); glColor3f(1, 0, 0); glVertex2f(-1, 1); glColor3f(0, 1, 0); glVertex2f(1, 1); glColor3f(0, 0, 1); glVertex2f(1, -1); glEnd(); angle += 1; /*adjust angle for next frame*/ SDL_GL_SwapBuffers(); SDL_Delay(20); /*wait 20ms*/ } /*perform final commands then exit*/ SDL_Quit(); /*close SDL*/ fflush(stdout); /*update stdout*/ return 0; } | ||
prompt/stdout: will scale shape 2 times on the x-axis and 1.5 times on the y-axis will move shape to (1,1) drawing shape at 0 degrees drawing shape at 1 degrees drawing shape at 2 degrees drawing shape at 3 degrees drawing shape at 4 degrees drawing shape at 5 degrees drawing shape at 6 degrees drawing shape at 7 degrees drawing shape at 8 degrees drawing shape at 9 degrees drawing shape at 10 degrees drawing shape at 11 degrees drawing shape at 12 degrees drawing shape at 13 degrees drawing shape at 14 degrees drawing shape at 15 degrees drawing shape at 16 degrees drawing shape at 17 degrees drawing shape at 18 degrees drawing shape at 19 degrees drawing shape at 20 degrees drawing shape at 21 degrees drawing shape at 22 degrees drawing shape at 23 degrees drawing shape at 24 degrees | ||
output inside window:
|
While we can apply all three transformations, we must be careful to do so in the right order. The ideal order is scale, rotate, translate (as introduced in this post). Were we to rotate after translating, the shape would rotate around the origin rather than its own origin; and if we rotate then scale in this example, the shape would be stretched horizontally at all times and appear to have a bad aspect ratio.
Despite my opinion that scale, rotate, then translate is the ideal order, the example shows the commands in the order translate, rotate, then scale. This is because OpenGL reverses the order of the transformations when creating matrices (except glLoadIdentity). It can be tricky, so be careful to remember this quirk of OpenGL.
So, onto translation. This is where the extra 1 at the end of our points comes in handy: were it not for this 1, moving the shape with matrices would be very difficult. Here is a diagram which translates one of the square's points:
The column on the right of the modelview matrix consists of the x, y, and z we wish to move the shape by. Each of these are multiplied by the 1 at the end of the point and added to the point's x, y, or z. For this case, the 2d point (1, -1) is moved to (2, 0) since an x shift of 1 and a y shift of 1 was used.
With that, I will wrap up this post. For documentation on OpenGL transformations, see http://www.opengl.org/sdk/docs/man2/xhtml/glScale.xml, http://www.opengl.org/sdk/docs/man2/xhtml/glRotate.xml, and http://www.opengl.org/sdk/docs/man2/xhtml/glTranslate.xml. For more information about rotation matrices, see the page http://en.wikipedia.org/wiki/Rotation_matrix. For a more in-depth tutorial, check out http://www.videotutorialsrock.com/opengl_tutorial/transform/text.php.
Thanks again for reading; the next post will discuss how to setup a 3D camera and use 3D transformations!
P.S. I had to change the matrix diagrams from transparent GIF's to PNG's with a white background for better readability. Also, I fixed some awkward spacing .
No comments:
Post a Comment