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

Dans ce tutoriel, je vais vous apprendre comment faire un système de particules semi-complexes. Une fois que vous aurez compris comment le système de particules fonctionne, créer des effets de feu, fumée, fontaines d'eau et plein d'autres sera très facile !

Toutefois, je dois vous prévenir ! Jusqu'à présent je n'ai jamais écrit de système de particules. J'ai eu l'idée que ce « fameux » système de particules était un code très complexe. J'ai fait plusieurs essais dans le passé, mais j'avais abandonné en réalisant que je ne pouvais pas contrôler tous les points sans devenir fou.

Vous pouvez ne pas me croire lorsque je vous dis ceci, mais le tutoriel est écrit sans aucune base. Je n'ai emprunté aucune idée, et je n'avais aucune information technique devant moi. J'ai commencé à penser aux particules et tout d'un coup ma tête s'est remplie d'idées (cerveau qui se mettait en marche ?). À la place de penser chaque particule comme étant un pixel devant aller du point« A » au point« B », et faire ceci ou cela, j'ai décidé qu'il serait mieux de penser chaque particule comme un objet individuel répondant à l'environnement autour de lui. J'ai donné une vie à chaque particule, un comportement aléatoire, une couleur, une vitesse, appliqué une gravité et plus encore.

Très vite, j'eus un projet terminé. Je levai les yeux vers l'horloge et réalisai que les « aliens » étaient venus me chercher une fois de plus. Quatre autres heures de passées ! Je me rappelle m'être arrêté de temps en temps pour boire du café et vaciller, mais quatre heures... ?

Donc, bien que le programme, d'après moi semble bien et marche comme je le souhaitais, il se peut que ce ne soit pas la bonne façon de faire un système de particules. Je m'en fiche, tant que cela fonctionne bien et que je peux l'utiliser dans mes projets ! Si vous êtes le genre de personne qui nécessite de savoir que vous êtes conforme, alors prenez quelques heures pour parcourir le net à la recherche d'informations. Soyez prévenu. Les quelques extraits de code que vous pourrez trouver pourront sembler énigmatiques.

Le tutoriel se base sur la leçon 1. Par contre, il y a plein de nouveau code. Donc je vais réécrire toutes les sections de code qui contiennent des changements (et faire que cela soit plus facile à comprendre).

En utilisant le code de la leçon 1, nous allons ajouter cinq nouvelles lignes au tout début du programme. La première ligne (stdio.h) nous permet de lire les données à partir de fichiers. C'est la même ligne que nous avions ajoutée dans les tutoriels précédents sur l'application des textures. La deuxième ligne définit le nombre de particules que nous allons créer et afficher à l'écran. Define, indique à notre programme que MAX_PARTICLES sera égal à la valeur que nous spécifions. Dans ce cas, 1000. La troisième ligne sera utilisée pour enclencher ou non le mode« arc-en-ciel ». Nous allons le définir activé par défaut. « qp » et« rp » sont des variables utilisées pour éviter la barre espace et la touche « Entrée » de se répéter lorsqu'elles sont maintenues enfoncées.

 
Sélectionnez
#include <windows.h>            // Fichier d'entête pour Windows 
#include <stdio.h>            // Fichier d'entête pour les entrées/sorties     (AJOUT)  
#include <gl\gl.h>            // Fichier d'entête pour la bibliothèque OpenGL32 
#include <gl\glu.h>            // Fichier d'entête pour la bibliothèque GLu32 
#include <gl\glaux.h>            // Fichier d'entête pour la bibliothèque GLaux 

#define    MAX_PARTICLES    1000        // Nombre de particules à créer  (AJOUT)  

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éfini à TRUE par défaut 
bool    fullscreen=TRUE;        // Drapeau de plein écran défini à plein écran par défaut 
bool    rainbow=true;            // Mode arc-en-ciel ?  (AJOUT)  
bool    sp;                // Barre espace pressée ?  (AJOUT)  
bool    rp;                // Touche « Entrée » pressée ?  (AJOUT) 

Les quatre prochaines lignes sont des variables diverses. La variable« slowdown » contrôle la vitesse du mouvement des particules. Plus le nombre est grand, plus les particules sont lentes. Plus le nombre est petit, plus les particules bougent vite. Si la valeur est définie trop petite, les particules bougeront trop vite ! La vitesse de déplacement des particules va affecter leurs mouvements sur l'écran. Des particules lentes n'iront pas aussi loin. Gardez ça à l'esprit.

Les variables  « xspeed » et « yspeed » nous permettent de contrôler la direction de la queue. « xspeed » va être ajoutée à la vitesse courante de la particule sur l'axe des X. Si « xspeed » est positif notre particule va se déplacer plus sur la droite. Si « xspeed » est négatif, notre particule va se déplacer sur la gauche. Plus grande est la valeur, plus elle se déplace dans cette direction. « yspeed » fonctionne de la même façon, mais sur l'axe des Y. La raison pour laquelle je dis « PLUS » est spécifique dans une direction parce que d'autres facteurs affectent la direction du déplacement de nos particules. « xpseed » et « yspeed » nous aident à faire se déplacer les particules dans la direction souhaitée.

Finalement nous avons une variable « zoom ». Nous utilisons cette variable pour se déplacer dans ou en dehors de la scène. Avec les systèmes de particules, il est quelquefois agréable de pouvoir s'éloigner ou bien d'effectuer un zoom très proche.

 
Sélectionnez
float    slowdown=2.0f;            // Ralentissement des particules 
float    xspeed;                // Vitesse sur l'axe des X  (pour permettre le contrôle de la direction de la queue par le clavier)  
float    yspeed;                // Vitesse sur l'axe des Y  (pour permettre le contrôle de la direction de la queue par le clavier)  
float    zoom=-40.0f;            // Utilisé pour le dézoomage

Maintenant nous définissons une variable appelée « loop ». Nous allons l'utiliser pour prédéfinir les particules et pour les dessiner à l'écran. « col » sera utilisée pour garder une trace de la couleur assignée aux particules. « delay » sera utilisée pour parcourir les couleurs pour le mode arc-en-ciel.

Finalement, nous définissons à par un espace pour une texture (la texture des particules). J'ai décidé d'utiliser une texture plutôt que les points OpenGL pour quelques raisons. La plus importante est parce que les points ne sont pas aussi rapides que ça, et qu'ils ne sont pas beaux. Deuxièmement, les textures sont beaucoup plus cool. :) Vous pouvez utiliser des particules carrées, une petite image de vous, une image d'une étoile, etc. Plein de possibilités !

 
Sélectionnez
GLuint    loop;                // Variable pour les diverses boucles 
GLuint    col;                // Couleur courante 
GLuint    delay;                // Délai de l'effet arc-en-ciel 
GLuint    texture[1];            // Emplacement de stockage pour la texture des particules 

Ok, maintenant la partie amusante. La prochaine partie de code crée une structure pour décrire une seule particule. C'est l'endroit où nous allons donner aux particules certaines caractéristiques.

Nous commençons par la variable booléenne « active ». Si la variable est TRUE, notre particule est vivante et bien portante. Si elle est FALSE notre particule est morte et nous l'avons éteinte ! Dans le programme, je n'utilise pas « active », mais c'est une variable pratique à inclure.

Les variables « life » et « fade » contrôlent le temps d'affichage d'une particule et sa brillance tant qu'elle est vivante. La variable « life » est graduellement décrémentée par la valeur gardée dans « fade ». Dans ce programme cela va faire que certaines particules vont brûler plus longtemps que d'autres.

 
Sélectionnez
typedef struct                        // Crée une structure pour une particule 
{ 
    bool    active;                    // Active  (Oui / Non)  
    float    life;                    // Vie de la particule 
    float    fade;                    // Vitesse d'extinction

Les variables « r », « g » et « b » contiennent l'intensité rouge, l'intensité verte et l'intensité bleue de notre particule. Plus « r » est proche de 1.0f, plus la particule va être rouge. Mettre les 3 variables à 1.0f va créer une particule blanche.

 
Sélectionnez
    float    r;                    // Valeur du rouge 
    float    g;                    // Valeur du vert 
    float    b;                    // Valeur du bleu

Les variables « x », « y » et « z » contrôlent l'emplacement de la particule qui va être dessinée sur l'écran. « x » contient la position de notre particule sur l'axe des X. « y » contient la position de notre particule sur l'axe des Y, et finalement « z » contient la position de notre particule sur l'axe des Z.

 
Sélectionnez
    float    x;                    // Position en X 
    float    y;                    // Position en Y 
    float    z;                    // Position en Z

Les trois prochaines variables sont importantes. Ces trois variables contrôlent la vitesse de déplacement et la direction de la particule spécifiquement aux axes. Si « xi » est une valeur négative, notre particule se déplacera sur la gauche. Si elle est positive la particule se déplacera sur la droite. Si « yi » est une valeur négative, notre particule se déplacera vers le bas. Si elle est positive, la particule se déplacera vers le haut. Finalement, si « zi » est négative la particule se déplacera dans l'écran, si elle est positive, la particule se déplacera vers nous.

 
Sélectionnez
    float    xi;                    // Direction en X 
    float    yi;                    // Direction en Y 
    float    zi;                    // Direction en Z

Enfin, les trois dernières variables ! Chacune de ces variables peut être pensée comme de la gravité. Si « xg » est une valeur positive, notre particule va être tirée sur la droite. Si c'est une valeur négative notre particule va être tirée sur la gauche. Donc si notre particule se déplace sur la gauche (négative) et que nous appliquons une gravité positive, la vitesse va finir par ralentir à tel point que notre particule va commencer à se déplacer dans la direction opposée. « yg » tire vers le haut ou le bas et « zg » vers nous ou au loin de l'observateur.

 
Sélectionnez
    float    xg;                    // Gravité en X 
    float    yg;                    // Gravité en Y 
    float    zg;                    // Gravité en Z

« Particles » est le nom de notre structure.

 
Sélectionnez
} 
particles;                        // Structure pour les particules

Maintenant nous créons un tableau appelé « particle ». Ce tableau va stocker MAX_PARTICLES. Dit en français : nous créons un emplacement de stockage pour 1000 (MAX_PARTICLES) particules. Cet espace va stocker les informations de chaque particule.

 
Sélectionnez
particles particle[MAX_PARTICLES];            // Tableau de particules (place pour les informations sur les particules) 

Nous raccourcissons le code requis par le programme en stockant 12 couleurs différentes dans un tableau de couleurs. Pour chaque couleur de 0 à 11 nous stockons l'intensité en rouge, l'intensité en vert et finalement l'intensité en bleu. La table de couleur ci-dessous stocke 12 couleurs différentes de dégradé du rouge au violet.

 
Sélectionnez
static GLfloat colors[12][3]=                // Couleurs de l'arc-en-ciel 
{ 
    {1.0f,0.5f,0.5f},{1.0f,0.75f,0.5f},{1.0f,1.0f,0.5f},{0.75f,1.0f,0.5f}, 
    {0.5f,1.0f,0.5f},{0.5f,1.0f,0.75f},{0.5f,1.0f,1.0f},{0.5f,0.75f,1.0f}, 
    {0.5f,0.5f,1.0f},{0.75f,0.5f,1.0f},{1.0f,0.5f,1.0f},{1.0f,0.5f,0.75f} 
}; 

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

Notre code de chargement de Bitmap n'a pas changé.

 
Sélectionnez
AUX_RGBImageRec *LoadBMP(char *Filename)        // Charge une image Bitmap 
{ 
    FILE *File=NULL;                // Descripteur de fichier 
    if (!Filename)                    // Vérifie qu'un nom de fichier a été donné 
    { 
        return NULL;                // Si non, retourne NULL 
    } 

    File=fopen(Filename,"r");            // Contrôle si le fichier existe 
    if (File)                    // Est-ce que le fichier existe ? 
    { 
        fclose(File);                // Ferme le descripteur 
        return auxDIBImageLoad(Filename);    // Charge le Bitmap et renvoie un pointeur 
    } 
    return NULL;                    // Retourne NULL si le chargement a échoué 
}

Cette section de code charge le fichier Bitmap (appelant le code ci-dessus) et le convertit en une texture. « Status » est utilisée pour garder une trace de si la texture a été créée et chargée.

 
Sélectionnez
int LoadGLTextures()                        // Ouvre un Bitmap et le convertit en texture 
{ 
    int Status=FALSE;                    // Indicateur du statut 

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

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

Notre code de chargement de texture va charger le Bitmap pour les particules et le convertir en une texture filtrée linéairement.

 
Sélectionnez
    if (TextureImage[0]=LoadBMP("Data/Particle.bmp"))    // Charge le Bitmap des particules 
    { 
        Status=TRUE;                    // Met la variable Status à TRUE 
        glGenTextures(1, &texture[0]);            // Crée une texture 
 
        glBindTexture(GL_TEXTURE_2D, texture[0]); 
        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[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); 
    } 

    if (TextureImage[0])                    // Si la texture existe 
    { 
        if (TextureImage[0]->data)            // Si l'image de la texture existe 
        { 
            free(TextureImage[0]->data);        // Libère l'image de la texture 
        } 
        free(TextureImage[0]);                // Libère la structure de l'image 
    } 
    return Status;                        // Renvoie le statut 
}

Le seul changement que j'ai fait au code de redimensionnement est de mettre une distance de profondeur plus grande. À la place de 100.0f, nous pouvons voir les particules jusqu'à 200.0f unités dans l'écran.

 
Sélectionnez
GLvoid ReSizeGLScene(GLsizei width, GLsizei height)        // Redimensionne et initialise la fenêtre OpenGL 
{ 
    if (height==0)                        // Évite une division par zéro 
    { 
        height=1;                    // La hauteur est de 1 
    } 

    glViewport(0, 0, width, height);            // Réinitialise la vue courante

    glMatrixMode(GL_PROJECTION);                // Sélectionne la matrice de projection 
    glLoadIdentity();                    // Réinitialise la matrice de projection 

    // Calcul du rapport largeur/hauteur de la fenêtre
    gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,200.0f);      (MODIFIÉ)  

    glMatrixMode(GL_MODELVIEW);                // Sélectionne la matrice de modelview 
    glLoadIdentity();                    // Réinitialise la matrice de modelview 
}

Si vous utilisez le code de la leçon 1, remplacez-le par le code ci-dessous. J'ai ajouté le code pour charger notre texture et pour initialiser le fondu pour nos particules.

 
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 la texture n'a pas été chargée, retourner FALSE 
    }

Nous vérifions que les ombres douces sont activées, définissons la couleur du fond à noir, désactivons le test de profondeur et activons le fondu et le mappage des textures. Après avoir activé le mappage des textures nous sélectionnons notre texture pour les particules.

 
Sélectionnez
    glShadeModel(GL_SMOOTH);                              // Active les ombres douces 
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);                    // Fond d'écran noir 
    glClearDepth(1.0f);                                // Configuration du tampon de profondeur 
    glDisable(GL_DEPTH_TEST);                        // Désactive le test de profondeur 
    glEnable(GL_BLEND);                            // Active le fondu 
    glBlendFunc(GL_SRC_ALPHA,GL_ONE);                    // Type du fondu à appliquer 
    glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);            // Pour avoir des jolis calculs de perspective 
    glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);                    // Pour avoir des jolis points 
    glEnable(GL_TEXTURE_2D);                        // Active l'application de texture 
    glBindTexture(GL_TEXTURE_2D,texture[0]);                // Sélectionne notre texture

Le code ci-dessous va initialiser chacune des particules. Nous commençons par activer toutes les particules. Si une particule n'est pas active, elle n'apparaîtra pas à l'écran, peu importe combien de vie elle a.

Après avoir activé les particules, nous leur donnons la vie. Je doute que la façon dont j'applique la vie et teins les particules soit la meilleure, mais une fois de plus, cela marche bien ! 1.0f équivaut à la vie pleine. La particule est aussi à son maximum de luminosité.

 
Sélectionnez
    for (loop=0;loop<MAX_PARTICLES;loop++)                    // Initialise toutes les particules 
    { 
        particle[loop].active=true;                    // Active toutes les particules  
        particle[loop].life=1.0f;                    // Remplit toutes jauges de vie des particules 

Nous définissons la vitesse à laquelle la particule va s'assombrir en donnant à « fade » une valeur aléatoire. On va soustraire « fade » à la variable « life » à chaque fois que la particule va être dessinée. La valeur que nous allons avoir est une valeur aléatoire entre 0 et 99. Nous la divisons par 1000 pour avoir une très faible valeur décimale. Finalement nous ajoutons 0.003 pour que la vitesse d'assombrissement ne soit jamais à 0.

 
Sélectionnez
        particle[loop].fade=float(rand()%100)/1000.0f+0.003f;        // Vitesse d'assombrissement aléatoire.

Maintenant que notre particule est active et que nous lui avons donné vie, il est temps de lui donner une couleur. Pour l'effet initial, nous voulons que chaque particule ait une couleur différente. Ce que je fais c'est définir la couleur selon l'une des douze que nous avons défini dans la table au début de ce programme. Les mathématiques sont simples. Nous prenons notre variable « loop » et la multiplions par le nombre de couleurs de notre table puis la divisons par le maximum de particules (MAX_PARTICLES). Cela évite d'avoir la valeur finale plus grande que le nombre maximum de couleurs (12).

Quelques exemples rapides : 900*(12/900)=12. 1000*(12/1000)=12, etc.

 
Sélectionnez
        particle[loop].r=colors[loop*(12/MAX_PARTICLES)][0];        // Sélectionne la couleur rouge de l'arc-en-ciel 
        particle[loop].g=colors[loop*(12/MAX_PARTICLES)][1];        // Sélectionne la couleur verte de l'arc-en-ciel 
        particle[loop].b=colors[loop*(12/MAX_PARTICLES)][2];        // Sélectionne la couleur bleue de l'arc-en-ciel

Maintenant nous allons définir la direction pour que chaque particule se déplace selon une vitesse. Nous allons multiplier le résultat par 10.0f pour créer une impressionnante explosion lorsque le programme démarre.

Nous finirons avec une valeur aléatoire positive ou négative. Cette valeur va être utilisée pour déplacer les particules avec une direction aléatoire à une vitesse aléatoire.

 
Sélectionnez
        particle[loop].xi=float((rand()%50)-26.0f)*10.0f;        // Vitesse aléatoire sur l'axe des X 
        particle[loop].yi=float((rand()%50)-25.0f)*10.0f;        // Vitesse aléatoire sur l'axe des Y 
        particle[loop].zi=float((rand()%50)-25.0f)*10.0f;        // Vitesse aléatoire sur l'axe des Z

Finalement, nous définissons la gravité agissant sur chaque particule. Contrairement à l'habituelle gravité qui tire les choses vers le bas, notre gravité peut tirer vers le haut, bas, gauche, droit, en avant, en arrière. Pour commencer nous nous ne voulons pas une gravité trop forte tirant vers le bas. Pour ce faire nous définissons « xg » à 0.0f. Pas de mouvement sur l'axe des X. Nous mettons « yg » à -0.8f. Cela crée un tirage vers le bas. Si la valeur était positive cela tirerait vers le haut. Nous ne voulons pas de tirage vers l'avant ou l'arrière donc nous définissons « zg » à 0.0f.

 
Sélectionnez
        particle[loop].xg=0.0f;                        // Définit le tirage horizontal à 0 
        particle[loop].yg=-0.8f;                    // Définit un tirage vertical vers le bas 
        particle[loop].zg=0.0f;                        // Définit un tirage sur l'axe des Z à 0 
    } 
    return TRUE;                                // L'initialisation s'est bien passée 
}

Maintenant la partie amusante. La prochaine section de code est l'endroit où nous dessinons les particules, vérifions la gravité, etc. C'est important que vous compreniez ce qu'il se passe, donc lisez attentivement. :)

Nous réinitialisons la matrice de modelview seulement une fois. Nous allons positionner les particules en utilisant la commande glVertex3f() au lieu d'utiliser les translations, ce qui fait que nous n'altérons pas la matrice de modelview pendant que l'on dessine les particules.

 
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

Nous commençons par créer une boucle. Cette boucle va mettre à jour chacune de nos particules.

 
Sélectionnez
    for (loop=0;loop<MAX_PARTICLES;loop++)                    // Boucle au travers de toutes les particules 
    {

La première chose, que nous faisons, est de vérifier si la particule est active. Si elle n'est pas active, elle ne sera pas mise à jour. Dans ce programme elles sont toutes actives, tout le temps. Mais dans votre propre programme, vous pouvez faire en sorte que certaines particules soient inactives.

 
Sélectionnez
        if (particle[loop].active)                    // Si la particule est active 
        {

Les trois prochaines variables « x », « y » et « z » sont des variables temporaires que nous allons utiliser pour garder la position x, y, z. Remarquez que nous ajoutons le « zoom » à la position en z pour que notre scène soit déplacée dans l'écran en se basant sur la variable gardée par « zoom ». particle[loop].x contient la position en x pour n'importe quelle particule que nous dessinons (boucle des particules). particle[loop].y contient la position en y de la particule et particle[loop].z contient la position en z.

 
Sélectionnez
            float x=particle[loop].x;                // Prend la position de notre particule sur l'axe des X 
            float y=particle[loop].y;                // Prend la position de notre particule sur l'axe des Y 
            float z=particle[loop].z+zoom;                // Prend la position de notre particule sur l'axe des Z + Zoom

Maintenant que nous avons la position de la particule, nous pouvons la colorer. particle[loop].r contient l'intensité rouge de notre particule, particle[loop].g contient l'intensité verte et particle[loop].b contient l'intensité bleue. Remarquez que j'utilise la vie des particules comme valeur de l'alpha. Plus la particule se meurt, plus elle devient transparente, jusqu'à ce qu'elle n'existe plus. C'est pourquoi la vie des particules ne devrait jamais être plus grande que 1.0f. Si vous avez besoin de particules qui brûlent plus longtemps, essayez de réduire la vitesse d'extinction pour que les particules ne disparaissent pas aussi vite.

 
Sélectionnez
            // Dessine la particule en utilisant les valeurs RGB. Éteint la particule en se basant sur sa vie 
            glColor4f(particle[loop].r,particle[loop].g,particle[loop].b,particle[loop].life);

Nous avons la position des particules et leur couleur définie. Tout ce qui nous reste à faire est de dessiner notre particule. Au lieu d'utiliser un rectangle texturé, j'ai décidé d'utiliser une chaîne de triangles texturés pour accélérer un peu le programme. La plupart des cartes 3D dessinent les triangles beaucoup plus rapidement que les quadrilatères. Certaines cartes 3D vont convertir le quadrilatère en 2 triangles pour vous, mais d'autres ne le feront pas. Donc nous allons faire le travail nous-même. Nous commençons par dire à OpenGL que nous voulons dessiner une chaîne de triangles.

 
Sélectionnez
            glBegin(GL_TRIANGLE_STRIP);                // Construit le carré à partir d'une chaine de triangles
Image non disponible

Citation venant directement du livre rouge (red book) d'OpenGL : « Une chaîne de triangles dessine une série de triangles (polygone à 3 cotés) en utilisant les vertex V0, V1, V2 puis V2, V1, V3 (regardez l'ordre), puis V2, V3, V4, etc. ». L'ordre permet de s'assurer que les triangles vont toujours être dessinés avec la même orientation donc que la chaîne peut correctement former une surface. Conserver l'orientation est importante pour la plupart des opérations comme le culling. Il faut au moins 3 points pour dessiner quelque chose.

Donc le premier triangle est dessiné en utilisant les vertex 0, 1 et 2. Si vous regardez l'image vous allez voir que les points 0, 1 et 2 construisent effectivement le premier triangle (haut droit, haut gauche, bas droit). Le deuxième triangle est dessiné en utilisant les vertex 2, 1 et 3. Encore une fois, si vous regardez l'image, les vertex 2, 1 et 3 créent le deuxième triangle (bas droit, haut gauche, bas gauche). Remarquez que les deux triangles sont dessinés dans le même sens (sans inverse des aiguilles d'une montre). J'ai vu quelques sites qui proclament que tous les deuxièmes triangles sont dans la direction opposée. Ce n'est pas le cas. OpenGL va réarranger les vertex pour être sûr que les triangles soient dans le même sens !

Il y a deux bonnes raisons d'utiliser les chaînes de triangles. Premièrement, après avoir spécifié les trois premiers vertex pour le premier triangle, vous n'avez plus qu'à spécifier un unique point pour chaque nouveau triangle. Ce point va être combiné avec les 2 vertex précédents pour créer un triangle. Deuxièmement, en réduisant les données nécessaires pour créer un triangle votre programme va aller plus vite, et le nombre de lignes de code, ou de données requises pour dessiner un objet est grandement réduit.

Note : le nombre de triangles que vous allez voir sur l'écran est le nombre de vertex que vous spécifiez moins 2. Dans le code ci-dessous nous avons quatre vertex et nous ne voyons que deux triangles.

 
Sélectionnez
                glTexCoord2d(1,1); glVertex3f(x+0.5f,y+0.5f,z); // Haut droit 
                glTexCoord2d(0,1); glVertex3f(x-0.5f,y+0.5f,z); // Haut gauche 
                glTexCoord2d(1,0); glVertex3f(x+0.5f,y-0.5f,z); // Bas droit 
                glTexCoord2d(0,0); glVertex3f(x-0.5f,y-0.5f,z); // Bas gauche

Finalement nous indiquons à OpenGL que nous avons fini de dessiner notre chaîne de triangles.

 
Sélectionnez
            glEnd();                        // Fin de la construction de la chaîne de triangles

Maintenant nous pouvons déplacer les particules. Les mathématiques ci-dessous peuvent sembler étranges, mais une fois encore, c'est très simple. Premièrement, nous prenons la position en X de la particule courante. Puis nous ajoutons le mouvement, sur l'axe des X à la particule, divisé par le ralentissement multiplié par 1000. Donc si notre particule était au centre de l'écran sur l'axe des X (0), notre variable de mouvement (xi) pour l'axe des X était +10 (déplacement sur la droite) et le ralentissement (slowdown) égal à 1, nous nous déplacerions sur la droite de 10/(1*1000), ou 0.01f. Si nous augmentions le ralentissement à 2, nous nous déplacerions seulement de 0.005f. J'espère que cela vous aide à comprendre comment marche le ralentissement.

C'est aussi pourquoi multiplier la valeur de départ par 10.0f a fait que les pixels bougeaient beaucoup plus vite, créant une explosion.

Nous utilisons la même formule pour les axes Y et Z pour déplacer les particules sur l'écran.

 
Sélectionnez
            particle[loop].x+=particle[loop].xi/(slowdown*1000);    // Déplace la particule sur l'axe des X par la vitesse 
            particle[loop].y+=particle[loop].yi/(slowdown*1000);    // Déplace la particule sur l'axe des Y par la vitesse 
            particle[loop].z+=particle[loop].zi/(slowdown*1000);    // Déplace la particule sur l'axe des Z par la vitesse

Après avoir calculé le nouvel emplacement de la particule, nous devons ajouter la gravité ou résistance. Dans la première ligne ci-dessous, nous le faisons en ajoutant notre résistance (xg) à la vitesse de déplacement (xi).

Disons que notre vitesse de déplacement était 10 et que notre résistance était 1. Chaque fois que notre particule était dessinée la résistance aurait agi dessus. Donc la deuxième fois qu'on aurait dessiné, la résistance aurait agi, et la vitesse de déplacement serait passée de 10 à 9. Cela ralentissant un peu la particule. La troisième fois que la particule aurait été dessinée, la résistance aurait encore agi et notre vitesse de déplacement serait descendue à 8. Si la particule brûle pendant plus de 10 boucles, elle va certainement se déplacer dans la direction opposée, car la vitesse de déplacement sera devenue négative.

La résistance est appliquée sur la vitesse de déplacement sur les axes Y et Z de la même façon que pour l'axe des X.

 
Sélectionnez
            particle[loop].xi+=particle[loop].xg;            // Prend en compte le tirage sur l'axe des X 
            particle[loop].yi+=particle[loop].yg;            // Prend en compte le tirage sur l'axe des Y 
            particle[loop].zi+=particle[loop].zg;            // Prend en compte le tirage sur l'axe des Z

La prochaine ligne enlève de la vie à la particule. Si nous ne faisions pas ceci, la particule ne s'éteindrait jamais. Nous prenons la vie de la particule courante et nous lui soustrayons sa valeur d'extinction. Chaque particule va avoir une valeur d'extinction différente, donc elles vont brûler à différentes vitesses.

 
Sélectionnez
            particle[loop].life-=particle[loop].fade;        // Réduit la vie de la particule de la valeur de « fade »

Maintenant nous vérifions si la particule est encore vivante après lui avoir pris de la vie.

 
Sélectionnez
            if (particle[loop].life<0.0f)                    // Si la particule s'est éteinte 
            {

Si la particule est morte (complètement brûlée), nous allons la ressusciter. Nous faisons cela en lui redonnant le plein de vie et une nouvelle vitesse d'extinction.

 
Sélectionnez
                particle[loop].life=1.0f;                // Donne une nouvelle vie 
                particle[loop].fade=float(rand()%100)/1000.0f+0.003f;    // Valeur aléatoire d'extinction

Nous réinitialisons aussi la position de la particule au centre de l'écran. Nous faisons cela en réinitialisant les positions en x, y et z à zéro.

 
Sélectionnez
                particle[loop].x=0.0f;                    // Centre sur l'axe des X 
                particle[loop].y=0.0f;                    // Centre sur l'axe des Y 
                particle[loop].z=0.0f;                    // Centre sur l'axe des Z

Après que la particule ait été replacée au centre de l'écran, nous lui donnons une nouvelle vitesse de déplacement/direction. Remarquez que j'ai augmenté la vitesse maximale et la vitesse minimale à laquelle la particule peut se déplacer de 50 à 60, mais nous n'allons pas multiplier la vitesse de déplacement par 10. Nous ne voulons pas une explosion pour cette fois, nous voulons des particules se déplaçant plus lentement.

Remarquez aussi que j'ai ajouté « xspeed » à la vitesse de déplacement sur l'axe des X, et « yspeed » à la vitesse de déplacement sur l'axe des Y. Cela nous donne le contrôle sur la direction du déplacement des particules plus tard dans le programme.

 
Sélectionnez
                particle[loop].xi=xspeed+float((rand()%60)-32.0f);    // Vitesse et direction sur l'axe des X 
                particle[loop].yi=yspeed+float((rand()%60)-30.0f);    // Vitesse et direction sur l'axe des Y 
                particle[loop].zi=float((rand()%60)-30.0f);        // Vitesse et direction sur l'axe des Z

Finalement nous assignons une nouvelle couleur à la particule. La variable « col » contient un nombre de 0 à 11 (12 couleurs). Nous utilisons cette variable pour obtenir l'intensité rouge, verte et bleue à partir de notre table que nous avons définie au début du programme. La première ligne ci-dessous définit l'intensité rouge (r) à la valeur conservée dans colors[col][0]. Donc si « col » était 0, l'intensité rouge serait 1.0f. Les valeurs vertes et bleue sont lues de la même façon.

Si vous ne comprenez pas comment j'obtiens les valeurs de 1.0f pour l'intensité rouge lorsque « col » est égal à 0, Je vais ré-expliquer plus en détails. Regardez au tout début du programme. Trouvez la ligne : static GLfloat colors[12][3]. Remarquez qu'il y a 12 groupes de 3 nombres. Le premier des trois nombres est l'intensité rouge. Le deuxième est l'intensité verte et le troisième, l'intensité bleue. [0], [1] et [2] ci-dessous représentent les trois premières valeurs que je viens de mentionner. Si « col » est égal à 0, nous voulons avoir le premier groupe. 11 est le dernier groupe (12ème couleur).

 
Sélectionnez
                particle[loop].r=colors[col][0];            // Sélectionne le rouge dans la table 
                particle[loop].g=colors[col][1];            // Sélectionne le vert dans la table 
                particle[loop].b=colors[col][2];            // Sélectionne le bleu dans la table 
            }

La ligne ci-dessous contrôle la gravité. En appuyant sur la touche 8 du pavé numérique, nous augmentons « yg » (gravité sur y). Cela va provoquer un tirage vers le haut. Le code est placé ici dans le programme, car cela rend notre vie plus facile en appliquant la gravité à toutes les particules grâce à la boucle. Si le code avait été en dehors nous aurions créé une autre boucle pour faire la même chose, donc nous le faisons ici tout aussi bien.

 
Sélectionnez
            // Si la touche 8 du pavé numérique est appuyée et la gravité est plus petite que 1.5 alors on augmente le tirage vers le haut 
            if (keys[VK_NUMPAD8] && (particle[loop].yg<1.5f)) particle[loop].yg+=0.01f;

Cette ligne a l'effet contraire. En appuyant sur 2 nous diminuons « yg » créant une poussée vers le bas plus forte.

 
Sélectionnez
            // Si la touche 2 du pavé numérique est appuyée et la gravité est plus grande que -1.5 alors on augmente la poussée vers le bas 
            if (keys[VK_NUMPAD2] && (particle[loop].yg>-1.5f)) particle[loop].yg-=0.01f;

Maintenant nous modifions le tirage vers la droite. Si la touche 6 du pavé numérique est appuyée, nous augmentons le tirage.

 
Sélectionnez
            // Si la touche 6 est appuyée et la gravité en X est plus petite que 1.5, alors on augmente le tirage vers la droite 
            if (keys[VK_NUMPAD6] && (particle[loop].xg<1.5f)) particle[loop].xg+=0.01f;

Finalement, si la touche 4 du clavier est pressée, nos particules vont être tirées sur la gauche. Ces touches nous donnent des résultats vraiment cool. Par exemple, vous pouvez faire un flux de particules tirant vers le haut. En ajoutant de la gravité vers le bas, vous pouvez transformer le flux de particules en une fontaine à eau !

 
Sélectionnez
            // Si la touche 4 est appuyée et la gravité en X plus grande que -1.5 alors on augmente le tirage vers la gauche 
            if (keys[VK_NUMPAD4] && (particle[loop].xg>-1.5f)) particle[loop].xg-=0.01f;

J'ai ajouté ce code juste pour m'amuser. Mon frère pensait que l'explosion était un superbe effet. :) En appuyant sur la touche tabulation toutes les particules vont être réinitialisées au centre de l'écran. La vitesse de déplacement sera une fois encore multipliée par 10, créant une super explosion de particules. Après la mort des particules, votre effet d'origine va de nouveau réapparaître.

 
Sélectionnez
            if (keys[VK_TAB])                        // La touche de tabulation provoque une explosion 
            { 
                particle[loop].x=0.0f;                    // Centre sur l'axe des X 
                particle[loop].y=0.0f;                    // Centre sur l'axe des Y 
                particle[loop].z=0.0f;                    // Centre sur l'axe des Z 
                particle[loop].xi=float((rand()%50)-26.0f)*10.0f;    // Vitesse aléatoire sur l'axe des X 
                particle[loop].yi=float((rand()%50)-25.0f)*10.0f;    // Vitesse aléatoire sur l'axe des Y 
                particle[loop].zi=float((rand()%50)-25.0f)*10.0f;    // Vitesse aléatoire sur l'axe des Z 
            } 
        } 
    } 
    return TRUE;                                    // Tout s'est bien passé 
}

Le code dans KillGLWindow(), CreateGLWindow() et WndProc() n'a pas changé, donc nous allons le sauter pour passer directement à WinMain(). Je vais réécrire toute la section de code pour rendre la lecture plus facile à comprendre.

 
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 Particle Tutorial",640,480,16,fullscreen)) 
    { 
        return 0;                        // Quitte si la fenêtre n'est pas créée 
    }

C'est notre première modification dans WinMain(). J'ai ajouté du code pour savoir si l'utilisateur veut exécuter en plein écran ou en mode fenêtré. S'il décide d'utiliser le plein écran, je change la variable « slowdown » (ralentissement) à 1.0f à la place de 2.0f. Vous pouvez laisser ce morceau de code si vous voulez. Je l'ai ajouté pour accélérer le mode plein écran sur ma 3dfx (qui s'exécute BEAUCOUP plus lentement qu'en mode fenêtré pour certaines raisons).

 
Sélectionnez
    if (fullscreen)                            // Si nous sommes en mode plein écran  (AJOUT)  
    { 
        slowdown=1.0f;                        // Accélère les particules (problèmes 3dfx)  (AJOUT)  
    } 

    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 » à 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 
        { 
            if ((active && !DrawGLScene()) || keys[VK_ESCAPE])    // Met à jour la fenêtre seulement si 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)

J'ai été un peu négligeant avec le prochain morceau de code. Habituellement je ne mets pas tout sur une ligne, mais cela rend le code un peu plus propre. :)

La ligne ci-dessous vérifie si la touche « + » du pavé numérique est appuyée. Si c'est le cas et que « slowdown » est plus grand que 1.0f nous décrémentons « slowdown » de 0.01f. Cela va faire que les particules vont bouger plus vite. Rappelez-vous du code ci-dessus, lorsque j'ai parlé de ralentissement et de son effet sur la vitesse de déplacement des particules.

 
Sélectionnez
                if (keys[VK_ADD] && (slowdown>1.0f)) slowdown-=0.01f;        // Accélère les particules

Cette ligne vérifie si la touche « - » du pavé numérique est appuyée. Si c'est le cas et que « slowdown » est plus petit que 4.0f nous augmentons la valeur de « slowdown ». Cela va faire que les particules vont bouger plus lentement. J'ai limité à 4.0f, car je ne voulais pas trop les ralentir. Vous pouvez changer les vitesses minimum et maximum par ce que vous voulez. :)

 
Sélectionnez
                if (keys[VK_SUBTRACT] && (slowdown<4.0f)) slowdown+=0.01f;    // Ralentit les particules

La ligne ci-dessus vérifie si la touche Page haut est appuyée. Si c'est le cas, la variable « zoom » est incrémentée. Cela déplace les particules vers nous.

 
Sélectionnez
                if (keys[VK_PRIOR]) zoom+=0.1f;        // Zoom

La ligne suivante a l'effet contraire. En appuyant sur Page bas, « zoom » est décrémentée et la scène se déplace dans l'écran. Cela nous permet de voir plus de choses à l'écran, mais les particules sont plus petites.

 
Sélectionnez
                if (keys[VK_NEXT]) zoom-=0.1f;        // Dézoom

La prochaine section de code vérifie si la touche « Entrée » a été appuyée. Si elle l'a été et qu'elle n'est pas maintenue, nous allons dire à l'ordinateur que la touche a été appuyée en mettant « rp » à true. Alors nous allons faire basculer le mode arc-en-ciel. Si « rainbow » était à true, elle va devenir false. Si elle était à false, elle va devenir true. La dernière ligne vérifie si la touche « Entrée » a été relâchée. Si elle a été relâchée, « rp » est mis à false, disant à l'ordinateur que la touche n'est plus appuyée.

 
Sélectionnez
                if (keys[VK_RETURN] && !rp)        // Entrée appuyée 
                { 
                    rp=true;            // Définit le drapeau pour indiquer que la touche a été appuyée 
                    rainbow=!rainbow;        // Active/désactive le mode arc-en-ciel 
                } 
                if (!keys[VK_RETURN]) rp=false;        // Si « Entrée » est relâchée, nous enlevons le drapeau

Le code ci-dessous est un peu déroutant. La première ligne vérifie si la barre espace est appuyée, mais pas maintenue. Cela vérifie aussi si le mode arc-en-ciel est activé, et s'il l'est, on vérifie si la variable « delay » est plus grande que 25. « delay » est un compteur que j'utilise pour créer l'effet d'arc-en-ciel. Si vous changiez la couleur à chaque trame, les particules seraient toutes d'une couleur différente. En créant un retard, un groupe de particules prendra une couleur, avant que cette couleur ne change.

Si la barre espace a été appuyée ou si le mode arc-en-ciel est activé et le retard plus grand que 25, la couleur va être modifiée !

 
Sélectionnez
                if ((keys[' '] && !sp) || (rainbow && (delay>25)))    // Espace ou mode arc-en-ciel 
                {

La ligne ci-dessous a été ajoutée pour que l'arc-en-ciel soit désactivé si la barre d'espace est appuyée. Si nous n'éteignions pas le mode arc-en-ciel, les couleurs continueraient de boucler jusqu'à ce que la touche « Entrée » soit appuyée. Il est logique que quelqu'un qui appuie sur la barre espace au lieu de la touche « Entrée » veuille changer les couleurs.

 
Sélectionnez
                    if (keys[' ']) rainbow=false;    // Si la touche espace est appuyée on désactive le mode arc-en-ciel

Si la touche espace est appuyée ou si le mode arc-en-ciel est activé et le retard est plus grand que 25, nous allons faire savoir à l'ordinateur que la barre espace a été appuyée en mettant « sp » à true. Puis nous remettons le retard à 0 pour qu'il recommence à compter jusqu'à 25. Finalement nous allons augmenter la variable « col » pour que la couleur prenne l'entrée suivante de la table des couleurs.

 
Sélectionnez
                    sp=true;            // Définit un drapeau pour dire que la barre espace est appuyée 
                    delay=0;            // Réinitialise le retard de rotation des couleurs de l'arc-en-ciel 
                    col++;                // Change la couleur des particules

Si la couleur est plus grande que onze, nous devons la remettre à zéro. Si nous ne le faisions pas, notre programme essayerait de trouver une 13ème couleur. Nous n'avons que douze couleurs ! Essayer d'obtenir des informations sur une couleur qui n'existe pas planterait le programme.

 
Sélectionnez
                    if (col>11) col=0;        // Si la variable est trop grande, nous la réinitialisons 
                }

Finalement, si la barre espace est relâchée, nous le faisons savoir à l'ordinateur en remettant « sp » à false.

 
Sélectionnez
                if (!keys[' '])    sp=false;        // Si la barre espace est relâchée, on réinitialise le drapeau

Maintenant nous allons prendre le contrôle sur les particules. Vous rappelez-vous que nous avons créé deux variables au début de notre programme ? La première s'appelait « xspeed » la seconde « yspeed ». Rappelez-vous aussi que lorsqu'une particule a complètement brûlé, nous lui donnons une nouvelle vitesse de déplacement en ajoutant « xpeed » ou « yspeed ». En faisant cela nous pouvons influencer la direction des particules lors de leur création.

Par exemple, disons que notre particule a une vitesse de déplacement de 5 sur l'axe des X et de 0 sur l'axe des Y. Si nous diminuons « xspeed » jusqu'à -10, nous nous déplacerions à une vitesse de -10 (xspeed) +5 (vitesse de déplacement initiale). Donc au lieu de se déplacer à une vitesse de 10 sur la droite, nous nous déplacerions à une vitesse de -5 sur la gauche. Logique ?

Peu importe. La ligne ci-dessous vérifie si la flèche haut est appuyée. Si c'est le cas, « yspeed » va être incrémentée. Cela va faire que nos particules vont se déplacer vers le haut. Les particules se déplaceront vers le haut à une vitesse maximale de 200. Quelque chose de plus rapide ne sera pas beau.

 
Sélectionnez
                // Si la flèche haut est appuyée et la vitesse sur Y est plus petite que 200, nous augmentons la vitesse 
                if (keys[VK_UP] && (yspeed<200)) yspeed+=1.0f;

La ligne suivante vérifie si la flèche bas est appuyée. Si oui, « yspeed » va être diminuée. Cela va faire les particules se déplacer vers le bas. Encore une fois, la vitesse maximum vers le bas sera de 200.

 
Sélectionnez
                // Si la flèche bas est appuyée et que la vitesse en Y est plus grande que -200, nous augmentons la vitesse vers le bas 
                if (keys[VK_DOWN] && (yspeed>-200)) yspeed-=1.0f;

Maintenant nous vérifions si la flèche droite est appuyée. Si oui, « xspeed » va être incrémentée. Cela va faire les particules se déplacer vers la droite. Une limite de vitesse à 200 est appliquée.

 
Sélectionnez
                // Si la flèche droite est appuyée et la vitesse sur X plus petite que 200 nous augmentons la vitesse 
                if (keys[VK_RIGHT] && (xspeed<200)) xspeed+=1.0f;

Finalement nous vérifions si la flèche gauche est appuyée. Si oui... vous l'avez deviné... « xspeed » est diminuée et les particules vont commencer à se déplacer sur la gauche. Une limite de vitesse à 200 est appliquée.

 
Sélectionnez
                // Si la flèche gauche est appuyée et si la vitesse sur X est plus grande que -200 alors nous augmentons la vitesse vers la gauche 
                if (keys[VK_LEFT] && (xspeed>-200)) xspeed-=1.0f;

La dernière chose que nous devons faire est d'incrémenter la variable « delay ». Comme je l'ai dit avant, « delay » est utilisée pour contrôler la vitesse du changement des couleurs lorsque nous sommes en mode arc-en-ciel.

 
Sélectionnez
                delay++;            // Incrémente le retard sur le cycle des couleurs du mode arc-en-ciel

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

 
Sélectionnez
                if (keys[VK_F1])        // Est-ce que F1 est appuyé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être 

                    // Recréer la fenêtre OpenGL 
                    if (!CreateGLWindow("NeHe's Particle Tutorial",640,480,16,fullscreen)) 
                    { 
                        return 0;    // Si la fenêtre n'a pas été créée 
                    } 
                } 
            } 
        } 
    } 
    // Fermeture 
    KillGLWindow();                        // Fermer la fenêtre 
    return (msg.wParam);                    // Quitter le programme 
}

Dans cette leçon, j'ai essayé d'expliquer en détail toutes les étapes nécessaires pour faire un simple, mais impressionnant système de particules. Le système de particules peut être utilisé dans les jeux pour créer vos propres effets comme le feu, l'eau, la neige, les explosions, les étoiles filantes et bien plus. Le code peut être facilement modifié pour avoir plus de paramètres et de nouveaux effets (feux d'artifice par exemple).

Merci à Richard Nutman pour avoir suggéré de placer les particules avec glVertex3f() à la place de réinitialiser la matrice modelview et de repositionner chaque particule avec glTranslated(). Les deux méthodes fonctionnent, mais sa méthode réduit le travail que l'ordinateur doit effectuer avant de dessiner chaque particule, donc le programme tourne plus vite.

Merci à Antoine Valentim pour avoir suggéré d'utiliser les chaînes de triangles qui aident à accélérer le programme et d'introduire une nouvelle commande dans ce tutoriel. Les retours sur ce tutoriel sont bons, j'apprécie !

J'espère que vous avez aimé ce tutoriel. Si vous avez eu des problèmes pour le comprendre, ou que vous avez trouvé une erreur dans le tutoriel, s'il vous plaît, faites-le-moi savoir. Je voudrais faire le meilleur tutoriel 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 zoom61 pour ses corrections.

V. Liens