I. Contributions▲
Remarque : la traduction des autres tutoriels est en cours, mais votre aide serait appréciée ! Si vous voulez aider, n'hésitez pas à envoyer un message privé ou un mail vers nehe arrobase redaction-developpez point com.
II. Tutoriel▲
II-A. Introduction▲
Commençons par le début. Chargez le projet du tutoriel n°6 dans Visual C++ ou un autre éditeur C++ et ajoutez la ligne suivante, juste après les autres lignes '#include'. Ce '#include' ci-dessous nous permet de travailler avec les fonctions mathématiques comme le sinus et le cosinus.
#include
<math.h>
// Pour la fonction sin()
II-B. Structure de données▲
Nous allons utiliser un tableau pour stocker les coordonnées x, y et z de la grille. Cette grille est composée de 45 points sur 45, ce qui par conséquent constitue 44 carrés sur 44. L'entier 'wiggle_count' (comprendre compteur d'ondulation) va stocker le coefficient de rapidité des vagues de la texture. Pour qu'à chaque seconde l'image soit belle, la variable 'hold' va stocker un flottant pour lisser les ondulations du drapeau. Les lignes suivantes peuvent être ajoutées en haut du programme, quelque part en dessous de la dernière ligne '#include', mais avant la ligne 'GLuint texture[1]'.
float
points[45
][45
][3
]; // Le tableau pour stocker les coordonnées de la "vague"
int
wiggle_count =
0
; // Le compteur utilisé pour contrôler la vitesse des ondulations
GLfloat hold; // Le flottant pour lisser les ondulations
II-C. La fonction LoadGLTextures▲
Allez à la fonction 'LoadGLTextures()', plus bas dans le code. Nous voulons utiliser la texture appelée 'Tim.bmp'. Trouvez la ligne 'LoadBMP(« Data/NeHe.bmp »)' et remplacez-la par 'LoadBMP(« Data/Tim.bmp »)'.
if
(TextureImage[0
]=
LoadBMP("Data/Tim.bmp"
)) // Charge le bitmap
II-D. La fonction InitGL▲
Maintenant ajoutez le code suivant à la fin de la fonction InitGL() avant le return TRUE.
glPolygonMode( GL_BACK, GL_FILL ); // La face de derrière est dessinée pleine
glPolygonMode( GL_FRONT, GL_LINE ); // La face de devant n'est dessinée qu'en mode fil de fer
Ces lignes indiquent que nous voulons que les faces arrière des polygones soient complètement remplies et que les faces avant soient juste dessinées avec des lignes. C'est principalement un point de vue personnel. Cela a un rapport avec l'orientation des polygones ou la direction des sommets. Allez voir le 'Red Book' pour plus de précisions là-dessus. Tant que j'y suis, laissez-moi vous dire que ce livre est un atout majeur qui m'a aidé à apprendre OpenGL, sans oublier le site de NeHe ! Merci NeHe. Achetez le 'Red Book' écrit par Addison-Wesley. C'est une ressource incontournable selon moi. OK, retour au tutoriel. Juste en dessous du code ci-dessus, et avant la ligne 'return TRUE', ajoutez les lignes suivantes.
II-E. La boucle▲
// Boucle sur le plan des X
for
(int
x =
0
; x <
45
; x++
)
{
// Boucle sur le plan des Y
for
(int
y =
0
; y <
45
; y++
)
{
// On applique la forme d'une vague à notre grille
points[x][y][0
] =
float
((x /
5.0
f) -
4.5
f);
points[x][y][1
] =
float
((y /
5.0
f) -
4.5
f);
points[x][y][2
] =
float
(sin((((x /
5.0
f) *
40.0
f) /
360.0
f) *
3.141592654
*
2.0
f));
}
}
Je remercie Graham Gibbons pour m'avoir suggéré d'utiliser une boucle sur un entier afin d'éliminer les pointes sur l'ondulation. Les deux boucles ci-dessus initialisent les coordonnées de notre grille. J'initialise les compteurs dans les boucles elles-mêmes pour garder à l'esprit que ce sont de simples variables de boucle. Nous utilisons des boucles avec des entiers pour prévenir d'étranges bugs graphiques faisant apparaître des pointes lorsqu'on utilise des flottants. Nous divisons les variables x et y par 5 (exemple: 45 / 5 = 9) et enlevons 4.5 pour chacun d'entre eux pour centrer la « vague ». On passe de l'intervalle [0, 45) à [-4.5, 4.5)). Le même effet pourrait être réalisé avec une translation, mais je préfère cette méthode. La valeur finale 'points[x][y][2]' est notre valeur sinusoïdale. La fonction 'sin()' prend en entrée des radians. Nous prenons notre valeur en degré, qui est notre 'float_x' multiplié par '40.0f'. Une fois que nous avons fait ça, pour convertir les degrés en radians : on divise par 360.0f, on multiplie par PI, ou une approximation, et on multiplie par 2.0f.
II-F. DrawGLScene▲
Je vais réécrire la fonction 'DrawGLScene' depuis le début pour nettoyer un peu le code et le remplacer par le suivant.
int
DrawGLScene(GLvoid) // Dessine notre scène OpenGL
{
int
x, y; // Variables de boucle
float
float_x, float_y, float_xb, float_yb; // Utilisé pour casser le drapeau en petits carrés
Différentes variables sont utilisées pour contrôler les boucles. Regardez le code ci-dessous, la plupart d'entre elles ne servent pas à autre chose qu'au contrôle des boucles et à la sauvegarde de valeurs temporaires.
glClear(GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT); // Efface la scène et le tampon de profondeur
glLoadIdentity(); // Réinitialise la matrice courante
glTranslatef(0.0
f,0.0
f,-
12.0
f); // Déplace de 12 unités dans l'écran
glRotatef(xrot,1.0
f,0.0
f,0.0
f); // Rotation sur l'axe des X
glRotatef(yrot,0.0
f,1.0
f,0.0
f); // Rotation sur l'axe des Y
glRotatef(zrot,0.0
f,0.0
f,1.0
f); // Rotation sur l'axe des Z
glBindTexture(GL_TEXTURE_2D, texture[0
]); // Sélection de notre texture
Vous avez déjà vu ce code, bien entendu. C'est le même qu'au tutoriel n°6 sauf que j'ai un petit peu éloigné la scène de la caméra.
glBegin(GL_QUADS); // Commence à dessiner les carrés
for
( x =
0
; x <
44
; x++
) // Boucle sur le plan sur l'axe des X (44 Points)
{
for
( y =
0
; y <
44
; y++
) // Boucle sur le plan de l'axe des Y (44 Points)
{
II-G. Rendu▲
C'est simplement le lancement de la boucle qui dessine nos polygones. J'utilise des entiers ici, pour éviter d'utiliser la fonction 'int()' comme je l'avais fait avant pour récupérer la référence du tableau qui est retournée comme un entier.
float_x =
float
(x)/
44.0
f; // Crée un point en virgule flottante pour la valeur X
float_y =
float
(y)/
44.0
f; // Crée un point en virgule flottante pour la valeur Y
float_xb =
float
(x+
1
)/
44.0
f; // Crée un point en virgule flottante pour la valeur X + 0.0227f
float_yb =
float
(y+
1
)/
44.0
f; // Crée un point en virgule flottante pour la valeur Y + 0.0227f
Nous utilisons quatre variables pour les coordonnées de texture, ci-dessus. Chaque polygone (un carré de notre grille) est rattaché à une section de 1/44 par 1/44 de la texture. La boucle va spécifier le sommet en bas à gauche en premier et ensuite nous ajoutons les trois autres sommets (exemple : x + 1 ou y + 1).
glTexCoord2f( float_x, float_y); // Première coordonnée de texture ( Bas gauche )
glVertex3f( points[x][y][0
], points[x][y][1
], points[x][y][2
] );
glTexCoord2f( float_x, float_yb ); // Seconde coordonnée de texture ( Haut gauche )
glVertex3f( points[x][y+
1
][0
], points[x][y+
1
][1
], points[x][y+
1
][2
] );
glTexCoord2f( float_xb, float_yb ); // Troisième coordonnée de texture ( Haut droit )
glVertex3f( points[x+
1
][y+
1
][0
], points[x+
1
][y+
1
][1
], points[x+
1
][y+
1
][2
] );
glTexCoord2f( float_xb, float_y ); // Quatrième coordonnée de texture ( Bas droit )
glVertex3f( points[x+
1
][y][0
], points[x+
1
][y][1
], points[x+
1
][y][2
] );
}
}
glEnd(); // Fin de dessin des carrés
Les lignes ci-dessus sont simplement les appels aux fonctions OpenGL pour traiter les données dont nous venons de parler. Quatre appels différents à 'glTexCoord2f()' et 'glVertex3f()'. Remarquez que les carrés sont dessinés dans le sens horaire. Cela signifie que la face que vous voyez en premier est la face arrière. Cette face est remplie. L'autre face est dessinée en fil de fer. Si vous dessinez dans le sens antihoraire, vous verrez en premier la face avant, cela signifie que vous verrez une texture en fil de fer à la place d'une texture pleine.
if
( wiggle_count ==
2
) // Ralentit la vague (Toutes les deux images seulement)
{
Si nous avions dessiné deux scènes, alors nous aurions voulu boucler nos valeurs sinusoïdales pour donner un effet de « mouvement ».
for
( y =
0
; y <
45
; y++
) // Boucle sur les axes des Y
{
hold=
points[0
][y][2
]; // Sauvegarde la valeur courante de la partie gauche de la vague
for
( x =
0
; x <
44
; x++
) // Boucle sur les axes des X
{
// Valeurs de la vague équivalentes à celle sur la droite
points[x][y][2
] =
points[x+
1
][y][2
];
}
points[44
][y][2
]=
hold; // Dernière valeur qui devient la valeur de l'extrémité gauche
}
wiggle_count =
0
; // Remise du compteur à zéro
}
wiggle_count++
; // Incrémentation du zéro
Ce que nous faisons ici, c'est sauvegarder la première valeur de chaque ligne et nous déplacer sur la gauche d'un cran, pour obtenir l'effet d'ondulation. La valeur que nous avons sauvegardée est alors ajoutée à la fin pour créer un effet sans fin à travers la texture. Finalement nous remettons à zéro le compteur 'wiggle_count' pour garder un effet correct. Le code ci-dessus a été modifié par NeHe (Févr. 2000) pour enlever un défaut sur l'ondulation de la surface de la texture. Maintenant, l'ondulation est douce.
xrot+=
0.3
f; // Incrémentation de la variable de rotation de l'axe des X
yrot+=
0.2
f; // Incrémentation de la variable de rotation de l'axe des Y
zrot+=
0.4
f; // Incrémentation de la variable de rotation de l'axe des Z
return
TRUE; // Retour
II-H. Conclusion▲
Les valeurs de rotation standards de NeHe. :) Et c'est fini. Compilez et vous devriez avoir un joli effet de « vague » texturée. Je ne suis pas quoi dire de plus sauf, whaouh… c'était long ! Mais j'espère que vous pourrez comprendre et en retirer quelque chose. Si vous avez des questions, ou si vous avez des points que vous voulez que j'éclaircisse ou me dire à quel point je code divinement bien, ;) alors envoyez-moi un message. C'était compliqué, mais vraiment prenant. Maintenant, je respecte beaucoup plus les gens comme Nehe, Merci à tous. Bosco (bosco4@home.com) 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 à le comprendre :
- Borland C++ Builder 6 (Conversion par Christian Kindahl)
- C# (Conversion par Brian Holley)
- Code Warrior 5.3 (Conversion par Scott Lupton)
- CygWin (Conversion par Stephan Ferraro)
- Delphi (Conversion par Michal Tucek)
- Dev C++ (Conversion par Dan)
- Euphoria (Conversion par Evan Marshall)
- Game GLUT (Conversion par Milikas Anastasios)
- Irix (Conversion par Rob Fletcher)
- Java (Conversion par Jeff Kirby)
- Jedi-SDL (Conversion par Dominique Louis)
- JOGL (Conversion par Nicholas Campbell)
- LCC Win32 (Conversion par Robert Wishlaw)
- Linux (Conversion par Richard Campbell)
- Linux GLX (Conversion par Mihael Vrbanec)
- Linux SDL (Conversion par Ti Leggett)
- LWJGL (Conversion par Mark Bernard)
- Mac OS (Conversion par Anthony Parker)
- Mac OS X/Cocoa (Conversion par Bryan Blackburn)
- MASM (Conversion par Nico (Scalp))
- VC++/OpenIL (Conversion par Denton Woods)
- Pelles C (Conversion par Pelle Orinius)
- Power Basic (Conversion par Angus Law)
- Visual Basic (Conversion par Ross Dawson)
- Visual Studio
- Visual Studio NET (Conversion par Grant James)
IV. Remerciements▲
Merci à Freddec_2010, LittleWhite, mabu et fearyourself pour leur relecture.