Une introduction à l'OpenGL "Moderne" - Chapitre 2: Hello World: Le Slideshow

Sommaire

Mise à jour: J'ai résolu les soucis que certaines personnes ont pu rencontrer, tout particulièrement ceux concernant la compilation sur Unix et Visual C++. Ce chapitre à également sa page Reddit.

:longBar:

Dans le chapitre précédent, je vous présentais un schéma du pipeline graphique. C'est l'heure de le mettre en pratique. Avant que nous essayions rendre une scène 3d, je vais faire comme dans tout bon tutorial, à savoir, une simple application "hello world" en deux dimension, afin de présenter les bases de l'API OpenGL.

Nous allons prendre cette image:

gl2-hello-1.png

Nous allons ensuite la dessiner dans une fenêtre.

Mais les images qui ne bougent pas, c'est un peu ennuyeux. C'est pourquoi nous allons rendre l'exercice un peu plus intéressant en faisant des fondus enchainés avec cette image:

gl2-hello-2.png

Ce n'est certes pas très passionnant comme programme, mais au delà de sa simplicité, celui ci nous exercera à quasiment toutes les parties d'OpenGL qu'un programme plus complexe nécessiterai.

github-logo_128.png

Octocat: La mascotte de Github

Le code source complet est en ligne sur Github, ici. Composé de 380 lignes de C et de deux shaders d'une douzaine de ligne chacun, ce code (qui ne fait que dessiner une image sur l'écran) peut sembler exagéré. Cela dis, une grande partie de ce code pose les bases des démos à venir.

Le fichier hello-gl.c contient tout ce qui concerne le rendu OpenGL, tandis que util.c contient les fonctions qui vont nous permettre de lire des images TGA.

J'ai inclus les deux images, hello1.tga et hello2.tga, dans ce format, car il est facile à "décoder" sans avoir à utiliser une librairie externe.

Les codes des shaders sont dans deux fichiers:

Dans ce chapitre, j'expliquerai comment les différentes parties du programme hello-gl utilise l'API OpenGL pour envoyer des données dans le pipeline graphique et comment il le fait fonctionner.

Je donnerai également un bref aperçu du langage GLSL quand nous verront shaders.

C'est beaucoup de choses à faire dans un seul billet de blog, c'est pourquoi je diviserai ce chapitre en quatre parties.

Dans cette première partie, nous allons ouvrir une fenêtre avec GLUT. Dans la seconde partie, nous allons implémenter le buffer et les textures qui contiendront respectivement les vertices bruts et les images de notre programme.

Après ça, nous écriront le code des shaders qui traiteront ces données pour en faire l'image final, et nous les implémenterons dans notre application OpenGL.

Dans le dernier article, nous parlerons des appels aux fonctions OpenGL qui servent à rendre la scène sur l'écran.

Maintenant que vous avez le plan en tête, commençons à coder!

Nous allons commencer par configurer GLUT pour avoir une fenêtre vide sur l'écran.

:longBar:
Les fichiers d'entete OpenGL
#include <stdlib.h>
#include <GL/glew.h>
#ifdef __APPLE__
#  include <GLUT/glut.h>
#else
#  include <GL/glut.h>
#endif

Chaque plateforme a ses headers OpenGL à différents endroits, mais avec GLEW, vous n'avez pas à vous soucier de ça.

Le fait d'inclure GL/glew.h vous permet de récupérer tout les headers OpenGL, sans que vous ayez à aller les récupérer vous même.

Malheureusement, ce n'est pas le cas de GLUT, et pour l'inclure, vous devez passer par quelques tricks multi-plateforme (les ifdef).

Son header est généralement dans GL/glut.h, mais le framework GLUT livré avec MacOS X utilise les conventions de header Apple. Ducoup, le header de GLUT est dans GLUT/glut.h.

Il y a également un bug dans la façon dont les versions récentes des headers C standards de Visual Studio interagissent avec glut.h, ce qui requière que stdlib.h soit déclaré avant glut.h.

:longBar:
Mise en place de notre fenêtre avec GLUT
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
    glutInitWindowSize(400, 300);
    glutCreateWindow("Hello World");
    glutDisplayFunc(&render);
    glutIdleFunc(&update_fade_factor);

GLUT fournit une interface au système de fenêtrage (window system) limité mais simple et portable.

Après avoir initialisé GLUT en appelant la fonction glutInit, nous utilisons glutInitDisplayMode pour spécifier les buffers dont notre framebuffer par défaut disposera.

Dans notre cas, un buffer de couleur (GLUT_RGB) avec double buffering (GLUT_DOUBLE) est suffisant.

La technique du double buffering permet de stocker deux color buffers dans le framebuffer et d'alterner à chaque frame entre le buffer affiché sur l'écran et celui qui est en court de rendu, ce qui permet d'avoir une animation fluide.

Si nous avons besoin d'un buffer de profondeur (depth buffer) ou d'un buffer de masque (stencil buffer), nous pouvons l'appeler ici.

Ensuite, nous nous servons de glutInitWindowSize pour initialiser la taille de notre fenêtre à 400x300 (la taille de nos images) et nous créons notre fenêtre avec glutCreateWindow.

Pour finir, nous faisons appel à deux fonctions qui "reçoivent" les events (callbacks) de la fenêtre:

  • glutDisplayFunc qui calcul notre image quand la fenêtre lui demande de s'afficher.
  • glutIdleFunc qui met continuellement à jour le "fade factor" entre les deux images.
glewInit();
if (!GLEW_VERSION_2_0) {
    fprintf(stderr, "OpenGL 2.0 not available\n");
    return 1;
}

Une fois que GLUT a créé notre fenêtre, nous préparons OpenGL afin de pouvoir y faire des appels.

La première chose que nous faisons est d'initialiser GLEW. Lorsque glewInit est appelé, il définit des appels au fonctions OpenGL en se basant sur la version ainsi que les extensions supportées par votre matériel.

Nous vérifions que GLEW_VERSION_2_0 est définit pour être certain que le matériel qui exécuté le programme a au moins OpenGL 2.0.

Une fois que la version est vérifié, l'utilisation de GLEW devient quasiment invisible, et nous n'aurons plus besoin d'interagir avec lui.

if (!make_resources()) {
        fprintf(stderr, "Failed to load resources\n");
        return 1;
    }
 
    glutMainLoop();
    return 0;
}

GLEW initialisé, nous appelons notre fonction make_resources qui set nos ressources OpenGL.

Nous verront cette fonction durant les prochaines parties de ce chapitre.

Si nos ressources sont correctement chargé, glutMainLoop prend le relais.

Il affiche la fenêtre, commence par recevoir les events de l'UI, et appel les fonctions que l'on a setté en réponse à ses events (les callbacks).

Il fermera aussi le programme pour nous quand l'utilisateur le quittera.

Le return 0 supprime simplement les avertissements du compilateur et il n'est, de toute façon, jamais réellement atteint.

:longBar:
Compiler et lancer notre programme

Arrivé ici, nous pouvons écrire le code des callbacks de GLUT, de la fonction make_resources, et faire fonctionner notre (au combien inutile) programme.

static int make_resources(void)
{
    return 1;
}
static void update_fade_factor(void)
{
}
static void render(void)
{
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glutSwapBuffers();
}

glClearColor set une couleur de fond RGBA (dans notre cas, blanche) que glClear utilise pour remplir le buffer de color du framebuffer.

glutSwapBuffers amène ensuite notre color buffer à l'écran.

Avec tout ça en place, nous pouvons maintenant compiler et lancer notre programme.

Cette version est dans le reposit de Github et s'appelle hello-gl-dummy.c.

La commande qui compile le programme et le link aux libs OpenGL, GLUT et GLEW peut varier suivant les plateformes.

Sur la plupart des systèmes Unix, elle devrait ressembler à ça:

gcc -o hello-gl-dummy hello-gl-dummy.c \
    -I/usr/X11R6/include -L/usr/X11R6/lib \
    -lGL -lGLEW -lglut

Sur MacOS X:

# Assuming GLEW was installed to /opt/local
gcc -o hello-gl-dummy hello-gl-dummy.c \
    -I/opt/local/include -L/opt/local/lib \
    -framework OpenGL -framework GLUT -lGLEW

Sur Windows avec Visual C++:

cl /Fohello-gl-dummy.obj /c hello-gl-dummy.c
link /out:hello-gl-dummy.exe hello-gl-dummy.obj \
    opengl32.lib glut32.lib glew32.lib

Sur Windows avec mingw:

gcc -o hello-gl-dummy.exe hello-gl-dummy.c \
    -lopengl32 -lglut32 -lglew32

Le reposit contient également les makefiles de chacune de ses plateformes.

Vous pouvez compiler cette version du programme en utilisant hello-gl-dummy (ou hello-gl-dummy.exe sur Windows):

make -f Makefile.MacOSX hello-gl-dummy # or Makefile.Unix or Makefile.Mingw
nmake /f Nmakefile.Windows hello-gl-dummy.exe

Une fois que vous avez compilé le programme, vous devriez être capable de le lancer et d'avoir une fenetre blanche, comme promis:

gl2-dummy-screenshot.png

Fermez la fenêtre, ou, sur MacOS X, quittez l'application.

:longBar:
Chapitre suivant

Avec la satisfaction d'avoir une fenêtre ouverte devant nous, nous somme prêt à remplir OpenGL de vertex et d'images.

Dans le prochain article, j'introduirais les buffers et textures OpenGL.

Vous pouvez retrouver l'article original sur la page de Joe Groff, un fois de plus: Merci à lui!