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 !

 
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

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.

 
Sélectionnez
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]'.

 
Sélectionnez
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. :)

 
Sélectionnez
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.

 
Sélectionnez
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.

Image non disponibleImage non disponible

 
Sélectionnez
    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 !

 
Sélectionnez
    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.

 
Sélectionnez
    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.

 
Sélectionnez
        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.

 
Sélectionnez
    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. :)

 
Sélectionnez
        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.

 
Sélectionnez
        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.

 
Sélectionnez
            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.

 
Sélectionnez
        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).

 
Sélectionnez
    else                                    // Sinon 
    {

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

 
Sélectionnez
        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.

 
Sélectionnez
            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.

 
Sélectionnez
        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. :)

 
Sélectionnez
    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.

 
Sélectionnez
    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. :)

 
Sélectionnez
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.

 
Sélectionnez
                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.

 
Sélectionnez
                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.

 
Sélectionnez
                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.

 
Sélectionnez
                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.

 
Sélectionnez
                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 -> Image non disponible Ceci est l'image ->Image non disponible

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 :

IV. Remerciements

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

V. Liens