TD OpenGL

[ Introduction | Matrices | Projection | Transformations | Couleurs | Fonctions de base | z-Buffer | Primitives | Clavier | Transparence | Lissage | Eclairage | Textures | Ombrage ]
Liens:

Introduction

retour en haut

Exercice 1: compilation d'un exemple

Nous allons utiliser la bibliothèque GLUT (GL Utility Toolkit) qui fournit plusieurs fonctions pour faciliter l'utilisation d'OpenGL.

GLUT n'est pas installé sur les stations,

  1. chargez la sur http://www.xmission.com/~nate/glut.html (la version "-bin").
  2. décompressez le fichier dans un de vos repertoires (par exemple Z:\glut)
  3. dans Visual C++, indiquez dans le menu "Tools --> Options" que les fichiers d'entêtes (headers) dont vous allez avoir besoin sont dans Z:\glut
  4. téléchargez le fichier exemple.c
  5. compilez le en utilisant Visual C++ (un projet sera alors créé), (il vous faudra aussi indiquer où se situe la bibliothèque GLUT pour le "linkage", ou recopier cette bibliothèque dans le repertoire contenant exemple.c)
  6. lancez l'exécutable créé (sûrement dans un répertoire DEBUG)


Les matrices dans OpenGL

retour en haut

Il y a deux types de matrices dans OpenGL: la matrice appelée ModelView qui sert à manipuler les objets et la matrice ProjectionView qui sert à manipuler la caméra, c'est à dire définir la projection à dans la fenêtre. Voici la chaîne de traitement des points :

Matrice MODELVIEW

Matrice PROJECTION

Division perspective

Viewport transformation

Il existe une "matrice courante" que l'on spécifie avec la commande glMatrixMode(GL_MODELVIEW) ou glMatrixMode(GL_PROJECTION)

En général on travaille tout le temps sur la matrice MODELVIEW sauf lorsqu'on doit modifier les paramètres intrinsèques de la caméra (focale, taille du viewport, etc...).

On peut multiplier la matrice courante par une matrice quelconque avec glMultMatrix.

La fonction glLoadIdentity() permet de reinitialiser la matrice courante à la matrice identité.


La Projection

retour en haut

Dans OpenGL, la projection est gérée par une matrice spécifique, appelée GL_PROJECTION. Les fonctions glOrtho, gluOrtho2D, gluPerspective et gluLookAt permettent de définir facilement une matrice de projection. Voyons en détail gluPerspective:
Paramètres:

Seuls les objets situés entre les plans near et far seront dessinés (opération effectuée lors du cliping).

La caméra regarde l'axe des y.


Les Transformations

retour en haut

Plusieurs fonctions permettent de multiplier (à droite) la matrice courante par une matrice de transformation : N.B.: les paramètres sont des "floats". Pour déplacer la scène 3D (par exemple la faire tourner), il ne faut pas manipuler la matrice PROJECTION mais la matrice MODELVIEW.

Exercice 2

Allez manipuler l'applet Java sur http://www.cs.princeton.edu/~min/cs426/classes/threed.html. Vous devez y voir une scène 3D (en bas) et 2 projections fixes (en haut).
  1. Déplacez la caméra (en cliquant dans les projections fixes sur la caméra), observez le résultat
  2. Déplacez le plan de projection "near" (en utilisant la barre en bas à gauche), observez le résultat

Les objets de GLUT

Des objets élémentaires peuvent être créés en appelant des fonctions de GLUT: Par exemple, pour mettre un cube en fil de fer centré en (1,2,3):
// on choisi la matrice courante 
   glMatrixMode(GL_MODELVIEW); 
// on la change en l'identité
   glLoadIdentity();
// si on veut faire tourner le cube autour de l'axe passant par
// (0,0,0) et (1,1,0)
   glRotatef(30,1.0,1.0,0.0);
// on translate le cube en (1,2,3))
   glTranslatef(1.0,2.0,3.0)
// on dessine le cube
   glutWireCube(0.8);
Attention, l'ordre des transformations se fera en remontant, ie: translation puis rotation.

Les couleurs

retour en haut

les couleurs ont ici 3 composantes: R (rouge), V (vert), B (bleu), et sont positionnées avec glColor3f(R,V,B).

mais on peut en définir une 4eme pour la transparence, ainsi on pourra rendre les objets plus ou moins opaque pour voir plus ou moins à travers (fonction glColor4f).


Les fonctions de bases d'OpenGL

retour en haut

à mettre dans le "main":
  // initialisation
  glutInit(&argc, argv); 

  // initialisation du mode d'affichage
  glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);

  // taille et position de la fenetre de l'application
  glutInitWindowSize (250, 250);
  glutInitWindowPosition (100, 100);
  // creation de la fenêtre
  glutCreateWindow ("Exemple");

  // couleur du fond (ici: noir)
  glClearColor (0.0, 0.0, 0.0, 0.0);

  // positionnement du nom de la fonction permettant de dessiner le
  // contenu de la fenêtre
  glutDisplayFunc(display);

  // positionnement du nom de la fonction appelée lors du
  // redimensionnement de la fenêtre de l'appliquation
  glutReshapeFunc(reshape);

  // pour démarrer une boucle infinie (pour l'instant)
  glutMainLoop();

  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutMainLoop();

Gestion de la profondeur : z-Buffer

retour en haut

Exercice 3: z-buffer

  1. Afficher un cube dessiné avec des facettes bleues, en perspective, centré en (2,2,2), avec les arêtes de longueur 1.
  2. Afficher une sphère avec des facettes, centré en (2,2,2), de rayon 1.1 .
  3. L'affichage vous paraît-il naturel ?
  4. Pour gérer la profondeur, il faut utiliser l'algorithme du z-buffer et ainsi éliminer les parties qui devraient être cachées.
    Pour cela: ajouter "GLUT_DEPTH" au mode d'affichage:
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);

    Activer le z-buffer:
    glEnable(GL_DEPTH_TEST);

    Et avant chaque reaffichage (donc dans le fonction display), réinitialiser le buffer du z-buffer:
    glClear(GL_DEPTH_BUFFER_BIT);

Les primitives

retour en haut

Pour définir un point en 3D défini avec des coordonnées de type "float", il faut utiliser glVertex3f(x,y,z), où 3 pour 3 dimensions, f pour le typage, et x,y,z sont les coordonnées.

Des structures particulières peuvent être construites en utilisant des points définis par glVertex3f. On défini le type de structure et on délimite les points de cette structure par

Documentation http://www.delafond.org/traducmanfr/X11/man3/glBegin.3x.html
Structures utilisables: Exemples: GLUT utilise ces primitives pour construire les cubes, sphères et théières.

Exercice 4 : Tracé du repère

  1. Faites afficher le repère de l'espace en utilisant la primitive GL_LINES, en utilisant une couleur différente pour chaque axe (on reculera la caméra pour pouvoir observer l'origine du repère)
  2. Afficher un cube centré en O, ayant subi une rotation.

Exercice 5 : Gestion du clavier

retour en haut

pour utiliser les touches du clavier, il suffit de définir une fonction (par exemple "key") donc l'entête est static void key(unsigned char c, int x, int y)c correspondra au caractère tapé.

Il n'y a plus qu'à déclarer dans le "main" le nom de la fonction en charge de caractères tapés au clavier ainsi: glutKeyboardFunc(key); Pour forcer à redessiner la scène, il faut utiliser la fonction glutPostRedisplay();
Exemple

  1. reprenez l'exercice précédent, et modifiez le code pour que lorsque qu'on appuie sur la touche 'a', le cube tourne sur lui même (selon l'axe que vous voulez); bien entendu le repère ne devra pas bouger
  2. ajouter une touche pour déplacer le cube
  3. ajouter une touche pour passer en mode fil de fer (avec glPolygonMode(GL_FRONT_AND_BACK, GL_LINES); )
  4. ajouter une touche pour passer en mode facettes (avec glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);)

La transparence (blending)

retour en haut

OpenGL permet d'afficher un objet avec composition de la couleur de chacun de ses pixels avec la couleur du pixel déjà présent dans l'image à cette place -> transparence.

Le niveau de transparence est considéré comme une composante supplémentaire de la couleur, elle peut être définie avec glColor4f(R,V,B,alpha)alpha désigne le niveau de transparensce entre 0 et 1.

Pour la transparence, les valeurs habituelles de sfactor et dfactor sont GL_SRC_ALPHA et GL_ONE_MINUS_SRC_ALPHA:

ATTENTION: La composition entre les pixels présents et placés implique que l'ordre de dessin des objets graphiques a une influence sur l'image finale.


Liens: blending selon Buffa

Exercice 6

  1. Dans le programme avec le cube et la sphère, rendez la sphère à moitié transparente
  2. changer l'ordre de dessin du cube et de la sphère, que ce passe-t-il ?
...

Lissage

retour en haut

Le lissage permet d'attenuer l'aspect des facettes des objets en modes facettes.

Il existe deux méthodes de lissage: uni (tous les pixels d'une même facette seront de la même couleur) et Gouraud (interpolation linéaire en fonction des facettes voisines, voir partie sur l'éclairage). Pour utiliser le lissage: glShadeModel(GL_FLAT); pour uni, glShadeModel(GL_SMOOTH); pour Gouraud.

Exercice 7

Ajouter une touche pour change le mode de lissage (par exemple 'M' pour le mode "FLAT" et 'm' le mode "SMOOTH").

Eclairage

retour en haut

La théorie

L'activation de l'éclairage se fait grâce à glEnable(GL_LIGHTING);

Les lumières

Chaque lumière est identifiée par un numéro X (<8) et nommée par "GL_LIGHTX". pour chaque lumière on peut définir une couleur pour la lumière "diffuse", "amibiante", "speculaire", par exemple:
   float blanc[4] = { 1.0,1.0,1.0,1.0 };
   float noir[4]  = { 0.0,0.0,0.0,1.0 };
   glLightfv(GL_LIGHT0, GL_DIFFUSE, blanc);// couleur de lumière diffuse
   glLightfv(GL_LIGHT0, GL_AMBIENT, noir); // couleur de lumière ambiante
donnera une lumière blanche avec une lumière ambiante noire.

Propriétés de void glLight{i f}[v](GLenum nb,GLenum pname,TYPE p);

pname

rôle

GL_AMBIENT

(R,G,B,A): couleur de la lumière ambiante ajoutée à la scène

GL_DIFFUSE

(R,G,B,A): couleur de la lumière diffuse liée à la source lumineuse

GL_SPECULAR

(R,G,B,A): couleur de la lumière spéculaire liée à la source lumineuse

GL_POSITION

(x,y,z,w): position de la lumière

  • si w = 0, lumière placée à l'infini, (x,y,z) donne la direction vers la source de lumière

  • si w != 0, lumière non placée à l'infini, (x,y,z) donne la position

GL_SPOT_DIRECTION

orientation du cone d'ouverture dans le cas d'un spot

GL_SPOT_EXPONENT

exposant d'un spot (plus l'exposant est grand plus le spot est focalisé sur son axe)

GL_SPOT_CUTOFF

angle d'ouverture d'un spot


NB: la 4eme composante de "blanc" et "noir" correspond à la transparence de la couleur (alpha).

Pour donner la position de la lumière 0: glLightfv(GL_LIGHT0,GL_POSITION,light0_pos);

Pour allumer la lumière 0: glEnable(GL_LIGHT0);.
Pour éteindre la lumière 0: glDisable(GL_LIGHT0);.

Les matériaux

Il faut définir le type de surface de chaque objet afin d'en déterminer les propriétés reflectrices.
Pour simplifier, utilisez:

glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,couleur);

avant la description de l'objet que vous voulez afficher,
avec couleur: float couleur[4] = { R , V , B , alpha } (alpha: transparence);

Textures

retour en haut

L'opération de texturage consiste à recouvrir un objet d'une image pour donner l'impression que cet objet est constitué du matériel représenté sur l'image (du bois, du marbre, ...).

L'intérêt du texturage est que cette technique permet de créer de manière simple des détails sur les objets qu'il aurait fallu autrement modéliser au moyen de surfaces. Les modèles restent géométriquement simples et rapide d'affichage avec une qualité d'affichage qui être bonne ou même excellente.

Etapes de l'utilisation d'une texture:
  1. spécification de la texture,
  2. spécification de la technique à utiliser pour appliquer la texture à chaque pixel,
  3. autorisation du plaquage,
  4. dessin de la scène qui sera implicitement texturée au moyen des paramètres définis en (1) et (2).
La suite Exemple

L'ombrage (ray-tracing)

retour en haut

Le principal algorithme permettant d'obtenir des effets d'ombre sur les objets est le lancer de rayons (ray tracing, Appel(1968), Whitted(1980)). Dans sa formulation sommaire, le principe est le suivant :

Pour chaque point de l'écran (plan de projection),

  1. on détermine les points d'intersection entre les facettes de l'objet 3D et le rayon visuel. Parmi tous les points obtenus, on retient celui qui est le plus proche de l'observateur.
  2. On trace un rayon secondaire allant de ce point vers une source lumineuse Si. Si ce rayon intercepte une facette, on considère que le point P est ombré, sinon il est pleinement éclairé. Il faut évidemment refaire cette opération pour toutes les autres sources lumineuses.

En fait, un rayon lumineux ne se réfléchit pas seulement, il se transmet. Il faut donc tenir compte à la fois de la réflexion ( lumière spéculaire) et de la réfraction (lumière transmise). On est alors conduit à raffiner l'algorithme précédent en tenant compte pour toutes les intersections de la lumière réfléchie et de la lumière transmise.

On obtient ainsi pour chaque rayon visuel un arbre de rayonnement dans lequel à chaque noeud i on applique la relation plus complète
Ii=Iai+Idi+Isi+Iti

avec, dans le cas le plus simple,
Isi= ks Is(i-1)) et Iti = ktIt(i-1)


Sélection souris (picking)

...
Sébastien Choplin
Last modified: Mon Nov 29 00:49:57 CET 2004