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
Certains paragraphes qui datent de 2000 sont obsolètes.
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.
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)
|
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 |
[] |
|
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) */
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 */
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() ;
où 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]
si # = 4 : 4 arguments (x,y,z,w) => x/w, y/w, z/w
si # = 3 -> w = 1.
si # = 2 -> z = 0. et w = 1.
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
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) ;
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) ;
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) ;
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) ;
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é.
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.
On peut définir un motif de remplissage par :
void glPolygonStipple (const GLubyte *pmask);
où 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 */
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.
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}.
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 :
les coordonnées des points (utilisées par glVertex*) par groupes de 2, 3 ou 4 (2D, 3D ou homogènes) valeurs,
les coordonnées des normales (utilisées par glNormal*) par groupes de 3 valeurs,
les composantes des couleurs (utilisées par glColor*) par groupes de 3 ou 4 selon le cas (RGB, RGBA),
les index de couleur (utilisés un par un par glIndex*),
les flags de traçage des arêtes internes (utilisés par glEdgeFlag*) ,
les coordonnées de texture (utilisées par glTexCoord*) par groupes de 1 à 4.
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ù :
size est le nombre de coordonnées par élément (3 ou 4 par exemple pour les couleurs),
type est le type des valeurs (GL_INT, ….)
stride est l’offset en octets entre deux éléments consécutifs dans le tableau,
pointer est le pointeur sur la première coordonnée du premier élément à prendre en compte.
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.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.
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() ;
mode vaut GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_QUAD_STRIP, GL_QUADS, ou GL_POLYGON,
first est l’index de départ dans les tableaux activés,
count est le nombre d’éléments à utiliser.
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.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);
stride est l’offset en octets entre deux sommets consécutifs dans le tableau, (si 0, c’est celui du format qui est utilisé).
pointer est le pointeur sur la première coordonnée du premier sommets dans le tableau,
format spécifie tous les types d’éléments présents, les types des valeurs de ces éléments, les décalages éventuels,… Exemple :
GL_V2F : Vertex de 2 flottants par point,
GL_V3F : Vertex de 3 flottants par point,
GL_C4UB_V2F : Couleurs de 4 UnsignedByte et Vertex de 2 flottants par point,
GL_C4UB_V3F : Couleurs de 4 UnsignedByte et Vertex de 3 flottants par point,
GL_C3F_V3F : Couleurs de 3 flottants et Vertex de 3 flottants par point,
GL_N3F_V3F : Normales de 3 flottants et Vertex de 3 flottants par point,
GL_C4F_N3F_V3F : Couleurs de 4 flottants, Normales de 3 flottants et Vertex de 3 flottants par point,
GL_T2F_V3F : Textures de 2 flottants et Vertex de 3 flottants par point,
GL_T4F_V4F : Textures de 4 flottants et Vertex de 3 flottants par point,
GL_T2F_C4UB_V3F : Textures de 2 flottants, Couleurs de 4 UnsignedByte et Vertex de 3 flottants par point,
GL_T2F_C3F_V3F : Textures de 2 flottants, Couleurs de 3 flottants et Vertex de 3 flottants par point,
GL_T2F_N3F_V3F : Textures de 2 flottants, Normales de 3 flottants et Vertex de 3 flottants par point,
GL_T2F_C4F_N3F_V3F : Textures de 2 flottants, Couleurs de 4 flottants, Normales de 3 flottants et Vertex de 3 flottants par point,
GL_T4F_C4F_N3F_V3F : Textures de 4 flottants, Couleurs de 4 flottants, Normales de 3 flottants et Vertex de 3 flottants par point.
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) ;
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) ;
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 */
où 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.
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 :
fx = fy = 1. : aspect identique
fx = -1 : inversion droite-gauche
fy = -1 : inversion haut bas
fx, fy qcq : modification des proportions.
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 */
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).
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 */
La pile GL_MODEL_VIEW permet de déplacer les objets dans la scène par rapport à l'observateur (ou l’observateur par rapport à la scène).
La pile GL_PROJECTION permet de définir le mode de projection et le volume de clipping (dans le repère observateur).
La pile GL_TEXTURE permet de manipuler les textures
Les opérations se font dans l'ordre suivant :
1.Définition de la zone écran utilisée glViewport(xleft, ybas, large, haut);
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.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.
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 :
la position de l'observateur O : eyex, eyey, eyez
un point sur la ligne de visée M : centerx, centery, centerz
la verticale l'observateur K : upx, upy, upz
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.
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();
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) ;
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 :
Angley est l'angle de vue vertical = (2.*atan((Top-Bottom)/2./Near)*180./M_PI)
Aspect = (Right-Left)/(Top-Bottom)
Near et Far : distances des deux plans de clipping, doivent être positives
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) ;
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ù :
plane = GL_CLIP_PLANEi avec i = 0 à 5
coefs : pointe sur les 4 coefficients A,B,C,D de l'équation Ax+By+Cz+D<0 des points éliminés. L'équation est spécifiée dans le repère courant, au niveau où apparait l'appel de la primitive.
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);
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ù :
xleft et ybas donnent la position dans la fenêtre (en pixels) du coin bas-gauche affecté au coin Left,Bottom de la scène. Généralement on fait xleft = ybas =0 si on veut tracer dans toute la fenêtre (les pixels x < xleft || y < ybas sont inaccessibles).
w et h donnent la largeur et hauteur en pixels de la partie de la fenêtre affectée à Right-Left et Top-Bottom.
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 :
x = xp + w * (X_d - Left)/(Right - Left)
y = yp + h * (Y_d - Bottom)/(Top - Bottom)
Un point est non visible si :
X_d < Left ou X_d > Right (hors tronc de projection)
x > largeur fenêtre (cas où xp+w > largeur fenêtre)
et idem en y.
Par défaut on a avant tout recadrage de la fenêtre :
glViewport (0, 0, w_wind, h_wind);
où 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) ;
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.
Chargement d’une matrice unité comme matrice courante par :
void glLoadIdentity(void);
Chargement d’une matrice donnée comme matrice courante par :
void glLoadMatrix{fd} (const TYPE *mat);
Postmultiplication de la matrice courante C = C*M par :
void glMultMatrix{fd} (const TYPE *mat);
Translation de la matrice courante C = C*T(dx, dy, dz) par :
void glTranslate{fd} (TYPE dx, TYPE dy, TYPE dz);
Rotation C = C*Rot(angle, u), angle en degré par :
void glRotate{fd} (TYPE angle, TYPE ux, TYPE uy, TYPE uz);
Mise à l'échelle C = C*K par :
void glScale{fd} (TYPE kx, TYPE ky, TYPE kz) ;
Empilage de la matrice courante. La pile est décalée d'un cran vers le bas avec duplication de la matrice courante en haut, qui se trouve ainsi en 1ere et 2eme position :
void glPushMatrix(void) ;
Dépilage de la matrice courante. La pile est décalée d'un cran vers le haut. La matrice qui était en 1 est perdue. Celle qui était en 2 remonte en 1 et etc :
void glPopMatrix (void) ;
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 |
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ù :
GL_xxxx = GL_BACK : tracé seulement dans le buffer invisible,
GL_xxxx = GL_FRONT : tracé seulement dans le buffer visible,
GL_xxxx = GL_FRONT_AND_BACK : tracé dans les 2 buffers.
GL_xxxx = GL_NONE : tracés fictifs (pour initialiser le Zbuffer par ex .)
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() ;
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) ;
où 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) ;
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.
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 */
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() ;
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);
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.
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. Que toutes les faces aient des normales définies (pour évaluer la quantité de lumière reçue et ré-émise),
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.Créer des sources de lumière, les sélectionner et les positionner,
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 :
•La lumière ambiante (paramètre GL_AMBIENT) est une lumière qui arrive de toutes les directions ou rebondit dans toutes les directions (même en provenance d’une source ponctuelle). Il y a une lumière ambiante générale par défaut, qui peut être supprimée. Une source ponctuelle peut émettre une partie de lumière ambiante qui s’ajoute à la l’ambiante générale.
•La lumière diffuse (paramètre GL_DIFFUSE) est une lumière qui est émise dans une direction donnée par les sources mais qui est réfléchie uniformément dans toutes les directions par les objets. La lumière renvoyée est calculée avec en plus un facteur d’atténuation fonction de l’angle d’incidence de la lumière sur l’objet (en cosinus).
•La lumière spéculaire ((paramètre GL_SPECULAR) est une qui est émise d’une direction donnée par les sources et qui est réfléchie autour d'une direction privilégiée (comme un faisceau laser) par les objets. Par rapport au calcul effectué pour la réflexion diffuse, il y a en plus un facteur d’atténuation fonction de l’angle de vue de la face par l’observateur (en cosinus).
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).
Dans ce qui suit j'utilise les 5 niveaux d'éclairage, définis par des couleurs RVBA, suivants :
•noir, eteint, eteinte = (0., 0., 0., 1.)
•faible = (0.2, 0.2 , 0.2 , 1.)
•moyen, moyenne = (0.5, 0.5, 0.5, 1.)
•fort, forte = (0.8, 0.8, 0.8, 1.)
•puissant, puissante (1., 1., 1., 1.)
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.
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) :
•mode = GL_FALSE : Par défaut, observateur situé à l’infini.,
•mode = GL_TRUE : observateur situé à l'origine du repère openGL.
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.
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.
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 :
•Si toutes les faces internes sont invisibles, il est inutile de calculer leur éclairage, on laisse GL_LIGHT_MODEL_TWO_SIDE à GL_FALSE qui est la valeur par défaut,
•si des faces internes peuvent être vues par des ouvertures, il faut mettre GL_LIGHT_MODEL_TWO_SIDE à GL_TRUE,
•il faut impérativement qu'il y ait concordance entre l'orientation des faces définies par le sens de parcours des sommets qui les décrivent ET le sens des normales externes,
On définit une source lumineuse localisée par :
void glLight{if}[v](GLenum nolum, GLenum mode, TYPE param);
où 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);
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é :
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;
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) :
•indique que la couleur de réflexion de la lumière GL_DIFFUSE ne concernera que les faces avant et sera fixé par les futurs glColor,
•le rendu des autres types de lumière dépend des glMaterialfv(face, type, couleur) précédemment spécifiés,
•Si glEnable(GL_COLOR_MATERIAL) est placé avant ce glColorMaterial(GL_FRONT, GL_DIFFUSE), momentanément les valeurs par défauts GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE sont actives ce qui fait que ces lumières prennent la valeur du dernier glColor actif, et de plus, comme la lumière GL_AMBIENT n'est pas prise en compte dans notre exemple, les futurs glColor ne la concernent pas et elle va conserver cette couleur !!
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.
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).
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.
Vérifier :
1.Que des normales sont définies pour toutes les faces à tracer et bien unitaires (attention à glScale qui modifie la taille des normales),
2.Que glEnable(GL_LIGHTING) est actif,
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.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 :
•glMaterialfv(GL_FRONT, GL_AMBIENT, faible);
•glMaterialfv(GL_FRONT, GL_DIFFUSE, forte);
•glMaterialfv(GL_FRONT, GL_SPECULAR, eteinte);
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.
Cette définition écrase le dessin pré-existant de même indice :
void glNewList(GLuint ix, GLenum mode);
....
/* routines de desssin */
....
void glEndList
où :
ix est un numéro positif choisi pour identifier le dessin,
mode : GL_COMPILE ou GL_COMPILE_AND_EXECUTE
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 */
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.
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..
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)
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.
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.
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.
•GL_ACCUM_BUFFER_BIT : Accumulation buffer clear value
•GL_COLOR_BUFFER_BIT : GL_ALPHA_TEST enable bit - Alpha test function and reference value - GL_BLEND enable bit - Blending source and destination functions - Constant blend color - Blending equation - GL_DITHER enable bit - GL_DRAW_BUFFER setting - GL_COLOR_LOGIC_OP enable bit - GL_INDEX_LOGIC_OP enable bit - Logic op function - Color mode and index mode clear values - Color mode and index mode writemasks
•GL_CURRENT_BIT : Current RGBA color - Current color index - Current normal vector - Current texture coordinates - Current raster position - GL_CURRENT_RASTER_POSITION_VALID flag - RGBA color associated with current raster position - Color index associated with current raster position - Texture coordinates associated with current raster position - GL_EDGE_FLAG flag
•GL_DEPTH_BUFFER_BIT : GL_DEPTH_TEST enable bit - Depth buffer test function - Depth buffer clear value - GL_DEPTH_WRITEMASK enable bit
•GL_ENABLE_BIT : GL_ALPHA_TEST flag - GL_AUTO_NORMAL flag - GL_BLEND flag - Enable bits for the user-definable clipping planes - GL_COLOR_MATERIAL - GL_CULL_FACE flag - GL_DEPTH_TEST flag - GL_DITHER flag - GL_FOG flag - GL_LIGHTi (all) - GL_LIGHTING flag - GL_LINE_SMOOTH flag - GL_LINE_STIPPLE flag - GL_COLOR_LOGIC_OP flag - GL_INDEX_LOGIC_OP flag - GL_MAP1_x where x is a map type - GL_MAP2_x where x is a map type - GL_MULTISAMPLE flag - GL_NORMALIZE flag - GL_POINT_SMOOTH flag - GL_POLYGON_OFFSET_LINE flag - GL_POLYGON_OFFSET_FILL flag - GL_POLYGON_OFFSET_POINT flag - GL_POLYGON_SMOOTH flag - GL_POLYGON_STIPPLE flag - GL_SAMPLE_ALPHA_TO_COVERAGE flag - GL_SAMPLE_ALPHA_TO_ONE flag - GL_SAMPLE_COVERAGE flag - GL_SCISSOR_TEST flag - GL_STENCIL_TEST flag - GL_TEXTURE_1D flag - GL_TEXTURE_2D flag - GL_TEXTURE_3D flag - Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
•GL_EVAL_BIT : GL_MAP1_x enable bits, where x is a map type - GL_MAP2_x enable bits, where x is a map type - 1D grid endpoints and divisions - 2D grid endpoints and divisions - GL_AUTO_NORMAL enable bit
•GL_FOG_BIT : GL_FOG enable bit - Fog color - Fog density - Linear fog start - Linear fog end - Fog index - GL_FOG_MODE value
•GL_HINT_BIT : GL_PERSPECTIVE_CORRECTION_HINT setting - GL_POINT_SMOOTH_HINT setting - GL_LINE_SMOOTH_HINT setting - GL_POLYGON_SMOOTH_HINT setting - GL_FOG_HINT setting - GL_GENERATE_MIPMAP_HINT setting - GL_TEXTURE_COMPRESSION_HINT setting
•GL_LIGHTING_BIT : GL_COLOR_MATERIAL enable bit - GL_COLOR_MATERIAL_FACE value - Color material parameters that are tracking the current color - Ambient scene color - GL_LIGHT_MODEL_LOCAL_VIEWER value - GL_LIGHT_MODEL_TWO_SIDE setting - GL_LIGHTING enable bit - Enable bit for each light - Ambient, diffuse, and specular intensity for each light - Direction, position, exponent, and cutoff angle for each light - Constant, linear, and quadratic attenuation factors for each light - Ambient, diffuse, specular, and emissive color for each material - Ambient, diffuse, and specular color indices for each material - Specular exponent for each material - GL_SHADE_MODEL setting
•GL_LINE_BIT : GL_LINE_SMOOTH flag - GL_LINE_STIPPLE enable bit - Line stipple pattern and repeat counter - Line width
•GL_LIST_BIT : GL_LIST_BASE setting
•GL_MULTISAMPLE_BIT : GL_MULTISAMPLE flag - GL_SAMPLE_ALPHA_TO_COVERAGE flag - GL_SAMPLE_ALPHA_TO_ONE flag - GL_SAMPLE_COVERAGE flag - GL_SAMPLE_COVERAGE_VALUE value - GL_SAMPLE_COVERAGE_INVERT value
•GL_PIXEL_MODE_BIT : GL_RED_BIAS and GL_RED_SCALE settings - GL_GREEN_BIAS and GL_GREEN_SCALE values - GL_BLUE_BIAS and GL_BLUE_SCALE - GL_ALPHA_BIAS and GL_ALPHA_SCALE - GL_DEPTH_BIAS and GL_DEPTH_SCALE - GL_INDEX_OFFSET and GL_INDEX_SHIFT values - GL_MAP_COLOR and GL_MAP_STENCIL flags - GL_ZOOM_X and GL_ZOOM_Y factors - GL_READ_BUFFER setting
•GL_POINT_BIT : GL_POINT_SMOOTH flag - Point size
•GL_POLYGON_BIT : GL_CULL_FACE enable bit - GL_CULL_FACE_MODE value - GL_FRONT_FACE indicator - GL_POLYGON_MODE setting - GL_POLYGON_SMOOTH flag - GL_POLYGON_STIPPLE enable bit - GL_POLYGON_OFFSET_FILL flag - GL_POLYGON_OFFSET_LINE flag - GL_POLYGON_OFFSET_POINT flag - GL_POLYGON_OFFSET_FACTOR - GL_POLYGON_OFFSET_UNITS
•GL_POLYGON_STIPPLE_BIT : Polygon stipple image
•GL_SCISSOR_BIT : GL_SCISSOR_TEST flag - Scissor box
•GL_STENCIL_BUFFER_BIT : GL_STENCIL_TEST enable bit - Stencil function and reference value - Stencil value mask - Stencil fail, pass, and depth buffer pass actions - Stencil buffer clear value - Stencil buffer writemask
•GL_TEXTURE_BIT : Enable bits for the four texture coordinates - Border color for each texture image - Minification function for each texture image - Magnification function for each texture image - Texture coordinates and wrap mode for each texture image - Color and mode for each texture environment - Enable bits GL_TEXTURE_GEN_x, x is S, T, R, and Q - GL_TEXTURE_GEN_MODE setting for S, T, R, and Q - glTexGen plane equations for S, T, R, and Q - Current texture bindings (for example, GL_TEXTURE_BINDING_2D)
•GL_TRANSFORM_BIT : Coefficients of the six clipping planes - Enable bits for the user-definable clipping planes - GL_MATRIX_MODE value - GL_NORMALIZE flag - GL_RESCALE_NORMAL flag -
•GL_VIEWPORT_BIT : Depth range (near and far) - Viewport origin and extent.
Le masque spécial GL_ALL_ATTRIB_BITS est la réunion de tous.
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
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().
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();
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);
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.
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.
// 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().
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);
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();
glDrawPixels, glReadPixels : page 384
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)
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) ;
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);
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);