Mémo OPENGL

-------------

Michel Llibre - Juillet 2017, d'après :

OpenGL Programming Guide" de J.Neider, T. Davis et M. Woo,

OpenGL SuperBible de Richard S. Wright Jr. et Michael Swett => http://opengl.czweb.org/

https://www.opengl.org/sdk/docs/man2/

-------------

TABLE DES MATIÈRES

1 Installation – Compilation (Obsolète)        4

1.1 Pour PC (Paragraphe obsolète)        4

1.2 Sur Sun (Paragraphe obsolète)        4

2 Primitives de conditionnement        5

2.1 Suffixes de type utilisés en fin de nom des primitives        5

2.2 Effacement de la fenêtre        5

2.3 Activation des tracés        5

3 Primitives de tracé        6

3.1 Tracés divers constitués par une liste de sommets        6

3.1.1 Primitive générale de tracé        6

3.1.2 Primitive rectangle        6

3.2 Attributs des points, lignes, polygones        7

3.2.1 Taille des points        7

3.2.2 Taille des lignes        7

3.2.3 Traits tiretés        7

3.2.4 Mode de tracé point, ligne (contour), remplissage des faces        7

3.2.5 Spécification de la couleur des tracés        8

3.2.6 Spécification d’un motif de remplissage des faces        8

3.2.7 Orientation et pré-élimination des faces        8

3.2.8 Spécification des normales        9

3.3 Les tableaux de tracés OpenGL        9

3.4 Textes, bitmaps et pixmaps        11

3.4.1 Bitmaps mémoires ordinaires        11

3.4.2 Pixmaps        12

3.4.2.1.1 GL_TRUE        13

3.4.2.1.2 GL_FALSE        13

3.4.3 Bitmaps polices de windows        13

4 Les transformations : mouvement et point de vue        14

4.1 Scène et point de vue        14

4.2 Perspective ou non        15

4.2.1 Projection orthographique : 2D        15

4.2.2 Perspective : 3D        15

4.3 Résumé sur la méthode de projection        15

4.4 Plans de clipping supplémentaires        16

4.5 Transformation en pixels        16

5 Manipulation de matrices        18

5.1 Sélection de la matrice courante        18

5.2 Opérations de base sur la matrice courante        18

6 Les buffers d’affichage        19

6.1 Les différents buffers        19

6.2 Le mode double buffer        19

6.3 Le Zbuffer        19

6.4 Le buffer stencil        20

6.5 Le buffer d’accumulation        21

7 La couleur sans éclairage        22

7.1 Le dithering (synthèse de couleurs intermédiaires)        22

7.2 Les dégradés (Shade)        22

8 La couleur avec éclairage        23

8.1 Paramètres généraux d’éclairage        23

8.1.1 Position de l'observateur        24

8.1.2 Présence/absence d'une lumière ambiante générale        24

8.1.3 Amélioration du rendu des textures        24

8.1.4 Raccourci dans le calcul de l'éclairage        24

8.2 Sources de lumières localisées        24

8.3 Le renvoi de la lumière par la matière        25

8.3.1 Mode détaillé        25

8.3.2 Mode maintenu (color-tracking) :        26

8.3.3 Visualisation d'une source lumineuse        27

8.4 La transparence (blending)        27

8.5 Le brouillard (à faire)        27

8.6 Si l'éclairage ne fonctionne pas !        27

9 Les display lists        28

9.1 Définition d'un dessin par display list        28

9.2 Gestion automatique des indices de display listes        28

9.3 Exécution de multiples display listes        28

10 Les textures 2D        30

11 Les quadrics glu        31

12 Les mosaïques glu        32

13 Sauvegarde et récupération des éléments        34

13.1 La pile des attributs        34

13.2 Accès directs aux éléments        35

14 La librairie GLUT        36

14.1 Exemple d’utilisation        36

14.2 Animations temporisées        36

14.2.1 Activations à intervalles réguliers        36

14.2.2 Synchronisation temporelle        36

14.3 Interface clavier        37

14.4 Interface souris        37

14.5 Exemple de création de menu        37

14.6 Formes plus ou moins simples :        37

15 Utilitaires, rappels et divers        39

15.1 Renvois à la superbible        39

16 Spécificités WIN32        40

16.1 Le Pixel Format        40

16.2 L’utilisation ds fenêtres Win32        40

16.3 Pour sauver une image dans un .bmp        41

16.4 Pour imprimer l’image.        42

17 Temps réel        43

 

1Installation – Compilation (Obsolète)

Certains paragraphes qui datent de 2000 sont obsolètes.

1.1Pour PC (Paragraphe obsolète)

La librairie glut32.lib et sa dll glut32.dll se trouvent sur le CD « Bible OpenGL » dans X:\tools\GLUT-3.7\LIB\GLUT et sont à copier, la lib dans c:\Program Files\Microsoft Visual Studio\VC98\Lib et la dll dans un répertoire system ou system32 de windows. L'entête glut.h se trouve sur le CD « Bible OpenGL » dans X:\tools\GLUT-3.7\INCLUDE\GL et est à copier dans c:\Program Files\Microsoft Visual Studio\VC98\Include\GL.

 

 Pour faire les exécutables avec Microsoft Developper Studio , ajouter (si nécessaire)  :

#include <windows.h>  /* avant les lignes  suivantes  */

#include <GL/gl.h>    /* pour les glXxxx */

#include <GL/glu.h>   /* pour les gluXxxx */

#include <GL/glut.h>  /* pour les glutXxxx */

#include <wingdi.h>   /* pour les wglXxxx */

 

 Ajouter si nécessaire les librairies suivantes dans menu Project/Settins, onglet Link, Category General, fenêtre Objet/library :

opengl32.lib glu32.lib et glut.lib.

 

 En mode Win32 Console Application, une fenêtre DOS est ouverte en plus de la fenêtre graphique. On peut l'utiliser pour faire des entrées et des sorties avec stdin, stdout et stderr.

 

 En mode Win32 Application : Comme les routines ne comportent pas de  procédure WinMain, il faut ajouter l'option suivante dans menu Project/Settins, onglet Link, Category General, fenêtre Project Options :

-entry:mainCRTStartup

  après subsystem:windows par exemple.

1.2Sur Sun (Paragraphe obsolète)

Les inclusions suivantes sont généralement nécessaires :

#include <GL/gl.h>    /* pour les glXxxx */

#include <GL/glu.h>   /* pour les gluXxxx */

#include <GL/glut.h>  /* pour les glutXxxx */

ATTENTION à respecter les majuscules pour Gl.

Exemple de Makefile pour bounce.c :

.KEEP_STATE : ;

CPPFLAGS += -I/usr/local/include $(COMPILERFLAGS) $(INCLUDE)

LDLIBS   += -lGLU -lGL -lglut -LX11 -lXi -lXmu -lm

 

CC = gcc

 

MODULES = bounce.o

bounce : $(MODULES)

 $(LINK.c) -o $@ $(MODULES) $(LDFLAGS) $(LDLIBS)

 

clean  :

 rm -f bounce.o $(MODULES)

 

2Primitives de conditionnement

2.1Suffixes de type utilisés en fin de nom des primitives

 

Type

C

Opengl

b

entier 8bits

char

GLbyte

s

entier 16 bits

short

GLshort

i

entier 32 bits

long

GLint, GLsizei

f

réel 32 bits

float

GLfloat, GLclampf

d

reel 64 bits

double

GLdouble, GLclampd

ub

entier0 de 8 bits

unsigned char

GLubyte, GLboolean

us

entier0 de 16 bits

unsigned short

GLushort

ui

entier0 de 32 bits

unsigned long

GLuint, GLenum, GLbitfield

v

vecteur

[]

 

2.2Effacement de la fenêtre

En fait on efface 1, 2, 3 ou 4 buffers qui ont pour nom :

 

GL_COLOR_BUFFER_BIT

le buffer que l’on voit à l’écran, dit buffer couleur

GL_DEPTH_BUFFER_BIT

le Zbuffer (qui mémorise les z des points les + en avant)

GL_ACCUM_BUFFER_BIT

le buffer d’accumulation

GL_STENCIL_BUFFER_BIT

le buffer stencil

 

Exemple : Effacement du buffer couleur et du Zbuffer :

glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

La fenêtre est remplie par la clearcolor par défaut et tous les z du Zbuffer sont mis à la cleardepth par défaut.

 

Ces valeurs sont fixées à l’aide des primitives suivantes :

glClearColor (0.0, 0.0, 0.0, 0.0);  /* C’est noir : 0 Rouge, 0 Vert, 0 Bleu, ? Alpha */    glClearDepth (1.0);                 /* z = 1. (voir + loin) */

2.3Activation des tracés

Pour forcer le début d'exécution des commandes graphiques qui précèdent un point du programme :

glFlush ();

Pour forcer la fin d'exécution des commandes graphiques qui précèdent un point du programme. Il n’y a pas de retour au point du programme tant que les commandes graphiques émises ne sont pas complètement exécutées :

glFinish (); /* A la fin du Redraw par exemple */

 

3Primitives de tracé

3.1Tracés divers constitués par une liste de sommets

3.1.1Primitive générale de tracé

Pour dessiner des points, des segments, des polygones, etc.

glBegin (mode) ;

    void glVertex{234}{sifd}[v] (TYPE coords); /* 1er sommet */

    void glVertex{234}{sifd}[v] (TYPE coords); /* sommet suivant */

     ...                                       /* etc */

glEnd() ;

mode peut prendre les valeurs suivantes :

GL_POINTS

m points isolés

GL_LINES

m/2 segments de lignes non connectés

GL_LINE_STRIP

m-1 segments de ligne connectés

GL_LINE_LOOP

m segments refermés sur le 1er point.

GL_TRIANGLES

m/3 triangles isolés

GL_TRIANGLE_STRIP

m-2 triangles liés.

GL_TRIANGLE_FAN

1 éventail de m-2 triangles liés (1er point=sommet)

GL_QUADS

m/4 quadrilatères isolés

GL_QUAD_STRIP

(m-2)/2 quadrilatères liés

GL_POLYGON

1 polygone supposé convexe

( m est le nomnbre de points glVextex)

 

Dans glVertex#{sifd}[v]

 

Seules les primitives suivantes sont permises entre glBegin et glEnd :

glVertex*()

glColor*()  : Spécifie la couleur des sommets qui suivent

glIndex*() : Définit l’index de couleur active

glNormal*() : Spécifie la normale aux sommets qui suivent

glEvalCoord*() : Génère les coordonnées

glEvalPOINT*() : Génère les coordonnées

glCallList(), glCallLists(): Génère une liste de tracés

glTexCoord*() : Définit les coordonnées de texture

glMultiTexCoord*ARB() : Définit les coordonnées de texture

glEdgeFlag*() : indicateur segments réels ou virtuels

glMaterial*(): Définit les propriétés des matériaux

glArrayElement(): Extrait les données du tableau des sommets

3.1.2Primitive rectangle

Il y existe une primitive particulière pour le rectangle. C’est un polygone défini par deux point oppposés qui n'a qu'un coté de type GL_FRONT si les deux points sont sur la diagonale ppale, et de type GL_BACK dans l'autre cas (voir glCullFace) dans le cas par défaut où les face avant sont dans le sens direct (sens GL_CCW) et non dans le sens rétrograde (sens GL_CW, voir glFrontFace) :

void glRect{sifd} (TYPE x1, TYPE y1, TYPE x2, TYPE y2) ;

void glRect{sifd}v (TYPE *p1, Type *p2) ;

3.2Attributs des points, lignes, polygones

3.2.1Taille des points

Elle est spécifiée en pixels par :

void glPointSize(GLfloat size) ;

On obtient la taille en cours des points par :

void glGetFloatv (GL_POINT_SIZE, GLfloat *size) ;

On obtient les tailles min et max admissibles pour les points par :

void glGetFloatv (GL_POINT_SIZE_RANGE, GLfloat *range) ;

Le min est dans range[0] et le max dans range[1].

On obtient le step min de la taille d'un point par :

void glGetFloatv (GL_POINT_SIZE_GRANULARITY, GLfloat *stepsize) ;

3.2.2Taille des lignes

Elle est spécifiée en pixels par :

void glLineWidth(GLfloat width) ;

On obtient la largeur courante des lignes par :

void glGetFloatv (GL_LINE_WIDTH, GLfloat *size) ;

On obtient la largeur min et max d'une ligne par :

void glGetFloatv (GL_LINE_WIDTH_RANGE, GLfloat *range) ;

Le min est dans range[0] et le max dans range[1].

On obtient le step min de la taille de la largeur d'une ligne par :

void glGetFloatv (GL_LINE_WIDTH_GRANULARITY, GLfloat *stepsize) ;

3.2.3Traits tiretés

Définition du format par :

void glLineStiplle (GLint factor, GLushort pattern) ;

où :

factor : réduit à [1-255] multiplie le pattern qui suit.

pattern : 1 allumé, 0 éteint des poids faibles vers les forts.

Exemple de patterns :

0x00FF : --------........--------........--------........

0x0C0F : ----......--....----......--....----......--....

0xAAAA : .-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-

0x18FF : --------...--...--------...--...--------...--...

Mise en oeuvre des tirets par :

glEnable (GL_LINE_STIPPLE) ;

glDisable (GL_LINE_STIPPLE) ;

3.2.4Mode de tracé point, ligne (contour), remplissage des faces

On peut  tracer ou non les contours, remplir ou non l’intérieur, etc.  On spécifie cela par :

void glPolygonMode (GLenum face, GLenum mode);

où :

mode = GL_POINT : pour tracer les sommets uniquement

mode = GL_LINE : pour tracer le pourtour uniquement

mode = GL_FILL (par défaut): pour remplir la surface intérieure

et :

face = GL_FRONT : le mode concerne la face avant uniquement

face = GL_BACK  : le mode concerne la face arrière uniquement

face = GL_FRONT_AND_BACK (par défaut): le mode concerne les 2 faces

 

Pour éviter de tracer des sous arêtes internes on peut interdire le tracé de certaines arêtes par :

void glEdgeFlag (GLboolean flag) ;

void glEdgeFlagv (const GLboolean *flag) ; /* pointe 1 seul flag */

où :

flag = GL_TRUE (par défaut) : les segments partant du point suivant sont traçables

flag = GL_FALSE (0) : les segments partant du point suivant ne seront pas tracés.

Les segments concernés sont les segments dont le point de départ suit l'appel de glEdgeFlag. Le segment qui arrive en ce point n'est pas concerné.

3.2.5Spécification de la couleur des tracés

 La couleur des tracés qui suivent est est spécifié, généralement en mode R,V,B (compris entre 0 et 1) par :

glColor3f(GLclampf Rouge, GLclampf Vert, GLclampf Bleu) ;

La couleur des lignes ou des surfaces est soit uniforme, soit interpolée entre celle qui est évaluée au niveau des sommets :

glShadeModel (GL_FLAT);

glShadeModel (GL_SMOOTH); /* valeur par défaut */

Avec GL_FLAT, les points lignes et surfaces prennent la dernière couleur uniforme attribuée au dernier sommet de la primitive.

Avec GL_SMOOTH, les points lignes et surfaces prennent une couleur dégradée entre les différentes couleurs spécifiées aux divers sommets de la primitive. A choisir de préférence quand on veut simuler des effets d’éclairage.

La couleur effectivement affichée dépend du mode d'éclairage. Si l'éclairage n'est pas actif (c'est le cas par défaut) les objets sont simplement de la couleur spécifiée par les glColor3f …

Si l'éclairage est activé par :

glEnable(GL_LIGHTING);

la couleur de l’objet résulte de la multiplication des couleurs d’éclairages par sa propre couleur, compte tenu des direction d’éclairage, des normales, etc . Voir section sur l'éclairage.

3.2.6Spécification d’un motif de remplissage des faces

On peut définir un motif de remplissage par :

void glPolygonStipple (const GLubyte *pmask);

pmask pointe sur un masque de remplissage 32x32 bits. glPixelStore[if](?) permet de spécifier l'interprétation du masque. Par défaut, c'est du placage, bit à bit.

 

void glEnable (GL_POLYGON_STIPPLE) ; /* Autorise le mouchetage */

void glDisable (GL_POLYGON_STIPPLE) ; /* Pas de mouchetage */

3.2.7Orientation et pré-élimination des faces

Les faces sont orientées. On choisit la convention d’orientation (convention directe/indirecte) par :

void glFrontFace (GLenum mode) ;

où :

mode = GL_CCW  (par défaut) : counterclockwise = face avant numérotée dans le sens direct.

mode = GL_CW : clockwise = face avant numérotée dans le sens indirect.

 

Pour accélérer le dessin en lignes cachées, on peut effectuer une préélimination de certaines faces. Dans un premier temps, il faut autoriser cette possibilité par :

void glEnable (GL_CULL_FACE) ; /* Autorise l'élimination */

void glDisable (GL_CULL_FACE) ; /* Pas d'élimination (par défaut) */

et éventuellement spécifier les faces à éliminer par :

void glCullFace (GLenum mode) ;

où :

mode = GL_FRONT : Eliminer les faces avants qui deviennent invisibles.

mode = GL_BACK (défaut) : Eliminer les faces arrière qui deviennent invisibles.

mode = GL_FRONT_AND_BACK : Eliminer toutes les faces (deviennent invisibles).

 

Exemple : Pour faire des lignes cachées en mode fil de fer :

glCullFace (GL_BACK) ;             /* Eliminer les faces arrière */

glPolygonMode (GL_FRONT, GL_LINE); /* Tracer face avant en mode arêtes */

glEnable (GL_CULL_FACE) ;          /* Autorise la pré-élimination */

glEnable (GL_DEPTH_TEST) ;         /* Active le zBuffer (voir + loin) */

 

Lorsque GL_CULL_FACE est actif, une face a un coté visible et un coté invisible dans les modes  GL_FRONT et GL_BACK (spécifiés par glCullFace (mode)). Ce coté ne dépend que du sens de parcours de son périmètre (et n'a rien à voir avec les normales). Si le coté invisible est du coté observateur la face est pré-éliminée. Toujours lorsque GL_CULL_FACE est actif, les faces décrites après mise du  mode à GL_FRONT_AND_BACK sont pré-éliminées quel que soit leur coté.

On a intérêt à mettre glEnable(GL_CULL_FACE) par défaut afin d'accélérer le traitement.

Si on veut qu'une plaque soit visible des deux cotés on mettra glDisable(GL_CULL_FACE) avant sa définition puis glEnable(GL_CULL_FACE) après sa définition pour revenir à l'état par défaut.

Une face pré-éliminée n'apparaît pas à l'écran, et on voit ce qu'il y a derrière comme si la plaque n'existait pas.

Les plaques décrites après un glDisable(GL_CULL_FACE)sont vues des deux cotés.

La couleur de la face visible d'un seul coté dépend de la direction et du sens de sa normale.  Si la normale est dirigée en sortant du coté avant elle sera éclairée normalement, sinon elle sera sombre.

La couleur d'une plaque visible des deux cotés (avec GL_CULL_FACE inactif) est calculée à partir de la direction et du sens de sa normale. - Lorsque le coté avant est vue par l'observateur, sa couleur sera claire si la normale sort de ce coté  et sombre dans le cas contraire. - Lorsque le coté arrière est vue par l'observateur, sa couleur sera claire si la normale sort du coté  avant et sombre si elle sort coté arrière. Pour une double face, il n'y a donc qu'une seule normale à définir qui est celle du coté avant, et l'éclairage apparait correctement quelle que soit la face vue par l'observateur.

3.2.8Spécification des normales

Les normales sont utilisées pour le calcul de la lumière réfléchie.

On peut spécifier une normale locale qui est attribuée aux sommets qui suivent par :

void glNormal3{bsidf} (TYPE ux, Type uy, TYPE uz) ;

void glNormal3{bsidf}v (const TYPE *u) ;

 

On peut demander la normalisation automatique des normales par :

void glEnable (GL_NORMALIZE) ;

void glDisable (GL_NORMALIZE) ; /* par défaut */

 

Il y a intérêt à fournir des normales normées à 1 et à désactiver la normalisation automatique car les opérations traditionnelles ne modifient pas cette norme. Si on utilise des opérations qui modifient les tailles, on doit activer la normalisation.

 

Attention : Les normales générées automatiquement par les surfaces des polynomes, qui définissent les faces extérieure et intérieure, soit par l'ordre de définition des points soit en modifiant la convention à l'aide de glFrontFace, ont pour le calcul de la lumière un sens (et peut-être une direction) quelconque. Pour l'éclairage, il y a intérêt à spécifier les normales avec les glNormal3{bsidf}.

3.3Les tableaux de tracés OpenGL

OpenGL offre quelques primitives de gestion des tableaux qui permettent d’accélérer les tracés. Il faut au préalable calculer et stocker dans des tableaux standard C (alloués en général) les valeurs successives à utiliser par les primitives, dans l’ordre où elles sont normalement utilisées. On peut ainsi mémoriser :

Pour affecter ces tableaux au système de tracé, il faut activer cette possibilité par :

glEnableClientState(tableau) ;

avec tableau = une ou plusieurs des possibilités suivantes :

GL_VERTEX_ARRAY : si des sommets sont mémorisés

GL_NORMAL_ARRAY : si des normales sont mémorisés (pour l’éclairage)

GL_COLOR_ARRAY : si des couleurs individuelles sont mémorisés en RVB

GL_INDEX_ARRAY : si des couleurs individuelles sont mémorisés en index

GL_EDGE_FLAG_ARRAY : si des arêtes (internes ou autres) ne sont pas tracées

GL_TEXTURE_COORD_ARRAY : si des textures sont utilisées.

Ensuite pour décrire un tracé, il faut positionner un pointeur sur la première des valeurs utilisées pour chaque type d’élément par :

glVertexPointer(size, type, stride, pointer);   /* Si vertex utilisés */

glNormalPointer(type, stride, pointer);         /* Si normales utilisées */

glColorPointer(size, type, stride, pointer);    /* Si couleurs utilisées */

glIndexPointer(type, stride, pointer);          /* Si index-couleurs utilisés */

glEdgeFlagPointer(type, stride, pointer);       /* Si EdgeFlags utilisés */

glTexCoordPointer(size, type, stride, pointer); /* Si Textures utilisées */

où :

Si un tableau qui ne contient que des couleurs, est donné en argument à glColorPointer , on met stride à 0 pour indiquer que les valeurs de couleurs sont compactées.

Si un tableau qui contient des ensembles points + couleurs est passé en argument à glColorPointer , on met stride à la valeur de l’offset en octets entre deux éléments consécutifs de même nature dans le tableau, et pointer doit pointer sur la première couleur, et idem pour glVertexPointer,…

Ensuite diverses possibilités pour la description du tracé :

  1. 1.Détaillée groupe élément par groupe d’élément, dans n’importe quel ordre, par exemple : 

glBegin(GL_TRIANGLES) ;

    For (i=0 ;i<nbelem ;i++) glArrayElement(i) ;

glEnd() ;

Si on a mémorisé des points, des couleurs et des normales, ce sont ainsi les nbelem premiers points, couleurs et normales qui sont placés entre glBegin et glEnd.

Attention à ne pas modifier les tableaux référencés entre glBegin et glEnd.

 

  1. 2.Tracé d’un paquet global par : 

glDrawArrays(mode, first,count) ;

qui est équivalent à :

glBegin(mode) ;

    For (i=0 ;i<count ;i++) glArrayElement(i+first) ;

glEnd() ;

 

3.  Idem dans un ordre différent à l’aide d’un tableau contenant les indices dans l’ordre d’utilisation. En notant Tabidx ce tableau, la primitive s’écrit :

glDrawElements(mode, count, type, Tabidx) ; /* type est le type des indices de Tabidx */

qui est équivalent à :

glBegin(mode) ;

    For (i=0 ;i<count ;i++) glArrayElement(Tabidx[i]) ;

glEnd() ;

 

  1. 1.Idem avec vérification d’indices : 

glDrawRangeElements(mode, start, end, count, type, Tabidx) ;

Les index utilisés Tabidx[i] doivent appartenir à l’intervalle [start, end].

 

Pour affecter un tableau contenant des éléments de différentes natures (sauf EdgeFlag et Index), on peut faire les affectations des différents types les uns après les autres comme précédemment indiqué avec des glXxxxPointer, en faisant attention à la valeur du décalage stride, ou les affecter par un seul appel à :

glInterleavedArrays(format, stride, pointer);

Pour fabriquer un ensemble de données contenant des couleurs en 4 UB, il peut y avoir intérêt à utiliser des structures plutot qu’essayer de compacter les 4 UB dans un mot de 32 bits.

 

Pour désactiver une affectation tableau devenue inutile :

glDisableClientState(tableau) ;

 

3.4Textes, bitmaps et pixmaps

Les textes et bitmaps sont monochromes (couleur courante de tracé), alors que les pixmaps sont des images qui comportent des couleurs.

Les textes, bitmaps et pixmaps affichent (par défaut) leur coin bas-gauche à la position courante d'affichage x,y. Cette position se modifie à l'aide de la primitive :

glRasterPos2*(x,y) ;

Pour décaler la position courante d'affichage des bitmaps, comme il n'y a pas de primitive toute faite, on peut utiliser :

glBitmap(0,0,0,0,dx,dy,NULL) ;

3.4.1Bitmaps mémoires ordinaires

Un bitmatp est monochrome peut être défini dans un tableau d’octets monodimensionnnel en décrivant les bits à 1 en hexadécimal par des GLubyte (0x00 à 0xFF), ligne par ligne de gauche à droite et du bas vers le haut. Exemple :

8421842184218421842184218421842184218421842184218421

  **  **  ******  **      **       ****

  **  **  ******  **      **      ******

  **  **  **      **      **      **  **

  **  **  **      **      **      **  **

  ******  ****    **      **      **  **

  ******  ****    **      **      **  **

  **  **  **      **      **      **  **

  **  **  **      **      **      **  **

  **  **  ******  ******  ******  ******

  **  **  ******  ******  ******   ****

 

GLubyte hello[] = /* 64*40 (largeur*hauteur) bitmap */

{

       0x33,   0x3F,   0x30,   Ox30,   0x1E, 0,0,0,  /* dernière ligne */

       0x33,   0x3F,   0x30,   Ox30,   0x3F, 0,0,0,

       0x33,   0x30,   0x30,   Ox30,   0x33, 0,0,0,

       0x33,   0x30,   0x30,   Ox30,   0x33, 0,0,0,

       0x3F,   0x3C,   0x30,   Ox30,   0x33, 0,0,0,

       0x3F,   0x3C,   0x30,   Ox30,   0x33, 0,0,0,

       0x33,   0x30,   0x30,   Ox30,   0x33, 0,0,0,

       0x33,   0x30,   0x30,   Ox30,   0x33, 0,0,0,

       0x33,   0x3F,   0x3F,   Ox3F,   0x3F, 0,0,0,

       0x33,   0x3F,   0x3F,   Ox3F,   0x1E, 0,0,0   /* premère ligne */

} ;

Conséquemment à un bug des librairies OpenGL Microsoft, il faut aligner chaque ligne de pixels sur une frontière 32 bits, d’où les 3 zéros ajoutés en fin de chaque ligne de pixels.

Ce bitmap s’affiche en x,y, simplement par :

glRasterPos2*(x,y) ;

glBitmap(64,40,x0,y0,dx,dy,hello) ; /* affichage bitmap N&B */

x0,y0 sont les coordonnées du point clé du bitmap par rapport à son coin bas-gauche. C’est le point clé qui est positionnné en x,y. Le prochain appel à glBitmap positionnera le point clé en x+dx,y+dy.

Il faut noter par ailleurs que lorsque le x,y courant est hors fenêtre, le bitmap n'est pas affiché, même s'il devrait être partiellement visible. Pour palier cela, il faut décaler le x,y afin qu'il soit intérieur à la fenêtre et corriger en conséquence x0,y0 pour que le bitmap occupe la même place.

3.4.2Pixmaps

Les fichiers bitmap.h et bitmap.c décrits dans la superbible, chapitre 7 pages 260-263, permettent de lire sur disque un fichier *.bmp (LoadDIBitmap), de capturer la fenêtre écran dans un fichier mémoire (ReadDIBitmap) et de sauver ce fichier sur disque (SaveDIBitmap).

Lecture sur disque :

BITMAPINFO *BitmapInfo; /* Structure win32 */

GLubyte    *BitmapBits;

/*....*/

BitmapBits = LoadDIBitmap("c:/windows/ssm1.bmp", &BitmapInfo);

Capture une partie de la fenêtre et écriture sur disque :

BITMAPINFO *BitmapInfo; /* Bitmap information */

GLubyte    *BitmapBits; /* Bitmap data */

/*....*/

BitmapBits = ReadDIBitmap(xi,yi,xs,ys, &BitmapInfo) ; /* Capture */

SaveDIBitmap("c:/temp/save.bmp", BitmapInfo, BitmapBits) ;

 

On l'affiche avec le coin base gauche à la position courante, par :

glDrawPixels(w, h, GL_BGR_EXT, GL_UNSIGNED_BYTE, BitmapBits); /* affichage bitmap couleur */

avec théoriquement :

w = BitmapInfo->bmiHeader.biWidth;

h = BitmapInfo->bmiHeader.biHeight;

 

On peut modifier l'aspect de l'affichage en faisant précéder glDrawPixels de :

glPixelZoom(fx, fy);

avec :

 

Remarque 1 : La capture écran est effectuée par un :

glReadPixels(x, y, width, height, GL_BGR_EXT, GL_UNSIGNED_BYTE, BitmapBits);

On conditionne la capture (glReadPixels) et l'affichage (glDrawPixels) de bitmaps par la primitive glPixelTransfer*() qui permet de modifier l'aspect et les couleurs de l'image (pas utilisé si pas de modification de l'image) et par glPixelStore[if](pname, param) qui permet de sauter des lignes ou des colonnes :

à l'affichage en mémoire :

GL_PACK_SWAP_BYTES

0.0.0.0.1GL_TRUE

Pour rétablir l'ordre des octets (laisser le défaut)

GL_PACK_LSB_FIRST

GL_FALSE

Pour inverser l'ordre des bits dans l'octet (laisser le défaut)

GL_PACK_ROW_LENGTH

0

Largeur image (si 0, width est utilisé)

GL_PACK_SKIP_PIXELS

0

Pixels (colonnes) à sauter

GL_PACK_SKIP_ROWS

0

Lignes à sauter

GL_PACK_ALIGNMENT

4

Alignement 32 bits (4 because bugs dans Win32)

et idem à la capture :

GL_UNPACK_SWAP_BYTES

0.0.0.0.1GL_FALSE

Pour rétablir l'ordre des octets (laisser le défaut)

GL_UNPACK_LSB_FIRST

GL_FALSE

Pour inverser l'ordre des bits dans l'octet (laisser le défaut)

GL_UNPACK_ROW_LENGTH

0

Largeur image (si 0, width est utilisé)

GL_UNPACK_SKIP_PIXELS

0

Pixels (colonnes) à sauter

GL_UNPACK_SKIP_ROWS

0

Lignes à sauter

GL_UNPACK_ALIGNMENT

4

Alignement 32 bits (4 because bugs dans Win32)

Remarque 2 : Par défaut on lit et on écrit dans le GL_FRONT buffer en mode GL_SINGLE et dans le GL_BACK buffer en mode GL_DOUBLE. On peut lire ou écrire dans un autre buffer  en modifiant l'origine ou la destination par :

glReadBuffer(GL_FRONT) ; /* par défaut en GL_SINGLE */

glDrawBuffer(GL_FRONT) ; /* par défaut en GL_SINGLE */

3.4.3Bitmaps polices de windows

Pour utiliser les polices bitmap de Windows il faut inclure :

#include <windows.h>

#include <wingdi.h>

et utiliser la librairie opengl32.lib au link.

1 - On sélectionne la police désirée, de hauteur h, d'épaisseur e, par :

/* HFON */ fontid = CreateFont(h, 0, 0, 0, e, f_ital, f_under, f_barre,

                        ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,

                        DRAFT_QUALITY, DEFAULT_PITCH, nompolice);

SelectObject(hdc, fontid);

L'épaisseur e peut prendre les valeurs FW_DONTCARE(0), FW_THIN(100), ,FW_ULTRALIGHT(200), FW_LIGHT (300), FW_NORMAL (400), FW_MEDIUM (500), FW_SEMIBOLD (600), FW_BOLD (700), FW_ULTRABOLD (800), FW_HEAVY (900).

Les flags f_ital, f_under, f_barre permettent de sélectionner une police italique, soulignée ou barrée.

A la place du jeu ANSI_CHARSET on peut prendre également les jeux DEFAULT_CHARSET, OEM_CHARSET ou SYMBOL_CHARSET.

Les noms de polices les plus courants utilisables sont Arial, Times ou Times New Roman, Courier, Courier New, Helvetica, Symbol et System.

Si cette sélection n'est pas effectuée, c'est la police system standard (taille environ 12) qui est utilisée

2 - Ensuite on crée une série de playlist avec les bitmaps des caractères de la manière suivante :

Charliste = glGenLists(256) ; /* 256 listes successives pour 256 caractères */

wglUseFontBitmaps(wglGetCurrentDC(), 0, 256, Charliste) ;

3 - On écrira ensuite le texte :

char texte[] = "Salut Michel ! Comment ça va ?" ;

glListBase(Charliste);

glCallLists(strlen(texte, GL_UNSIGNED_BYTE, texte);

Et c'est tout !  

La superbible chapitre 7, exemple fonttest offre des primitives de sélection de fontes et de print formattées qui peuvent être assez pratique (pages 247-249).

4Les transformations : mouvement et point de vue

Pour faciliter la manipulation des matrices, Opengl fournit des outils qui travaillent sur 3 piles de matrices. Les opérations se font sur la pile active. On active une pile par :

glMatrixMode(GL_XXXX) /* GL_XXXX = GL_MODELVIEW ou GL_PROJECTION ou GL_TEXTURE */

Les opérations se font dans l'ordre suivant :

  1. 1.Définition de la zone écran utilisée glViewport(xleft, ybas, large, haut); 

  2. 2.Définition du mode de projection (cf. 4.2). Pour cela on active d'abord la pile  GL_PROJECTION par glMatrixMode(GL_PROJECTION); qu'on initialise avec une matrice identité glLoadIdentity(); puis on la transforme en matrice de projection par la méthode de son choix  (cf. 4.2). 

  3. 3.Definition de la scène (cf. 4.1). Pour cela on active d'abord la pile  GL_MODELVIEW par glMatrixMode(GL_MODELVIEW); qu'on initialise avec une matrice identité glLoadIdentity(); puis dessine la scène. 

 

4.1Scène et point de vue

A l'origine l'observateur et les objets sont au même point origine (0,0,0). L'observateur regardant dans la direction des z négatifs, cad avec Ox à droite et Oy vers le haut.

Les manipulations de matrices permettent de déplacer ou modifier la scène (où la caméra en sens inverse). Elles sont toujours définies dans le repère relatif courant. Exemple :

1. Exemlpe de définition de la position du repère scène :

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

// On éloigne la scène de l'oeil, dans la direction du regard (-z) pour l'avoir devant

glTranlatef(0.f, 0.f, -Dz);

// On met l'axe Z scène à gauche et l'axe x scène vers l'avant par Rz(-90)

glRotatef(-90.f, 0.f, 1.f, 0.f);

// On met l'axe Z scène vers le haut et y scène vers la droite par Rx(-90)

glRotatef(-90.f, 1.f, 0.f, 0.f);

La définition et les emplacement des repères caméra et scène sont arbitraires. Les transformations précédentes peuvent également être considérées comme celles qui positionnent la caméra par rapport à la scène. Si on veut bouger la caméra, on modifie ces transformations. Si on considère qu'à ce niveau, on est dans le repère scène, les transformations qui suivent peuvent être considérées comme celles qui contruisent la scène. On a intérêt à mémoriser la matrice scène dans la pile par un glPushMatrix() de mamière à la retrouver au besoin par un glPopMatrix() , par exemple pour placer les éclairages relativement à ce repère scène, après avoir fait de nombreuses modifications au cours de la construction de la scène.

 

La primitive Glu :

void gluLookAt (double eyex, double eyey, double eyez

                double centerx, double centery, double centerz,

                double upx, double upy, double upz) ;

permet de définir un déplacement à faire subir à la caméra :

O est l’origine du repère caméra et OM et K définissent deux directions de base, mais il est préférable de gérer soit même ces mouvements.

4.2Perspective ou non

La projection sur l'écran met en oeuvre une matrice de projection qui définit la zone de la scène qui est prise en compte par rapport à la position de la caméra. Pour définir cette matrice (pour la modifier), il faut appeler les primitives :

void glMatrixMode (GL_PROJECTION);

glLoadIdentity();

 

4.2.1Projection orthographique : 2D

Pour définir une projection orthographique (sans perspective) :

void glOrtho (GLdouble Left, GLdouble Right, GLdouble Bottom,

              GLdouble Top, GLdouble Near, GLdouble Far) ;

Left, Right, Top et Bottom définissent les plans de coupe droite, gauche, haut et bas.

Near et Far sont les distances à l'observateur des 2 plans limitant la zone vue. Ils varient donc en sens inverse des z du repère. Leurs signes peuvent être quelconques.

 

Par défaut on a :

glOrtho (-1., 1., -1., 1., -1., 1.);

 

Pour définir dans une fenêtre de largeur hauteur = w * h un cube de vue de pour afficher un rectangle proportionnel à X * Y, on peut utiliser :

if (w <= h)

    glOrtho (0.0f, X, 0.0f, Y*h/w, Near, Far);

    /* glOrtho (-0.5*X., 0.5*X, -0.5*Y*h/w, 0.5*Y*h/w, Near, Far); */

else

    glOrtho (0.0f, X*w/h, 0.0f, Y, 1.0f, -1.f);

    /* glOrtho (-0.5*X*w/h, 0.5*X*w/h, -0.5*Y, 0.5*Y, 1.0f, -1.f); */

comme celà le rectangle proportionnel à X * Y occupe au mieux le coin bas gauche (/* le centre */) du viewport glViewport(0, 0, w, h);

 

Pour des scènes 2D, qui ont z = 0 par défaut, on peut utiliser :

void gluOrtho2D (GLdouble Left, GLdouble Right, GLdouble Bottom, GLdouble Top) ;

 

4.2.2Perspective : 3D

Pour définir une projection avec perspective :

void glFrustum (GLdouble Left, GLdouble Right, GLdouble Bottom,

                GLdouble Top, GLdouble Near, GLdouble Far) ;

Near et Far doivent être positifs (plans de coupe du tronc de cône de vue en avant l’observateur).

Left, Right, Top et Bottom  définissent les positions des plans de coupes au niveau du plan avant (Near).

 

Pour une vue symétrique standard, dans l'axe caméra, on a Left = -Right et Bottom = -Top.

Dans ce dernier cas, alternativement, on peut utiliser la primitive :

void gluPerspective (GLdouble Angley, GLdouble Aspect, GLdouble Near, GLdouble Far) ;

avec :

4.3Résumé sur la méthode de projection

En résumé on définit les caractéristiques de la caméra en mode GL_PROJECTION avec :

gluPerspective (Angley,Aspect,Near, Far) ;

ou glFrustum ou glOrtho, et on la positionne dans le mode GL_MODELVIEW  avec :

gluLookAt(eyex,eyey,eyez,centerx,centery,centerz,upx,upy,upz) ;

4.4Plans de clipping supplémentaires

On peut définir un minimum de 6 plans supplémentaires de clipping par des appels de la primitive :

void glClipPlane(GLenum plane, cont GLdouble *coefs) ;

où :

 

Pour activer ou désactiver ce clippling (désactif par défaut) il faut, avant les dessins des motifs appeler une des primitives :

void Enable (GL_CLIP_PLANEi);  

void Disable (GL_CLIP_PLANEi);  /* par défaut */

 

Certaines implémentations d’OpenGL supportent plus de 6 plans supplémentaires. On peut obtenir le nombre max de plans de clipping supplémentaires supportés par :

glGetIntegerv (GL_MAX_CLIP_PLANES);

4.5Transformation en pixels

Un dessin 2D, où une scène 3D projeté en 2D par les moyens précédents (par exemple par gluOrtho2D (Left, Right, Bottom, Top)) est en tout ou partie affiché sur une partie de la fenêtre écran par une transformation de cadrage entre une partie de la fenêtre et la scène 2D.

 

La partie de la scène située dans le rectangle Left, Right, Bottom, Top  est transformée en coordonnée pixels de la partie de fenêtre définie par :

void glViewport (GLint xleft, Glint ybas, GLsizei w, GLsizei h);

où :

On a la correspondance suivante :

pixels

scène 2D

xleft

Left

ybas

Bottom

w

Right-Left

h

Top-Bottom

d'où (X_d, Y_d) scène 2D vaut (x, y) pixels :

Un point est non visible si :

et idem en y.

 

Par défaut on a avant tout recadrage de la fenêtre :

glViewport (0, 0, w_wind, h_wind);

w_wind et h_wind sont les dimensions initiales de la fenêtre. Si la fenêtre est modifiée, il faut donc faire cet appel pour redéfinir la correspondance.

Scène

 

Observateur

 

Rep.Clip

 

Normalisées

 

Ecran

x0

 

 xE

 

xC

 

xC/wC

 

Pixx

y0

ModelView

yE

Projection

yC

1/Pers

yC/wC

ViewPort

Pixy

z0

--------->

zE

--------->

zC

----->

zC/Wc

------->

 

w0

 

wE

 

wC

 

1

 

 

 

Au cours de cette transformation de cadrage (des x,y de la scène en x,y pixels) les z de la scène vue (compris entre Near et Far par rapport à l’observateur sont également convertis entre 0 et 1 (Near -> 0 et Far -> 1). Le z convertit est ainsi la distance au plan de vision near, avec une distance maximale de 1 pour le plan far. Cette corespondance peut affecter la précision de la suppression des surfaces cachées.  On peut modifier cette correspondance (0,1) par :

GlDepthRange(Glclampd znear, Glclampd zfar) ;

5Manipulation de matrices

5.1Sélection de la matrice courante

Ces manipulations affectent la matrice placée au sommet de la pile active. Rappelons que l’on sélectionne une des 3 piles GL_MODELVIEW, GL_PROJECTION ou GL_TEXTURE par glMatrixMode (). La matrice au sommet est dite matrice courante.  En général la pile GL_MODELVIEW à 32 places et la pile GL_PROJECTION n'en a que 2.

glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH);

glGetIntegerv(GL_MAX_PROJECTION_STACK_DEPTH);

fournisssent le nombre maximal de places dans chacune des 2 piles.

5.2Opérations de base sur la matrice courante

void glLoadIdentity(void);

void glLoadMatrix{fd} (const TYPE *mat);

void glMultMatrix{fd} (const TYPE *mat);

void glTranslate{fd} (TYPE dx, TYPE dy, TYPE dz);

void glRotate{fd} (TYPE angle, TYPE ux, TYPE uy, TYPE uz);

void glScale{fd} (TYPE kx, TYPE ky, TYPE kz) ;

void glPushMatrix(void) ;

void glPopMatrix (void) ;

6Les buffers d’affichage

6.1Les différents buffers

L’affichage est réalisé à partir de plusieurs buffers et de plusieurs modes d’utilisation de ces buffers. Le choix entre les différentes possibilités peut être réalisé par exemple par :

glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB); /* mode RGB, double buffer */

 

La primitive glut utilise les constantes suivantes :

GLUT_RGB, GLUT_RGBA

Mode couleur RGB

GLUT_INDEX

Mode couleur par index dans une table

GLUT_SINGLE

Buffer simple

GLUT_DOUBLE

Buffer double

GLUT_ACCUM

Utilisation d’un buffer d’accumulation

GLUT_ALPHA

Utilisation d’un buffer alpha

GLUT_DEPTH

Utilisation d’un Zbuffer

GLUT_STENCIL

Utilisation d’un buffer stencil

GLUT_MULTISAMPLE

Utilisation d’un buffer multisample

GLUT_STEREO

Utilisation d’un buffer stereo

GLUT_LUMINANCE

Mode niveau de gris

6.2Le mode double buffer

En mode double buffer, on trace par défaut dans le GL_BACK buffer, alors que seul le GL_FRONT buffer est visible. On peut modifier ce comportement (par défaut) avec la primitive :

glDrawBuffer(GL_xxx) ;

où :

Dans le cas de buffers stereo, d’autres possibilités (supportées par peu de cartes graphiques) sont offertes (GL_LEFT_FRONT, GL_LEFT_BACK, GL_RIGTH_BACK et GL_RIGTH_FRONT).

 

Le basculement de buffer s’effectue soit directement avec l’API Win32 :

HDC dc ; /* device context de la fenêtre utilisée pour les tracés OpenG */

swapBuffers(dc) ;

ou bien avec la primitive glut :

glutSwapBuffers() ;

6.3Le Zbuffer

Le Zbuffer conserve la distance à l’observateur des point tracés, distance ramenée au domaine 0 à 1 qui correspondent aux near et far du frustrum. Si deux points sont tracés sur le même pixel, par défaut seul le dernier tracé apparait sur l'écran. Pour faire des lignes cachées il ne faut tracer que celui dont le z est le plus proche de l’observateur.  Pour cela il faut activer une sélection des points à tracer en fonction de leur distance à l’observateur par :

glEnable (GL_DEPTH_TEST) ;

glDisable (GL_DEPTH_TEST) ; /* on conserve le dernier point tracé */

 

La relation de comparaison des points est sélectionnée par :

GlDepthFunc(relation) ;

relation est l’une des valeurs suivantes :

GL_NEVER

Toujours fausse

GL_LESS

Vraie si distance < à celle du Zbuffer  (par défaut)

GL_EQUAL

Vraie si distance = à celle du Zbuffer

GL_LEQUAL

Vraie si distance à celle du Zbuffer

GL_GREATER

Vraie si distance > à celle du Zbuffer

GL_NOTEQUAL

Vraie si distance de celle du Zbuffer

GL_GEQUAL

Vraie si distance à celle du Zbuffer

GL_ALWAYS

Toujours vraie

 

Pour faire des lignes cachées, il est inutile de positionner GlDepthFunc(GL_LESS) car c’est la valeur par défaut. Par contre il faut refaire la Raz du Zbuffer avant tout retracé de la scène en réinitialisant tous les z au max, à savoir 1.

GlClearDepth(1.) ; /* Valeur initilale par défaut */

On peut faire des effets bizarres en traçant ce qui est vu de l’arrière en sélectionnant GlDepthFunc(GL_GREATER) et en initialisant le Zbuffer par GlClearDepth(0.) ;

En lignes cachées ordinaire, on peut cacher tout ce qui est au delà de la distance d de l’observateur par l’initialisation :

GlClearDepth(1.0*(d-near)/(far-near)) ;

 

On peut modifier la correspondance (near,near) -> (0,1) pour éventuellement augmenter la précision des calculs avec la primitive :

GlDepthRange(Glclampd znear, Glclampd zfar) ;

6.4Le buffer stencil

C’est un buffer qui permet de faire des masques pour interdire ou autoriser les tracés dans certaines zones de la fenêtre. Il faut appeler glutInitDisplayMode avec GLUT_STENCIL pour pouvoir l’utiliser et il faut l’activer par :

glEnable(GL_STENCIL_TEST). Ne pas oublier de l’effacer, comme les autres, par :

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

Par défaut il est mis à zéro.

 

Pour définir les zones interdites on appellera par exemple les fonctions suivantes avant de dessiner les masques :

glStencilFunc(GL_ALWAYS,1,1) ; /* dessins systématiquement pris en compte */

glStenclOp(GL_REPLACE, GL_REPLACE, GL_REPLACE) ; /* Maj du buffer stencil */

Les dessins suivants vont laisser une empreinte à 1 dans le buffer stencil.

Ensuite on re-efface les buffers standards, et on interdit le tracé dans les zones précédentes par :

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glStencilFunc(GL_NOTEQUAL, 1, 1); /* Acces si pas égal à 1 */

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); /* Pas de modification du buffer stencil */

Les dessins suivants ne se feront que dans les zones où le stencil buffer n’est pas à 1. Au contraire, pour ne tracer que dans les masques, utiliser GL_EQUAL à la place de GL_NOTEQUAL.

 

Les différents modes de masquages acceptés par glStencilFunc(mode,ref,mask) sont GL_NEVER, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL, GL_EQUAL, GL_NOTEQUAL et GL_ALWAYS. Avec GL_NEVER les tracés sont bloqués (pas de tracé ou d’accès au stencil). Pour GL_ALWAYS les tracés ou accès au stencil passent tous. Pour les autres relations, il y a tracé ou accès au stencil si (ref & mask) vérifie la relation avec (stencil & mask). S’il y a accès,  ref  est inscrit dans le stencil.

Les différentes opérations sur la valeur concernée du stencil, que l’on précise avec glStencilOp(fail, zfail, zpass) sont :

GL_KEEP

Pas de modification de la valeur du stencil

GL_ZERO

Raz  de la valeur du stencil

GL_REPLACE

Remplacement de la valeur du stencil par ref

GL_INC

Incrémente la valeur du stencil

GL_DECR

Décrémente la valeur du stencil

GL_INVERT

Bitwise inverse la valeur du stencil

Ces actions interviennent :

fail : lorsque le test stencil échoue,

zfail : lorsque le test stencil est Ok, mais le test Zbuffer échoue,

zpass : tests stencil et Zbuffer Ok.

6.5Le buffer d’accumulation

Il est généralement utilisé pour moyenner des dessins (effets de trouble ou d’antialiasing). Il faut appeler glutInitDisplayMode avec GLUT_ACCUM pour pouvoir l’utiliser.

Une succession de tracés est accumulée dans le buffer d’accumulation en appelant :

glAccum(GL_ACCUM, faccou) ;  /* accval = oldaccval + faccou*valcou */

Lors du premier tracé, il faut appeler :

glAccum(GL_LOAD, facini) ;  /* accval = facini*valcou */

Finalement, pour transferer dans le buffer couleur, il faut appeler :

glAccum(GL_RETURN, 1.);

 

Remarque : On peut modifier le contenu du buffer d’accumulation avec :

glAccum(GL_ADD, value) ;  /* accval = oldaccval + value */

glAccum(GL_MULT, factor) ;  /* accval *= factor */

7La couleur sans éclairage

On obtient le nombre de plans de couleurs disponibles dans une couleur ou dans le color-index buffer par :

glGetIntegerv(GL_RED_BITS, &nr); /* -> nr : nb des bits pour le rouge */

et aussi GL_BLUE_BITS, GL_GREEN_BITS, GL_ALPHA_BITS et GL_INDEX_BITS.

 

En mode RGBA , la luminosité du rouge, du vert et du bleu est normalement codée entre 0 et 1. On doit avoir pour 0.5 une impression d'intensité lumineuse moitié entre le noir (0) et le max (1). La correction gamma est censé réaliser cela. La couleur courante des tracés suivants est spécifiée par :

glColor*(....);

Au niveau de l’* on spécifie le nombre d’arguments (3 ou 4 si la valeur alpha est donnée), puis le type des arguments : b pour byte de –128 à 127, s pour short de –32768 à 32767, i pour integer de –2^31 à 2^31-1, ub pour unsigned byte de 0 à 255, us pour unsigned short de 0 à 65535,  et ui pour unsigneg integer de 0 à 2^32-1.

 

Pour définir une couleur en mode RGB entre 0 et 255, on a par exemple :

glColor3ub(0,255,128) ;  /*idem RGB(0,255,128) macro de windows*/

 

La couleur d’effacement (fond d’écran) est choisie par glClearColor*() ;

 

En mode palette, la couleur est choisie dans la table des couleurs par son index par la primitive :

glIndex{s|i|f|ub}[v]([p]Type);

 

La couleur d’effacement (fond d’écran) est choisie par glClearIndex() ;

7.1Le dithering (synthèse de couleurs intermédiaires)

Le dithering est la réalisation de couleurs intermédiaires non disponibles par juxtaposition de pavés mosaïqués avec des couleurs primaires. Il peut être autorisé ou non par :

glEnable (GL_DITHER);

glDisable (GL_DITHER);

 

7.2Les dégradés (Shade)

Une ligne ou une face peut être dessinée d’une seule couleur ou par un dégradé entre les couleurs spécifiées au niveau des différents sommets. Ce choix est effectué par :

glShadeModel (GL_SMOOTH); /* dégradé */

glShadeModel (GL_FLAT); /* uniforme */

Avec GL_FLAT, les points lignes et surfaces prennent une couleur uniforme attribuée au dernier sommet qui définit la forme (dans un cas de groupes de triangles ou rectangles, ou au premier sommet dans le cas d’un polygone unique).

En mode palette (color-index) l’effet de GL_SMOOTH peut être bizarre.

Quand on veut simuler les effets de l’éclairage, il y a intérêt à choisir le mode GL_SMOOTH.

8La couleur avec éclairage

Par défaut, le calcul d’éclairage n’est pas activé. Dans ce cas, un objet a sa couleur définie par les valeurs RVB[A] actives lors de sa définition.

Le calcul d’éclairage est activé par :

glEnable(GL_LIGHTING);

La couleur de l’objet résulte de la multiplication des couleurs d’éclairages par sa propre couleur, compte tenu des direction d’éclairage, des normales, etc .

Pour simuler l’éclairage, il faut :

  1. 1. Que toutes les faces aient des normales définies (pour évaluer la quantité de lumière reçue et ré-émise), 

  2. 2.Créer, sélectionner les paramètres généraux d’éclairage qui définissent les faces traitées, les modes de dégradés, etc. 

  3. 3.Créer des sources de lumière, les sélectionner et les positionner, 

  4. 4.Définir les propriétés d’absorption et de renvoi de la lumière par la matière des objets. La lumière renvoyée ne dépend que des couleurs respectives de la lumière incidente et de la couleur de l’objet (multiplication terme à terme des composantes). 

L'observateur voit  les faces colorées par la couleur qu'elles renvoient. Il ne voit pas les sources de lumière (sauf si on a mis un objet en mode GL_EMISSION à leur emplacement, cf. plus loin).

 

La lumière émise par les sources ou ré-émise par les matériaux est divisée en 3 types :

 

Le calcul de l'éclairage est fait au niveau de chaque sommet, puis étendu au niveau de la facette soit avec un rendu unique dans le cas de glShadeModel (GL_FLAT), soit avec un rendu dégradé interpolé dans le cas de glShadeModel (GL_SMOOTH) qui est bien meilleur. Ce calcul qui n'est fait qu'au niveau de chaque sommet peut avoir des effets inattendus si le pavage est trop grossier. Par exemple la lumière d'un spot  qui éclaire une plaque définie uniquement par ses 4 sommets ne sera pas prise en compte s'ils ne sont pas éclairés par le spot. Par contre si la plaque est pavée, elle sera prise en compte si des sommets du pavage sont éclairés.

Les sources localisées sont positionnées dans la scène en mode GL_MODELVIEW et affectées par les transformations

matricielles qui précèdent leur positionnement. On peut ainsi, rendre fixe une source par rapport à la scène (en la positionnant au même niveau qu’on décrit la scène) , la lier à un objet mobile ou au mouvement de la caméra (en la positionnant juste après le positionnement de la caméra).

8.1Paramètres généraux d’éclairage

Dans ce qui suit j'utilise les 5 niveaux d'éclairage, définis par des couleurs RVBA, suivants :

Les paramètres généraux de l'éclairage concernent la position de l'observateur, la présence/absence d'une lumière ambiante générale, l'amélioration du rendu des textures et un raccourci (appliqué par défaut) dans les calculs de l'éclairage qui est à désactiver si des volumes présentent des ouvertures.

8.1.1Position de l'observateur

La lumière peut être calculée pour un observateur situé à l'origine du repère openGL, ou pour un observateur situé à l'infini devant l'écran (direction +Z du repère openGL).

glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, mode) :

8.1.2Présence/absence d'une lumière ambiante générale

La scène est par défaut éclairée par une lumière ambiante générale faible. On peut modifier cela avec :

glLightModel{if}v(GL_LIGHT_MODEL_AMBIENT, eclairage) avec par exemple eclairage = eteint pour la supprimer.

8.1.3Amélioration du rendu des textures

Le rendu des textures peut-être amélioré par l'appel de

glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, mode)

avec mode = GL_SEPARATE_SPECULAR_COLOR.  Par défaut mode = GL_SINGLE_COLOR  produit un rendu approximatif.

8.1.4Raccourci dans le calcul de l'éclairage

Les faces internes d'un corps ne sont, en général, pas éclairées. Il est alors inutile de calculer leur éclairage. Ce raccourci de calcul est effectué par défaut. Mais si des faces internes sont visibles, ce raccourci n'est pas bon et il faut mettre en œuvre le calcul complet, ce qui est fait par l'appel de :

glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, mode) avec mode =  GL_TRUE .Ceci peut être fait localement avant le dessin du corps présentant des ouvertures, puis on refait cet appel avec mode = GL_FALSE ensuite pour éviter des calculs inutiles pour les autres corps.

Remarque importante : Les instructions glNormal3{..fd}(...)  définissent des vecteurs plus ou moins localement orthogonaux à la surface du volume qui sont supposés être dirigées du coté extérieur du volume. L'instruction glFronface(GL_CCW) définit un état (l'état par défaut) qui signale qu'une face dont les sommets successifs se suivent dans le sens trigo est une face externe. L'instruction glFronface(GL_CW) définit l'état inverse (une face dont les sommets successifs se suivent dans le sens horaire est une face externe). S'il y a des discordances entre le sens des normales (dirigées vers l'extérieur) et la face externe spécifiée par le sens de parcours des sommets, les calculs d'éclairage sont faussés et des résultats aberrants peuvent survenir.

En conclusion :

8.2Sources de lumières localisées  

On définit une source lumineuse localisée par :

void glLight{if}[v](GLenum nolum, GLenum mode, TYPE param);

nolum = GL_LIGHT0 à GL_LIGHT7 (on peut mettre 8 sources localisées au max) et où mode et param par défaut sont définis par :

GL_AMBIENT

0.,0.,0.,1.

source ambiante (éteinte)

GL_DIFFUSE

1.,1.,1.,1.

source localisée (puissante)

GL_SPECULAR

1.,1.,1.,1.

source cohérente (puissante)

GL_POSITION

0.,0.,1.,0.

position de la source (à l’infini)

GL_SPOT_DIRECTION

0.,0.,-1.

direction de l'éclairage

GL_SPOT_CUTOFF

180.

demi-angle éclairé (180 ou 0 à 90)

GL_SPOT_EXPONENT

0.

coefficient de focalisation (0 à 128)

GL_CONSTANT_ATTENUATION

1.

a

GL_LINEAR_ATTENUATION

0.

b

GL_QUADRATIC_ATTENUATION

0.

c

Remarque : Les valeurs par défaut de GL_SPECULAR et GL_DIFFUSE concernent GL_LIGHT0. Pour les autres les valeurs par défaut sont (0.,0.,0.,1.) c'est-à-dire éteintes.

Le mode GL_SPOT_CUTOFF permet de faire une lumière spot qui n’éclairent que dans un cône. Il faut alors définir sa direction par le mode GL_SPOT_DIRECTION.

Si d est la distance entre la source et l'objet, les lumières qui ne proviennent pas de l’infini sont atténuées par le facteur 1/(a+b.d+c.d^2)

Exemple :

GLfloat eteint []={0.,0.,0.,1.} ;

GLfloat puissant []={1.,1.,1.,1.} ;

GLfloat firstdiag[]={1.,1.,1.,0.} ; /* venant de l’infini 1ere diagonale */

GlLigthfv(GL_LIGHT2, GL_AMBIENT, eteint) ;

GlLigthfv(GL_LIGHT2, GL_DIFFUSE, puissant) ;

GlLigthfv(GL_LIGHT2, GL_SPECULAR, puissant) ;

GlLigthfv(GL_LIGHT02, GL_POSITION, firstdiag) ;

Ceci n'allume par la source GL_LIGHT2. Pour autoriser l’émission de la lumière de la source GL_LIGHT2 il faut faire :

glEnable(GL_LIGHT2);

8.3Le renvoi de la lumière par la matière

La lumière renvoyée par la matière, qui permet de voir l'objet et sa couleur, est seulement visible par l'observateur. Cette lumière renvoyée n'éclaire pas les autres objets.

Ce renvoi peut être spécifié par deux modes, un mode simplifié global et un mode détaillé :

8.3.1Mode détaillé

C'est le mode qui fonctionne par défaut, ou après abandon du mode maintenu (color-tracking), expliqué au paragraphe suivant

La couleur des objets résulte de la couleur des lumières émises et des coefficients de réflexion de chaque composante de la lumière propre à chaque objet. On peut modifier ce taux de réflexion pour chacun des 3 types de lumière avant le tracé de chaque objet à l'aide de l'appel de l'instruction :

glMaterial{if}[v](face, type, params);

avec face = GL_FRONT, GL_BACK ou GL_FRONT_AND_BACK.

Le tableau suivant donne les valeurs par défaut de params pour les différentes valeurs de type (ce sont les mêmes valeurs par défaut pour toutes les valeurs de face)  :

type

params

GL_AMBIENT

0.2,0.2,0.2,1.

GL_DIFFUSE

0.8,0.8,0.8,1.

GL_SPECULAR

0.,0.,0.,1.

Pour type, on peut utiliser GL_AMBIENT_AND_DIFFUSE ce qui revient a appeler successivement avec GL_AMBIENT puis GL_DIFFUSE avec les mêmes autres arguments.

Exemple :

Glfloat cuivre[] = { 0.7f, 0.3f,  0.1f, 1.0f };

glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cuivre);

On a spécifié une couleur cuivrée et on définit cette couleur pour toutes les faces avant suivantes en réflexion des lumières ambiente et diffuse.

La lumière spéculaire produit des reflets (contrairement aux deux autres). La taille de ce reflet peut être contrôlée par l'instruction :

glMateriali(face, GL_SHININESS, [0-128]);

ou glMaterialfv(face, GL_SHININESS, &valeur); si on veut utiliser une valeur non entière pour  GL_SHININESS.

le paramètre GL_SHININESS peut varier de 0 à 128. Plus le nombre est élevé, plus le reflet sera concentré et brillant.

Tableau des coefficients de réflexion (RVB et shininess) de quelques matériaux :

 

Materiau

Ambiente

Diffuse

Speculaire

Alpha

Plastique noir

0.0, 0.0, 0.0

0.01, 0.01, 0.01

0.5, 0.5, 0.5

32

Cuivre

0.32941, 0.22353,  0.02745

0.78039, 0.56863, 0.11373

0.99216, 0.94118, 0.80784

27.897

Bronze

0.2125, 0.1275, 0.054

0.714, 0.4284, 0.18144

0.39355, 0.27191, 0.16672

25.6

Chrome

0.25, 0.25, 0.25

0.4, 0.4, 0.4

0.7746, 0.7746, 0.7746

76.8

Cuivre rouge

0.19125, 0.0735, 0.0225

0.7038, 0.27048,  0.0828

0.25678, 0.13762, 0.08601

12.8

Or

0.24725, 0.1995, 0.0745

0.75164, 0.60648, 0.22648

0.62828, 0.5558, 0.36607

51.2

Étain

0.10588, 0.05882, 0.11373

0.42745, 0.47059, 0.54118

0.3333, 0.3333, 0.52157

9.84615

Argent

0.19225, 0.19225, 0.19225

0.50754, 0.50754, 0.50754

0.50827, 0.50827, 0.50827

51.2

Argent brillant

0.23125, 0.23125, 0.23125

0.2775, 0.2775, 0.2775

0.77391, 0.77391, 0.77391

89.6

Attention à l'utilisation de la lumière spéculaire qui est très sensible à la taille des normale. Un rendu incorrect peut-être dû à l'utilisation de glScale qui modifie la taille des normales. Si on ne peut se passer de  glScale, faire un glEnable(GL_NORMALIZE) pour qu'openGL re-norme les normales;

8.3.2Mode maintenu (color-tracking) :

Dans ce mode maintenu, nommé color tracking, diverses couleurs de réflexion pour un type de lumière parmi : GL_EMISSION, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, ou GL_AMBIENT_AND_DIFFUSE),  et pour une face parmi : GL_FRONT, GL_BACK ou GL_FRONT_AND_BACK  peuvent -être spécifiées pour une suite d'objets par de simples glColor successifs qui précèdent le dessin de ces objets sans avoir à utiliser pour chaque nouvelle couleur un glMaterial{if}[v](face, type, params). Le choix du couple (type de lumière, face)  maintenu  est effectué par l'instruction :

glColorMaterial(face, type); avec  face =  GL_FRONT_AND_BACK par défaut et type =  GL_AMBIENT_AND_DIFFUSE par défaut (par défaut : c'est-à-dire avant que cette instruction soit appelée).

Ce mode maintenu n’est pas actif par défaut. On l'active après avoir défini le couple à maintenir (par l'instruction précédente) par l'instruction :

glEnable(GL_COLOR_MATERIAL);

Un seul type est actif à la fois. Pour les autres types (non maintenus) on continuera de spécifier la couleur de réflexion de manière détaillée avec des glMaterialfv, et en particulier pour GL_SHININESS type de lumière qui n'est pas pris en compte par l'instruction glColorMaterial(face, type).

A titre d'exemple, glColorMaterial(GL_FRONT, GL_DIFFUSE) :

 

Si des faces internes sont visibles, il faut utiliser GL_FRONT_AND_BACK sinon ces faces  seront en niveaux de gris.

On désactive le color tracking par glDisable(GL_COLOR_MATERIAL).

Si une texture est utilisée en  color tracking, il faut avant le dessin d'objet texturé mettre par un glColor une couleur neutre, c'est-à-dire un blanc ou un gris plus ou moins foncé qui va modérer la couleur de la texture.

 

En conclusion : Dans le cas d'un rendu simple, si on peut tout faire en color tracking, la programmation sera beaucoup plus simple. Mais si on veut un rendu plus réaliste, il est préférable de passer au mode matériau local. Faire attention au problème posé par les glScale.

8.3.3Visualisation d'une source lumineuse

L'instruction :

glMaterialfv(face, GL_EMISSION, params);

permet de simuler partiellement l'émission de lumière par un objet. Avant le dessin de l'objet on fait un appel avec pour  params une lumière forte (par exemple), mais (IMPORTANT), dès la fin de son dessin il faut faire un nouvel appel de glMaterialfv(face, GL_EMISSION, params) avec pour  params une lumière éteinte, sinon les objets suivant seront également des émetteurs de lumière. Mais cette lumière n'est visible que de l'observateur. Elle n'éclaire pas les autres objets. Pour   compléter cette simulation, il faut mettre une lumière (type GL_LIGTH#) à l'emplacement de l'objet.

L'appel avec GL_EMISSION s'utilise aussi bien avec glEnable(GL_COLOR_MATERIAL) qu'avec glDisable(GL_COLOR_MATERIAL).

8.4La transparence (blending)

On réalise la transparence  d'un objet en effectuant un mélange entre la couleur primaire (Ro,  Go, Bo) de l'objet et la couleur du fond  (Rf, Gf, Bf) derrière l'objet. Ce mélange est fonction de la composante alpha (Ao) de l'objet.

Ao = 0 -> objet transparent (donc du fond)

Ao = 1 -> objet opaque (de sa couleur primaire).

En clair : (R, G, B) = Ao*(Ro,  Go, Bo)+(1-As)* (Rf, Gf, Bf)

Pour mettre en oeuvre il faut d'abord spécifier la fonction mélange avec une source alpha de typs As,1-As :

glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_ALPHA);

Puis, il faut autoriser le mélange par :

glEnable(GL_BLEND);

Les objets transparents doivent être tracés après les objets qu'ils sont susceptibles de cacher pour que ces objets soient déjà sur le fond. S'ils sont tracés avant, ils prennent place dans le Z-buffer et interdisent ainsi la prise en compte des objets qu'ils cachent.

8.5Le brouillard (à faire)

8.6Si l'éclairage ne fonctionne pas !

Vérifier :

  1. 1.Que des normales sont définies pour toutes les faces à tracer et bien unitaires (attention à glScale qui modifie la taille des normales), 

  2. 2.Que glEnable(GL_LIGHTING) est actif, 

  3. 3.Qu'il y a au moins une source de lumière allumée, par exemple glEnable(GL_LIGHT0) ou que l'ambiante globale soit puissante glLightModelfv(GL_LIGHT_MODEL_AMBIENT, puissante). 

  4. 4.Que la configuration du renvoi de la lumière par les matériaux est correcte, soit en mode global par glEnable(GL_COLOR_MATERIAL) et éventuellement glColorMaterial(face, mode), soit en mode local, après un éventuel glDisable(GL_COLOR_MATERIAL) avec la spécification des trois 3 types de renvois de lumière par des glMaterialfv(...). 

Ce dernier point est le plus délicat. Si à un certain point on utilise le conditionnement global, à savoir glEnable(GL_COLOR_MATERIAL), les couleurs des tracés successifs ne prennent en compte que les glColor. Des objets colorés par des textures seront teintés par la dernier glColor appelé. Pour qu'ils soient bien rendus, il faut, soit appeller préalablement un glColor(blanc) ou glColor(gris), soit repasser en conditionnement local en appelant glDisable(GL_COLOR_MATERIAL) et re-positionner les trois types de lumière ambiante, diffuse et spéculaire qui, a priori ont perdu leurs valeurs par défaut : exemple :

 

9Les display lists

Les display lists permettent de prédéfinir des figures. Les routines de tracé sont enregistrées mais pas les routines de calcul. Seuls les résultats numériques nécessaires aux tracés sont enregistrés.

9.1Définition d'un dessin par display list

Cette définition écrase le dessin pré-existant de même indice :

void glNewList(GLuint ix, GLenum mode);

    ....

    /* routines de desssin */

    ....

void glEndList

où :

 

On ne peut définir une nouvelle display list à l’intérieur de la définition d’une autre. De plus, certaines routines ne sont pas utilisables à l'intérieur des display listes, en particulier celle qui nécessitent des lectures d'info systèmes (car elles peuvent être exécutées à distance sur une autre moniteur à travers un réseau).

 

Les tracés ultérieurs se font par appel de :

glCallList (ix);

 

Une display liste peut être appelée à l'intérieur de la définition d'une autre, (mais pas elle-même). Cette semi-récursivité est limitée à un maximum (64 en standart) qui s'obtient par :

getIntegerv (GL_MAX_LIST_NESTING, GLint *nbln_max);

 

Lorsqu’elle ne servent plus les display lists peuvent être détruites.

GlDeleteLists(First, nx) ; /* Détruit nx display list à partir de first */

9.2Gestion automatique des indices de display listes

On peut demander la réservation d'une suite de range indices consécutifs inutilisés par :

GLint glGenLists (GLsizei range);

La procédure renvoi la valeur du premier indice ou zéro si l'allocation est impossible.

 

La suppression d’une série de range display listes, à partir de l’indice ix est effectuée par :

glDeleteLists (GLuint ix, GLsizei range);

 

La primitive :

GLboolean glIsList (GLuint ix);

renvoie TRUE si une display liste portant l'index ix est définie.

9.3Exécution de multiples display listes

La primitive :

void glListBase (GLuint base);

permet de définir une base (0 par défaut) de décalage des indices multiples à tracer.

 

La primitive :

void glCallLists (GLsizei n, GLenum type, const GLvoid *pIx);

demande l’exécution du tracé de n display listes dont les indices sont calculés en ajoutant base aux éléments pointés par pIx. type fournit le type pointé par pIx parmi les types suivants : GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT, ou GL_2BYTES, GL_3BYTES, GL4BYTES (est-ce que ces 3 derniers sont utilisés pour redresser les octets en questions ?).

 

Exemple :

char s[3]="AB";

glListBase(base) ;

glCallLists (2, GL_BYTE, (char *) s);

produit le tracé des display listes d'indice base+'A' et base+'B'. Cette procédure est surtout utilisée pour dessiner des textes..

10Les textures 2D

Les fichiers texture.h et texture.c décrits dans la superbible, chapitre 8 pages 282-265, permettent de lire une texture sur disque à partir d'un fichier *.bmp (TextureLoad) :

 

GLuint                           /* O - Numéro de l'objet texture ou 0 si erreur */

TextureLoad(char      *filename, /* I - Nom du fich *.bmp à lire */

            GLboolean alpha,     /* I - Genère alpha ? (GL_TRUE/GL_FALSE) */

            GLenum    minfilter, /* I - filtre réducteur (GL_NEAREST/GL_LINEAR)*/

            GLenum    magfilter, /* I - flitre amplificateur (GL_NEAREST/GL_LINEAR) */

            GLenum    wrap)      /* I - Repète ou clampe (GL_REPEAT/GL_WRAP) */

Exemple :

F16Texture = TextureLoad("camoflage.bmp", GL_FALSE, GL_NEAREST,

                                GL_NEAREST, GL_REPEAT);

F16Texture est un numéro utilisé comme un objet dans les primitives suivantes.

 

Ne pas oublier de faire glEnable(GL_TEXTURE_2D) avant tout tracé utilisant des textures.

 

glBindTexture(GL_TEXTURE_2D, F16Texture);

Cette instruction associe ensuite une texture à toutes les surfaces qui sont définies à la suite de cette instruction, jusqu'à ce qu'une nouvelle association soit définie ou jusqu'à ce qu'il y ait une interdiction d'association par un :

glDisable(GL_TEXTURE_2D);

 

Au cours de la définition d'une surface par ses sommets (x,y,z), on met en concordance un point (s,t) de la texture  avec le sommet (x,y,z) de la surface avec par la séquence :

glTexCoord2f(s,t) ; /* 0 <= s,t <= 1. */

glVertex3d(x,y,z);

La spécification du point de la texture  précède celle du sommet correspondant de la surface.

La texture dont les coins bas-gauche et haut-droit ont respectivement pour coordonnées (0.,0.) et (1.,1.) est étirée sur la surface pour satisfaire la concordance des points ainsi associés.

 

Une association automatique peut être effectuée entre des points d'une surface et les points (s,t) de la texture. Voir glTexGen*., glEnable(GL_TEXTURE_GEN_S) et glEnable(GL_TEXTURE_GEN_T).

 

Un objet texturé peut être dessiné en mode global color tracking après un glEnable(GL_COLOR_MATERIAL), mais dans ce cas il doit être précédé d'un glColor avec blanc ou gris, ou bien en mode local matériau après un glDisable(GL_COLOR_MATERIAL), mais dans ce cas il faut spécifier les 3 types de réflexion des matériaux, par exemple par :

    glMaterialfv(GL_FRONT, GL_AMBIENT, faible);

    glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);

    glMaterialfv(GL_FRONT, GL_SPECULAR, eteinte);

Attention à l'utilisation de la lumière spéculaire qui est très sensible à la taille des normale. Un rendu incorrect peut-être dû à l'utilisation de glScale qui modifie la taille des normales. Si on ne peut se passer de  glScale, faire un glEnable(GL_NORMALIZE) pour qu'openGL re-norme les normales, ou à partir d'openGL 1.2, glEnable(GL_RESCALE_NORMAL)

 

 

 

11Les quadrics glu

Les quadrics glu sont des objets prédéfinis que l'on peut représenter sous différents styles, avec différentes textures, etc.

A l'initialisation, les états par défaut sont positionnés par :

GLUquadricObj *monobj ;

monobj = gluNewQuadric();

puis on indique éventuellement qu'une texture sera associée à la quadric par :

gluQuadricTexture(monobj, GL_TRUE) ; /* GL_FALSE par défaut */

Si c'est le cas, on charge la texture par un TextureLoad.

 

A la définition des tracés :

On modifie le style avec lequel l’ objet suivant sera tracé par :

gluQuadricDrawStyle(monobj, drawstyle) ;

avec pour drawstyle, le choix parmi :

GLU_FILL : mode surface par remplissage des facettes (défaut) ,

GLU_LINE : tracé en fil de fer,

GLU_SILHOUETTE : tracé en fil de fer des arêtes externes uniquement,

GLU_POINT : tracé en mode points.

 

On peut définir également si les normales sont à générer :

gluQuadricNormals(monobj, normals) ;

avec pour normals, le choix parmi :

GLU_NONE : pas de normales générées,

GLU_FLAT : une normale générée par polygone

GLU_SMOOTH : une nomale générée par sommet (par défaut)

Ces indicateurs sont à mettre en cohérence avec celui de glShadeModel. En effet, si un veut des faces de couleur unique (GL_FLAT) , il est inutile de générer plusieurs normales par face, et on choisira donc GLU_FLAT. Par contre si on veut un effet de dégradé (GL_ SMOOTH), il faut générer une normale par sommet (GLU_ SMOOTH) afin que les couleurs calculées pour les sommets soient différentes, ce qui permet de réaliser ensuite le dégradé de couleur au niveau de la face.

 

On définit le sens des normales des objets quadriques par :

gluQuadricOrientation(monobj, orientation) ;

avec pour orientation le choix entre :

GLU_OUTSIDE : normales extérieures (par défaut)

GLU_INSIDE : normales intérieures.

 

On peut lier une texture aux surfaces des objets en faisant :

glBindTexture(GL_TEXTURE_2D, nomTexture);

puis en autorisant l’association de la texture à la quadrique par :

gluQuadricTexture(monobj, GL_TRUE) ; /* GL_FALSE par défaut */

Si c'est le cas, on charge la texture par un TextureLoad.

 

L'objet est construit à partir des primitives suivantes :

gluCylinder (monobj, Rbase, Rtop, hauteur, nbray, nbetag) ; /* Tronc de cône */

gluDisk(monobj, Rayin, Rayext, nbray, nbtours) /* rondelle */

gluPartialDisk(monobj, Rayin, Rayext, nbray, nbtours, Angldeb, Anglfin) ; /* Secteur */

gluSphere(monobj, Ray, nbmeri, nbparal) ; /* Sphère */

 

Lorsque la quadric ne sert plus, on peut la détruire par gluDeleteQuadric(monobj); En particulier, si elle a été mémorisée dans la définition d'une display liste, elle peut être détruite dès la cloture de celle-ci.

 

12Les mosaïques glu

Les mosaïques glu sont des surfaces complexes pouvant être non convexes qui sont automatiquement pavées et éventuellement texturées par la librairie glu.

A l'initialisation, une mosaïque vide est d'abord créé par :

#ifdef GLU_VERSION_1_2

#define Mosaique GLUtesselator

#else

#define Mosaique GLUtriangulatorObj

#endif /* GLU_VERSION_1_2 */

Mosaique *monobj ;

monobj = gluNewTess();

 

Ensuite il faut déclarer les routines (généralement OpenGL) utilisées pour définir les surfaces :

gluTessCallback(monobj, GLU_TESS_BEGIN, glBegin);

gluTessCallback(monobj, GLU_TESS_END, glEnd);

gluTessCallback(monobj, GLU_TESS_VERTEX, (void (CALLBACK *)())glu_vertex);

..........

où à titre d'exemple la fonction glu_vertex serait définie par :

void APIENTRY glu_vertex(GLdouble *xyz)

{glTexCoord2dv(xyz + 3); glVertex3dv(xyz);}

ce qui permet de mettre en concordance le sommet avec une texture.

 

Puis on les décrits par leurs contours successifs (externes puis éventuellement internes) par :

#ifdef GLU_VERSION_1_2

gluTessBeginPolygon(monobj, NULL);

  gluTessBeginContour(monobj);

    gluTessVertex(monobj,sommet_ext1, data_e1) ;

    gluTessVertex(monobj,sommet_ext2, data_e2) ;

    gluTessVertex(monobj,sommet_ext3, data_e3) ;

    gluTessVertex(monobj,sommet_ext4, data_e4) ;

    ......

  gluTessEndContour(monobj);  

  gluTessBeginContour(monobj);

    gluTessVertex(monobj,sommet_in1, data_i1) ;

    gluTessVertex(monobj,sommet_in2, data_i2) ;

    gluTessVertex(monobj,sommet_in3, data_i3) ;

    gluTessVertex(monobj,sommet_in4, data_i4) ;

    ......

  gluTessEndContour(monobj);  

gluTessEndPolygon(monobj);

#else

gluBeginPolygon(monobj);

    gluTessVertex(monobj,sommet_ext1, data_e1) ;

    gluTessVertex(monobj,sommet_ext2, data_e2) ;

    gluTessVertex(monobj,sommet_ext3, data_e3) ;

    gluTessVertex(monobj,sommet_ext4, data_e4) ;

    ......

  gluNextContour(monobj,GLU_INTERIOR);

    gluTessVertex(monobj,sommet_in1, data_i1) ;

    gluTessVertex(monobj,sommet_in2, data_i2) ;

    gluTessVertex(monobj,sommet_in3, data_i3) ;

    gluTessVertex(monobj,sommet_in4, data_i4) ;

    ......

gluEndPolygon(monobj);

#end

 

Les sommets* pointent sur des GLdouble* contenant les coordonnées des sommets du contour et les data* pointent sur un buffer de données transmises à la callback poistionnée par gluTessCallback(monobj, GLU_TESS_VERTEX, glu_vertex);:

 

Lorsque la mosaïque ne sert plus, on peut la détruire par : gluDeleteTess(monobj); En particulier, si elle a été mémorisée dans la définition d'une display liste, elle peut être détruite dès la cloture de celle-ci.

13Sauvegarde et récupération des éléments

13.1La pile des attributs

On peut utiliser une pile pour sauvegarder, puis restaurer la plupart des attributs, contextes et états de la machine openGL. Cette pile d'attribut est initialement vide. On y sauvegarde un ou un ensemble d'attributs par l'instruction glPushAttrib(mask). L'argument mask est un masque de bit où les bits à 1 indiquent les attributs, contextes ou états à sauvegarder. Généralement c'est parce qu'on va les modifier par la suite. On rétablit le fonctionnement correspondant aux attributs sauvegardés par l'instruction glPopAttrib(). L'utilisation est délicate si on fait plusieurs push avant de faire un pop.

La liste suivante fournit des masques prédéfinis pour un certain nombre de contextes.

Le masque spécial GL_ALL_ATTRIB_BITS est la réunion de tous.

13.2Accès directs aux éléments

Quasiment tous les éléments peuvent être accédés par le biais de glGetTypev(nom, *vecteur) avec pour Type :  Boolean, Double, Float, Integer. D'autres accès encore plus direct existent, voir sur l'URL  https://www.opengl.org/sdk/docs/man2 :

glGetActiveAttrib, glGetActiveUniform, glGetAttachedShaders, glGetAttribLocation, glGetBufferParameteriv, glGetBufferPointerv, glGetBufferSubData, glGetClipPlane, glGetColorTable, glGetColorTableParameter, glGetCompressedTexImage, gGetConvolutionFilter, glGetConvolutionParameter, glGetError, glGetHistogram, glGetHistogramParameter, glGetLight, glGetMap, glGetMaterial, glGetMinmax, glGetMinmaxParameter, glGetPixelMap, glGetPonterv, glGetPolygonStipple, glGetProgram, glGetProgramInfoLog, glGetQueryiv, glGetQueryObject, glGetSeparableFilter, glGetShader, glGetShaderInfoLog, glGetShaderSource, glGetString, glGetTexEnv, gletTexGen, glGetTexImage, glGetTexLevelParameter, glGetTexParameter, glGetUniform, glGetUniformLocation, glGetVertexAttrib, glGetVertexAttribPointerv, glIsEnabled

14La librairie GLUT

14.1Exemple d’utilisation

void main(void)

{

/* Initialisation du mode des buffers video de la fenetre */

    glutInitDisplayMode (GLUT_SINGLE | AUX_RGB);

/* Creation de la fenetre */

    glutCreateWindow ("NomAppli") ;

/* Spécification callback de tracé */

    glutDisplayFunc (Renderscene) ; /* void Renderscene(void) : prog de tracé */

/* Spécification callback indiquant la taille fenêtre et ses modifications */

    glutReshapeFunc (ChangeSize) ;  /* void ChangeSize(GLsizei w, Glsizei h) */

/* Routine d'initialisations diverses avant tracé */

    SetupRC();                      /* Pour initialisations */

/* Boucle sans fin de traitement des évènements */

    glutMainLoop() ;                /* Boucle traitement des évènements */

}

 

 

Si GLUT_SINGLE terminer Renderscene() par glFinish() ou glFlush(), et si GLUT_DOUBLE le terminer par glutSwapBuffers().

14.2Animations temporisées

14.2.1Activations à intervalles réguliers

Pour des animations temporisées on peut utiliser :

glutTimerFunc(delay, fonction, arg);

qui produit un appel différé après delay millisecondes de la callback fonction(arg). Celle-ci faits ses calculs et produit le dessin de la scène par un appel à glutPostRedisplay()qui lui-même produira un nouvel appel de la callback Renderscene().

A la fin la routine fonction, elle doit se terminer par un appel à glutTimerFunc(delay, fonction, arg); qui permettra sa réactivation. Le premier appel de glutTimerFunc(delay, fonction, arg) doit se trouver dans lllle main avant glutMainLoop();

 

14.2.2Synchronisation temporelle

Pour effectuer une synchronisation temporelle, avec calcul de paramètres fonction du temps écoulé, on peut utiliser le temps écoulé depuis le dernier appel :

curtime  = GetClock();

distance = curtime - LastTime;

LastTime = curtime;

où la routine GetClock est définie par :

double GetClock(void)

{

#ifdef WIN32

  SYSTEMTIME t;      /* Current time of day */

  GetSystemTime(&t);

  return (((t.wHour * 60.0) + t.wMinute) * 60 + t.wSecond + t.wMilliseconds * 0.001);

#else /* UNIX */

  struct timeval t; /* Current time of day */

  gettimeofday(&t, NULL);

  return ((double)t.tv_sec + 0.001 * (double)t.tv_usec);

#endif /* WIN32 */

}

Cet intervalle distance sert en particulier dans la fonctions idle() qui s'exécute en tache de fond, appelée à intervalles irréguliers quand tous les messages sont traités. Cette fonction est positionnée par la primitive glut :

glutIdleFunc(Idle);

14.3Interface clavier

Pour traiter les touches ordinaires du clavier mettre dans le main : glutKeyboardFunc(Fonction).

Il y aura appel de la callback Fonction(unsigned char key, int x, int y) avec key = le code ASCII de la touche. La position de la souris (x,y) est également transmise à la callback.

Pour traiter les touches spéciales mettre dans le main : glutSpecialFunc(Fonction).

Il y aura appel de la callback Fonction(int key, int x, int y) avec key = le code de la touche , à savoir  GLUT_KEY_UP (ou DOWN), GLUT_KEY_LEFT (ou RIGHT), GLUT_KEY_PAGE_UP (ou DOWN), GLUT_KEY_HOME, GLUT_KEY_END, GLUT_KE_INSERT, GLUT_KEY_F1 ...., GLUT_KEY_F12. La position de la souris (x,y) est également transmise à la callback.

14.4Interface souris    

 Pour traiter les mouvements de la souris mettre dans le main : glutMouseFunc(Fonction). Il y aura appel de la callback Fonction(int button, int state, int x, int y) dés qu'un évènement souis intervient avec :

button = GLUT_LEFT_BUTTON ou GLUT_RIGHT_BUTTON ou GLUT_MIDDLE_BUTTON,

state = GLUT_UP ou GLUT_DOWN,

La position de la souris (x,y) est également transmise à la callback.      

14.5Exemple de création de menu

    // Sousmenu nWireMenu

    nWireMenu = glutCreateMenu(Menufonc);

    glutAddMenuEntry("Sphere",1);

    .....

    // Sousmenu nSolidMenu

    nSolidMenu = glutCreateMenu(Menufonc);

    glutAddMenuEntry("Ballon",11);

    .....

    // Menu pricipal

    nMainMenu = glutCreateMenu(Menufonc);

    glutAddSubMenu("Wire", nWireMenu);

    glutAddSubMenu("Solid", nSolidMenu);

    // Attachement.

    glutAttachMenu(GLUT_RIGHT_BUTTON);

 

A la sélection d'un item du menu il y appel de la callback Menufonc(valeur) ; où valeur vaut le code associé à l'item sélectionné. Généralement Menufonc traite valeur puis produit un nouveau tracé par un appel à glutPostRedisplay() qui produira l'activation de Renderscene().

14.6Formes plus ou moins simples :

glutWireSphere(1.0f, 25, 25);

glutSolidSphere(1.0f, 25, 25);

glutWireCube(1.0f); /* Origine 0, coté=1 */

glutSolidCube(1.0f);

glutWireCone(0.30f, 1.1f, 20, 20);

glutSolidCone(0.30, 1.1f, 20, 20);

glutWireTorus(0.3f, 1.0f, 10, 25);

glutSolidTorus(0.3f, 1.0f, 10, 25);

glutWireDodecahedron();

glutSolidDodecahedron();

glutWireOctahedron();

glutSolidOctahedron();

glutWireTetrahedron();

glutSolidTetrahedron();

glutWireIcosahedron();

glutSolidIcosahedron();

glutWireTeapot(1.0f);

glutSolidTeapot(1.0f);

15Utilitaires, rappels et divers

 

Tracé d'un point :

#define Trapoint(x1,y1) glBegin(GL_POINTS); glVertex2f ((x1) , (y1)); glEnd();

 

Tracé d'une ligne :

#define Traligne(x1,y1,x2,y2) glBegin(GL_LINES); \

        glVertex2f ((x1) , (y1)); glVertex2f ((x2),(y2)); glEnd();

 

15.1Renvois à la superbible

glDrawPixels, glReadPixels : page 384

 

16Spécificités WIN32

16.1Le Pixel Format

C’est un format qui décrit les caractéristiques 3D que l’on désire utiliser, avec en particulier le nombre de plans couleurs,  le mode simple ou double buffer, la profondeur du Z buffer …..

Windows offre 24 formats soft générique, mais la carte graphique peut offrir des formats hard (ICD : Installable Client Driver) ou hard-soft (MCD : Mini Client Driver). La procédure suivante permet de sélectionner le format hard (s’il existe) ou soft le plus proche de celui qui est désiré :

 

void SetDCPixelFormat(HDC hDC, DWORD dwFlags, int nPlanes)

{

static PIXELFORMATDESCRIPTOR pfd ;

int fd ;

 

 memset(&pdf,0,sizeof(pfd)) ;

pfd.nSize = sizeof(pfd) ;     // Size of this structure

pfd.nVersion = 1 ;       // Version of this structure

pfd.dwFlags = PFD_SUPPORT_OPENGL | // Support OpenGL calls in window

dwFlags ;     // autres Flags passés en arg

 pfd.iPixelType = PFD_TYPE_RGBA ;    // RGBA Color mode

 pfd.cColorBits = nPlanes ;      // 32 (ou 24 pour PFD_DRAW_TO_BITMAP)

 pfd.cRedBits = 8 ;        // nb de bits rouge dans le buffeur RGBA

 pfd.cGreenBits = 8 ;      // nb de bits vert dans le buffeur RGBA

 pfd.cBlueBits = 8 ;        // nb de bits bleu dans le buffeur RGBA

 pfd.cDepthBits = 16 ;      // Size of depth buffer

 

 fd = ChoosePixelFormat(hDC, &pfd);

 SetPixelFormat(hDC, fd, &pfd);

}

 

Pour dwFlags on prendra :

PFD_DRAW_TO_WINDOW // pour dessiner à l’écran

PFD_DOUBLEBUFFER  // double buffer (si animation)

PFD_DRAW_TO_BITMAP // pour dessiner dans un bitmap mémoire (-> fichier .bmp)

16.2L’utilisation ds fenêtres Win32

Il faut les créer et les gérer avec les API Win32

 

Style enregistrement classe :

CS_HREDRAW | CS_VREDRAW |  CS_OWNDC

Les fenêtres qui accueillent des tracés Opengl doivent avoir pour style de classe CS_OWNDC pour conserver les choix effectués sur le DC et pas revenir systématiquement aux valeurs par défaut et les styles de création

 

Style création de la fenêtre :

WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN et WS_CLIPIBLINGS

ou bien

WS_POPUP | WS_CLIPCHILDREN et WS_CLIPIBLINGS

Les deux derniers pour éviter que le RC OpenGL soit utilisé hors de sa fenêtre et WS_POPUP si on ne veut pas de

Cadre afin de faire une fenêtre pouvant occuper tout l’écran.

De même que sous Win32 un device context (DC) contient un état des paramètres graphiques associés à une fenêtre, sous OpenGL un Rendering Context mémorise cet état. Un rendering contexte est créé par :

 HGLRC hRC; // OpenGL rendering context

 HDC hDC ;   // Windows DC

 …

hRC = wglCreateContext(hDC) ;

Plusieurs RC peuvent être associés à une même fenêtre, mais un seul peut être actif à la fois dans un thread, c’est le RC courant, qui est alors associé à un DC et à une fenêtre (ou bitmap, ou imprimante, ..). Un même RC peut être utilisé pour différentes fenêtres (elles doivent avoir le même Pixel format défini + loin)

Un RC est rendu courant et associé à un fenêtre par :

void wglMakeCurrent(HDC hDC, HGLRC hRC) ;

En standard, un RC est crée et associé à sa fenêtre. On aura en static dans la WndProc :

static HGLRC hRC = NULL; // Permanent OpenGL rendering context

static HDC hDC = NULL;   // Permanent Windows GDI DC privé.

 

Et on effectuera les traitements suivants :

 

switch(message) {

case WM_CREATE :

hDC = GetDC(hWnd); // -> hDC en static, une fois pour toutes

// Choix du Pixel format

SetDCPixelFormat(hDC, PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER, 32); // (cf ^)

hRC = wglCreateContext(hDC); // On crée le RC

wglMakeCurrent(hDC, hRC); // On rend le RC courant

………

return 0 ;

case WM_DESTROY :

wglMakeCurrent(hDC, NULL);

……… // détruire les display listes, textures, .. desallouer, .. puis :

wglDeleteContext(hRC) ;

ReleaseDC(hWnd, hDC);

………

PostQuitMessage(0) ;

return 0 ;

case WM_SIZE :

ChangeSize(LOWORD(lParam),HIWORD(lParam)) ;

return 0 ;

case WM_PAINT :

RenderScene() ;

SwapBuffers(hDC) ;

ValidateRect(hWnd,NULL) ; // Pour indiquer que c’est fait, car on n’utilise

// pas la paire BeginPaint – EndPaint.

return 0 ;

}

return DefWindowProc(hWnd,message,wParam,lParam) ;

16.3Pour sauver une image dans un .bmp

On peut lire l’image sur l’écran au moyen de glReadPixel (cf Chapitre 7 « Raster Graphics in OpenGL »), mais ceci peut conduire à récupérer l’image d’une autre fenêtre se trouvant au-dessus la fenêtre OpenGL. De plus la profondeur du buffeur écran est dans ce cas limitée a 8 ou 16 bits .

La méthode proposée dans la superbible consiste à dessiner l’image dans un contexte mémoire. On effectue donc les étapes suivantes :

 

BITMAPINFO info;

Glubyte   *bits;  /* Buffer for bitmap data */

HDC   dc;

HBITMAP bitmap;

HGLRC rc;     /* OpenGL rendering context */

 

 

// Création du contexte mémoire dc

dc = CreateCompatibleDC(NULL)

 

// Création du bitmap et liaison avec le dc

memset(&info,0,sizeof(info)) ;

info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER) ;

info.bmiHeader.biWidth = Width ;

info.bmiHeader.Height = Height ;

info.bmiHeader.biPlanes = 24 ;

info.bmiHeader.biCompression = BI_RGB ;

bitmap = CreateDIBSection(dc, &info, DIB_RGB_COLORS, &bits, NULL, 0) ;

SelectObject(dc, bitmap) ;

 

// Choix du format pixel

SetDCPixelFormat(dc, PFD_DRAW_TO_BITMAP, 24); // (cf ^)

 

// Creation et liaison du contexte de rendu

hRC = wglCreateContext(dc); // On crée le RC

wglMakeCurrent(dc, rc);  // On rend le RC courant

 

// …………… On effectue les tracés

 

// On sauve le bitmap (cf fichier bitmap.c)

SaveDIBitmap("terrain.bmp", &info, bits);

 

// On restitue tout

wglDeleteContext(rc) ;

DeleteObject(bitmap) ;

DeleteDC(dc);

 

16.4Pour imprimer l’image.

17Temps réel

Pour accélérer la procédure, on peut supprimer le ValidateRect(hWnd,NULL) dans le WM_PAINT

 

On peut également remplacer la traditionnelle boucle while(GetMessage… par :

 

while(1)

{

if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

 {

   TranslateMessage(&msg);

   DispatchMessage(&msg);

 

   // Break when program terminates

   if(msg.message == WM_QUIT) break;

 }

 

 if(hRC) // s’il existe on dessine.

 {

   // Call OpenGL drawing code

   RenderScene();

 

   // Call function to swap the buffers

   SwapBuffers(hDC);

 }

}

 

Ne pas oublier de mettre hRC à NULL dans le WM_DESTROY, juste apprès le wlDeleteContext(hRC);