Article     Discussion     Modifier     Historique     Forums     Salon IRC

Créer et visualiser une scène 2D avec OpenGL

Un article de Games Creators Network.


Sommaire

[modifier] Objectif

Nous avons vu dans un premier temps le squelette d'une application OpenGL, ce qui nous a permis de créer une fenêtre à fond noir. L'étape suivante consisite maintenant à dessiner des primitives simples dans notre scène.

Figure 1 : La scène à obtenir
Figure 1 : La scène à obtenir

[modifier] Définir les "objets"

[modifier] Comment les définir ?

Les objets que nous voulons intégrer à notre scène, à savoir un carré et un triangle, sont représentés, en OpenGL, par des listes de sommets (vertex).

La déclaration d'une liste de sommets commence par l'appel à la fonction glBegin(GLenum mode) et se termine par glEnd(). Entre ces 2 fonctions, on déclare les sommets grâce à la fonction glVertex__( ). La variable mode identifie le type de figures que l'on veut créer, et peut valoir une des constantes suivantes :

GL_POINTS traite chaque sommet comme un point
GL_LINES les couples de sommets forment des lignes séparées
GL_LINE_STRIP tous les sommets sont connectés 1 à 1
GL_LINE_LOOP comme GL_LINE_STRIP, avec une ligne en plus qui relie le dernier sommet au premier
GL_TRIANGLES les triplets de sommet forment des triangles
GL_TRIANGLE_STRIP les triangles sont connectés entre eux
GL_TRIANGLE_FAN les triangles sont connectés, et le premier sommet est commun à tous les triangles
GL_QUADS les quadruplets de sommets forment des quadrilatères
GL_QUAD_STRIP les quadrilatères sont connectés
GL_POLYGON tous les sommets forment un polygone convexeDans notre cas, on désire créer un triangle et un carré. Puisque nous ne nous intéresserons qu'à des objets 2D, nous utiliserons la fonction glVertex2i(int x, int y) pour définir les sommets. Voici donc la portion de code qui définit un triangle :


glBegin(GL_TRIANGLES);
 glVertex2i(0,1);
 glVertex2i(-1,0);
 glVertex2i(1,0);
glEnd();

Passons maintenant au carré :

glBegin(GL_QUADS);
 glVertex2d(2,-1);
 glVertex2d(4,-1);
 glVertex2d(4,1);
 glVertex2d(2,1);
glEnd();

[modifier] Où définir les objets ?

Nous avons définit, dans le tutoriel précédent, la fonction Display qui nous a permis de créer un fond noir pour notre fenêtre. C'est encore cette fonction qui va nous servir à dessiner les objets dans la scène : on y place donc le code de définition du rectangle et du triangle :

void Display()
{
 glClearColor(0,0,0,0); // selectionne la couleur noire (qui est celle par défaut)
 glClear(GL_COLOR_BUFFER_BIT); // efface le frame buffer
 // Définition du triangle
 // Définition du carré
 glFlush();
}

[modifier] Résultat intermédiaire

Si on regarde le résultat à ce stade de notre programme, seul le triangle apparaît dans la fenêtre, et à une échelle trop grande par rapport à l'objectif que l'on s'est fixé.

Figure 2 : Résultat intermédiaire
Figure 2 : Résultat intermédiaire

L'explication est simple : nous avons défini nos objets par leur sommets sans se préoccuper de leur position dans la scène. Nous avons juste choisi les coordonnées des sommets de façon à créer les formes que nous voulions. Or, si on visualise une scène directement, sans modifier les propriétés de visualisation, la fenêtre est centrée sur le repère du monde, et positionnée de telle manière à se superposer au carré unitaire ((-1,1)(1,1)(1,-1)(-1,-1)) :

Figure 3 : Visualisation par défaut
Figure 3 : Visualisation par défaut

On comprend mieux alors pourquoi seulement notre triangle ((-1,0)(0,1)(1,0)) apparaît à l'écran, et pourquoi il occupe toute sa largeur.

Nous allons donc voir maintenant comment modifier notre scène pour que les objets apparaissent correctement dans la fenêtre.

[modifier] Mettre en place la scène

[modifier] Rendu d'une scène

D'une manière générale, le procédé d'affichage d'une scène 3D ou 2D en OpenGL passe par 4 étapes :

  • Viewing Transformation (Transformation de Visualisation) : ici, on place la caméra dans l'espace ;
  • Modeling Transformation (Modélisation) : on place les objets dans la scène ;
  • Projection Transformation (Projection) : on ajuste le grossissement ;
  • ViewPort Transformation (Affichage) : on determine les caractéristiques (taille et position) de l'image dans la fenêtre de résultat.

Dans OpenGL, ces tranformations sont représentées par des matrices M, et un vecteur (vertex) v de la scène se trouve transformé de la façon suivante : v' = M * v.

Ainsi, pour disposer les éléments de la scène dans l'espace, on dispose de 3 matrices : on utilise la matrice de PROJECTION pour décrire la caméra, la matrice MODELVIEW pour positionner les objets et les lumières, et la matrice TEXTURE pour les textures. Toujours suivant le principe de la machine d'état, on ne peut manipuler qu'une matrice à la fois, et on dispose d'une fonction pour indiquer à OpenGL la matrice que l'on utilise : glMatrixMode(GLenum mode). L'argument mode peut alors prendre les valeurs suivantes :

GL_MODELVIEW on utilise la matrice modelview (par défaut) GL_PROJECTION on utilise la matrice de projection GL_TEXTURE on utilise la matrice de textureToutes les opérations sur les matrices s'effectuant sur la matrice courante (MODELVIEW par défaut), il faudra faire appel à glMatrixMode dès que l'on voudra en changer.

Notons que pour initialiser la matrice courante, on la charge avec la matrice Identité (matrice dont les éléments de la diagonale valent 1, et les autres 0) grâce à la fonction glLoadIdentity().

[modifier] Mise en oeuvre : l'événement "Reshape"

[modifier] Activer l'événement Reshape

Rappelons qu'ici, nous souhaitons modifier notre configuration objets-caméra afin de pouvoir visualiser correctement notre scène. En fait, nous travaillerons sur la matrice de projection, afin que les objets, dont nous avons déjà établis les coordonnées, apparaissent dans le "champ"de la caméra.

Toutefois, avant de commencer à configurer la caméra, commençons par nous demander à quel moment, dans l'éxécution du programme, devra-t-on effectuer cette opération. Le faire à chaque fois que la scène est dessinée serait inutile, puisque notre objectif est simple (2D, scène fixe) et ne nécessite pas de mouvement de caméra.

Nous allons donc activer un nouveau callback, qui réagira à la création de la fenêtre, et à son redimensionnement : c'est l'événement Reshape, qui s'active grâce à la fonction glutReshapeFunc :

void Reshape(int, int); // à insérer dans les définitions de fonctions
....
glutReshapeFunc(Reshape); // à insérer dans le main()
....
void Reshape(int w, int h)
{
 // w et h sont les dimensions de la fenêtre
 // code de la fonction
}

Voyons maintenant comment "remplir" notre fonction Reshape.

[modifier] Transformation d'affichage

D'abord, choisissons notre transformation d'affichage (Viewport). En fait, il s'agit de définir la zone rectangulaire de la fenêtre de résultat qui servira à afficher la scène. Ceci est réalisé grâce à la fonction glViewport (GLint x, GLint y, GLsizei w, GLsizei h), où (x,y) est le coin inférieur gauche du "Viewport", et w et h sa longueur et sa hauteur.

Figure 4.1 : (0,0,w,h)
Figure 4.1 : (0,0,w,h)
Figure 4.2 : (0,0,w/2,h)
Figure 4.2 : (0,0,w/2,h)
Figure 4.3 : (0,0,2*w,h)
Figure 4.3 : (0,0,2*w,h)

Ces images montrent l'influence de la modification du Viewport, où w et h sont les dimensions de la fenêtre. Dans notre cas, nous utiliserons toute la fenêtre d'affichage, donc la transformation d'affichage se codera par :

glViewport(0,0,w,h);

[modifier] Transformation de projection

Comme nous l'avons vu plus haut, c'est la matrice de projection qui est utilisée pour décrire la caméra. Dans un premier temps, il convient donc d'avertir OpenGL que la matrice de projection est celle sur laquelle nous allons travailler (elle devient la matrice courante) :

glMatrixMode(GL_PROJECTION);

Avant de faire toute modification sur la matrice, on la réinitialise :

glLoadIdentity();

Il reste maintenant à définir notre matrice de projection. Pour cela, nous avons à disposition une fonction permettant de créer une projection 2D orthogonale : gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top), où left et right sont les coordonnées des plans de coupe verticaux droite et gauche, et bottom et top les coordonnées des plans de coupe horizontaux.

D'après les coordonnées des objets de notre scène, on peut les englober par exemple dans un carré ((-5,-5)(-5,5)(5,5)(5,-5)). Donc dans un premier temps, choisissons ces coordonnées pour celles de nos plans de coupe.

gluOrtho2D(-5,5,-5,5) donne le résultat suivant :

Figure 5 : projection orthogonale non normée
Figure 5 : projection orthogonale non normée

On a bien nos 2 objets dans la fenêtre de résultat, mais il reste un problème : le carré n'en est plus un, puisque sur la fenêtre de résultat, il est clair que ses 4 côtés ne sont pas de longeur égales. Ce phénomène de distorsion s'explique très simplement : depuis le départ, nous affichons la scène dans une fenêtre de taille (640*480). En revenant quelques instants sur la visualisation de la figure 3, il est clair que le repère orthonormédu monde apparaît orthogonal mais non normédans la fenêtre de résultat : pour parler moins mathématiquement :), deux longueurs égales ne le sont pas dans la fenêtre de résultat.

Il nous faut donc prendre en compte le quotient ratio=w/hpour normer notre repère de visualisation, et différencier les cas w>h (fenêtre plus large que haute) et w<h (fenêtre plus haute que large) :

 float L; // Longueur entre les 2 plans de coupe verticaux
 float H; // Hauteur entre les 2 plans de coupe horizontaux
 if (w<=h)
 {
  H=(GLfloat) (10*h/w);
  L=10.0;
 }
 else
 {
  H=10.0;
  L=(GLfloat) (10*w/h);
 }
 gluOrtho2D(-L/2,L/2,-H/2,H/2);



Ce document a été publié sur la version 3 du G.C.N. par Le-Gritche.

  • Auteur Original : Le-Gritche
  • Date de publication : 10 janvier 2002

 

Rechercher
Installer l'extension de recherche Plus d'informations

 

Comprendre
Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. - Benjamin Franklin

 

Partager
La connaissance est la seule chose qui s'accroit lorsqu'on la partage. - Sacha Boudjema

 

Créer
L'imagination est plus importante que la connaissance. - Albert Einstein

 

 

Le wiki en images Le wiki en images Image du mois: «Snowball: un prototype de jeu développé avec NeL.