Professional Documents
Culture Documents
Les transformations
Ce chapitre vient introduire la notion de transformation, notion que nous pourrons ds prsent appliquer en 2D. Si vous avez la tte qui tourne lors de la lecture de ce chapitre, c'est normal ! Ce sont les effets de la rotation que nous allons dcouvrir.
* la symtrie centrale quivaut une rotation de 180 et certaines symtries axiales sont dfinissables via des changements d'chelle (nous y reviendrons lorsque nous verrons comment raliser un miroir en OpenGL) Je ne vais pas m'taler sur l'explication des actions de ces transformations sur les angles, les longueurs... c'tait le boulot de votre prof de collge ! Pour ma part je vais vous parler des matrices, car c'est sous cette forme que sont utilises les transformations en OpenGL (et dans la gomtrie spatiale en gnral). Les matrices vous connaissez ? Oui euh... non, pas la matrice ok... c'est pas gagn... Une matrice se reprsente sous la forme d'un tableau de nombres... mais attention son sens et son utilisation vont au-del de sa reprsentation. Ce n'est pas qu'un tableau . D'une manire gnrale une matrice reprsente une transformation. La matrice dfinit la manire d'voluer d'un systme. Ici ce qui nous intresse c'est l'utilisation gomtrique des matrices pour prdire numriquement le rsultat d'une transformation. Je prendrai l'exemple de la rotation 2D car il est facilement comprhensible et reprsentable. Le principe est le mme pour la 3D, avec une dimension en plus bien videmment.
Cours laborer par Youssef Mami
La rotation d'angle thta sur le plan (voir dessin ci dessus), s'crit matriciellement ci-dessus), :
Appliquons-lui la transformation dcrite par la matrice. Pour ce faire calculons lui simplement le produit* matrice x vecteur : V' = M x V * Un produit matrice x vecteur est assez simple, le dessin ci dessous l'explique en ci-dessous couleur.
Et en effet en vrifiant graphiquement, le vecteur rouge est bien obtenu par rotation de 90 du vecteur noir comme nous l'avions prvu par le calcul calcul.
Ici nous avons vu comment une matrice pouvait tre utilise pour raliser une rotation. En ralit les matrices utilises sont des matrices 4x4 qui peuvent reprsenter la fois une rotation, une translation et une mise l'chelle . Je ne dtaillerai p pas ici les mathmatiques des matrices 3D, mon but tant juste pour l'instant de vous faire comprendre qu'une matrice peut tre (et sera) utilise pour faire des transformations gomtriques.
GL_MODELVIEW
GL_TEXTURE
Transformations cumulatives
Comme je vous l'ai dit tout l'heure en OpenGL on modifie rarement la matrice directement (en affectant des valeurs). On vient plutt modifier la matrice existante grce un appel de fonction. Par exemple si la transformation actuellement stocke dans la matrice est rotation de 90 et qu'on vient demander faire une rotation de -10, la matrice contiendra en fait la transformation cumule c'est--dire rotation de 80. Pour viter de cumuler des transformations et repartir zro en quelque sorte il faut rinitialiser la matrice (toute analogie avec un film est purement fortuite...).
glLoadIdentity();
Ainsi nous rajouterons ce morceau de code avant de faire un quelconque dessin :
glMatrixMode( GL_MODELVIEW ); glLoadIdentity( );
Pile de matrices
Vous l'avez compris maintenant, appliquer une transformation en OpenGL revient multiplier la matrice actuelle par la matrice de notre nouvelle transformation. Avant d'effectuer une transformation, il faut savoir si nous allons l'appliquer uniquement un objet, ou tous ceux qui seront dfinis par la suite. En effet si on applique une rotation un triangle, et qu'on dessine un carr juste aprs il subira aussi cette transformation. Deux fonctions permettent d'viter a :
Aprs un glPushMatrix, on continue travailler avec l'tat actuel de la matrice, on a juste rajout la possibilit de revenir en arrire. On peut toujours, tout moment, recommencer zro et rinitialiser la matrice de transformation la matrice identit : glLoadIdentity(); Quand une matrice est sauvegarde, elle est mise en tte de la pile (de sauvegarde) des matrices. Un appel glPopMatrix prend la matrice en tte, l'enlve de la pile et l'utilise comme matrice de transformation actuelle. La profondeur de cette pile est de 32 matrices pour GL_MODELVIEW, on peut donc faire 32 appels conscutifs glPushMatrix. Maintenant que nous savons comment prserver notre matrice, voyons tout de mme ce qui nous intresse ici... la modifier pour appliquer des transformations.
Les transformations
Transformation = changement de repre.
Repre de base
Translation
La translation permet de dplacer le repre actuel selon un vecteur V = (x,y,z) ou x, y, et z sont rels. Mme si on ne veut dplacer que selon une composante, il faut dfinir les autres (en les mettant 0).
glTranslatef ( x , y, z );
Exemple ici :
glTranslated(-2,-1,0);
Donne :
Rotation
La rotation fait tourner le repre actuel d'un angle thta (exprim en degr) selon l'axe dfini par le vecteur V = (x,y,z) ou x, y, et z sont des rels :
glRotated ( thta, x , y, z );
Gnralement on ne fait tourner qu'autour d'un des axes principaux (X Y ou Z) la fois. Les rotations sur le plan (X,Y) se font autour de l'axe Z, donc pour faire tourner notre repre de 45 il faut faire :
Remarque : Je vous l'ai dit tout l'heure, chaque transformation modifie la matrice. Si on ne revient pas en arrire (remise zro, ou restitution d'une matrice sauvegarde), les transformations se combinent. C'est pour a qu'ici le repre a tourn partir de la position qui lui avait t donne aprs la translation.
Changement d'chelle
Ce n'est pas vraiment une homothtie car on peut changer d'chelle diffremment selon les axes. Elle permet de transformer les axes du repre afin de grossir, diminuer, tirer les objets qui y seront dessins ( scale en anglais). Ainsi si l'on appelle
glScalef ( i, j, k );
le nouveau repre sera tel que x' = i * x, y' = j * y, z' = k * z. Si l'on souhaite ne pas modifier un axe en particulier (par exemple Z quand on fait de la 2D) il faut mettre 1 et non 0. Gnralement on applique le mme facteur tous les axes pour ne pas dformer, mais rien ne nous empche de transformer diffremment les axes :
glPushMatrix(); glTranslated(-2,0,0); glRotated(45,0,0,1); dessin1(); //triangle rouge glPopMatrix(); glRotated(45,0,0,1); glTranslated(-2,0,0); dessin2(); //triangle bleu
NB :Remarquez au passage l'utilisation qui a t faite de glPushMatrix( ) et glPopMatrix( ), qui nous ont permis, aprs avoir dessin le triangle rouge, de revenir au repre de base afin d'entamer les transformations pour le triangle bleu.
Pour tre sr que vous ayez bien saisi les finesses des transformations, rien de tel qu'un exercice pratique et rigolo !
Exercice :
Une grue
Je vous propose de vous familiariser avec les transformations par l'intermdiaire d'un petit exercice simple : la construction d'une grue 2D contrle par le clavier.
d'une base ; d'un grand bras ; d'un petit bras ; d'un fil ; d'une caisse.
l'angle entre le bras et la base ; l'angle entre le petit bras et le grand bras ; la longueur du fil (pour faire monter et descendre la caisse).
Gestion du clavier
Je vous laisse libres du choix des touches. Pour ma part j'ai utilis les flches directionnelles : haut/bas pour le fil, gauche/droite pour les bras (shift enfonc pour le grand bras). Une rception des vnements avec SDL_WaitEvent suffit car on veut ne faire bouger la grue que lors de l'appui sur une touche. Pensez toutefois activer la rptition des touches au pralable* :
SDL_EnableKeyRepeat(10,10);
Pour avoir quelque chose d'un chouilla raliste, je vous conseille de limiter la plage de valeurs que peuvent prendre vos variables (angles et longueur). J'utilise :
angle grand bras/base entre 10 et 90 ; angle petit bras/grand bras entre -90 et 90 ; longueur entre 10 et 100.
Dessin de la grue
Nous restons encore en 2D mais pour faciliter les choses il serait bien de pouvoir avoir des coordonnes de l'ordre des pixels. Pour ce faire nous allons modifier la matrice de projection pour faire de la projection 2D dont nous spcifierons cette fois les dimensions (alors qu'elles taient par dfaut entre -1 et 1 au pralable).
SDL_WM_SetCaption("Exercice : une grue", NULL); SDL_SetVideoMode(LARGEUR_ECRAN, HAUTEUR_ECRAN, 32, SDL_OPENGL); glMatrixMode( GL_PROJECTION ); glLoadIdentity( ); gluOrtho2D(0,LARGEUR_ECRAN,0,HAUTEUR_ECRAN);
Enfin dernier conseil avant de vous lcher dans la nature, il peut tre utile tout moment de savoir o est le repre actuel et comment il est orient. Pour ce faire voici une petite fonction que vous pouvez appeler n'importe quand dans votre dessin pour dboguer et mieux visualiser vos transformations.
* Dessine le repre actuel pour faciliter la comprhension des transformations. Utiliser echelle pour avoir un repre bien orient et positionn mais avec une chelle diffrente. */
void dessinerRepere(unsigned int echelle = 1) { glPushMatrix(); glScalef(echelle,echelle,echelle); glBegin(GL_LINES); glColor3ub(0,0,255); glVertex2i(0,0); glVertex2i(1,0); glColor3ub(0,255,0); glVertex2i(0,0); glVertex2i(0,1); glEnd(); glPopMatrix(); }
dessinerRepere(50);
Jobtiens :
Je vois donc ici que je suis prt faire la premire rotation et dessiner le grand bras. Remarque : En ce qui concerne le fil, il doit toujours tre la verticale. Il faut donc que vous trouviez, une fois au bout du petit bras, un moyen d'annuler les rotations pour remettre le repre l'endroit .
Correction :
#include #include #include #include <SDL/SDL.h> <GL/gl.h> <GL/glu.h> <cstdlib>
#define LARGEUR_BASE 50 #define HAUTEUR_BASE 20 #define LARGEUR_BRAS_1 150 #define HAUTEUR_BRAS_1 15 #define LARGEUR_BRAS_2 50 #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 longueur = 50; void Dessiner(); int main(int argc, char *argv[]) { SDL_Event event; SDL_Init(SDL_INIT_VIDEO); atexit(SDL_Quit); SDL_WM_SetCaption("Exercice : une grue", 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); Dessiner(); while(SDL_WaitEvent(&event)) while { switch(event.type) switch
return 0; }
/* Dessine le repre actuel pour faciliter la comprhension des transformations. Utiliser "echelle" pour avoir un repre bien orient et positionn mais avec une chelle diffrente. */
void dessinerRepere(unsigned int echelle = 1) { glPushMatrix(); glScalef(echelle,echelle,echelle); glBegin(GL_LINES); glColor3ub(0,0,255); glVertex2i(0,0); glVertex2i(1,0); glColor3ub(0,255,0); glVertex2i(0,0); glVertex2i(0,1); glEnd(); glPopMatrix(); } void Dessiner() { glClear( GL_COLOR_BUFFER_BIT ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity( );
// La base
glColor3ub(254,128,1); dessineRectangle(LARGEUR_BASE,HAUTEUR_BASE);
// Le grand bras
glRotated(angle1,0,0,1); glColor3ub(248,230,7); dessineRectangle(LARGEUR_BRAS_1,HAUTEUR_BRAS_1);
/* J'annule les rotations pour avoir mon repre align avec le repre d'origine */
glRotated(-angle1-angle2,0,0,1);
/* Je descends en bas du fil (avec un petit dcalage sur X pour anticiper le dessin de la caisse */
glTranslated(-TAILLE_CAISSE/2,-longueur,0);