Dans cet article, je vais présenter un travail effectué dans le cadre d'un TP pendant ma troisième année de cycle ingénieur. Ceci sera l'occasion de présenter aussi quelques principes de la programmation 3D.

< param name="movie" value="http://gregory.corgie.free.fr/Ressources/Flash/flash_flv_player/flvplayer.swf?file=../../Videos/OpenGl/TPOpengl_cut&autoStart=false" /> < param name="wmode" value="transparent" />


Voici les sources. Il doit tout y avoir : le code et les textures !


Les Display lists


La display list est une méthode d'utilisation du pipeline graphique d'OpenGl. Revenons aux bases, pour afficher une primitive graphique, on a l'habitude de procéder ainsi:

  1. glBegin(GL_TRIANGLES); // Définir le type de primitive
  2. glVertex3f( 0.0f, 1.0f, 0.0f); // Sommet 1
  3. glVertex3f(-1.0f,-1.0f, 0.0f); // Sommet 2
  4. glVertex3f( 1.0f,-1.0f, 0.0f); // Sommet 3
  5. glEnd();

Le code est clair. On définit trois coordonnées, celles-ci permettent de définir une géométrie (un triangle ici). La seule petite subtilité est que l'ordre de déclaration des sommets permet de définir un sens pour la normale. En effet, à chaque triangle est assigné une normale, cette normale est utilisée en autre pour des optimisations de rendu : le culling.

Au passage, toutes les géométries sont constituées de triangles. En effet, si l'on prend l'exemple d'une sphère quelconque, en zoomant dessus, on peut distinguer les facettes qui la compose.

Le culling

Le culling permet d’éviter d’avoir à dessiner des triangles ‘’inutiles’’. Si l’on doit dessiner un ballon de foot, il ne sert à rien de dessiner toutes les facettes qui sont du coté opposé à la caméra. Ce qui fait gagner du temps précieux! Pout activer le culling, on a l'instruction:

glEnable(GL_CULL_FACE); // Activation du culling

Il faut ensuite définir la condition de culling

glCullFace(GL_FRONT); // Culling des faces dont la normale est orientée à l'encontre du sens de la caméra

ou

glCullFace(GL_BACK); // Culling des faces dont la normale est orientée dans le sens de la caméra


Pourquoi une display list alors ?

  1. Gain de lisibilité : Imaginer que vous avez à tracer un système de particules ( quads texturés identiques mais à des emplacements différents ), auriez vous envie d'écrire à la main tous les sommets ?
  2. Gain d'execution : La seconde raison (beaucoup plus valable), est que le coup de transfert des sommets entre la mémoire vive et le GPU est beaucoup moins rapide que les transferts de la mémoire de la carte graphique au GPU. Les displays list sont directement stockées dans la mémoire de la carte graphique.
  3. Le rendu d'objets identiques : Revenons à notre système de particules. Il suffit de créer une display list qui affiche un quad texturé et de la changer position courante entre chaque rendu de particule (via un changement de la matrice courante).

Dans mon petit programme, la totalité des géométries ont été rendu via des display lists. Pour la syntaxe, il suffit de regarder les sources. A noter que l'on peut mettre quasiment toutes les instructions entre les bornes de déclaration.

Générer un landscape à partir d'une heightmap

N'ayant aucun talent de graphiste, je ne me suis pas lancé dans la modélisation d'un monde à l'aide d'un logiciel de 3D.

La lecture d'une image 2D

Au contraire, je suis parti d'une image 2D composée de pixels en niveau de gris. Je parcours tous les pixels de l'image et à chaque valeur de gris, je fais correspondre une hauteur. Avec un maillage moyen, on obtient un résultat satisfaisant.

Attribuer la bonne texture à la bonne hauteur

Même si tout celà n'est pas flagrant, le landscape ne possède pas une texture unique. En effet pour donner quelquechose de sympa, on souhaite attribuer une texture en fonction de la hauteur. En gros, au sommet du monde, on veut de la neige alors qu'au bas, on veut de l'eau. Pour se faire on va considérer que le maillage composant le landscape a des coordonnées de textures (UVs) variant de 0 à 1.

Ensuite pour chaque vertex composant le landscape, on cherche ses coordonnées UV. On demande ensuite la hauteur correspondant à ce vertex. En fonction de celle-ci et de seuils prédéfinis, on va chercher la couleur correspondant aux UVs dans la texture associée (texture de neige par exemple).

Si j'avais mis plus de sommets pour le maillage du landscape, le résultat aurait été meilleur mais bon...^^

Le ciel / la skybox

Pour rendre le ciel, je ne me suis pas compliqué la vie. J'utilise 6 pauvres plans placés assez loin avec des textures différentes. Normalement les skyboxs dans les jeux vidéo ont plutôt une forme sphérique grossière. Comme elle est placée assez loin de la caméra, ça ne sert à rien de la booster en facettes.

L'eau

Simuler un mouvement d'eau

Voilà l'eau c'est joli, mais quand ça bouge c'est mieux! Ici, je fais quelque chose de très simple. Pour simuler un mouvement, je fais simplement scroller la texture d'eau associée au plan. Comment fait-on ?

Revenons aux bases... Une fois une texture chargée en mémoire (chose assez pénible d'ailleurs, j'y reviendrai peut-être), on peut associer à une géométrie des coordonnées de texture. Imaginons que l'on veut dessiner un plan texturé:

glBegin( GL_QUADS );
glTexCoord2f( 0.f, 0.f); glVertex3f( -1.f, 1.f, 0.f );
glTexCoord2f( 0.f, 1.f); glVertex3f( -1.f, -1.f, 0.f );
glTexCoord2f( 1.f, 1.f); glVertex3f( 1.f, 1.f, 0.f );
glTexCoord2f( 1.f, 0.f); glVertex3f( 1.f, -1.f, 0.f);
glEnd();

On donne les 4 sommets, et on attribue les coordonnées de texture à chaque sommet via la commande glTexCoord2f(u,v). Ensuite entre les sommets, les coordonnées seront interpolées.

Alors maintenant faisons bouger la texture... Imaginons que nous mettons une variable Tps à la place des coordonnées u de cette façon

glBegin( GL_QUADS );
glTexCoord2f( Tps, 0.f); glVertex3f( -1.f, 1.f, 0.f );
glTexCoord2f( Tps, 1.f); glVertex3f( -1.f, -1.f, 0.f );
glTexCoord2f( Tps + 1.f, 1.f); glVertex3f( 1.f, 1.f, 0.f );
glTexCoord2f( Tps + 1.f, 0.f); glVertex3f( 1.f, -1.f, 0.f);
glEnd();

A chaque trame, on incrémente Tps. De cette manière, la texture va défiler (uniquement selon l'axe des u) au fur et à mesure du temps ^^. Remarque: Il faut penser à remettre à zéro le Tps lorsqu'elle atteint une certaine valeur.

Le reflet (assez compliqué ... a venir)




Faire une caméra

Rappelons que la notion de caméra est purement abstraite. OpenGl ne "sait" pas ce qu'est une caméra, ce qui est tout à fait normal. De plus, il faut bien comprendre que ce n'est pas l'objet caméra qui se déplace mais bien tout le monde qui est déplacé. Une caméra revient donc à faire une simple transformation (translation/rotation) du monde permettant de compenser le mouvement de la caméra.




SI VOUS AVEZ BESOIN D'EXPLICATION SUR QQCHOSE, DEMANDEZ JE POSTERAI DES REPONSES !