Application de texture sur les polices vectorielles

Jeff Molofee (NeHe)

Traducteur

Alexandre Laurent

Après avoir posté les deux derniers tutoriels sur les polices bitmap et les polices vectorielles, j'ai reçu beaucoup de courriels venant de personnes se demandant comment elles pouvaient appliquer une texture sur les polices. Vous pouvez utiliser la génération automatique des coordonnées de texture. Cela va vous générer les coordonnées des textures par chaque polygone de la police. ♪

- Titre : Application de texture sur les polices vectorielles

- Auteur : Jeff Molofee (NeHe)

- Traducteur : Alexandre Laurent

- Parution : 19 décembre 2012

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2006 Nehe Gamedev.net. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Contributions

- II. Tutoriel

- III. Téléchargements

- IV. Remerciements

- V. Liens

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.

#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.

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.

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.

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

Les lignes suivantes n'ont pas changé.

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 !

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

Les lignes restantes n'ont pas changé.

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.

0.1f,                // Écart sur les contours

Les trois prochaines lignes restent les mêmes.

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 où 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.

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.

// 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.

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.

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.

int DrawGLScene(GLvoid)                        // Ici l'endroit où 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).

// 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.

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.

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.

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.

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 :

- Borland C++ Builder 6 (Conversion by Christian Kindahl) ;

- Code Warrior 5.3 (Conversion by Scott Lupton) ;

- Delphi (Conversion by Michal Tucek) ;

- Dev C++ (Conversion by Dan) ;

- Euphoria (Conversion by Evan Marshall) ;

- GLut (Conversion by David Phillip Oster) ;

- Java (Conversion by Jeff Kirby) ;

- LCC Win32 (Conversion by Robert Wishlaw) ;

- Mac OS (Conversion by David Phillip Oster) ;

- MASM (Conversion by Greg Helps) ;

- Pelles C (Conversion by Pelle Orinius) ;

- Visual Basic (Conversion by Ross Dawson) ;

- Visual C++ ;

- Visual Studio .NET (Conversion by Grant James).

IV - Remerciements

Merci Winjerome pour sa relecture ainsi que ClaudeLELOUP pour ses corrections.

V - Liens

Sommaire.

Tutoriel précédent : Polices vectorielles.

Tutoriel suivant : Effet de brouillard réaliste.