I. Contributions

Nous recherchons toujours des traducteurs pour finir ce projet de traduction des articles de NeHe. Votre aide serait appréciée ! Vous êtes intéressé ? Alors contactez-nous à l'adresse suivante : nehe@redaction-developpez.com .

II. Tutoriel

Bienvenue dans un nouveau tutoriel. Cette fois nous allons vous apprendre à utiliser les polices Bitmap.
Vous devez vous dire à vous-même « En quoi c'est si dur de mettre du texte à l'écran ? ». Si vous n'avez jamais essayé, ce n'est pas si facile !

Bien sûr, vous pouvez lancer un programme de dessin, écrire du texte dans une image, charger l'image dans votre programme OpenGL, activer le fondu et appliquer le texte à l'écran. Mais ceci est une perte de temps, le résultat final ressort souvent flou ou pixelisé dépendant du type de filtrage que vous utilisez, et bien que votre image ait une composante alpha, votre texte va finir transparent (fondu avec les objets à l'écran) une fois appliqué à l'écran.

Si vous avez déjà utilisé le bloc-notes, Microsoft Word ou d'autres logiciels de traitement de texte, vous devez avoir remarqué les différents types de police disponibles. Ce tutoriel va vous apprendre à utiliser exactement la même police dans vos programmes OpenGL. Bien sûr… toute police installée sur votre ordinateur peut être utilisée dans vos démos.

Les polices Bitmap sont cent fois mieux que les polices graphiques (textures). Vous pouvez changer le texte à la volée. Pas besoin de faire des textures pour chaque mot ou lettre que vous voulez écrire à l'écran. Vous avez juste à positionner le texte et utiliser ma commande OpenGL pour afficher le texte à l'écran.

J'ai essayé de faire la fonction aussi simple que possible. Tout ce que vous avez à faire est écrire glPrint("Hello"). C'est aussi simple que ça. Peu importe. Vous pouvez dire en voyant cette introduction que je suis extrêmement content de ce tutoriel. Cela m'a pris environ une heure et demie pour créer le programme. Pourquoi autant de temps ? Parce qu'il n'y a aucune information disponible sur l'utilisation des polices Bitmap, sauf si, bien sûr, vous aimez le code MFC. Dans l'esprit de garder le code simple j'ai décidé qu'il serait sympa si je l'écrivais simplement pour comprendre le code C :)

Une petite chose, ce code est spécifique à Windows. Il utilise des fonctions wgl de Windows pour construire la police. Apparemment Apple a un support agl qui devrait faire la même chose et X a glx. Malheureusement, je ne peux garantir ce code portable. Si quelqu'un a du code indépendant de la plateforme pour dessiner des polices à l'écran, envoyez-le et j'écrirai un autre tutoriel sur les polices.

Nous allons commencer avec le code typique de la leçon 1. Nous allons ajouter le fichier d'entête stdio.h pour les opérations d'entrée/sortie standard. Le fichier d'entête stdarg.h pour analyser le texte et convertir les variables en texte, et finalement le fichier d'entête math.h pour déplacer le texte sur l'écran en utilisant SIN et COS.

 
Sélectionnez
#include <windows.h>                                            // Fichier d'entête pour Windows
#include <math.h>                                               // Fichier d'entête pour la bibliothèque mathématique           (AJOUT)
#include <stdio.h>                                              // Fichier d'entête pour les entrées/sorties    (AJOUT)
#include <stdarg.h>                                             // Fichier d'entête pour les fonctions à arguments variables    (AJOUT)
#include <gl\gl.h>                                              // Fichier d'entête pour la bibliothèque OpenGL32
#include <gl\glu.h>                                             // Fichier d'entête pour la bibliothèque GLu32
#include <gl\glaux.h>                                           // Fichier d'entête pour la bibliothèque GLaux

HDC             hDC=NULL;                                       // Contexte de périphérique privé GDI
HGLRC           hRC=NULL;                                       // Contexte de rendu permanent
HWND            hWnd=NULL;                                      // Contient notre ID de la fenêtre
HINSTANCE       hInstance;                                      // Contient l'instance de l'application

Nous allons aussi ajouter trois nouvelles variables. base va contenir le numéro de la première liste d'affichage que nous allons créer. Chaque caractère requiert sa propre liste d'affichage. Le caractère 'A' est 65 dans la liste d'affichage, 'B' est 66, 'C' est 67, etc. Donc 'A' va être enregistré dans la liste d'affichage base+65.

Ensuite nous créons deux compteurs (cnt1 et cnt2). Ces compteurs vont compter à différentes fréquences et sont utilisés pour déplacer le texte sur l'écran en utilisant SIN et COS. Cela crée un mouvement semi-aléatoire sur l'écran. Nous allons aussi contrôler la couleur des lettres avec les compteurs.

 
Sélectionnez
GLuint  base;                                                   // Base des listes d'affichage pour le jeu de polices
GLfloat cnt1;                                                   // 1er compteur utilisé pour déplacer le texte et pour la coloration
GLfloat cnt2;                                                   // 2nd compteur utilisé pour déplacer le texte et pour la coloration

bool    keys[256];                                              // Tableau utilisé pour la fonction du clavier
bool    active=TRUE;                                            // Drapeau de fenêtre active, défini à TRUE par défaut
bool    fullscreen=TRUE;                                        // Drapeau de plein écran, défini à TRUE par défaut

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);           // Prototype de WndProc

La partie suivante construit la police actuelle. Ceci était la partie la plus difficile à écrire. 'HFONT font' indique à Windows que nous allons manipuler une police Windows. oldfont est utilisé pour des questions de propreté.

Ensuite nous définissons base. Nous faisons ceci en créant un groupe de 96 listes d'affichage avec glGenLists(96). Suite à la création des listes d'affichage, la variable base contiendra le numéro de la première liste.

 
Sélectionnez
GLvoid BuildFont(GLvoid)                                        // Construit notre police Bitmap
{
        HFONT   font;                                           // ID de la police windows
        HFONT   oldfont;                                        // Utilisé pour des questions de propreté

        base = glGenLists(96);                                  // Espace de stockage pour 96 caractères (AJOUT)

Maintenant la partie amusante. Nous allons créer notre police. Nous commençons par spécifier la taille de la police. Vous pouvez remarquer que c'est un nombre négatif. En ajoutant un moins, nous disons à Windows de nous trouver une police basée sur la taille des caractères. Si nous utilisions un nombre positif nous aurions une police basée sur la cellule.

 
Sélectionnez
        font = CreateFont(      -24,                            // Taille de la police (AJOUT)

Ensuite nous spécifions la largeur de la cellule. Vous pouvez remarquer que nous l'avons définie à 0. En mettant 0, Windows va utiliser la valeur par défaut. Vous pouvez jouer avec cette valeur si vous voulez, faire la police large, etc.

 
Sélectionnez
                                0,                              // Largeur de la police

L'angle d'échappement va tourner la police. Malheureusement ceci n'est pas une fonctionnalité très utile. Bien que vous puissiez tourner de 0, 90, 180 et de 270 degrés, la police va être rognée pour rentrer dans les bordures d'un rectangle invisible. L'angle d'orientation, d'après l'aide de MSDN spécifie l'angle, en dixièmes de degré, entre chaque ligne de base du caractère et l'axe des X du périphérique. Malheureusement, je n'ai aucune idée de ce que cela signifie :(

 
Sélectionnez
                                0,                              // Angle d'échappement
                                0,                              // Angle d'orientation

Le poids de la police est un super paramètre. Vous pouvez entrer un nombre entre 0 et 1000 ou vous pouvez utiliser une des valeurs prédéfinies. FW_DONTCARE est 0, FW_NORMAL est 400, FW_BOLD est 700 et FW_BLACK est 800. Il y a beaucoup plus de valeurs prédéfinies, mais ces quatre-là sont un bon exemple. Plus la valeur est élevée, plus la police est épaisse (plus gras).

 
Sélectionnez
                                FW_BOLD,                        // Poids de la police

Italique, souligné et barré peuvent être soit TRUE soit FALSE. Simplement, si souligné est TRUE, la police va être soulignée. Si c'est FALSE, elle ne le sera pas. :)

 
Sélectionnez
                                FALSE,                          // Italique
                                FALSE,                          // Souligné
                                FALSE,                          // Barré

L'identifieur du jeu de caractères décrit le type du jeu de caractères que vous souhaitez utiliser. Il y en a de trop pour tous les expliquer. CHINESEBIG5_CHARSET, GREEK_CHARSET, RUSSIAN_CHARSET, DEFAULT_CHARSET, etc. ANSI est celui que j'utilise, bien que le DEFAULT marcherait sans doute tout aussi bien.

Si vous êtes intéressé dans l'utilisation des polices comme Webdings ou Wingdings, vous devez utiliser SYMBOL_CHARSET à la place de AINSI_CHARSET.

 
Sélectionnez
                                ANSI_CHARSET,                   // Identifiant du jeu de caractères

La précision du rendu est très importante. Cela indique à Windows le type du jeu de caractères à utiliser s'il y en a plus d'un de disponible. OUT_TT_PRECIS indique à Windows que s'il y a plus d'un type de police, de choisir celui avec le même nom, en sélectionnant la version TRUETYPE de la police. Les polices Truetype rendent toujours mieux, notamment lorsque vous les grossissez. Vous pouvez toujours utiliser OUT_TT_ONLY_PRECIS, qui tente TOUJOURS d'utiliser une police TRUETYPE.

 
Sélectionnez
                                OUT_TT_PRECIS,                  // Précision du rendu

La précision de la coupure est le type de coupure à faire sur la police si celle-ci va en dehors de la région de coupure. Pas grand-chose à dire dessus, laissez-le juste à la valeur par défaut.

 
Sélectionnez
                                CLIP_DEFAULT_PRECIS,            // Précision du découpage

La qualité de rendu est très importante. Vous pouvez avoir PROOF, DRAFT, NONANTIALIASED, DEFAULT ou ANTIALIASED. Nous savons tous que les polices ANTIALIASED rendent bien :) L'anticrénelage d'une police a le même effet que vous avez lorsque vous activez la douceur des polices dans Windows. Cela rend tout moins déchiqueté.

 
Sélectionnez
                                ANTIALIASED_QUALITY,            // Qualité de rendu

Ensuite nous avons les options de famille et de pitch. Pour le pitch vous pouvez avoir DEFAULT_PITCH, FIXED_PITCH et VARIABLE_PITCH, et pour la famille vous pouvez avoir FF_DECORATIVE, FF_MODERN, FF_ROMAN, FF_SCRIPT, FF_SWISS, FF_DONTCARE. Jouez avec pour trouver ce que vous voulez faire. J'ai juste mis les deux à défaut.

 
Sélectionnez
                                FF_DONTCARE|DEFAULT_PITCH,      // Famille et pitch

Finalement… Nous avons le nom de la police. Lancez Microsoft Word ou autre éditeur de texte. Cliquez sur le menu ascenseur pour la police et trouvez une police qui vous plaît. Pour utiliser la police, remplacez 'Courier New' par le nom de la police que vous préférez utiliser.

 
Sélectionnez
                                "Courier New");                 // Nom de la police

oldFont garde la police utilisée avant. SelectObject va retourner la police (ou pinceau, ou remplisseur, ou n'importe quel autre objet GDI) qui était défini avant de changer pour la nouvelle police. La façon dont GDI travaille est telle que l'utilisation de la valeur de retour de SelectObject n'est pas très évidente. Au premier regard, on pourrait croire que le code sélectionne la nouvelle police et retourne un pointeur qui va être contenu dans oldFont.

 
Sélectionnez
        oldfont = (HFONT)SelectObject(hDC, font);               // Sélectionne la police que nous voulons
        wglUseFontBitmaps(hDC, 32, 96, base);                   // Construit 96 caractères en commençant par le caractère 32
        SelectObject(hDC, oldfont);                             // Sélectionne la police que nous voulons
        DeleteObject(font);                                     // Supprime la police
}

Le code qui suit est assez simple. Il supprime les 96 listes d'affichage de la mémoire en partant de la première spécifiée par base. Je ne suis pas sûr que Windows l'aurait fait pour vous, mais il est mieux d'être sûr que d'être désolé. :)

 
Sélectionnez
GLvoid KillFont(GLvoid)                                         // Détruit la liste des polices
{
        glDeleteLists(base, 96);                                // Détruit les 96 listes de caractères (AJOUT)
}

Maintenant la fonction utile d'affichage de texte OpenGL. Vous pouvez appeler cette partie du code avec la commande glPrint("Le message va ici"). Le texte est sauvegardé dans la chaine de caractères *fmt.

 
Sélectionnez
GLvoid glPrint(const char *fmt, ...)                            // Personnalisation de la fonction "Print" avec OpenGL
{

La première ligne ci-dessous crée un espace de stockage pour une chaine de caractères de 256 éléments. text est la chaine que nous allons afficher à l'écran. La seconde ligne en dessous, crée un pointeur qui pointe sur une liste d'arguments que nous passons avec la chaine de caractères. Si nous envoyons des variables avec le texte, le pointeur pointera sur celles-ci.

 
Sélectionnez
        char            text[256];                              // Garde notre chaine de caractères
        va_list         ap;                                     // Pointe sur la liste d'arguments

Les deux prochaines lignes de code vérifient qu'il n'y a rien à afficher. S'il n'y a pas de texte, fmt va être équivalent à rien (NULL), et rien ne sera dessiné à l'écran.

 
Sélectionnez
        if (fmt == NULL)                                        // S'il n'y a pas de texte
                return;                                         // Ne fait rien

Les prochaines lignes de code convertissent les symboles dans le texte en nombres qu'ils représentent. Le texte final et tous les symboles convertis sont stockés dans la chaine de caractères text. Je vais expliquer les symboles en détail un peu plus bas.

 
Sélectionnez
        va_start(ap, fmt);                                      // Analyse la chaine pour les variables
            vsprintf(text, fmt, ap);                            // Puis convertit les symboles en nombres
        va_end(ap);                                             // Les résultats sont dans text

Nous pouvons pousser la GL_LIST_BOT, ce qui évite glListBase d'être affecté par les autres listes d'affichage qui peuvent être utilisées dans notre programme.

La commande glListBase(base-32) est un peu dure à expliquer. Disons que nous voulons dessiner la lettre 'A', qui est représentée par le nombre 65. Sans glListBase(base-32) OpenGL ne saurait pas où trouver la lettre. Il irait regarder à la liste d'affichage 65, mais si base était égale à 1000, 'A' serait actuellement sauvegardé à la liste d'affichage 1065. Donc par la définition d'un point de départ, OpenGL va savoir où trouver la bonne liste d'affichage. La raison de soustraire 32 est que nous n'avons jamais fait les 32 premières listes d'affichage. Nous les avons passées. Donc nous devons le dire à OpenGL en soustrayant 32 à la valeur de base. J'espère que c'est clair.

 
Sélectionnez
        glPushAttrib(GL_LIST_BIT);                              // Met dans la pile le bit des listes d'affiches (AJOUT)
        glListBase(base - 32);                                  // Définit la base au caractère 32       (AJOUT)

Maintenant qu'OpenGL sait où sont stockées les lettres, nous pouvons lui dire d'écrire le texte à l'écran. glCallLists est une commande très intéressante. Elle est capable de mettre plus d'une liste d'affichage à la fois à l'écran.

La ligne ci-dessous fait la chose suivante : premièrement elle dit à OpenGL que nous allons afficher les listes à l'écran. strlen(text) trouve le nombre de lettres que nous avons à envoyer à l'écran. Ensuite la fonction nécessite de connaitre le plus grand numéro de liste qui va être utilisé. Nous n'envoyons pas plus de 255 caractères. Le paramètre des listes est traité comme un tableau d'octets non signés, chacun dans l'ensemble 0 à 255. Finalement, nous disons ce qu'il faut afficher en passant text (pointeur sur une chaine de caractères).

Au cas où vous seriez en train de vous demander pourquoi les lettres ne tombent pas côte à côte. Chaque liste d'affichage pour chaque caractère connait où le côté droit de la lettre est. Après que chaque lettre soit dessinée, OpenGL se déplace sur la droite de la lettre dessinée. La prochaine lettre ou objet va être dessiné en commençant par le dernier emplacement où OpenGL s'est déplacé, ce qui correspond à la droite de la dernière lettre.

Finalement, nous remettons en place le paramètre GL_LIST_BIT à la valeur qu'il avait avant que l'on utilise notre base avec glListBase(base-32).

 
Sélectionnez
        glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);      // Dessine la liste d'affichage text (AJOUT)
        glPopAttrib();                                          // Remet le bit de la liste d'affichage (AJOUT)
}

La seule chose de différente dans le code de InitGL() est la ligne BuildFont(). Elle appelle le code ci-dessus qui construit notre police donc OpenGL pourra l'utiliser plus tard.

 
Sélectionnez
int InitGL(GLvoid)                                              // Toute initialisation pour OpenGL va ici
{
        glShadeModel(GL_SMOOTH);                                // Activation des ombres douces
        glClearColor(0.0f, 0.0f, 0.0f, 0.5f);                   // Fond noir
        glClearDepth(1.0f);                                     // Paramétrage du tampon de profondeur
        glEnable(GL_DEPTH_TEST);                                // Activation du test de profondeur
        glDepthFunc(GL_LEQUAL);                                 // Le type de test de profondeur que l'on veut
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);      // Calcul de belle perspective

        BuildFont();                                            // Construction de la police

        return TRUE;                                            // L'initialisation s'est bien passée
}

Maintenant, passons au code pour l'affichage. Nous commençons par effacer l'écran et le tampon de profondeur. Nous appelons glLoadIdentity() pour tout réinitialiser. Ensuite nous nous déplaçons d'une unité dans l'écran. Si nous ne le faisions pas, le texte ne serait pas visible. Les polices Bitmap sont mieux quand vous utilisez une projection orthogonale plutôt qu'une perspective, mais la projection orthogonale rend mal, donc pour que cela marche, nous nous déplaçons.

Vous remarquerez que même si nous nous déplaçons plus dans l'écran, la taille de la police ne va pas rétrécir comme vous l'auriez espéré. Ce qui se passe réellement c'est que lorsque vous vous déplacez dans l'écran, vous allez avoir plus de contrôle sur l'emplacement du texte. Si vous vous déplacez d'une unité dans l'écran, vous pouvez placer le texte entre -0.5 et 0.5 sur l'axe des X. Si vous vous déplacez de 10 unités dans l'écran, vous pouvez placer le texte entre -5 et 5. Cela vous donne juste plus de contrôle au lieu d'utiliser des nombres décimaux pour la position du texte. Rien ne va changer dans la taille du texte. Même pas avec glScalef(x,y,z). Si vous voulez une police plus grande ou plus petite, faites-la plus grande ou plus petite lors de la création !

 
Sélectionnez
int DrawGLScene(GLvoid)                                         // Ici le code de dessin de la scène
{
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     // Efface l'écran et le tampon de profondeur
        glLoadIdentity();                                       // Réinitialise la vue
        glTranslatef(0.0f,0.0f,-1.0f);                          // Déplace d'une unité dans l'écran

Maintenant nous allons utiliser des mathématiques fantaisistes pour avoir les couleurs. Ne vous inquiétez pas si vous ne comprenez pas ce que je fais. J'aime bien jouer avec plein de variables et de combines pour arriver à mes résultats.

Dans ce cas, j'utilise les deux compteurs pour déplacer le texte sur l'écran et pour changer les couleurs rouge, vert et bleu. Le rouge ira de -1.0 à 1.0 en utilisant COS et le compteur 1. Le vert ira aussi de -1.0 à 1.0 en utilisant SIN et le compteur 2. Le bleu ira de 0.5 à 1.5 en utilisant COS et les compteurs 1 et 2. De cette façon bleu ne sera jamais 0, et le texte ne disparaîtra jamais complètement. Idiot, mais cela marche. :)

 
Sélectionnez
        // Changement de la couleur selon la position
        glColor3f(1.0f*float(cos(cnt1)),1.0f*float(sin(cnt2)),1.0f-0.5f*float(cos(cnt1+cnt2)));

Maintenant une nouvelle commande. glRasterPos2f(x,y) va positionner notre police Bitmap sur l'écran. Le centre de l'écran est 0,0. Remarquez qu'il n'y a pas d'axe Z. La police Bitmap utilise seulement l'axe des X (gauche/droite) et l'axe des Y (haut/bas). Parce que nous nous déplaçons d'une unité dans l'écran, le bord gauche est à -0.5 et le bord droit à 0.5. Vous pouvez remarquer que je me déplace de 0.45 pixel sur la gauche sur l'axe des X. Cela déplace le texte au centre de l'écran. Sinon, il aurait été plus sur la droite de l'écran car il aurait été dessiné du centre vers la droite.

Les mathématiques fantaisistes font la même chose que pour le choix de la couleur. Cela déplace le texte sur l'axe des X de -0.50 à -0.40 (rappel : nous enlevons 0.45 de la droite dès le départ). Cela garde tout le temps le texte sur l'écran. Cela oscille de gauche à droite en utilisant COS et le compteur 1. Cela se déplace de -0.35 à 0.35 sur l'axe des Y en utilisant SIN et le compteur 2.

 
Sélectionnez
        // Position du texte sur l'écran
        glRasterPos2f(-0.45f+0.05f*float(cos(cnt1)), 0.35f*float(sin(cnt2)));

Maintenant, ma partie préférée… Écrire le texte sur l'écran. J'ai essayé de faire super facile, et très convivial. Vous pouvez remarquer que cela ressemble beaucoup à une commande OpenGL, mixée avec la bonne vieille fonction d'affichage :) Tout ce qu'il y a à faire pour écrire du texte à l'écran est glPrint("{Tout ce que vous voulez}"). C'est facile. Le texte va être dessiné à l'écran à la position exacte que vous avez déterminée.

Shawn T. m'a envoyé un nouveau code qui permet à glPrint de passer des variables à l'écran. Cela signifie que vous pouvez incrémenter un compteur et l'afficher à l'écran ! Cela marche comme ça… Ci-dessous vous voyez le texte normal. Alors il y a un espace, un tiret, un espace et alors un "symbole" (%7.2f). Maintenant vous devez regarder %7.2f et vous dire « Bon sang, qu'est-ce que cela signifie ». C'est très simple. % est comme un marqueur qui dit de ne pas afficher 7.2f à l'écran, car cela représente une variable. Le 7 signifie d'afficher un maximum de sept chiffres pour la partie entière. Puis le symbole décimal, et juste après la virgule, un 2. Ce 2 signifie que vous n'allez afficher que deux chiffres à droite du symbole décimal. Finalement, le f. Le f signifie que le nombre que vous voulez afficher est un nombre à virgule flottante. Nous voulons afficher la valeur de cnt1 à l'écran. Comme exemple, si cnt1 était égal à 300.12345f le nombre finalement affiché serait 300.12. Le 3, 4 et 5 après le point seraient amputés car nous ne voulons que deux chiffres dans la partie décimale.

Je sais si que vous êtes un bon programmeur de C, ceci est absolument basique, mais il peut y avoir des personnes qui n'ont jamais utilisé printf. Si vous êtes intéressés et voulez en apprendre plus sur les symboles, achetez un livre, ou lisez la MSDN.

 
Sélectionnez
        glPrint("Active OpenGL Text With NeHe - %7.2f", cnt1);  // Afficher le texte à l'écran

Il reste àincrémenter les compteurs selon différentes valeurs pour que les couleurs et la position du texte changent.

 
Sélectionnez
        cnt1+=0.051f;                                           // Incrémente le premier compteur
        cnt2+=0.005f;                                           // Incrémente le second compteur
        return TRUE;                                            // Tout est OK
}

La dernière chose à faire est d'ajouter KillFont() à la fin de KillGLWindow() comme je le montre ci-dessous. C'est important d'ajouter cette ligne. Cela nettoie les choses avant de quitter le programme.

 
Sélectionnez
        if (!UnregisterClass("OpenGL",hInstance))               // Peut-on désenregistrer la classe
        {
                MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
                hInstance=NULL;                                 // Met hInstance à NULL
        }

        KillFont();                                             // Détruit la police
}

C'est tout… Tout ce que vous devez savoir pour utiliser les polices Bitmap dans vos propres applications OpenGL. J'ai recherché sur le net un tutoriel comme celui-ci, et je n'ai rien trouvé. Peut-être que mon site est le premier à couvrir ce sujet avec un code C facile à comprendre ? Peu importe. Profitez du tutoriel et codez bien !

Jeff Molofee (NeHe)

III. Téléchargements

Compte tenu du nombre de versions de codes sources pour les tutoriels NeHe, nous les laissons en anglais. En principe, si vous avez compris le code présenté dans ce tutoriel (et les tutoriels antérieurs), vous n'aurez pas de mal à les comprendre :

IV. Remerciements

Merci à Winjerome pour sa relecture et ClaudeLELOUP pour ses corrections.

V. Liens