OpenGL ::Transformations on openGL :: Material 3
consists of :
- A touch of madness mathematical
- OpenGL matrices
- Transformations
- Exercise: a crane
A touch of madness mathematical
Transformations ... a big word. Taking our good old book Math 4 e 3 or e we find as elementary transformations:
* Central symmetry is equivalent to a 180 ° rotation and axial symmetries are certain definable via changes of scale (we will when we see how to make a mirror OpenGL)
I'm not going to spread on the explanation of actions of these changes on the angles, lengths ... it was the job of your college prof! For my part I'll talk about matrices, as it is in this form that are used transformations in OpenGL (and the spatial geometry in general). matrices you know? Uh ... yes No, not the matrix ... ok is not won ... A matrix is represented as an array of numbers ... but beware its meaning and use are beyond its representation. This is not a "board". Generally a matrix represents a transformation. The matrix defines how to develop a system. Here what interests us is the use of geometric matrices to predictnumerically the result of a transformation. I take the example of 2D rotation because it is easily understandable and representable. The principle is the same for 3D, with an added dimension of course.
The rotation angle theta on the plane (see drawing above), can be written in matrix form:
Taking the particular case of the rotation angle theta = 90 °, the matrix becomes:
Now take a basis vector, say the vector V coordinate V = (1,1) .
Apply her "transformation" described by the matrix. To do this simply compute the product matrix * vector x: V = M x V
* x A matrix vector product is quite simple, the drawing below explains the color.
We obtain as you can see vector V = (-1,1) .
And indeed checking graphically the red vector is obtained by 90 ° rotation of the vector black as we expected by calculation.
Here we saw how a matrix can be used to perform a rotation.
actually used matrices are matrices 4x4 which can represent both a rotation, a translation and a "scaling". I will not discuss the mathematics of 3D matrices, my goal is just to make you understand now that a matrix can be (and is) used to make geometric transformations.
- translation;
- rotation;
- * symmetry;
- scaling.
* Central symmetry is equivalent to a 180 ° rotation and axial symmetries are certain definable via changes of scale (we will when we see how to make a mirror OpenGL)
I'm not going to spread on the explanation of actions of these changes on the angles, lengths ... it was the job of your college prof! For my part I'll talk about matrices, as it is in this form that are used transformations in OpenGL (and the spatial geometry in general). matrices you know? Uh ... yes No, not the matrix ... ok is not won ... A matrix is represented as an array of numbers ... but beware its meaning and use are beyond its representation. This is not a "board". Generally a matrix represents a transformation. The matrix defines how to develop a system. Here what interests us is the use of geometric matrices to predictnumerically the result of a transformation. I take the example of 2D rotation because it is easily understandable and representable. The principle is the same for 3D, with an added dimension of course.
The rotation angle theta on the plane (see drawing above), can be written in matrix form:
Taking the particular case of the rotation angle theta = 90 °, the matrix becomes:
Now take a basis vector, say the vector V coordinate V = (1,1) .
Apply her "transformation" described by the matrix. To do this simply compute the product matrix * vector x: V = M x V
* x A matrix vector product is quite simple, the drawing below explains the color.
We obtain as you can see vector V = (-1,1) .
And indeed checking graphically the red vector is obtained by 90 ° rotation of the vector black as we expected by calculation.
Here we saw how a matrix can be used to perform a rotation.
actually used matrices are matrices 4x4 which can represent both a rotation, a translation and a "scaling". I will not discuss the mathematics of 3D matrices, my goal is just to make you understand now that a matrix can be (and is) used to make geometric transformations.
OpenGL matrices
Most of the time, OpenGL, we do not handle them these matrices directly but you should know that there are three matrices that we will need to use via simple function calls:
As I told you earlier in OpenGL seldom modified matrix directly (assigning values). We just modify the existing array rather through a function call. For example, if the currently stored in the transformation matrix is rotated 90 ° and we have just apply to one rotation of -10 ° , the matrix will in fact contain the cumulative transformation that is to say 80 ° rotation . To avoid accumulating changes and start from scratch somehow must reset the matrix (the analogy with a film is purely coincidental ...).
Before making changes to the transformation matrix must be sure of its initial state. For this it is reset to the identity matrix . ** Transform a point by the identity matrix is itself basically it becomes nothing. To do this we use the function:
And we will add this piece of code before any drawing:Code: C + +-Select
You understood now apply a transformation in OpenGL is to multiply the current matrix by the matrix of our new transformation.
Before performing a transformation, we must know if we will only apply to an object, or all those which will be defined later.
Indeed, if we apply a rotation to a triangle, and that draws a square just after it will also undergo this transformation.
allow two functions to avoid it:
After glPushMatrix , we continue to work with the current state of the matrix, we just added the ability to go back.
still can, at any time to start over and reset the transformation matrix to the identity matrix: glLoadIdentity ();
When a matrix is saved, it is placed at the top of the stack (backup) matrices.
A call glPopMatrix takes the matrix head, remove it from the stack and uses current transformation matrix as . the depth of the stack is 32 dies GL_MODELVIEW , we can do 32 consecutive calls to glPushMatrix . now that we know how to preserve our matrix, still see what interests us here ... change to apply transformations.
GL_PROJECTION | in which we define the projection mode (orthogonal view) |
GL_MODELVIEW | to position the objects in the scene (camera, vertices, lights and other effects). It is that we will handle the most. |
GL_TEXTURE | for textures. We will see in the chapter on how the texture is to define a translation through this matrix will allow us to make animated textures. |
Cumulative changes
As I told you earlier in OpenGL seldom modified matrix directly (assigning values). We just modify the existing array rather through a function call. For example, if the currently stored in the transformation matrix is rotated 90 ° and we have just apply to one rotation of -10 ° , the matrix will in fact contain the cumulative transformation that is to say 80 ° rotation . To avoid accumulating changes and start from scratch somehow must reset the matrix (the analogy with a film is purely coincidental ...).
Reset a matrix
Before making changes to the transformation matrix must be sure of its initial state. For this it is reset to the identity matrix . ** Transform a point by the identity matrix is itself basically it becomes nothing. To do this we use the function:
glLoadIdentity ();
And we will add this piece of code before any drawing:Code: C + +-Select
1 2 | glMatrixMode ( GL_MODELVIEW ); glLoadIdentity ( ); |
Stack of matrices
You understood now apply a transformation in OpenGL is to multiply the current matrix by the matrix of our new transformation.
Before performing a transformation, we must know if we will only apply to an object, or all those which will be defined later.
Indeed, if we apply a rotation to a triangle, and that draws a square just after it will also undergo this transformation.
allow two functions to avoid it:
- glPushMatrix () : saves the current matrix;
- glPopMatrix () : returns the matrix saved.
After glPushMatrix , we continue to work with the current state of the matrix, we just added the ability to go back.
still can, at any time to start over and reset the transformation matrix to the identity matrix: glLoadIdentity ();
When a matrix is saved, it is placed at the top of the stack (backup) matrices.
A call glPopMatrix takes the matrix head, remove it from the stack and uses current transformation matrix as . the depth of the stack is 32 dies GL_MODELVIEW , we can do 32 consecutive calls to glPushMatrix . now that we know how to preserve our matrix, still see what interests us here ... change to apply transformations.
Transformations
Transformation = change of reference. Transformation = change of reference. Transformation = change of reference. Oh by the way, did you know that: Transformation = change of reference ? Let me emphasize (heavily) because that is what it must assimilate to become the king of transformations. In real life to apply a transformation to an object we would place first in the world then we would turn for example. Here we must think in terms of reference: by successive modifications of the matrix GL_MODELVIEW place ourselves, look, dimensionnons the coordinate system in which will then drew our purpose. So we first apply the changes we want, then at the last moment we draw our object. therefore I will illustrate the transformation of the amendment to the mark. And to make an impression on the fact that the transformations are accumulated until the matrix is not reset, I will do the transformations one after the other. landmark Let us start from the base, represented here only in the plane ( X, Y) for simplicity.
Marker base
The translation to move the reference current according to a vector V = (x, y, z) where x, y, and z are real. Even if you do not want to move that according to a component, you must define others (putting them at 0 ).
Example here:Code: C + +-Select
Gives:
Mark after translation
The rotation rotates the current mark by an angle theta (expressed in degrees ) along the axis defined by the vector V = (x, y, z)where x, y, and z are real:
Usually it is rotated only around a main axis (X, Y or Z) both .
rotations of the plane (X, Y) are about the axis Z, therefore rotate our benchmark 45 No it do:Code: C + +-Select
Mark after rotation
This is not really a homothety because you can change the axes scale differently.
It can transform coordinate axes to grow, shrink, stretch objects will be drawn ("scale" in English).
Thus if known
the new benchmark will be such that x = i * x, y * y = j, z = k * z . If you wish not to change a particular axis (eg Z when doing 2D) there must be 1 and not 0. Generally it applies the same factor for all axes to not deform, but nothing prevents us from transforming different axes:Code: C + +-Select
Mark after scaling
It is important for you to think about the order in which you apply your changes. For example, make a translation followed by a rotation does not necessarily have the same result as to rotate and translation.Code: C + +-Select
Order of transformations
To be sure that you have grasped the subtleties of transformations, nothing like a practical exercise and fun!
Marker base
Translation
The translation to move the reference current according to a vector V = (x, y, z) where x, y, and z are real. Even if you do not want to move that according to a component, you must define others (putting them at 0 ).
glTranslatef (x, y, z);
Example here:Code: C + +-Select
1 | glTranslated ( - 2 , - 1 , 0 ); |
Gives:
Mark after translation
Rotation
The rotation rotates the current mark by an angle theta (expressed in degrees ) along the axis defined by the vector V = (x, y, z)where x, y, and z are real:
glRotated (theta, x, y, z);
Usually it is rotated only around a main axis (X, Y or Z) both .
rotations of the plane (X, Y) are about the axis Z, therefore rotate our benchmark 45 No it do:Code: C + +-Select
1 | glRotated ( 45 , 0 , 0 , 1 ); |
Mark after rotation
I told you earlier, each transformation matrix changes. If you do not return back (reset or recovery of a matrix stored), transformations combine. That's why the mark here turned from the position which had been given after the translation.
Scaling
This is not really a homothety because you can change the axes scale differently.
It can transform coordinate axes to grow, shrink, stretch objects will be drawn ("scale" in English).
Thus if known
glScalef (i, j, k);
the new benchmark will be such that x = i * x, y * y = j, z = k * z . If you wish not to change a particular axis (eg Z when doing 2D) there must be 1 and not 0. Generally it applies the same factor for all axes to not deform, but nothing prevents us from transforming different axes:Code: C + +-Select
1 | glScalef ( 2 , 0.5 , 1 ); |
Mark after scaling
Importance of the order of transformations
It is important for you to think about the order in which you apply your changes. For example, make a translation followed by a rotation does not necessarily have the same result as to rotate and translation.Code: C + +-Select
1 2 3 4 5 6 7 8 9 | glPushMatrix (); glTranslated ( - 2 , 0 , 0 ); glRotated ( 45 , 0 , 0 , 1 ); Drawing1 (); / / red triangle glPopMatrix (); glRotated ( 45 , 0 , 0 , 1 ); glTranslated ( - 2 , 0 , 0 ); dessin2 () / / blue triangle |
Order of transformations
Note in passing that the use was made of glPushMatrix () and glPopMatrix () , which allowed us, after drawing the red triangle mark to return to base to begin the transformation to the blue triangle.
To be sure that you have grasped the subtleties of transformations, nothing like a practical exercise and fun!
Exercise: a crane
I suggest you familiarize yourself with the changes through a small simple exercise: to build a 2D crane controlled by the keyboard.
The crane is fairly simple and consists of:
I advise you to monitor the crane keyboard and thus can change:
I leave you free to choose keys. For my part I used the arrow keys: up / down to the wire, left / right arms (shift down to the long arm). Reception events withSDL_WaitEvent not just because we want to move the crane during the push of a button. Consider however snooze buttons beforehand *:Code: C + +-Select
* Values proposed by SDL ( SDL_DEFAULT_REPEAT_DELAY and SDL_DEFAULT_REPEAT_INTERVAL ) are too slow. With 10, 10 you'll have a more fluid movement.
For something a chouille realistic, I advise you to limit the range of values that can take your variables (angle and length). I use:
We are still 2D but to make things easier it would be nice to have the coordinates of the order of pixels. To do this we modify the projection matrix to the 2D projection which we specify that both dimensions (default when they were between -1 and 1 in advance).Code: C + +-Select
In this way we coordinate space as follows:
Finally last tip before you release into the wild, it may be useful to know at any time where the marker is present and how it is oriented. To do this here is a little function that you can call anytime in your drawing for "debug" and better visualize your changes.Code: C + +-Select
For example, after drawing my database if I callCode: C + +-Select
I get:
So I see here that I am ready to make the first rotation and draw the big arm.
I give you my version of the crane. This is only a guide nothing requires you to do "exactly" the same.Code: C + +-Select
The crane is fairly simple and consists of:
- a base;
- a large arm;
- a small arm;
- a wire;
- a box.
I advise you to monitor the crane keyboard and thus can change:
- the angle between the arm and the base;
- the angle between the short arm and the long arm;
- the length of the wire (to raise and lower the body).
Keyboard management
I leave you free to choose keys. For my part I used the arrow keys: up / down to the wire, left / right arms (shift down to the long arm). Reception events withSDL_WaitEvent not just because we want to move the crane during the push of a button. Consider however snooze buttons beforehand *:Code: C + +-Select
1 | SDL_EnableKeyRepeat ( 10 , 10 ); |
* Values proposed by SDL ( SDL_DEFAULT_REPEAT_DELAY and SDL_DEFAULT_REPEAT_INTERVAL ) are too slow. With 10, 10 you'll have a more fluid movement.
For something a chouille realistic, I advise you to limit the range of values that can take your variables (angle and length). I use:
- large angle arm / base between 10 ° and 90 °;
- small angle arm / long arm between -90 ° and 90 °;
- length between 10 and 100.
Drawing of the crane
We are still 2D but to make things easier it would be nice to have the coordinates of the order of pixels. To do this we modify the projection matrix to the 2D projection which we specify that both dimensions (default when they were between -1 and 1 in advance).Code: C + +-Select
1 2 3 4 5 6 | SDL_WM_SetCaption ( "Exercise: a crane" , NULL ); SDL_SetVideoMode ( LARGEUR_ECRAN , HAUTEUR_ECRAN , 32 , SDL_OPENGL ); glMatrixMode ( GL_PROJECTION ); glLoadIdentity ( ); gluOrtho2D ( 0 , LARGEUR_ECRAN , 0 , HAUTEUR_ECRAN ); |
In this way we coordinate space as follows:
Finally last tip before you release into the wild, it may be useful to know at any time where the marker is present and how it is oriented. To do this here is a little function that you can call anytime in your drawing for "debug" and better visualize your changes.Code: C + +-Select
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | / * Draw the current mark to facilitate the understanding of transformations. use "scale" to have a significant landmark oriented and positioned but with a scale |
For example, after drawing my database if I callCode: C + +-Select
1 | dessinerRepere ( 50 ); |
So I see here that I am ready to make the first rotation and draw the big arm.
Regarding the wire must always be vertical. It is therefore necessary to find, once at the end of small arms, a way to cancel the rotations to put the mark "in place."
Good luck!
Correction
I give you my version of the crane. This is only a guide nothing requires you to do "exactly" the same.Code: C + +-Select
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | <SDL/SDL.h> # Include # include <GL/gl.h> <GL/glu.h> # include # include <cstdlib> LARGEUR_BASE 50 # define # define HAUTEUR_BASE 20 LARGEUR_BRAS_1 150 # define # define HAUTEUR_BRAS_1 15 LARGEUR_BRAS_2 50 # define # define HAUTEUR_BRAS_2 10 # Define TAILLE_CAISSE 10 # Define LARGEUR_ECRAN (LARGEUR_BASE + + LARGEUR_BRAS_1 HAUTEUR_BRAS_2 + 50) # define HAUTEUR_ECRAN (HAUTEUR_BASE + + LARGEUR_BRAS_1 HAUTEUR_BRAS_2 + 50) int angle1 = 45 , int angle2 = - 20 ; int length = 50 ; void Draw (); int main ( int argc , char * argv []) { SDL_Event event ; SDL_Init ( SDL_INIT_VIDEO ) atexit ( SDL_Quit ); SDL_WM_SetCaption ( "Exercise: a crane" , NULL ); SDL_SetVideoMode ( LARGEUR_ECRAN , HAUTEUR_ECRAN , 32 , SDL_OPENGL ); glMatrixMode ( GL_PROJECTION ); glLoadIdentity ( ); gluOrtho2D ( 0 , LARGEUR_ECRAN , 0 , HAUTEUR_ECRAN ); SDL_EnableKeyRepeat ( 10 , 10 ); Draw (); while(SDL_WaitEvent(&event)) { switch(event.type) { case SDL_QUIT: exit(0); break; case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_UP: longueur --; if (longueur < 10) longueur = 10; break; case SDLK_DOWN: longueur ++; if (longueur > 100) longueur = 100; break; case SDLK_LEFT: if ((event.key.keysym.mod & KMOD_LSHIFT) == KMOD_LSHIFT) { angle1++; if (angle1 > 90) angle1 = 90; } else { angle2++; if (angle2 > 90) angle2 = 90; } break; case SDLK_RIGHT: if ((event.key.keysym.mod & KMOD_LSHIFT) == KMOD_LSHIFT) { angle1--; if (angle1 < 10) angle1 = 10; } else { angle2--; if (angle2 < -90) angle2 = -90; } break; } break; } Dessiner(); } return 0 ; } / * Draw a rectangle as a reference point the midpoint of / * Draw the current mark to facilitate the understanding of transformations. use "scale" to have a significant landmark oriented and positioned but with a scale void Draw () { glClear ( GL_COLOR_BUFFER_BIT ); glMatrixMode ( GL_MODELVIEW ); glLoadIdentity ( ); / * I move my initial Landmarks (currently at the bottom left of the screen) * / glTranslated ( LARGEUR_BASE / 2 , HAUTEUR_BASE , 0 ); / / The base glColor3ub ( 254 , 128 , 1 ); drauRectangle ( LARGEUR_BASE , HAUTEUR_BASE ); / / I place in the top center of the base glTranslated ( LARGEUR_BASE / 2 , HAUTEUR_BASE / 2 , 0 ); / / The long arm glRotated ( angle1 , 0 , 0 , 1 ); glColor3ub ( 248 , 230 , 7 ); drauRectangle ( LARGEUR_BRAS_1 , HAUTEUR_BRAS_1 ); / / I place myself at the end of the long arm glTranslated ( LARGEUR_BRAS_1 , 0 , 0 ); / / Then take care of the small arm glRotated ( angle2 , 0 , 0 , 1 ); glColor3ub ( 186 , 234 , 21 ); drauRectangle ( LARGEUR_BRAS_2 , HAUTEUR_BRAS_2 ); / / I place myself at the end of the short arm glTranslated ( LARGEUR_BRAS_2 , 0 , 0 ) / * I cancel my rotations for mark aligned with the mark of origin * / glRotated ( - angle1 - angle2 , 0 , 0 , 1 ); / / I draw the line glColor3ub ( 255 , 255 , 255 ); glBegin ( GL_LINES ) glVertex2i ( 0 , 0 ); glVertex2i ( 0 , - length ) glEnd (); / * I descend down the wire (with a small offset X to anticipate the design of the box * / glTranslated ( - TAILLE_CAISSE / 2 , - length , 0 ); / / And finally I draw cash glColor3ub ( 175 , 175 , 85 ); drauRectangle ( TAILLE_CAISSE , TAILLE_CAISSE ); glFlush (); SDL_GL_SwapBuffers (); } |
download the source code program ---->>> click here
No comments:
Post a Comment