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