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

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

Nous allons construire notre démo de la police texturée à partir du code de la leçon 14. S'il y a des changements dans une section particulière du programme, je vais réécrire le code entier de la section ce qui rendra les changements plus faciles à voir.

La partie suivante de code est similaire au code de la leçon 14, mais cette fois nous n'allons pas inclure le fichier stdarg.h.

 
Sélectionnez
#include <windows.h>                        // Fichier d'entête pour Windows 
#include <math.h>                        // Fichier d'entête pour la bibliothèque mathématique 
#include <stdio.h>                        // Fichier d'entête pour les entrées/sorties 
#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 nouvelle variable entière ici appelée texture[ ]. Elle sera utilisée pour stocker notre texture. Les trois dernières lignes qui étaient dans le tutoriel 14 n'ont pas changé ici.

 
Sélectionnez
GLuint    texture[1];                        // Une texture à appliquer (AJOUT) 
GLuint    base;                            // Base pour les listes d'affichage de la police 

GLfloat    rot;                            // Utilisée pour faire tourner le texte 

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

La partie suivante comporte quelques modifications. Dans ce tutoriel, je vais utiliser la police Wingdings pour afficher une tête de mort. Si vous voulez afficher du texte à la place, vous pouvez laisser le code comme il était dans la leçon 14, ou changer la police avec la vôtre.

Certains d'entre vous se demandaient comment utiliser la police Wingdings ce qui me fait une autre raison de ne pas utiliser les polices standards. La police Wingdings est une police de symboles, et requiert quelques modifications pour que cela marche. Ce n'est pas aussi facile de dire à Windows d'utiliser la police Wingdings. Si vous changez le nom de la police pour Wingdings, vous remarquerez que la police n'est pas sélectionnée. Vous devez indiquer à Windows que la police est une police de symboles et non pas une police de caractères normaux. Plus d'informations là-dessus, plus tard.

 
Sélectionnez
GLvoid BuildFont(GLvoid)                    // Construit notre police 
{ 
    GLYPHMETRICSFLOAT    gmf[256];            // Adresse du buffer qui contiendra la police 
    HFONT    font;                        // ID de Windows pour la police 

    base = glGenLists(256);                    // Emplacement pour 256 caractères 
    font = CreateFont(    -12,                // Hauteur de la police 
                0,                // Largeur de la police 
                0,                // Angle d'échappement 
                0,                // Angle d'orientation 
                FW_BOLD,            // Poids de la police 
                FALSE,                // Italique 
                FALSE,                // Souligné 
                FALSE,                // Barré

Ceci est la ligne magique ! À la place d'utiliser ANSI_CHARSET comme nous l'avions fait dans le tutoriel 14, nous allons utiliser SYMBOL_CHARSET. Cela indique à Windows que la police que nous construisons n'est pas une police typiquement faite de caractères. Une police de symboles n'est pas habituellement faite de petites images (symboles). Si vous oubliez de changer cette ligne, Wingdings, Webdings et toute autre police de symboles que vous essayerez d'utiliser ne fonctionneront pas.

 
Sélectionnez
                SYMBOL_CHARSET,            // Identifiant du jeu de caractères (MODIFIÉ)

Les lignes suivantes n'ont pas changé.

 
Sélectionnez
                OUT_TT_PRECIS,            // Précision du rendu 
                CLIP_DEFAULT_PRECIS,        // Précision du découpage 
                ANTIALIASED_QUALITY,        // Qualité de rendu 
                FF_DONTCARE|DEFAULT_PITCH,    // Famille et Pitch

Maintenant que nous avons sélectionné l'identifiant du jeu de symboles, nous pouvons sélectionner la police Wingdings !

 
Sélectionnez
                "Wingdings");            // Nom de la police (MODIFIÉ)

Les lignes restantes n'ont pas changé.

 
Sélectionnez
    SelectObject(hDC, font);                // Sélectionne la police que nous avons créée 
 
    wglUseFontOutlines(    hDC,                // Sélectionne le DC courant 
                0,                // Caractère de début 
                255,                // Nombre de listes d'affichage à construire 
                base,                // Début des listes d'affichage

Je vais permettre plus d'écart. Cela signifie qu'OpenGL ne va pas suivre le contour de la police très exactement. Si vous définissez l'écart à 0.0f, vous allez remarquer certains problèmes avec les textures sur les surfaces fortement courbées. Si vous définissez un écart plus grand, la plupart des problèmes vont disparaître.

 
Sélectionnez
                0.1f,                // Écart sur les contours

Les trois prochaines lignes restent les mêmes.

 
Sélectionnez
                0.2f,                // Épaisseur de la police dans l'axe des Z 
                WGL_FONT_POLYGONS,        // Utilisation de polygones, et non de lignes 
                gmf);                // Adresse du tampon  l'on doit recevoir les données 
}

Juste avant ReSizeGLScene() nous allons ajouter la section de code suivante, qui permet de charger notre texture. Vous devez reconnaître ce code venant des tutoriels précédents. Nous créons un espace de stockage pour l'image bitmap. Nous chargeons l'image. Nous disons à OpenGL de générer une texture, puis nous stockons la texture dans texture[0].

Je crée une texture MIP map parce que cela est plus beau. Le nom de la texture est lights.bmp.

 
Sélectionnez
AUX_RGBImageRec *LoadBMP(char *Filename)            // Charge une image bitmap 
{ 
    FILE *File=NULL;                    // Identifiant de fichier 

    if (!Filename)                        // Regarde si un nom de fichier a été donné 
    { 
        return NULL;                    // Si non, retourne NULL 
    } 

    File=fopen(Filename,"r");                // Vérifie si le fichier existe 

    if (File)                        // Est-ce que le fichier existe ? 
    { 
        fclose(File);                    // Ferme l'identifiant 
        return auxDIBImageLoad(Filename);        // Charge le bitmap et retourne un pointeur 
    } 

    return NULL;                        // Si échec, retourne NULL 
} 

int LoadGLTextures()                        // Charge des fichiers bitmap et convertit les textures 
{ 
    int Status=FALSE;                    // Indicateur de statut 

    AUX_RGBImageRec * TextureImage[1];            // Crée un espace de stockage pour les textures 

    memset(TextureImage,0,sizeof(void *)*1);        // Définit le pointeur à NULL 

    if (TextureImage[0]=LoadBMP("Data/Lights.bmp"))        // Charge le bitmap 
    { 
        Status=TRUE;                    // Définit le statut à TRUE 

        glGenTextures(1, &texture[0]);            // Crée la texture 

        // Build Linear Mipmapped Texture 
        glBindTexture(GL_TEXTURE_2D, texture[0]); 
        gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); 
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);

Les quatre lignes suivantes de code vont générer les coordonnées de texture automatiquement pour n'importe quel objet dessiné sur l'écran. La commande glTexGen() est extrêmement puissante, et complexe, et pour comprendre toutes les formules mathématiques impliquées il faudrait un autre tutoriel. Tout ce que vous devez savoir est que GL_S et GL_T sont les coordonnées de texture. Par défaut elles vont être définies pour prendre les valeurs de la position courante sur X et Y de l'écran et produire un vertex de texture. Vous pouvez remarquer que les objets ne sont pas texturés sur l'axe des Z… seulement des rayures vont apparaître. Les faces avant et arrière sont bien texturées, et c'est tout ce qui compte. X (GL_S) va couvrir l'application de la texture de gauche à droite et Y (GL_T) va couvrir l'application de la texture de haut en bas.

GL_TEXTURE_GEN_MODE nous permet de sélectionner le mode d'application de la texture que nous voulons utiliser sur les coordonnées S et T de la texture. Nous avons trois choix :

  • GL_EYE_LINEAR - La texture est attachée à l'écran. Elle ne bouge jamais. La texture est appliquée sur l'objet avec le morceau de la texture sur lequel il est au-dessus ;
  • GL_OBJECT_LINEAR - Ce mode est celui que l'on utilise. La texture est attachée à l'objet qui se déplace sur l'écran ; GL_SPHERE_MAP - le favori de tous. Crée un objet avec une réflexion métallique.

Il est important de noter que j'omets beaucoup de lignes de code. Nous pouvions aussi bien définir le GL_OBJECT_PLANE, mais par défaut il est défini avec les paramètres que l'on désire. Achetez un bon livre, si vous voulez en savoir plus, ou alors regardez l'aide MSDN cd / dvd.

 
Sélectionnez
        // Application des textures sur les contours de l'objet 
        glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); 
        // Application des textures sur les contours de l'objet 
        glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); 
        glEnable(GL_TEXTURE_GEN_S);            // Génération automatique des textures 
        glEnable(GL_TEXTURE_GEN_T);            // Génération automatique des textures 
    } 

    if (TextureImage[0])                    // Si la texture existe 
    { 
        if (TextureImage[0]->data)            // Si l'image de la texture existe 
        { 
            free(TextureImage[0]->data);        // Libération de la mémoire pour l'image de la texture 
        } 

        free(TextureImage[0]);                // Libération de la mémoire pour la structure de l'image 
    } 

    return Status;                        // Renvoie le statut 
}

Il y a quelques nouvelles lignes à la fin du code de InitGL(). BuildFont() qui a été déplacé en dessous du code de chargement de la texture. La ligne glEnable(GL_COLOR_MATERIAL) a été retirée. Si vous prévoyez d'appliquer des couleurs à la texture en utilisant glColor3f(r,g,b) remettez la ligne glEnable(GL_COLOR_MATERIAL) dans le code.

 
Sélectionnez
int InitGL(GLvoid)                        // L'initialisation de OpenGL va ici 
{ 
    if (!LoadGLTextures())                    // Va à la fonction de chargement de la texture 
    { 
        return FALSE;                    // Si la texture n'a pas été chargée, retourne FALSE 
    } 
    BuildFont();                        // Construit la police 

    glShadeModel(GL_SMOOTH);                // Activation de l'ombre douce 
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);            // Fond noir 
    glClearDepth(1.0f);                    // Mise en place du tampon de profondeur 
    glEnable(GL_DEPTH_TEST);                // Activation du test de profondeur 
    glDepthFunc(GL_LEQUAL);                    // Le type de test de profondeur à faire 
    glEnable(GL_LIGHT0);                    // Activation de la lampe 0 (rapide et sale) 
    glEnable(GL_LIGHTING);                    // Activation de l'éclairage 
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);    // Calcul de jolies perspectives

Activez le placage de texture 2D, et sélectionnez la première texture. Cela va plaquer la texture sur n'importe quel objet 3D dessiné à l'écran. Si vous voulez plus de contrôle, vous pouvez activer et désactiver le placage de texture vous-même.

 
Sélectionnez
    glEnable(GL_TEXTURE_2D);                // Activation du placage de texture 
    glBindTexture(GL_TEXTURE_2D, texture[0]);        // Sélectionne la texture 
    return TRUE;                        // L'initialisation est OK 
}

Le code de redimensionnement n'a pas changé, mais celui de DrawGLScene, oui.

 
Sélectionnez
int DrawGLScene(GLvoid)                        // Ici l'endroit  nous effectuons le dessin 
{ 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // Nettoyage de l'écran et du tampon de profondeur 
    glLoadIdentity();                    // Réinitialisation de la vue

Ici notre premier changement. À la place de garder un objet au milieu de l'écran, nous allons tourner autour en utilisant COS et SIN (aucune surprise). Nous allons nous déplacer de 3 unités dans l'écran (-3.0f). Sur l'axe des X, nous allons nous décaler de -1.1, tout à gauche, jusqu'à 1.1, tout à droite. Nous utiliserons la variable rot pour contrôler le décalage de gauche à droite. Nous nous décalerons de 0.8, tout en haut, à -0.8, tout en bas. Nous utiliserons aussi la variable rot pour ce décalage (vous pouvez aussi bien utiliser vos propres variables).

 
Sélectionnez
    // Positionne le texte 
    glTranslatef(1.1f*float(cos(rot/16.0f)),0.8f*float(sin(rot/20.0f)),-3.0f);

Maintenant nous faisons la rotation des normales. Ceci va provoquer la rotation du symbole sur les axes X, Y et Z.

 
Sélectionnez
    glRotatef(rot,1.0f,0.0f,0.0f);                // Tourne sur l'axe des X 
    glRotatef(rot*1.2f,0.0f,1.0f,0.0f);            // Tourne sur l'axe des Y 
    glRotatef(rot*1.4f,0.0f,0.0f,1.0f);            // Tourne sur l'axe des Z

Nous nous déplaçons un petit peu sur la gauche et sur le bas ainsi que vers la caméra pour centrer le symbole sur chaque axe. Sinon lorsque l'objet tourne, nous n'avons pas l'impression qu'il tourne autour de son propre centre. -0.35f est juste un nombre qui marche. J'ai dû jouer avec les valeurs pendant un petit moment parce que je ne suis pas sûr de la largeur de la police, qui peut aussi varier avec chaque police. Je ne suis pas sûr de savoir pourquoi les polices ne sont pas construites autour du point central.

 
Sélectionnez
    glTranslatef(-0.35f,-0.35f,0.1f);            // Centre sur l'axe des X, Y et Z

Finalement nous dessinons notre symbole de tête de mort puis nous augmentons la variable rot pour que notre symbole tourne et se déplace sur l'écran. Si vous n'arrivez pas à comprendre comment je peux obtenir une tête de mort en partant de la lettre 'N', faites ceci : démarrez Microsoft Word ou Wordpad. Allez dans le menu des polices. Sélectionnez la police Wingdings. Tapez un 'N' majuscule. Une tête de mort devrait apparaître.

 
Sélectionnez
    glPrint("N");                        // Dessine le symbole de tête de mort 
    rot+=0.1f;                        // Augmente la variable de rotation 
    return TRUE;                        // Continue 
}

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 quitter notre programme.

 
Sélectionnez
    if (!UnregisterClass("OpenGL",hInstance))        // Pouvons-nous 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 
}

Bien que je n'aie jamais été dans les détails, vous devez avoir une bonne compréhension sur le comment obtenir qu'OpenGL génère les coordonnées de texture pour vous. Vous ne devrez avoir aucun problème pour appliquer des textures sur vos polices par vous-même, ou même n'importe quel autre objet. Et seulement en changeant deux lignes de code, vous pouvez activer le plaquage de texture sur une sphère, ce qui est vraiment un effet cool.

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