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

Je sais que tout le monde en a marre des polices. Les tutoriels que j'ai faits jusqu'à présent n'affichent pas seulement du texte, ils affichent du texte en 3D, du texte avec des textures, et peuvent accepter des variables. Mais qu'est-ce qui se passe si vous portez votre code sur une machine qui ne supporte pas les Bitmaps ou les polices vectorielles ?

Grâce à Giuseppe D'Agata, nous avons un autre tutoriel sur les polices. Vous vous demandez ce qui pourrait manquer !? Si vous vous rappelez dans le premier tutoriel sur les polices, j'ai mentionné l'utilisation de textures pour dessiner les lettres à l'écran. Habituellement, lorsque vous utilisez des textures pour dessiner du texte à l'écran vous utilisez votre logiciel de dessin préféré, vous sélectionnez une police puis vous tapez les lettres que vous voulez afficher. Vous sauvegardez le Bitmap et le chargez dans votre programme comme texture. Ce n'est pas très efficace pour un programme qui requiert beaucoup de texte, ou du texte qui change continuellement !

Ce programme utilise seulement une texture pour afficher n'importe lequel des 256 caractères à l'écran. Gardez à l'esprit que les caractères font 16 pixels de large et environ 16 de hauteur. Si vous prenez votre texture standard de 256x256, il est facile de voir que vous pouvez mettre 16 lettres sur une ligne, et que vous avez un total de 16 lignes. Si vous avez besoin d'une explication plus détaillée : la texture a une largeur de 256 pixels, un caractère une largeur de 16 pixels. 256 divisé par 16 = 16. :)

Donc… Créons une démo d'une texture de police 2D. Ce programme utilise le code de la leçon 1. Dans la première section du programme, nous incluons les bibliothèques math et stdio. Nous avons besoin de la bibliothèque math pour bouger les lettres dans l'écran en utilisant SIN et COS et de la bibliothèque stdio pour être sûr que les Bitmaps que nous voulons utiliser existent avant d'essayer d'en faire des textures.

 
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 pour les entrées/sorties    (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 

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

Nous allons ajouter une variable appelée base pour pointer sur nos listes d'affichage. Nous ajouterons aussi texture[2] pour contenir les deux textures que nous allons créer. La texture 1 sera la texture de la police, et la texture 2 va être une texture de relief pour créer notre objet simple 3D.

Nous ajoutons une variable loop qui va être utilisée pour exécuter les boucles. Finalement nous ajoutons cnt1 et cnt2 qui seront utilisées pour déplacer le texte sur l'écran et faire tourner notre objet simple 3D.

 
Sélectionnez
GLuint    base;                                    // Base pour la liste d'affichage de la police 
GLuint    texture[2];                                // Stockage pour les textures de notre police 
GLuint    loop;                                    // Variables génériques pour les boucles 

GLfloat    cnt1;                                    // Premier compteur utilisé pour déplacer le texte et pour la coloration 
GLfloat    cnt2;                                    // Second compteur utilisé pour déplacer le texte et pour la coloration 

LRESULT    CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);                // Déclaration de WndProc

Maintenant le code du chargement des textures. C'est exactement le même que celui de la leçon sur le placage de texture (leçon 6).

 
Sélectionnez
AUX_RGBImageRec *LoadBMP(char *Filename)                    // Charge une image Bitmap 
{ 
    FILE *File=NULL;                            // Descripteur de fichier 
    if (!Filename)                                // Vérifie qu'un nom de fichier a été donné 
    { 
        return NULL;                            // Sinon, retourne NULL 
    } 
    File=fopen(Filename,"r");                        // Contrôle si le fichier existe 
    if (File)                                // Est-ce que le fichier existe ? 
    { 
        fclose(File);                            // Ferme le descripteur 
        return auxDIBImageLoad(Filename);                // Ouvre le Bitmap et renvoie un pointeur 
    } 
    return NULL;                                // Retourne NULL si le chargement a échoué 
}

Le code qui suit a aussi très peu changé par rapport au code des tutoriels précédents. Si vous n'êtes pas sûr de ce que fait chaque ligne, revenez en arrière et révisez.

Notez que TextureImage[ ] va contenir deux images RGB. C'est très important de vérifier deux fois le code qui gère le chargement et le stockage des textures. Un mauvais nombre pourrait provoquer des crashs ou des fuites de mémoire !

 
Sélectionnez
int LoadGLTextures()                                // Ouvre un Bitmap et le convertit en texture 
{ 
    int Status=FALSE;                            // Indicateur du statut 
    AUX_RGBImageRec *TextureImage[2];                    // Crée un espace pour les textures

La ligne suivante est la plus importante à voir. Si vous vouliez remplacer le 2 par n'importe quel autre nombre, de gros problèmes surviendraient. La double vérification ! Ce nombre doit correspondre au nombre que vous utilisez lorsque vous définissez TextureImages[ ].

Les deux textures que nous allons charger sont font.bmp (notre police), et bumps.bmp. La seconde texture peut être remplacée par n'importe quelle texture de votre choix. Je ne me sentais pas très créatif, donc la texture que j'ai décidé d'utiliser peut être un peu terne.

 
Sélectionnez
    memset(TextureImage,0,sizeof(void *)*2);                // Initialise les pointeurs à NULL 

    if ((TextureImage[0]=LoadBMP("Data/Font.bmp")) &&            // Charge le Bitmap de la police 
        (TextureImage[1]=LoadBMP("Data/Bumps.bmp")))            // Charge le Bitmap de la texture 
    { 
        Status=TRUE;                            // Met la variable Status à TRUE

Autre chose d'important à vérifier à deux fois. Je ne peux pas dire combien de courriels j'ai reçus venant de personnes demandant "Pourquoi je ne vois qu'une seule texture, ou pourquoi toutes mes textures sont blanches !?!". Généralement le problème vient de cette ligne. Si vous remplacez le 2 par un 1, seule une texture sera créée et la seconde texture apparaîtra toute blanche. Si vous remplacez le 2 par un 3, votre programme peut planter !

Vous devriez avoir un seul appel à glGenTextures(). Après glGenTextures() vous devriez générer toutes vos textures. J'ai vu des gens mettre une ligne glGenTextures() pour chaque texture qu'ils voulaient créer. Généralement cela peut faire que la nouvelle texture écrasera les textures que vous avez déjà créées. C'est une bonne idée de décider du nombre de textures que vous avez besoin de créer, d'appeler glGenTextures() une seule fois, et de construire toutes les textures. Il n'est pas sage de mettre glGenTextures() dans une boucle si vous n'avez aucune raison pour cela.

 
Sélectionnez
        glGenTextures(2, &texture[0]);                    // Créer deux textures 
 
        for (loop=0; loop<2; loop++)                    // Boucle pour toutes les textures 
        { 
            // Construction de toutes les textures 
            glBindTexture(GL_TEXTURE_2D, texture[loop]); 
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 
            glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[loop]->sizeX, TextureImage[loop]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data); 
        } 
    }

Les lignes suivantes vérifient si le fichier Bitmap que nous avons chargé pour construire notre texture utilise la mémoire RAM. Si c'est le cas, la mémoire est libérée. Remarquez que nous vérifions et libérons les deux images enregistrées. Si nous utilisions trois images différentes pour construire nos textures, nous devrions vérifier et libérer trois images.

 
Sélectionnez
    for (loop=0; loop<2; loop++) 
        { 
            if (TextureImage[loop])                        // Si la texture existe 
            { 
                if (TextureImage[loop]->data)             // Si l'image de la texture existe 
                { 
                    free(TextureImage[loop]->data);        // Libère l'image de la texture 
                } 
                free(TextureImage[loop]);            // Libère la structure de l'image 
        } 
    return Status;                                // Renvoie le statut 
}

Maintenant nous allons créer notre police. Je vais parcourir cette section de code en détail. Ce n'est pas vraiment complexe, mais il y a un peu de mathématiques à comprendre, et je sais que les maths ne sont pas une chose que tout le monde apprécie.

 
Sélectionnez
GLvoid BuildFont(GLvoid)                            // Construit la liste d'affichage de la police 
{

Les deux prochaines variables vont être utilisées pour contenir la position de chaque lettre dans la texture de la police. cx contiendra la position de gauche à droite dans la texture, et cy contiendra la position de haut en bas.

 
Sélectionnez
    float    cx;                                // Contient la coordonnée du caractère sur les X 
    float    cy;                                // Contient la coordonnée du caractère sur les Y

Après nous disons à OpenGL que nous voulons construire 256 listes d'affichage. La variable base va pointer sur l'emplacement de la première liste d'affichage. La seconde liste d'affichage sera base+1, la troisième base+2, etc.

La seconde ligne de code ci-dessous sélectionne la texture de la police (texture[0]).

 
Sélectionnez
    base=glGenLists(256);                            // Crée les 256 listes d'affichage 
    glBindTexture(GL_TEXTURE_2D, texture[0]);                // Sélectionne la texture de la police

Maintenant nous commençons la boucle. La boucle va créer les 256 caractères, sauvegardant chaque caractère dans sa propre liste d'affichage.

 
Sélectionnez
    for (loop=0; loop<256; loop++)                        // Boucle à travers les 256 listes d'affichage 
    {

La première ligne ci-dessous peut sembler un peu déroutante. Le symbole % signifie le reste après la division de loop par 16. cx va nous déplacer à travers la texture de la police de gauche à droite. Vous remarquerez plus tard dans le code, que nous enlevons cy à 1 pour nous déplacer de haut en bas au lieu de bas en haut. Le symbole % est assez dur à expliquer mais je vais essayer.

Tout ce qui nous concerne est ce (loop%16). Le /16.0f convertit juste le résultat en coordonnées de texture. Donc si loop était égal à 16… cx serait égal au reste de 16/16 qui est 0. Mais cy serait égal à 16/16 qui est 1. Donc nous descendrions de la hauteur d'un caractère, mais pas du tout sur la droite. Maintenant si loop était égal à 17, cx serait égal à 17/16 ce qui fait 1.0625. La reste .0625 est aussi égal à 1/16. Signifiant que nous nous déplacerions d'un caractère sur la droite. cy sera toujours égal à 1 parce que nous ne nous intéressons qu'au nombre à gauche du symbole décimal. 18/16 nous donnerait 2 sur 16, nous déplaçant de deux caractères sur la droite, et toujours un caractère vers le bas. Si loop était 32, cx serait encore une fois égal à 0, parce qu'il n'y a pas de reste à la division de 32 par 16, mais cy serait égal à 2. Parce le nombre sur la gauche de la décimale serait maintenant 2, nous déplaçant de deux caractères vers le bas sur notre texture. Est-ce que c'est clair ?

 
Sélectionnez
        cx=float(loop%16)/16.0f;                    // Position du caractère sur X 
        cy=float(loop/16)/16.0f;                    // Position du caractère sur Y

Ouf. :) OK. Donc maintenant nous construisons notre police 2D en sélectionnant un caractère sur la texture selon les valeurs de cx et cy. Dans la ligne ci-dessous nous ajoutons la valeur de loop à la valeur de base, si nous ne le faisions pas, chaque lettre serait construite dans la première liste d'affichage. Nous ne voulons certainement pas que cela arrive donc en ajoutant loop à base, chaque caractère que nous créons est sauvegardé dans la liste d'affichage suivante.

 
Sélectionnez
        glNewList(base+loop,GL_COMPILE);                // Début de la création de la liste

Maintenant que nous avons sélectionné la liste d'affichage que nous voulons construire, nous créons notre caractère. Ceci est réalisé en dessinant un carré, texturé avec un caractère provenant de la texture de la police.

 
Sélectionnez
            glBegin(GL_QUADS);                    // Utilisation d'un carré pour chaque caractère

cx et cy devraient contenir des petites valeurs décimales de 0.0f à 1.0f. Si cx et cy étaient égaux à 0, la première ligne de code ci-dessous serait alors : glTexCoord2f(0.0f, 1-0.0f - 0.0625f). Rappelez-vous que 0.0625 est exactement 1/16 de notre texture, ou la largeur/hauteur d'un de nos caractères. La coordonnée de texture serait le point en bas à gauche de notre texture.

Remarquez que nous utilisons glVertex2i(x,y) à la place de glVertex3f(x,y,z). Notre police est une police 2D, donc nous n'avons pas besoin d'une valeur pour z. Parce que nous utilisons un écran orthogonal, nous n'avons pas besoin de nous déplacer dans l'écran. Tout ce que vous avez à faire pour dessiner dans un écran orthogonal est de spécifier les coordonnées x et y. Parce que notre écran est en pixels de 0 à 639 et de 0 à 479, nous n'avons pas besoin de nombres flottants ni de nombres négatifs. :)

De la façon dont nous définissons l'écran orthogonal, (0,0) est situé en bas à gauche de l'écran. (640,480) va être en haut à droite de l'écran. 0 est le bord gauche de l'écran sur l'axe des X, 639 le bord droit de l'écran. 0 est le bord bas de l'écran sur l'axe des Y et 479 le bord haut de l'écran. Normalement, nous avons enlevé les coordonnées négatives. C'est aussi pratique pour les gens qui ne s'intéressent pas aux perspectives et qui préfèrent travailler avec des pixels plutôt qu'avec des unités. :)

 
Sélectionnez
                glTexCoord2f(cx,1-cy-0.0625f);            // Coordonnées de texture (bas gauche) 
                glVertex2i(0,0);                // Coordonnées du vecteur (bas gauche)

La prochaine coordonnée de texture est à 1/16 sur la droite de la coordonnée précédente (exactement la largeur d'un caractère). Donc cela va être le point en bas à droite de la texture.

 
Sélectionnez
                glTexCoord2f(cx+0.0625f,1-cy-0.0625f);        // Coordonnées de texture (bas droit) 
                glVertex2i(16,0);                // Coordonnées du vecteur (bas droit)

La troisième coordonnée de texture reste sur le côté droit de notre caractère, mais se déplace vers le haut d'un 1/16 sur notre texture (exactement la hauteur d'un caractère). Cela va être le point en haut à droite de notre caractère.

 
Sélectionnez
                glTexCoord2f(cx+0.0625f,1-cy);            // Coordonnées de texture (haut droit) 
                glVertex2i(16,16);                // Coordonnées du vecteur (haut droit)

Finalement, nous nous déplaçons sur la gauche pour notre dernière coordonnée, en haut à gauche de notre caractère.

 
Sélectionnez
                glTexCoord2f(cx,1-cy);                // Coordonnées de texture (haut gauche) 
                glVertex2i(0,16);                // Coordonnées du vecteur (haut gauche) 
            glEnd();                        // Fin de la construction du carré (caractère) 

Finalement, nous nous déplaçons de dix pixels sur la droite, nous plaçant à la droite de notre texture. Si nous ne nous déplacions pas, les lettres seraient dessinées les unes sur les autres. Parce que notre police est si étroite, nous ne voulons pas nous déplacer de 16 pixels sur la droite. Si nous le faisions, il y aurait de grands espaces entre chaque lettre. Se déplacer de seulement 10 pixels élimine ces espaces.

 
Sélectionnez
            glTranslated(10,0,0);                    // Se déplace sur la droite du caractère 
        glEndList();                            // Fin de la construction de la liste d'affichage 
    }                                    // Boucle jusqu'à la fin de la construction des 256 listes 
}

La partie suivante de code est la même que dans les autres tutoriels sur les polices, libérant les listes d'affichage avant la fin du programme. Toutes les 256 listes d'affichage en commençant à base vont être détruites. (Bonne chose à faire !)

 
Sélectionnez
GLvoid KillFont(GLvoid)                                // Détruit la police de la mémoire 
{ 
    glDeleteLists(base,256);                        // Détruit toutes les 256 listes d'affichage 
}

La prochaine section de code est là où tout notre affichage est réalisé. Pratiquement tout est nouveau donc je vais essayer d'expliquer chaque ligne en détail. Juste une petite note : beaucoup de choses peuvent être ajoutées à ce code, comme le support des variables, la taille des caractères, l'espacement, et aussi nombre de vérifications pour restaurer les paramètres dans leurs états initial, d'avant l'affichage.

glPrint() prend trois paramètres. Le premier est la position sur l'axe des X sur l'écran (la position de gauche à droite). Le suivant est la position sur l'axe des Y sur l'écran (de haut en bas… où 0 est le bas). Puis nous avons notre chaîne de caractères (le texte que nous voulons afficher), et finalement une variable appelée 'set'. Si vous regardez le Bitmap que Giuseppe D'Agata a fait, vous remarquerez qu'il y a deux séries de caractères. La première série contient les caractères normaux, et la deuxième les caractères en italique. Si set est à 0, la première série de caractères est sélectionnée. Si set est à 1 ou plus grand, la seconde série de caractères est sélectionnée.

 
Sélectionnez
GLvoid glPrint(GLint x, GLint y, char *string, int set)                //  l'affichage se passe 
{

La première chose que nous avons à faire est de s'assurer que set est à 0 ou 1. Si set est plus grand que 1, nous le mettrons à 1.

 
Sélectionnez
    if (set>1)                                // set plus grand que 1 
    { 
        set=1;                                // Alors, nous le mettons à 1 
    }

Maintenant nous sélectionnons notre texture de la police. Nous le faisons juste dans le cas où une autre texture aurait été sélectionnée avant d'appeler la fonction d'affichage de texte.

 
Sélectionnez
    glBindTexture(GL_TEXTURE_2D, texture[0]);                // Sélectionne notre police de la texture

Maintenant, nous désactivons le test de profondeur. La raison pour laquelle je fais ceci est pour avoir le fondu (blending) fonctionnant correctement. Si vous ne désactivez pas le test de profondeur, le texte peut être caché par quelque chose, ou le fondu ne sera pas correct. Si vous ne prévoyez pas de fondu pour le texte (donc que les espaces noirs ne soient pas visibles autour des lettres) vous pouvez laisser le test de profondeur activé.

 
Sélectionnez
    glDisable(GL_DEPTH_TEST);                        // Désactive le test de profondeur

Les prochaines lignes sont très importantes ! Nous sélectionnons notre matrice de projection. Juste après cela, nous utilisons la commande appelée glPushMatrix. glPushMatrix() sauvegarde la matrice courante (projection). Un peu comme le bouton de mémoire d'une calculatrice.

 
Sélectionnez
    glMatrixMode(GL_PROJECTION);                        // Sélectionne la matrice de projection 
    glPushMatrix();                                // Sauvegarde la matrice de projection

Maintenant que notre matrice de projection a été sauvegardée, nous réinitialisons la matrice et nous définissons notre écran orthogonal. Le premier et le troisième nombres (0) représentent le coin en bas à gauche de l'écran. Nous pouvions mettre le côté gauche à -640 si nous le voulions, mais pourquoi vouloir travailler avec des nombres négatifs si nous n'en avons pas besoin ? Le second et quatrième paramètres représentent le haut droit de l'écran. Il est sage de faire correspondre ces valeurs avec la résolution que vous utilisez. Il n'y a pas de profondeur donc nous mettons les valeurs de Z à -1 et 1.

 
Sélectionnez
    glLoadIdentity();                            // Réinitialise la matrice de projection 
    glOrtho(0,640,0,480,-1,1);                        // Met en place l'écran orthogonal

Maintenant nous sélectionnons la matrice modelview, et nous sauvegardons les paramètres courants avec glPushMatrix(). Nous réinitialisons alors la matrice modelview pour pouvoir travailler avec en utilisant la vue orthogonale.

 
Sélectionnez
    glMatrixMode(GL_MODELVIEW);                        // Sélectionne la matrice modelview 
    glPushMatrix();                                // Sauvegarde la matrice modelview 
    glLoadIdentity();                            // Réinitialise la matrice modelview

Avec nos paramètres de perspective sauvegardés et notre écran orthogonal défini, nous pouvons maintenant dessiner notre texte. Nous commençons par nous déplacer à la position à laquelle nous voulons dessiner notre texte sur l'écran. Nous utilisons glTranslated() à la place de glTranslatef() parce que nous travaillons avec les pixels, donc les valeurs décimales ne sont pas importantes. Après tout, vous ne pouvez pas avoir la moitié d'un pixel. :)

 
Sélectionnez
    glTranslated(x,y,0);                            // Positionne le texte (0,0 - bas gauche)

La ligne ci-dessous va sélectionner la série de la police à utiliser. Si nous voulions la seconde série , nous ajouterions 128 à la base de la liste d'affichage (128 est la moitié de 256 caractères). En ajoutant 128 nous sautons les 128 premiers caractères.

 
Sélectionnez
    glListBase(base-32+(128*set));                        // Sélectionne la série de la police (0 ou 1)

Maintenant, tout ce qui nous reste à faire est l'affichage des lettres à l'écran. Nous le faisons de la même façon que ce que nous avions fait dans les autres tutoriels sur les polices. Nous utilisons glCallLists(). strlen(string) est la longueur de notre chaîne de caractères (combien de caractères nous voulons dessiner), GL_UNSIGNED_BYTE signifie que chaque caractère est représenté par un unsigned byte (un octet peut être n'importe quelle valeur entre 0 et 255. Finalement, string contient le texte actuel à afficher à l'écran.

 
Sélectionnez
    glCallLists(strlen(string),GL_UNSIGNED_BYTE,string);            // Écrit le texte à l'écran

Tout ce qui reste à faire est de remettre la vue en mode perspective. Nous sélectionnons la matrice de projection et utilisons glPopMatrix() pour rappeler les paramètres qui ont été sauvegardés avec glPushMatrix(). C'est important de restaurer les choses dans l'ordre inverse de la sauvegarde.

 
Sélectionnez
    glMatrixMode(GL_PROJECTION);                        // Sélectionne la matrice de projection 
    glPopMatrix();                                // Restaure l'ancienne projection de matrice 

Maintenant nous sélectionnons la matrice modelview, et nous faisons la même chose. Nous utilisons glPopMatrix() pour restaurer la matrice modelview aux valeurs qui étaient avant que nous définissions l'affichage orthogonal.

 
Sélectionnez
    glMatrixMode(GL_MODELVIEW);                        // Sélectionne la matrice modelview 
    glPopMatrix();                                // Restaure l'ancienne matrice de projection

Finalement, nous activons le test de profondeur. Si vous ne l'avez pas désactivé précédemment, vous n'avez pas besoin de cette ligne.

 
Sélectionnez
    glEnable(GL_DEPTH_TEST);                        // Active le test de profondeur 
}

Rien n'a changé dans ReSizeGLScene() donc nous passons directement à InitGL().

 
Sélectionnez
int InitGL(GLvoid)                                // Toute la configuration d'OpenGL se met ici 
{

Nous passons à notre code de construction des textures. Si la construction échoue pour une raison quelconque, nous retournons FALSE. Cela permet à notre programme de savoir qu'une erreur est arrivée et se fermer proprement.

 
Sélectionnez
    if (!LoadGLTextures())                            // Va dans notre routine de chargement de texture 
    { 
        return FALSE;                            // Si la la texture n'a pas été chargée, retourne FALSE 
    }

S'il n'y a pas eu d'erreur, nous passons au code de construction de la police. Peu de choses peuvent mal se passer lorsque nous construisons notre police donc nous ne vérifions pas les erreurs.

 
Sélectionnez
    BuildFont();                                // Construction de la police

Maintenant faisons notre initialisation classique d'OpenGL. Nous définissons la réinitialisation de la couleur du fond à noir, la réinitialisation du tampon de profondeur à 1.0. Nous choisissons le mode de test de profondeur, ainsi que le mode de fondu. Nous activons les ombres douces, et finalement nous activons l'application de textures 2D.

 
Sélectionnez
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                    // Fond d'écran noir 
    glClearDepth(1.0);                            // Configuration du tampon de profondeur 
    glDepthFunc(GL_LEQUAL);                            // Le type de test de profondeur à faire 
    glClearDepth(1.0f);                            // Configuration du tampon de profondeur 
    glBlendFunc(GL_SRC_ALPHA,GL_ONE);                    // Sélectionne le type de fondu 
    glShadeModel(GL_SMOOTH);                        // Active les ombres douces 
    glEnable(GL_TEXTURE_2D);                        // Active le mappage de texture 2D 
    return TRUE;                                // Initialisation OK 
}

Le code de la section ci-dessous va créer notre scène. Nous dessinons les objets 3D en premier puis le texte pour qu'il apparaisse au-dessus des objets 3D, à la place d'avoir les objets 3D sur le texte. La raison pour laquelle je décide d'ajouter des objets 3D est pour vous montrer que la perspective et la vue orthogonale peuvent être utilisées en même temps.

 
Sélectionnez
int DrawGLScene(GLvoid)                                // Ici  nous faisons tous les dessins 
{ 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // Efface l'écran et le tampon de profondeur 
    glLoadIdentity();                            // Réinitialise la matrice modelview

Nous sélectionnons notre texture bumps.bmp pour construire notre petit objet 3D. Nous nous déplaçons de cinq unités dans l'écran pour pouvoir voir l'objet 3D. Nous le tournons selon l'axe des Z de 45 degrés. Cela va faire tourner notre carré de 45 degrés, dans le sens des aiguilles d'une montre et faire que le carré ressemblera plus à un diamant qu'à un carré.

 
Sélectionnez
    glBindTexture(GL_TEXTURE_2D, texture[1]);                // Sélectionne notre deuxième texture 
    glTranslatef(0.0f,0.0f,-5.0f);                        // Déplace de cinq unités dans l'écran 
    glRotatef(45.0f,0.0f,0.0f,1.0f);                    // Tourne sur l'axe des Z de 45 degrés (sens des aiguilles d'une montre)

Après avoir effectué la rotation de 45 degrés, nous tournons l'objet selon l'axe des X et des Y de 30 fois la variable cnt1. Cela fait que notre objet tourne comme un diamant tournant sur un point.

 
Sélectionnez
    glRotatef(cnt1*30.0f,1.0f,1.0f,0.0f);                    // Tourne sur l'axe des X et Y de cnt1 * 30 (gauche à droite)

Nous désactivons le fondu (nous voulons que l'objet 3D apparaisse solide), et nous définissons la couleur à blanc. Puis nous dessinons un simple carré avec une texture.

 
Sélectionnez
    glDisable(GL_BLEND);                            // Désactive le fondu avant de dessiner en 3D 
    glColor3f(1.0f,1.0f,1.0f);                        // Blanc 
    glBegin(GL_QUADS);                            // Dessine notre premier rectangle avec une texture 
        glTexCoord2d(0.0f,0.0f);                    // Premières coordonnées de texture 
        glVertex2f(-1.0f, 1.0f);                    // Premier vertex 
        glTexCoord2d(1.0f,0.0f);                    // Secondes coordonnées de texture 
        glVertex2f( 1.0f, 1.0f);                    // Second vertex 
        glTexCoord2d(1.0f,1.0f);                    // Troisièmes coordonnées de texture 
        glVertex2f( 1.0f,-1.0f);                    // Troisième vertex 
        glTexCoord2d(0.0f,1.0f);                    // Quatrièmes coordonnées de texture 
        glVertex2f(-1.0f,-1.0f);                    // Quatrième vertex 
    glEnd();                                // Fin du dessin du premier rectangle

Tout de suite après avoir dessiné le premier carré, nous effectuons une rotation de 90 degrés sur les axes X et Y. Puis nous dessinons un nouveau carré. Le second carré va passer au travers du milieu du premier, créant une jolie forme.

 
Sélectionnez
    glRotatef(90.0f,1.0f,1.0f,0.0f);                    // Rotation sur les axes X et Y de 90 degrés (gauche à droite) 
    glBegin(GL_QUADS);                            // Dessin du second carré texturé 
        glTexCoord2d(0.0f,0.0f);                    // Premières coordonnées de texture 
        glVertex2f(-1.0f, 1.0f);                    // Premier vertex 
        glTexCoord2d(1.0f,0.0f);                    // Secondes coordonnées de texture 
        glVertex2f( 1.0f, 1.0f);                    // Second vertex 
        glTexCoord2d(1.0f,1.0f);                    // Troisièmes coordonnées de texture 
        glVertex2f( 1.0f,-1.0f);                    // Troisième vertex 
        glTexCoord2d(0.0f,1.0f);                    // Quatrièmes coordonnées de texture 
        glVertex2f(-1.0f,-1.0f);                    // Quatrième vertex 
    glEnd();                                // Fin du dessin du premier carré

Après le dessin des deux carrés texturés, nous activons le fondu, et nous dessinons le texte.

 
Sélectionnez
    glEnable(GL_BLEND);                            // Activation du fondu 
    glLoadIdentity();                            // Réinitialisation de la vue

Nous utilisons la même coloration fantaisiste que dans les autres tutoriels. La couleur est changée progressivement en suivant la position du mouvement du texte sur l'écran.

 
Sélectionnez
    // Coloration basée sur la position du texte 
    glColor3f(1.0f*float(cos(cnt1)),1.0f*float(sin(cnt2)),1.0f-0.5f*float(cos(cnt1+cnt2)));

Ensuite nous dessinons notre texte. Nous utilisons toujours glPrint(). Le premier paramètre est la position sur les X. Le second paramètre la position sur les Y. Le troisième paramètre '"NeHe") est le texte à écrire à l'écran, et le dernier paramètre est la série de caractères à utiliser (0 - normal, 1 - italique).

Comme vous l'avez certainement deviné, nous déplaçons le texte sur l'écran en utilisant COS et SIN, suivant les deux compteurs cnt1 et cnt2. Si vous ne comprenez pas ce que SIN et COS font, revenez en arrière et lisez les tutoriels précédents.

 
Sélectionnez
    glPrint(int((280+250*cos(cnt1))),int(235+200*sin(cnt2)),"NeHe",0);    // Affiche le texte OpenGL à l'écran 

    glColor3f(1.0f*float(sin(cnt2)),1.0f-0.5f*float(cos(cnt1+cnt2)),1.0f*float(cos(cnt1))); 
    glPrint(int((280+230*cos(cnt2))),int(235+200*sin(cnt1)),"OpenGL",1);    // Affiche le texte OpenGL à l'écran

Nous définissons la couleur à bleu foncé et écrivons le nom de l'auteur en bas de l'écran. Nous allons écrire son nom sur l'écran une deuxième fois en utilisant des lettres blanches. Les lettres blanches sont un peu décalées sur la droite par rapport aux lettres bleues. Cela crée un effet d'ombre. (Si le fondu n'était pas désactivé, l'effet ne fonctionnerait pas.)

 
Sélectionnez
    glColor3f(0.0f,0.0f,1.0f);                        // Définit la couleur à bleu 
    glPrint(int(240+200*cos((cnt2+cnt1)/5)),2,"Giuseppe D'Agata",0);    // Affiche le texte à l'écran 
 
    glColor3f(1.0f,1.0f,1.0f);                        // Définit la couleur à blanc 
    glPrint(int(242+200*cos((cnt2+cnt1)/5)),2,"Giuseppe D'Agata",0);    // Affiche le texte à l'écran

La dernière chose à faire est d'augmenter les deux compteurs à différentes vitesses. Cela fait se déplacer le texte, et tourner l'objet 3D.

 
Sélectionnez
    cnt1+=0.01f;                                // Augmente le premier compteur 
    cnt2+=0.0081f;                                // Augmente le second compteur 
    return TRUE;                                // Tout s'est bien passé 
}

Le code dans KillGLWindow(), CreateGLWindow() et WndProc() n'a pas changé donc nous le passons.

 
Sélectionnez
int WINAPI WinMain(    HINSTANCE    hInstance,                // Instance 
            HINSTANCE    hPrevInstance,                // Instance précédente 
            LPSTR        lpCmdLine,                // Paramètres de la ligne de commande 
            int        nCmdShow)                // L'état d'affichage de la fenêtre 
{ 
    MSG        msg;                            // Structure d'un message Windows 
    BOOL    done=FALSE;                            // Variable pour gérer la boucle infinie 

    // Demander à l'utilisateur quel mode d'exécution (fenêtre ou plein écran) il préfère
    if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO) 
    { 
        fullscreen=FALSE;                        // Mode fenêtré 
    }

Le titre de la fenêtre a changé.

 
Sélectionnez
    // Créer notre fenêtre OpenGL 
    if (!CreateGLWindow("NeHe & Giuseppe D'Agata's 2D Font Tutorial",640,480,16,fullscreen)) 
    { 
        return 0;                            // Quitter si la fenêtre n'a pas été créée 
    } 

    while(!done)                                // Boucle qui s'exécute tant que done=TRUE 
    { 
        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))            // Y a-t-il un message en attente ? 
        { 
            if (msg.message==WM_QUIT)                // Avons-nous reçu un message pour quitter ? 
            { 
                done=TRUE;                    // Si c'est le cas, on met done a TRUE 
            } 
            else                            // Sinon on gère les messages 
            { 
                TranslateMessage(&msg);                // Traduire le message 
                DispatchMessage(&msg);                // Envoyer le message 
            } 
        } 
        else                                // S'il n'y a pas de message 
        { 
            // Dessine la scène. Vérifie si Echap a été appuyée 
            if ((active && !DrawGLScene()) || keys[VK_ESCAPE])    // Active? Y avait-il un message reçu pour quitter ?
            { 
                done=TRUE;                    // Échap ou DrawGLScene ont signalé qu'il faut quitter 
            } 
            else                            // On ne quitte pas, mise à jour de l'écran 
            { 
                SwapBuffers(hDC);                // Échange des buffers (Double Buffering) 
            } 
        } 
    } 

    // Fermeture

La dernière chose à faire est d'ajouter KillFont() à la fin de KillGLWindow() tout comme je le montre ci-dessous. C'est important d'ajouter cette ligne. Cela nettoie les choses avant de sortir de notre 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;                            // Définit hInstance à NULL 
    } 

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

Je pense que je peux officiellement dire que mon site enseigne toutes les possibilités pour afficher du texte à l'écran {sourire}. Globalement, je pense que c'est un assez bon tutoriel. Le code peut être utilisé sur n'importe quel ordinateur qui peut exécuter OpenGL, il est facile à utiliser et écrire du texte à l'écran en utilisant cette méthode nécessite très peu de puissance du processeur.

J'aimerais remercier Giuseppe D'Agata pour la version originale du tutoriel. Je l'ai grandement modifié, converti au nouveau code de base, mais sans son code, je ne l'aurais probablement pas écrit. Sa version a quelques options supplémentaires, comme l'espacement des caractères, etc., mais je l'ai remplacé par un superbe objet 3D {sourire}.

J'espère que tout le monde va apprécier ce tutoriel. Si vous avez des questions, envoyez un courriel à Giuseppe D'Agata ou à moi-même.

Giuseppe D'Agata

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 ainsi que ClaudeLELOUP pour ses corrections.

V. Liens