Recueil rapide sur glut

Transcription

Recueil rapide sur glut
Recueil rapide sur glut
Cours de synthèse d’image I
— Master 1 Ingénierie Informatique —
1
Présentation d’OpenGL et de Glut
Sommairement, OpenGL est une API (Application Programming Interface) en C permettant
de controler les cartes graphiques. Elle permet ainsi d’afficher sur l’écran des scènes 2D et 3D.
Glut est quand à elle une bibliothèque en C permettant de créer des fenêtres (fait l’interface
avec le serveur graphique du système : XWindows ou DirectX) dans lesquelles des scènes 3D
peuvent être affichées via OpenGL. GLu est une surcouche à OpenGL et propose un ensemble
de fonctions utilitaires. OpenGL est une API dite de bas niveau, car elle gère directement le
matériel. Glut est une bibliothèque plus haut niveau car elle fait l’interface avec le serveur
graphique, gère les interactions utilisateur...
Notez que la plupart des cartes graphiques du commerce (notamment Nvidia et ATI)
disposent de driver OpenGL. La dernière version d’OpenGL est la version 2.0. Glut est une
bibliothèque disponible pour les systèmes Linux et Windows. La dernière version est la 3.7.
1.1
Utiliser OpenGL et Glut
Pour la compilation, après avoir installer les bibliothèques, vous pouvez les utiliser en insérant
les headers dans le code C avec :
#include <GL/glut.h>
Notez que glut.h s’occupe d’appeler gl.h et glu.h.
Pour la phase de lien, il faut utiliser les flags suivant : -lGL -lGLU -lglut
1.2
D’autres gestionnaires de fenêtre 3D
Il existe bien sûr d’autres alternatives plus robuste, plus puissante et plus complexe que glut.
Glut a tout simplement l’avantage d’être très simple d’utilisation ce qui est un gros plus pour
l’enseignement. Voici une liste non exhaustive d’autres systèmes d’interface :
• SDL : orienté jeu (www.libsdl.org/)
• Qt (QGLWidget) : IHM complète multiplateforme (www.trolltech.com/products/qt/)
• GTK : IHM généraliste (www.gtk-fr.org/wakka.php?wiki=OpenGlEtGtk)
• Crazy Eddy GUI System : orienté jeu (www.cegui.org.uk/)
• libUFO : une autre IHM orientée OpenGL (libufo.sourceforge.net/)
• GLGooey : idem (permet la transparence des fenêtres) (glgooey.sourceforge.net/)
1
Autres choses utiles :
• Une interface haut niveau au dessus de glut : glui (glui.sourceforge.net/)
• Un système simple d’intégration du son : OpenAL (www.openal.org/)
2
Philosophie d’OpenGL
La stratégie utilisée par les cartes graphiques pour fonctionner, autant en 2D qu’en 3D, est
de redessiner entièrement la scène à chaque image (frame). Ainsi pour obtenir une video 3D
à 30 images par seconde, il faut dessiner la scène 30 fois chaque seconde. Cette stratégie a
un défaut évident, on ne profite jamais du résultat de la frame précédente pour calculer la
frame suivante. Mais bien d’autres avantages plus subtil expliquent ce choix (notamment la
gestion des faces cachées).
Ainsi pour chaque frame, l’ensemble des sommets, appelés vertices en anglais, est envoyé
à la carte graphique. Ceux-ci sont regroupés en ensemble de points, de lignes, de triangles ou
de quadrilatères. On peut également envoyer des sommets sous forme de maillages complets
appelés vertex arrays.
Ces sommets sont donc transmis à la carte graphique et vont traverser ce que l’on appelle
le pipeline graphique OpenGL. Ce pipeline effectue dans l’ordre les opérations suivantes :
opérations sur les sommets, projection et rasterisation, opérations sur les fragments d’image,
fixation dans le frambuffer. Le framebuffer représente l’image finale et tout son environnement
dont le depth buffer (buffer de profondeur), le stencil buffer, la couche alpha (la transparence)
ainsi que d’autre buffers de couleur.
Le détail du fonctionnement d’OpenGL vous sera donné en cours et ne sera donc pas
traité ici.
3
3.1
Fonctionnement de glut
Philosophie
Glut utilise un mécanisme dit de callback. Cela signifie que glut va prendre la main sur votre
programme et vous la redonner lors de certains évènements utilisateurs : clic sur la souris,
drag, appui sur une touche ou un redimensionnement de la fenêtre.
Ainsi une fois la fenêtre créée, seules certaines fonctions seront appelées par glut pour
modifier l’état de votre programme et de la scène.
2
3.2
Minimum vital pour avoir une fenêtre
Tout d’abord, il faut envoyer à glut les paramètres initiaux de l’application (ceux du main).
Cela se fait grâce à la fonction glutInit(int* argc,char** argv).
Créer une fenêtre se fait très facilement avec glut. Pour ce faire, vous devez utiliser la
fonction glutCreateWindow. Elle prend en argument une chaı̂ne de caractères représentant
le titre de la fenêtre. Elle retourne 0 si la création n’a pu avoir lieu ou un indentifiant de
fenêtre dans le cas contraire.
La seconde chose à faire est d’indiquer à glut la fonction utilisée pour le dessin. Cela
se fait en enregistrant la fonction que vous aurez préalablement créée grâce à la fonction
glutDisplayFunc(nom_fonction). nom_fonction est simplement le nom de votre fonction
de dessin (qui ne prend aucun argument et retourne void).
Enfin, pour laisser la main à glut qui va alors gérer les évènements, vous utilisez la fonction
glutMainLoop()
3.3
Customiser notre fenêtre
Voici un ensemble de fonctions permettant de personnaliser les fenêtres de glut. (quand les
types de retour ne sont pas indiqués, c’est void)
Signature de la fonction
int glutCreateWindow(char* nom)
Explication
Crée une fenêtre de nom nom
Renvoie un identifiant (int) de la fenêtre.
Envoie les paramêtres du
main à l’init de Glut.
Place la fenêtre en (x,y)
à partir du coin haut à gauche de l’écran
Fixe la longueur (w) et la hauteur (h)
de la fenêtre
Place la fenêtre en (x,y)
Redéfini la fenêtre à la taille (w,h)
Montre,
cache,
et iconifie la fenêtre
Met la fenêtre en mode plein écran
Fixe le titre de la fenêtre à nom
glutInit(int *argc,char **argv)
glutInitWindowPosition(int x,int y)
glutInitWindowSize(int w,int h)
glutPositionWindow(int x,int y)
glutReshapeWindow(int w,int h)
glutShowWindow()
glutHideWindow()
glutIconifyWindow()
glutFullScreen()
glutSetWindowTitle(char *nom)
La seule différence entre les fonctions glutInitWindowPosition, glutInitWindowSize et
les fonctions glutPositionWindow, glutReshapeWindow est que les premières doivent être
utilisées avant la création de la fenêtre alors que les secondes doivent être utilisées après sa
création.
3
3.4
Gérer l’affichage de son image
La première chose que l’on peut faire c’est définir l’environnement du framebuffer (cf. section
2). Cela permet notamment de choisir le nombre de couleurs du buffer : une luminance en
niveau de gris, une image en couleur RVB ou bien une image couleur avec une couche pour
la transparence (couche alpha). Vous pouvez également préciser si vous souhaitez utiliser le
depth buffer (buffer de profondeur) ou encore le stencil buffer. Enfin, vous pouvez spécifier si
vous utilisez un buffer ou un double buffer (cf. ci dessous). Pour gérer ces différentes options,
vous devrez utiliser la fonction glutInitDisplayMode qui prend en argument un ensemble
de code listé ci dessous :
Code
GLUT_RGBA
GLUT_RGB
GLUT_LUMINANCE
GLUT_INDEX
GLUT_DEPTH
GLUT_STENCIL
GLUT_SINGLE
GLUT_DOUBLE
Explication
Spécifient un buffer RVBA. En fait ce sont les mêmes
code car le RVB simple n’est pas implémenté
Spécifie un buffer ne contenant qu’une luminance
indiqué dans le canal rouge.
Mode de couleur indexé (avec carte de couleur)
Rajoute un buffer de profondeur
Rajoute un stencil buffer
Mode en simple buffer
Mode en double buffer
Par défaut, glut gère un environnement en simple buffer (GL_SINGLE), en couleur avec alpha
(GLUT_RGBA) et sans stencil ni depth buffer. Par exemple, pour obtenir un framebuffer en
couleur avec transparence, avec un depth buffer et en double buffer, il faut appeler :
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
Pour comprendre la différence entre l’utilisation d’un simple buffer ou d’un double bufer
voyons à quoi sert un double buffer. Un double buffer permet de disposer de 2 images en
même temps : une est affichée à l’écran (le buffer d’affichage), et l’autre sert d’image de travail
(le buffer de travail). Lorsque les opérations de dessin sont terminées dans le buffer de travail
alors celui-ci et le buffer d’affichage sont échangés (ou recopiés). On appelle ce processus le
swap. Le mode en simple buffer ne permet pas le swap et oblige de travailler directement sur
l’image affichée ce qui peut provoquer des phénomènes de scintillement et de papillotement de
l’image. Par contre, lorsque l’on fonctionne en double buffer, il faut explicitement demander
l’échange des deux buffers. Cela se fait grâce à la commande glutSwapBuffers(). En règle
générale, cette commande se situe à la fin de la fonction de dessin.
Enfin, une fois (à l’initialisation) la fenêtre affichée, celle-ci ne sera de nouveau dessinée
que si le programmeur le demande explicitement. Il s’agit en fait d’une demande (un post)
de redessinement au serveur graphique du système. Cette demande se fait en appelant la
fonction glutPostRedisplay() qui entrainera à plus ou moins courte échéance l’appel de
votre fonction de dessin. Attention à ne jamais appeler explicitement votre fonction de
dessin.
4
3.5
Les événements dans glut
A partir du moment où Glut prend la main (par l’appel de l’instruction glutMainLoop), on
ne peut plus gérer le programme. Heureusement, Glut peut nous redonner la main lorsque
certains événements se produisent. Par exemple, à chaque fois que la fenêtre doit être dessinée
Glut fait appel à la fonction définie lors de l’appel à glutDisplayFunc(...).
On peut forcer Glut à nous laisser la main en lui indiquant les fonctions à appeler
lorsque certains événements ont lieu. Cela se fait grâce à des fonctions du même type que
glutDisplayFunc(...). Ces fonctions sont indiquées ci-dessous. Ce mécanisme d’appel par
événement s’appelle un mécanisme de callback (rappel).
Fonctions
glutDisplayFunc(<nom_fonction>)
glutReshapeFunc(<nom_fonction>)
glutKeyboardFunc(<nom_fonction>)
glutMouseFunc(<nom_fonction>)
glutMotionFunc(<nom_fonction>)
glutIdleFunc(<nom_fonction>)
événement déclencheur
Signature de la fonction à implémenter
La fenêtre est dessinée.
void display(void);
La fenêtre est redimensionnée.
void reshape(int new_w,int new_h);
new_w : nouvelle longueur
new_h : nouvelle hauteur
Une touche est tapée au clavier.
void keyfonc(unsigned char key,int x,y);
key : Touche frappée (en code ASCII)
x,y : Position de la souris à cet instant
Un bouton de la souris est cliqué.
void souris(int qui,int state,int x,y);
qui : Quel bouton cliqué
GLUT_LEFT_BUTTON, GLUT_RIGHT_BUTTON
ou GLUT_MIDDLE_BUTTON
state : GLUT_DOWN ou GLUT_UP
x,y : Position de la souris à cet instant
Tant qu’un bouton souris est enfoncé.
void souris_down(int x,int y);
x,y : Position de la souris à cet instant
Rien (fonction effectuée en permanence).
void myidle();
Note : <nom_fonction> doit être remplacé par le nom de votre fonction appelée.
5
3.6
Des menus
Créer un menu se fait grâce à l’instruction :
int glutCreateMenu(<nom_fonction>);
La fonction est appelée à chaque fois que le menu est utilisé et doit avoir la signature
void <nom_fonction>(int val);
La valeur val est l’index de l’élément du menu cliqué par l’utilisateur. Pour ajouter des
éléments à ce menu, on utilise la fonction :
glutAddMenuEntry(char *label,int index);
où label est le texte de l’élément et index sa position dans le menu (on commence par 1).
Finalement, déclencher l’apparition du menu se fait à l’aide d’un bouton de la souris et pour
ce faire, il faut utiliser la fonction :
glutAttachMenu(int flag_bouton);
où flag_bouton peut prendre les valeurs GLUT_LEFT_BUTTON, GLUT_RIGHT_BUTTON
ou GLUT_MIDDLE_BUTTON . Cette fonction rattache le menu courant à un bouton de la souris.
4
En savoir plus...
Tout est dans les spécifications :
www.opengl.org/resources/libraries/glut/glut-3.spec.pdf
6