Free online content available in this course.

You can get support and mentoring from a private teacher via videoconference on this course.

Got it!

Last updated on 1/8/13

Les quadriques

Log in or subscribe for free to enjoy all this course has to offer!

Le mot quadrique, équivalent de « surface quadratique », a une connotation mathématique qui peut faire peur au premier abord. :euh:
Mais ce n'est rien d'autre qu'une surface non-linéaire (pas plane), et derrière ce nouveau mot encore plus barbare, OpenGL regroupe en fait la sphère, le cône, le cylindre, et le disque.
Nous verrons donc dans ce court chapitre comment enrichir un peu plus vos scènes avec ces quelques formes prédéfinies qui nous éviteront bien des efforts.

Principe d'utilisation

Nous le verrons juste après, il existe dans OpenGL des fonctions toutes faites pour dessiner une sphère, un cylindre, etc.
Cependant ces fonctions ont besoin d'informations sur les intentions du codeur : faut-il texturer l'objet à créer, faut-il l'afficher uniquement avec des traits ?
Pour cela nous utiliserons un champ (struct) de paramètres. Ces paramètres sont stockés dans une variable de type GLUquadric que l'on se doit d'utiliser d'une manière un peu particulière.

Création d'une variable de type GLUquadric

Ce n'est pas à nous directement de créer une variable de ce type. On doit utiliser un appel OpenGL qui nous renverra un pointeur sur le GLUquadric créé par OpenGL :

GLUquadric* params;
params = gluNewQuadric();

Nous pouvons donc maintenant utiliser la variable params créée pour paramétrer nos objets et les dessiner.

Paramétrage du GLUquadric

Ce champ n'est pas manipulable directement mais seulement par l'intermédiaire de fonctions. Une fois paramétré il sera utilisé pour tous les appels de dessin de quadriques définis après.

Style d'affichage

Les fonctions pour définir des quadriques utilisent tout comme nous des appels à glVertex pour définir les vertices des objets. Cependant comme nous n'avons pas accès directement au code, et donc au glBegin, nous ne pouvons pas spécifier par exemple par un glBegin(GL_LINES); que nous voulons que l'affichage soit fait avec des lignes.

Pour ce faire nous devons utiliser la fonction :

gluQuadricDrawStyle(params,style);
qui permet de définir quel sera le style d'affichage. style peut valoir :

Style

Explication

Exemple

GLU_POINT

L'objet sera dessiné avec des points.

Image utilisateurImage utilisateur

GLU_LINE

L'objet sera dessiné avec des lignes.

Image utilisateurImage utilisateur

GLU_FILL

L'objet sera dessiné avec des faces pleines.

Image utilisateurImage utilisateur
Coordonnées de texture

Nous l'avons vu dans le précédent chapitre, pour utiliser des textures il faut définir des coordonnées de texture avec glTexCoord2d. Si nous souhaitons utiliser des textures avec nos quadriques, il faut indiquer à OpenGL qu'il doit lui aussi incorporer les appels à glTexCoord2d lorsqu'on demandera de dessiner un quadrique. Nous utilisons donc la fonction :

gluQuadricTexture(params,texture);

où texture peut valoir GL_TRUE (vrai : pour activer les coordonnées de texture) ou GL_FALSE (faux : pour ne pas utiliser les coordonnées de texture).

glBindTexture(GL_TEXTURE_2D,texture1);
    GLUquadric* params = gluNewQuadric();
    //dessin de la sphere... (à venir)
    gluDeleteQuadric(params);
Image utilisateurImage utilisateur
glBindTexture(GL_TEXTURE_2D,texture1);
    GLUquadric* params = gluNewQuadric();
    gluQuadricTexture(params,GL_TRUE);
    //dessin de la sphere... (à venir)
    gluDeleteQuadric(params);
Image utilisateurImage utilisateur

Sur la première image c'était pas censé être une sphère ? On dirait un simple disque...

Sans texture et sans lumière, on ne peut pas comprendre que c'est une sphère. C'est un principe d'optique : la compréhension de la forme d'un objet sur une image 2D utilise pour beaucoup les différences de couleurs dues à l'éclairage (Shape from Shading). Nous réglerons donc ce problème dans quelques chapitres lorsque nous verrons la lumière. En attendant, avec les textures et le mouvement nous n'aurons vraiment pas de mal à discerner la forme de nos objets rassurez-vous.

Suppression du GLUquadric

Même si nous n'avons pas nous-mêmes utilisé de malloc (ou new en C++) pour créer le GLUquadric, il faut libérer la mémoire quand on ne souhaite plus l'utiliser par le biais de la fonction (entraperçue dans les exemples de code plus haut) :

gluDeleteQuadric(params);

Maintenant nous savons créer un champ de paramètres pour nos quadriques, le paramétrer et le supprimer. Il est temps de voir ce qui nous intéresse réellement, les quadriques et leurs fonctions de dessin !

Les quadriques

Pour bien comprendre quelles sont les faces et vertices générés par les appels suivants, tous les quadriques seront représentés en filaire. J'inclus aussi une version 3D du repère pour montrer que l'axe Z (bleu) est l'axe principal utilisé par les quadriques.

La sphère

Image utilisateur

gluSphere(GLUquadric* params,radius,slices,stacks);

  • Le premier paramètre est le champ de type GLUquadric que nous avons paramétré précédemment.

  • Le deuxième, radius, est le plus simple : c'est le rayon de la sphère.

  • slices est le nombre de tranches verticales qui composeront la sphère.

  • stacks est aussi un nombre de tranches mais pour les tranches horizontales.

L'influence des ces deux paramètres est résumée par l'image ci-dessous :

Image utilisateur

Plus ces deux nombres sont grands, plus la sphère est précise et ressemble en effet à une sphère. La valeur choisie (20x20) donne un résultat satisfaisant.

Le cylindre et le cône

Image utilisateur

gluCylinder(GLUquadric* params,base,top,height,slices,stacks);

  • Le premier paramètre est toujours le même champ de type GLUquadric.

  • Base est le rayon du cylindre en bas, top est le rayon du cylindre en haut. Pour avoir un vrai cylindre il faut donc utiliser la même valeur pour les 2, mais en utilisant des valeurs différentes pour base et top, nous aurons un cône ! (voir dessin ci-dessous)

  • slices est, comme pour la sphère, le nombre de divisions autour de l'axe Z et nous choisirons une valeur de l'ordre de 20 pour la même raison.

  • stacks ici ne sert pas à grand chose. Mettre une valeur différente de 1 ne changerait rien au niveau de la précision du cylindre/cône (à part quand on le regarde en filaire).

Image utilisateur

gluCylinder(params,1,0,2,20,1);

Le disque

Image utilisateur

gluDisk(GLUquadric* params,inner,outer,slices,loops);

  • Le premier paramètre est toujours le même champ de type GLUquadric.

  • inner est le rayon interne du disque, souvent à 0 mais peut (comme sur l'image) avoir une valeur différente.

  • outer est le rayon externe du disque.

  • slices est, comme précédemment, le nombre de divisions autour de l'axe Z.

  • loops ici ne sert pas à grand chose. Mettre une valeur différente de 1 rajouterait des faces à l'intérieur du disque mais ne rajoute pas de précision visible (à part en filaire).

Le disque partiel
Image utilisateur

gluPartialDisk(GLUquadric* params,inner,outer,slices,loops,start,sweep);

Le disque partiel est comme le disque normal sauf qu'il ne fait pas nécessairement 360°.

  • start est l'angle de départ du disque partiel. Malheureusement pour nous, les concepteurs d'OpenGL n'ont pas suivi la logique mathématique qui voudrait que les angles soient exprimés dans le sens trigonométrique (sens inverse des aiguilles d'une montre) avec 0° sur l'axe X. Ici 0° pour start place le début du disque sur l'axe Y (vert), 90° sur l'axe X (rouge).

  • sweep est la distance angulaire entre le début et la fin du disque partiel (180° sur le dessin plus haut).

Un même GLUquadric pour dessiner plusieurs quadriques

Vous l'avez compris maintenant, l'objet GLUquadric n'est pas un quadrique mais simplement un champ de paramètres utilisé lors de l'appel d'une fonction de dessin de quadrique pour spécifier le mode d'affichage. On peut donc tout à fait utiliser le même GLUquadric pour faire dessiner des quadriques tout en changeant, si on le souhaite, les paramètres en cours de route :

glBindTexture(GL_TEXTURE_2D,texture1);

    GLUquadric* params = gluNewQuadric();

    gluQuadricDrawStyle(params,GLU_LINE);
    gluCylinder(params,1,1,2,20,1);

    gluQuadricDrawStyle(params,GLU_FILL);
    gluQuadricTexture(params,GL_TRUE);
    glTranslated(0,0,1);
    gluSphere(params,0.75,20,20);

    gluDeleteQuadric(params);
Image utilisateurImage utilisateur

Exercice : une roquette

Ce chapitre n'a rien de compliqué mais jusqu'à présent j'ai fait tout le boulot en vous détaillant les fonctions pour utiliser les quadriques. Maintenant à vous ! :diable:
Pour vous faire la main sur ces nouvelles fonctions je vous propose de créer une roquette, basée sur celles que l'on trouve dans le jeu Half-Life premier du nom. Mon but n'est pas de faire de vous des apprentis terroristes mais juste des pros des quadriques ! :ange:

Image utilisateur

Une roquette inspirée d'Half-Life

Les textures

Les textures sont elles aussi tirées d'Half-Life et légèrement modifiées par mes soins. Vous les trouverez dans le pack ci-dessous (et dans le zip final).

Téléchargez les textures pour la roquette (4.53 Ko)

Schéma de la roquette

Image utilisateur

Comme vous pouvez le voir, la roquette est constituée de quatre éléments :

  1. un cône supérieur (texture rocket_top.jpg) ;

  2. un cône intermédiaire (texture rocket_middle.jpg) ;

  3. un cône inférieur (texture rocket_bottom.jpg) ;

  4. et enfin, ça ne se voit pas trop sur le schéma, un disque (texture rocket_motor.jpg).

Méthode pour la coder

Pour coder la roquette il nous faut donc procéder en deux étapes :

  • au lancement du programme il faut : charger toutes les textures utilisées ;

  • au moment de dessiner il faut :

    1. créer un GLUquadric ;

    2. paramétrer le quadrique pour qu'il génère les coordonnées de texture automatiquement ;

    3. dessiner le premier objet ;

    4. se translater à la base du deuxième objet ;

    5. dessiner le deuxième objet ;

    6. etc.

    7. détruire le GLUquadric.

N'oubliez pas non plus de changer de texture entre chaque quadrique pour ne pas vous retrouver avec une roquette uniformément... moche. ;)

Voilà vous avez tous les outils pour dessiner cette roquette. Référez-vous bien au schéma que je vous donne pour respecter les proportions. Toute roquette déformée ne sera pas acceptée pour une utilisation sur le terrain ! (Hum...)

À vous donc !

Correction

Je ne vous mets ici que le code intéressant. Je pars du principe que vous savez parfaitement charger les textures et initialiser l'application. Je fournis bien entendu le code complet dans l'archive finale.

/* J'ai choisi de faire une fonction Dessiner Rocket.
Je pourrai ainsi l'appeler plusieurs fois, et dans n'importe quelle
position initiale du repère initial */
void DrawRocket()
{
    glPushMatrix(); //pour que les transformations soient réversibles

    GLUquadric* params = gluNewQuadric(); //création du quadrique
    gluQuadricTexture(params,GL_TRUE); //activation des coordonnées de texture

    glBindTexture(GL_TEXTURE_2D,top); //texture du haut
    gluCylinder(params,0.5,0,1.6,20,1); //cône 1

    glBindTexture(GL_TEXTURE_2D,middle);
    glTranslated(0,0,-1.05); //je descends pour faire le 2ème cône
    gluCylinder(params,0.15,0.5,1.05,20,1); //cône 2

    glBindTexture(GL_TEXTURE_2D,bottom);
    glTranslated(0,0,-0.25); //je descends enfin tout en bas (sur le schéma)
    gluCylinder(params,0.3,0.15,0.25,20,1); //cône 3

    //et à la même position je dessine le disque de sortie des flammes
    glBindTexture(GL_TEXTURE_2D,motor);
    gluDisk(params,0,0.3,20,1); //disque 4

    gluDeleteQuadric(params); //je supprime le quadrique

    glPopMatrix(); //hop je remets tout comme je l'ai trouvé
}

void DrawGL()
{
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );
    gluLookAt(3,4,2,0,0,0,0,0,1); //je place la caméra à un endroit idéal

    DrawRocket(); //je dessine la 1ère roquette

    glTranslated(2,0,0); //je me déplace pour la 2ème roquette
    glRotated(90,1,0,0); /*je vais tourner celle-là pour que son axe principal
    soit horizontal */
    DrawRocket(); //et je la dessine

    glFlush();
    SDL_GL_SwapBuffers();
}

Comme vous le voyez j'ai décidé personnellement de faire une fonction pour dessiner la roquette. Je peux ainsi dessiner autant de roquettes que je veux sans alourdir le code.

Améliorations

Vous pouvez, si vous le souhaitez, créer une sphère représentant la Terre (avec la texture EarthLow.jpg du pack final) autour de laquelle la roquette tournerait.
Conseil : pour faire tourner la roquette autour de la Terre il suffit de bien réfléchir à l'ordre des transformations à faire. Comme ce n'est pas le but de cet exercice, qui se veut simple et rapide, je vous laisse imaginer la solution adéquate.

Image utilisateur

Téléchargez le projet Code::Blocks, l'exécutable Windows et le Makefile Unix (391 Ko)

Les quadriques, c'est fantastique et c'est magique ! Loin d'être mystiques elles sont quand même vachement pratiques ! :soleil:
C'est une solution très simple pour ajouter des objets un peu complexes dans vos scènes en attendant de savoir charger de vrais modèles 3D créés dans des logiciels 3D externes (Blender, 3dSmax...).
Dans le prochain chapitre nous verrons comment contrôler la caméra de manière plus poussée, et réaliserons entre autres un dérivé de Google Earth sans prétention qui utilisera justement la sphère. Que de bonheur en perspective !

Example of certificate of achievement
Example of certificate of achievement