Masquage

Jeff Molofee (NeHe)

Traducteur

Alexandre Laurent

Bienvenue dans le 20e tutoriel. Le format d'image Bitmap est supporté sur tous les ordinateurs et sur tous les systèmes d'exploitation. Non seulement il est facile de travailler avec lui, mais il est aussi très facile de le charger et l'utiliser comme une texture. Jusqu'à présent, nous avons utilisé le fondu pour placer du texte et autres images sur l'écran sans effacer ce qu'il y avait en dessous. C'est efficace, mais les résultats ne sont pas toujours jolis. ♪

- Titre : Masquage

- 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

La plupart du temps une texture fondue l'est de trop ou pas assez. Lorsque vous faites un jeu en utilisant des sprites, vous ne voulez pas que la scène derrière le personnage brille à travers son corps. Lorsque vous écrivez du texte à l'écran, vous voulez le texte solide et facile à lire.

C'est ici que le masquage (masking) devient pratique. Le masquage est un processus en deux étapes. Premièrement nous plaçons une image noire et blanche de notre texture sur le dessus de la scène. Le blanc représente la partie transparente de la texture. Le noir représente la partie solide. Dû au type de masquage que nous utilisons, seul le noir va apparaître sur la scène. Presque comme un effet d'emporte-pièce. Nous changeons alors les modes de fondu, et nous appliquons notre texture au-dessus de la coupure noire. Toujours à cause du mode de fondu utilisé, les seules parties de la texture qui vont être copiées sont les parties qui se trouvent au-dessus du masque noir.

Je vais récrire tout le programme de ce tutoriel à part les parties qui n'ont pas changé. Donc si vous êtes prêt pour apprendre quelque chose de nouveau, commençons !

#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

Nous allons utiliser sept variables globales dans ce programme. 'masking' est une variable booléenne (TRUE / FALSE) qui va garder l'état du masquage : activé ou non. 'mp' est utilisée pour être sûr que la touche 'M' n'est pas maintenue enfoncée. 'sp' est utilisée pour être sûr que la barre espace n'est pas maintenue enfoncée et la variable 'scene' va déterminer si nous dessinons la première ou la deuxième scène.

Nous définissons un espace pour cinq textures en utilisant la variable texture[5]. 'loop' est un compteur générique, nous allons l'utiliser plusieurs fois dans notre programme pour définir les textures, etc. Finalement nous avons une variable 'roll'. Nous utilisons 'roll' pour déplacer les textures sur l'écran. Cela va créer un effet soigné ! Nous allons aussi l'utiliser pour faire tourner l'objet de la scène 2.

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 à plein écran par défaut
bool    masking=TRUE;                                // Masquage activé/désactivé
bool    mp;                                    // Touche M pressée ?
bool    sp;                                    // Barre espace pressée ?
bool    scene;                                    // Quelle scène on dessine
GLuint    texture[5];                                // Espace pour cinq textures
GLuint    loop;                                    // Variable générique de boucle
GLfloat    roll;                                    // Texture roulante
LRESULT    CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);                // Déclaration pour WndProc

Le code du chargement des Bitmaps n'a pas changé. C'est le même que dans la leçon 6, etc.

Dans le code ci-dessous, nous créons un espace pour cinq images. Nous nettoyons l'espace et nous chargeons les cinq Bitmaps. Nous bouclons sur chaque image et la convertissons en une texture utilisable par notre programme. Les textures sont stockées dans 'texture[0-4]'.

int LoadGLTextures()                                // Ouvre les Bitmaps et les convertit en textures
{
int Status=FALSE;                            // Indicateur du statut
AUX_RGBImageRec *TextureImage[5];                    // Crée un espace pour les textures
memset(TextureImage,0,sizeof(void *)*5);                // Initialise le pointeur à NULL

if ((TextureImage[0]=LoadBMP("Data/logo.bmp")) &&            // Texture du logo
(TextureImage[1]=LoadBMP("Data/mask1.bmp")) &&            // Premier masque
(TextureImage[2]=LoadBMP("Data/image1.bmp")) &&            // Première image
(TextureImage[3]=LoadBMP("Data/mask2.bmp")) &&            // Deuxième masque
(TextureImage[4]=LoadBMP("Data/image2.bmp")))            // Deuxième image
{
Status=TRUE;                            // Définit le statut à TRUE
glGenTextures(5, &texture[0]);                    // Crée cinq textures

for (loop=0; loop<5; loop++)                    // Boucle à travers les cinq 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);
}
}
for (loop=0; loop<5; loop++)                        // Boucle à travers les cinq textures
{
if (TextureImage[loop])                        // Si la texture existe
{
if (TextureImage[loop]->data)                // Si l'image de la texture existe
{
free(TextureImage[loop]->data);            // Libère la mémoire de l'image de la texture
}
free(TextureImage[loop]);                // Libère la structure de l'image
}
}
return Status;                                // Retourne le statut
}

Le code de ReSizeGLScene() n'a pas changé donc nous le passons.

Le code de Init est réduit à l'essentiel. Nous chargeons nos textures, définissons la couleur de fond, définissons et activons le test de profondeur, activons les ombres douces et activons le placage des textures. Un programme simple, donc pas besoin d'initialisation complexe. :)

int InitGL(GLvoid)                                // Toute la configuration d'OpenGL se met ici
{
if (!LoadGLTextures())                            // Va dans notre routine de chargement de texture
{
return FALSE;                            // Si les textures n'ont pas été chargées, retourne FALSE
}

glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                    // Fond d'écran noir
glClearDepth(1.0);                            // Configuration du tampon de profondeur
glEnable(GL_DEPTH_TEST);                        // Active le test de profondeur
glShadeModel(GL_SMOOTH);                        // Active les ombres douces
glEnable(GL_TEXTURE_2D);                        // Active l'application de texture
return TRUE;                                // L'initialisation s'est bien passée
}

Maintenant la partie amusante. Notre code de dessin ! Nous commençons avec le même code que d'habitude. Nous réinitialisons la couleur de fond et le tampon de profondeur. Puis nous réinitialisons la matrice modelview, et nous reculons de deux unités pour que nous puissions voir notre scène.

int DrawGLScene(GLvoid)                                // C'est ici que nous faisons tous les rendus
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // Vide l'écran et le tampon de profondeur
glLoadIdentity();                            // Remet à zéro la matrice courante (modelview)
glTranslatef(0.0f,0.0f,-2.0f);                        // Déplace de deux unités dans l'écran

La première ligne ci-dessous sélectionne la texture 'logo'. Nous allons appliquer la texture sur l'écran en utilisant un carré. Nous spécifions les quatre coordonnées de texture avec les quatre vertex.

Description revue par Jonathan Roy : rappelez-vous qu'OpenGL est un système graphique basé sur les vertex. La plupart des paramètres que vous définissez sont enregistrés comme des attributs d'un vertex particulier. Les coordonnées de texture sont l'un de ces attributs. Vous spécifiez simplement les bonnes coordonnées de texture pour chaque vertex d'un polygone, et OpenGL va automatiquement remplir la surface entre les vertex avec la texture, à travers un processus connu sous le nom d'interpolation. L'interpolation est une technique géométrique standard qui laisse OpenGL déterminer comment les paramètres varient entre les vertex juste en connaissant les valeurs des paramètres aux vertex eux-mêmes.

Comme dans les leçons précédentes, nous supposons que nous avons un carré et que nous assignons les coordonnées de la texture comme suit : (0.0, 0.0) au coin en bas à gauche, (0.0, 1.0) au coin en haut à gauche, (1.0, 0.0) au coin en bas à droite et (1.0, 1.0) au coin en haut à droite. Maintenant que nous avons ces paramètres, pouvez-vous dire quelles coordonnées de texture correspondent au milieu du carré ? C'est juste, (0.5, 0.5). Mais nulle part dans le code vous n'avez spécifié ces coordonnées, n'est-ce pas ? Lorsque OpenGL dessine le carré, il le calcule pour vous. Et la vraie magie est qu'il le fait pour n'importe quelles position, taille ou orientation du polygone !

Dans cette leçon nous ajoutons une nouvelle notion en assignant aux coordonnées de texture des valeurs différentes de 0.0 et 1.0. Les coordonnées de texture sont dites normalisées. La valeur 0.0 correspond à un bord de la texture, et la valeur 1.0 au bord opposé, parcourant toute la largeur et la hauteur de la texture de l'image par pas de une unité, sans se soucier de la taille du polygone ou de la taille de l'image en pixels (ce dont nous n'avons pas besoin de s'inquiéter lorsque l'on applique une texture, et c'est ce qui rend la vie beaucoup plus facile). Au-dessus de 1.0, le placage de la texture recommence de l'autre côté et la texture se répète. En d'autres mots, les coordonnées de texture (0.3, 0.5) par exemple, correspondent au même pixel de l'image que les coordonnées (1.3, 0.5), ou (12.3, -2.5). Dans cette leçon, nous faisons un effet de tuiles en spécifiant une valeur de 3.0 à la place de 1.0, en faisant se répéter neuf fois la texture (3x3 tuiles) sur la surface du rectangle.

De plus, nous utilisons une variable 'roll' pour déplacer (ou faire glisser) la texture sur la surface du carré. Une valeur de 0.0 pour 'roll', qui est ajoutée à la coordonnée de texture verticale, signifie que le placage du bas de la texture du rectangle commence en bas de l'image de la texture, comme on peut le voir dans la figure de gauche. Lorsque 'roll' est égal à 0.5, le placage du bas du rectangle commence au milieu de la hauteur de l'image (voir la figure sur la droite). Le roulement de texture peut être utilisé pour faire de jolis effets comme des nuages qui se déplacent, ou des mots qui tournent autour d'un objet, etc.

glBindTexture(GL_TEXTURE_2D, texture[0]);                // Sélectionne notre texture du logo
glBegin(GL_QUADS);                            // Commence le dessin d'un carré texturé
glTexCoord2f(0.0f, -roll+0.0f); glVertex3f(-1.1f, -1.1f,  0.0f);    // Bas gauche
glTexCoord2f(3.0f, -roll+0.0f); glVertex3f( 1.1f, -1.1f,  0.0f);    // Bas droit
glTexCoord2f(3.0f, -roll+3.0f); glVertex3f( 1.1f,  1.1f,  0.0f);    // Haut droit
glTexCoord2f(0.0f, -roll+3.0f); glVertex3f(-1.1f,  1.1f,  0.0f);    // Haut gauche
glEnd();                                // Fin du dessin du carré

Peu importe… retour à la réalité. Maintenant nous activons le fondu. Pour avoir l'effet correct, nous devons désactiver le test de profondeur. C'est très important de le faire. Si vous ne désactivez pas le test de profondeur, vous allez sûrement ne rien voir. Votre image va disparaître !

glEnable(GL_BLEND);                            // Active le fondu
glDisable(GL_DEPTH_TEST);                        // Désactive le test de profondeur

La première chose à faire après avoir activé le fondu et désactivé le test de profondeur est de vérifier si nous allons masquer notre image ou la fondre avec l'ancienne méthode. La ligne de code ci-dessous vérifie si 'masking' est à TRUE. Si oui, nous allons définir le fondu pour avoir le masque dessiné correctement.

if (masking)                                // Le masquage est-il  activé ?
{

Si 'masking' est à TRUE la ligne ci-dessous va définir le fondu de notre masque. Un masque n'est qu'une copie de la texture que nous voulons dessiner à l'écran, mais en noir et blanc. Toutes les parties blanches vont être transparentes. Toutes les parties noires vont être SOLIDES.

La commande de fondu ci-dessous fait la chose suivante : la couleur de destination (couleur de l'écran) va être mise en noir si la section du masque qui est à copier est noire. Cela signifie que les sections de l'écran qui sont recouvertes par les parties noires de notre masque seront coloriées en noir. Tout ce qui se situait à l'écran sous ce masque aura été noirci. La section de l'écran en dessous des parties blanches du masque ne va pas changer.

glBlendFunc(GL_DST_COLOR,GL_ZERO);                // Fondu de la couleur avec zéro (noir)
}

Maintenant nous vérifions la scène qu'il faut dessiner. Si 'scene' est à TRUE nous allons dessiner la deuxième scène. Si la scène est à FALSE nous dessinerons la première.

if (scene)                                // Devons-nous dessiner la deuxième scène ?
{

Nous ne voulons pas de choses trop grandes donc nous nous déplaçons d'une unité de plus dans l'écran. Cela réduit la taille des objets.

Après ce déplacement, nous faisons une rotation de 0 à 360 degrés selon la valeur de 'roll'. Si 'roll' est à 0.0 nous faisons une rotation de 0 degré. Si 'roll' est à 1.0 nous faisons une rotation de 360 degrés. Rotation plutôt rapide, mais je ne voulais pas créer une nouvelle variable juste pour faire une rotation de l'image au centre de l'écran. :)

glTranslatef(0.0f,0.0f,-1.0f);                    // Déplace d'une unité dans l'écran
glRotatef(roll*360,0.0f,0.0f,1.0f);                // Tourne sur l'axe des Z de 360 degrés

Nous avons déjà le logo qui glisse sur l'écran et nous avons tourné la scène sur l'axe des Z provoquant une rotation dans le sens antihoraire des objets que nous dessinons, maintenant il ne nous reste plus qu'à vérifier si le masquage est activé. S'il l'est, nous dessinons notre masque puis notre objet. Si le masquage est désactivé, nous allons juste dessiner notre objet.

if (masking)                            // Est-ce que le masquage est activé ?
{

Si le masquage est à TRUE le code ci-dessous va dessiner notre masque sur l'écran. Notre mode de fondu devrait être défini comme il faut, car nous avons déjà vérifié le masquage lorsque nous avons défini le fondu. Maintenant tout ce que nous avons à faire est de dessiner le masque à l'écran. Nous sélectionnons le masque 2 (parce que c'est la deuxième scène). Après avoir sélectionné le masque de texture, nous plaquons la texture sur un carré. Le carré fait 1.1 unité de long pour qu'il remplisse un peu plus que l'écran. Nous voulons seulement avoir une texture de visible donc nos coordonnées de texture vont de 0.0 à 1.0.

Après le dessin de notre masque sur l'écran, une copie noire de notre texture finale va apparaître sur l'écran. Le résultat final va ressembler à une image découpée au cutter dans laquelle on aurait enlevé notre texture finale de l'écran, laissant un espace noir.

glBindTexture(GL_TEXTURE_2D, texture[3]);        // Sélectionne la seconde texture du masque
glBegin(GL_QUADS);                    // Commence le dessin du carré
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f, -1.1f,  0.0f);    // Bas gauche
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f, -1.1f,  0.0f);    // Bas droit
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f,  1.1f,  0.0f);    // Haut droit
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f,  1.1f,  0.0f);    // Haut gauche
glEnd();                        // Fin du dessin du carré
}

Maintenant que nous avons dessiné notre masque à l'écran, il est temps de rechanger de mode de fondu. Cette fois nous allons dire à OpenGL que nous voulons copier n'importe quelle partie de notre texture qui n'est pas noire à l'écran. Parce que la texture finale est une copie exacte de notre masque, mais avec des couleurs, les seules parties de notre texture qui vont être dessinées sont les parties au-dessus de la partie noire du masque. Parce que le masque est noir, rien de l'écran ne va ressortir sur notre texture. Cela nous laisse avec une texture solide semblant flotter sur le dessus de l'écran.

Remarquez que nous sélectionnons la deuxième image après avoir sélectionné le mode de fondu final. Cela sélectionne notre image colorée (l'image du second masque est basée sur celle-ci). Remarquez aussi que nous dessinons l'image pile au-dessus du masque. Mêmes coordonnées, mêmes vertex.

Si nous ne fixons pas un masque, notre image sera toujours copiée à l'écran, mais elle sera fondue avec le reste de l'écran.

glBlendFunc(GL_ONE, GL_ONE);                    // Copie les couleurs de l'image 2 à l'écran
glBindTexture(GL_TEXTURE_2D, texture[4]);            // Sélectionne la texture de la seconde image
glBegin(GL_QUADS);                        // Commence à dessiner un carré texturé
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f, -1.1f,  0.0f);    // Bas gauche
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f, -1.1f,  0.0f);    // Bas droit
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f,  1.1f,  0.0f);    // Haut droit
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f,  1.1f,  0.0f);    // Haut gauche
glEnd();                            // Fin du dessin du carré
}

Si 'scene' est à FALSE, nous dessinons la première scène (ma favorite).

else                                    // Sinon
{

Nous commençons par vérifier si le masquage est à TRUE ou FALSE, comme précédemment.

if (masking)                            // Le masquage est-il activé ?
{

Si le masquage est à TRUE, nous dessinons le masque 1 à l'écran (le masque pour la scène 1). Remarquez que la texture glisse de la droite vers la gauche ('roll' est ajouté aux coordonnées horizontales de texture). Nous voulons que la texture remplisse l'écran c'est pourquoi nous ne nous déplaçons jamais plus loin que l'écran.

glBindTexture(GL_TEXTURE_2D, texture[1]);        // Sélectionne la texture du premier masque
glBegin(GL_QUADS);                    // Dessine un carré texturé
glTexCoord2f(roll+0.0f, 0.0f); glVertex3f(-1.1f, -1.1f,  0.0f);    // Bas gauche
glTexCoord2f(roll+4.0f, 0.0f); glVertex3f( 1.1f, -1.1f,  0.0f);    // Bas droit
glTexCoord2f(roll+4.0f, 4.0f); glVertex3f( 1.1f,  1.1f,  0.0f);    // Haut droit
glTexCoord2f(roll+0.0f, 4.0f); glVertex3f(-1.1f,  1.1f,  0.0f);    // Haut gauche
glEnd();                        // Fin du dessin du carré
}

Encore une fois nous activons le fondu et sélectionnons la texture pour la scène 1. Nous appliquons la texture sur le dessus de son masque. Remarquez que nous faisons glisser aussi la texture sinon le masque et l'image finale ne seraient pas alignés.

glBlendFunc(GL_ONE, GL_ONE);                    // Copie l'image colorée 1 à l'écran
glBindTexture(GL_TEXTURE_2D, texture[2]);            // Sélectionne la première texture image
glBegin(GL_QUADS);                        // Commence le dessin de carré texturé
glTexCoord2f(roll+0.0f, 0.0f); glVertex3f(-1.1f, -1.1f,  0.0f);    // Bas gauche
glTexCoord2f(roll+4.0f, 0.0f); glVertex3f( 1.1f, -1.1f,  0.0f);    // Bas droit
glTexCoord2f(roll+4.0f, 4.0f); glVertex3f( 1.1f,  1.1f,  0.0f);    // Haut droit
glTexCoord2f(roll+0.0f, 4.0f); glVertex3f(-1.1f,  1.1f,  0.0f);    // Haut gauche
glEnd();                            // Fin du dessin du carré
}

Ensuite nous activons le test de profondeur, et désactivons le fondu. Cela évite d'étranges effets dans le reste du programme. :)

glEnable(GL_DEPTH_TEST);                        // Active le test de profondeur
glDisable(GL_BLEND);                            // Désactive le fondu

Finalement tout ce qu'il nous reste à faire est d'incrémenter la valeur de 'roll'. Si 'roll' est plus grand que 1.0 nous enlevons 1.0. Cela évite d'avoir des valeurs de 'roll' élevées.

roll+=0.002f;                                // Incrémente notre variable 'roll' pour les textures
if (roll>1.0f)                                // 'roll' est-il plus grand que 1 ?
{
roll-=1.0f;                            // Enlève 1 à 'roll'
}

return TRUE;                                // Tout est OK
}

Les fonctions KillGLWindow(), CreateGLWindow() et WndProc() n'ont pas changé donc nous les passons.

La première différence que vous allez remarquer dans le code WinMain() est le titre de la fenêtre. Maintenant elle s'appelle « NeHe's Masking Tutorial ». Changez-le en ce que vous voulez. :)

int WINAPI WinMain(    HINSTANCE    hInstance,                // Instance
HINSTANCE    hPrevInstance,                // Instance précédente
LPSTR        lpCmdLine,                // Paramètres de 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é
}

// Créer notre fenêtre OpenGL
if (!CreateGLWindow("NeHe's Masking Tutorial",640,480,16,fullscreen))
{
return 0;                            // Quitte si la fenêtre n'est pas 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 la touche 'Échap' et les messages pour quitter venant de DrawGLScene()
if ((active && !DrawGLScene()) || keys[VK_ESCAPE])    // Met à jour la fenêtre si seulement le programme est actif
{
done=TRUE;                    // ESC ou DrawGLScene() signale la fin du programme
}
else                            // On ne quitte pas encore, on va dessiner
{
SwapBuffers(hDC);                // Échange les tampons (Double tampon)

Maintenant notre gestion simple des touches. Nous regardons si la barre espace a été pressée. Si oui, nous définissons la variable 'sp' à TRUE. Si 'sp' est à TRUE, le code ci-dessous ne sera pas exécuté une deuxième fois, tant que la barre espace n'aura pas été relâchée. Cela évite que notre programme boucle sur les différentes scènes trop rapidement. Après avoir mis 'sp' à TRUE, nous basculons la scène. Si la variable était à TRUE, elle devient FALSE, et si elle était à FALSE elle devient TRUE. Dans notre code de dessin ci-dessus, si 'scene' est à FALSE la première scène est dessinée. Si 'scène' est à TRUE la seconde est dessinée.

if (keys[' '] && !sp)                // Si la barre espace est pressée ?
{
sp=TRUE;                // Dit au programme que la barre espace est maintenue
scene=!scene;                // Change la scène pour l'autre
}

Le code ci-dessous vérifie si nous avons relâché la barre espace (si PAS ' '). Si la barre espace a été relâchée, nous mettons 'sp' à FALSE disant au programme que la barre espace n'est PAS maintenue appuyée. En mettant 'sp' à FALSE, le code ci-dessus va vérifier si la barre espace a été de nouveau appuyée, et si c'est le cas, le cycle recommence.

if (!keys[' '])                    // La barre espace est-elle relâchée ?
{
sp=FALSE;                // Indique au programme que la barre espace a été relâchée
}

La prochaine section de code vérifie si la touche 'M' a été appuyée. Si elle a été appuyée, nous mettons 'mp' à TRUE, disant au programme de ne pas revérifier ceci tant que la touche n'a pas été relâchée, et nous changeons 'masking' de TRUE à FALSE ou de FALSE à TRUE. Si 'masking' est à TRUE, le code de dessin va utiliser le masquage. Si elle est à FALSE le masquage sera désactivé. Si 'masking' est éteint, les objets vont être fondus sur l'écran en utilisant la vieille méthode de fondu que nous avions utilisée jusqu'à présent.

if (keys['M'] && !mp)                // Si 'M' a été pressée
{
mp=TRUE;                // Indique au programme que 'M' est maintenue
masking=!masking;            // Change le mode de masquage
}

La dernière partie de code vérifie si nous avons arrêté d'appuyer sur 'M'. Si c'est le cas, 'mp' devient FALSE indiquant au programme que nous n'appuyons plus sur 'M'. Une fois la touche 'M' relâchée, nous pouvons appuyer dessus une autre fois pour changer le masquage.

if (!keys['M'])                    // 'M' a-t-elle  été relâchée ?
{
mp=FALSE;                // Indique au programme que 'M' a été relâchée
}

Comme tous les tutoriels précédents, vérifiez que le titre de la fenêtre est correct.

if (keys[VK_F1])                // Est-ce que F1 est pressée ?
{
keys[VK_F1]=FALSE;            // On passe la touche à FALSE
KillGLWindow();                // Fermer la fenêtre OpenGL
fullscreen=!fullscreen;            // Basculer entre le plein écran / mode fenêtré
// Recréer la fenêtre OpenGL
if (!CreateGLWindow("NeHe's Masking Tutorial",640,480,16,fullscreen))
{
return 0;            // Quitte, si la fenêtre n'a pas été créée
}
}
}
}
}
// Shutdown
KillGLWindow();                                // Fermer la fenêtre
return (msg.wParam);                            // Quitter le programme
}

Créer un masque n'est pas trop difficile. Cela prend un peu de temps. La meilleure façon de faire un masque si vous avez déjà une image est de charger votre image dans un programme d'art ou un programme pratique comme InfranView, et de réduire l'image à une image en niveaux de gris. Après avoir fait cela, augmentez le contraste pour que les pixels gris deviennent noirs. Vous pouvez aussi essayer de diminuer la clarté, etc. C'est important que le blanc soit un blanc clair et que le noir soit un noir pur. Si vous avez des pixels gris dans votre masque, la section de l'image va apparaître transparente. La façon la plus sûre d'avoir un masque qui est une parfaite copie de votre image est d'effectuer un tracé noir sur l'image. C'est aussi très important que votre image ait un fond NOIR et le masque un fond BLANC ! Si vous créez un masque et que vous remarquez une forme carrée autour de votre texture, soit votre blanc n'est pas assez clair (255 ou FFFFFF) soit votre noir n'est pas assez noir (0 ou 000000). Ci-dessous, vous pouvez voir un exemple d'un masque et de l'image qui va au-dessus du masque. L'image peut être de n'importe quelle couleur que vous voulez tant que le fond est noir. Le masque doit avoir un fond blanc et une copie noire de votre image.

Ceci est le masque -> Ceci est l'image ->

Eric Desrosiers a remarqué que vous pouviez aussi vérifier les valeurs de chaque pixel de votre image au moment du chargement. Si vous voulez le pixel transparent vous pouvez lui donner une valeur alpha de 0. Pour toutes les autres couleurs, vous pouvez donner une valeur alpha de 255. Cette méthode va aussi fonctionner, mais requiert un peu plus de code. Ce tutoriel est simple et ne nécessite que peu de code supplémentaire. Je ne ferme pas les yeux devant les autres techniques, mais lorsque j'écris un tutoriel, j'essaie de faire un code simple à comprendre et facile à utiliser. Je voulais juste montrer qu'il y a toujours d'autres façons de faire. Merci pour le retour d'Éric.

Dans ce tutoriel, je vous ai montré une simple, mais efficace façon de dessiner une partie d'une texture sans utiliser le canal alpha. Le fondu normal rend mal, habituellement (les textures sont transparentes ou pas), et le mappage de texture avec un canal alpha requiert que votre image supporte le canal alpha. C'est pratique de travailler avec les Bitmaps, mais elles ne supportent pas le canal alpha et ce programme nous montre comment contourner cette limitation des fichiers Bitmap, tout en montrant une façon pour créer des effets de recouvrement.

Merci à Rob Sante pour l'idée et le code d'exemple. Je n'avais jamais entendu parler de cette astuce jusqu'à ce qu'il me la montre. Il voulait me montrer que même si cette astuce fonctionne, cela prend deux passes, ce qui entraîne une chute de performance. Il recommande d'utiliser des textures qui supportent le canal alpha pour les scènes complexes.

J'espère que vous avez aimé ce tutoriel. Si vous avez n'importe quel problème pour le comprendre, ou que vous avez trouvé une erreur dans le tutoriel, s'il vous plaît, faites-le-moi savoir. Je veux faire les meilleurs tutoriels possible. Vos retours sont importants !

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) ;

- C# (Conversion by Brian Holley) ;

- Code Warrior 5.3 (Conversion by Scott Lupton) ;

- Cygwin (Conversion by Stephan Ferraro) ;

- Delphi (Conversion by Michal Tucek) ;

- Dev C++ (Conversion by Dan) ;

- Euphoria (Conversion by Evan Marshall) ;

- Game GLUT (Conversion by Alexandre Ribeiro de Sá) ;

- Irix/GLUT (Conversion by Rob Fletcher) ;

- Java (Conversion by Jeff Kirby) ;

- LCC Win32 (Conversion by Robert Wishlaw) ;

- Linux (Conversion by Daniel Davis) ;

- Linux/GLX (Conversion by Mihael Vrbanec) ;

- Linux/SDL (Conversion by Ti Leggett) ;

- LWJGL (Conversion by Mark Bernard) ;

- Mac OS (Conversion by Anthony Parker) ;

- Mac OS X/Cocoa (Conversion by Bryan Blackburn) ;

- MASM (Conversion by Christophe) ;

- Visual C++ / OpenIL (Conversion by Denton Woods) ;

- Pelles C (Conversion by Pelle Orinius) ;

- Visual Basic (Conversion by Edo) ;

- 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 : Moteur de particules utilisant les chaînes de triangles