OpenGL Introduction. - Département d`informatique et de génie logiciel
Download
Report
Transcript OpenGL Introduction. - Département d`informatique et de génie logiciel
INTRODUCTION
À
OPENGL
OpenGL est une interface logicielle capable de communiquer
avec le matériel graphique. Il s’agit d’une bibliothèque d’outils
de traitement graphique indépendante du matériel où l’on peut
créer des programmes interactifs produisant des images réalistes
ou abstraites en couleurs d’objets 3D.
Les lettres GL signifient Graphics Library.
Qu’est-ce qu’OpenGL ?
OpenGL est une interface logicielle destinée aux applications graphiques composée
d’environ 250 commandes distinctes.
C’est une interface indépendante du matériel qu’il est possible d’implémenter
sur de nombreuses plates-formes matérielles différentes.
OpenGL ne renferme pas à proprement parler de commandes permettant
d’effectuer des tâches de fenêtrage ou de récupérer des entrées utilisateur.
Il faudra faire appel notamment à un gestionnaire de fenêtres.
OpenGL ne renferme pas de commandes de haut niveau permettant de définir
des objets tridimensionnels complexes. Vous devez construire vos modèles à
partir d’un jeu restreint de formes géométriques primitives : des points, des
segments et des polygones.
On peut superposer à OpenGL une bibliothèque GLU (OpenGL Utility Library)
sophistiquée intégrant ces fonctionnalités : les surfaces quadriques et les techniques
de points de contrôle. Les primitives de GLU commencent par le préfixe glu.2
Pour chaque gestionnaire de fenêtres, une bibliothèque étend les fonctionnalités
du système afin de supporter OpenGL. Pour les ordinateurs X Window, l’extension
OpenGL correspondante est GLX où les routines emploient le préfixe glX.
Une autre librairie GLUT (OpenGL Utility Toolkit) est un ensemble d’outils
indépendant du gestionnaire de fenêtres, conçu afin de masquer les complexités
des différents appels au gestionnaire de fenêtres.
PROGRAMME D’APPLICATION
GL
GLU
GLUT
GLX
Librairie de base
Microsoft Windows requiert l’insertion de windows.h comme suit :
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
----fichiers personnels
----Si vous faites appel à GLUT, éviter les redondances et omettre GL et GLU.
Fonctionnalités d’OpenGL ?
Voici les principales opérations graphiques réalisées par OpenGL pour restituer
une image à l’écran :
Construire des formes géométriques à l’aide de primitives (points, segments,
polygones, images).
Positionner et orienter les objets dans un espace tridimensionnel et sélectionner
le point de vue le plus avantageux pour visualiser la scène composée.
Déterminer les couleurs des objets en prenant en compte les conditions d’éclairage
et leur texture.
Convertir les données géométriques et les caractéristiques physiques en valeurs
pour les pixels à l’écran.
4
Gestion des fenêtres
Surface rectangulaire d’affichage sur l’écran
(par défaut, il s’agit de l’écran au complet).
La fenêtre est définie par sa hauteur et sa largeur de même que sa position
en coordonnées écran (unités : « pixels »)
Dans des environnements riches, il peut exister plusieurs fenêtres.
En OpenGL, l’origine de la fenêtre est en bas à gauche tandis que les
données fournies par le système de gestion de fenêtres optent pour une
origine en haut à gauche.
glutInit (int *argcp, char **argv)
Permet une interaction entre le système de gestion de fenêtres et OpenGL.
Doit être appelée avant toute autre routine GLUT.
int glutCreateWindow(char * titre_de_la_fenêtre)
Création d’une fenêtre avec un titre au haut fourni en paramètre.
La fenêtre créée a une dimension et une position par défaut.
5
La primitive retourne un entier unique pour la nouvelle fenêtre.
Gestion des fenêtres
Avant la création de la fenêtre, on peut fixer les paramètres de celle-ci au-lieu
de se contenter de ceux par défaut :
void glutInitWindowSize(int largeur, int hauteur);
Fixe la largeur et la hauteur initiales, en pixels, de la fenêtre.
glutInitWindowPosition(int x, int y);
Fixe la position du coin supérieur gauche, en pixels, de la fenêtre.
Une fois la fenêtre créée, pour faire en sorte que la fenêtre s’affiche et afin de
conserver l’image produite à l’écran, il faudra appeler la primitive suivante :
void glutMainLoop(void);
Entre dans la boucle de traitement sans fin GLUT à moins que des
événements soient générés. Puisqu’aucun événement n’est prévu,
le programme exécuté sera mis en attente, l’image affichée à l’écran.
On peut détruire une fenêtre déjà créée dont le numéro est i :
void glutDestroyWindow(int i);
6
Gestion des fenêtres
Une fenêtre nouvellement créée devient la fenêtre courante. Pour rendre active une
autre fenêtre déjà créée, on a :
void glutSetWindow(int id);
Ainsi, nous pouvons avoir plusieurs fenêtres et chacune possède ses propres
caractéristiques.
On peut aussi créer des sous-fenêtres à l’intérieur d’une fenêtre déjà créée.
int glutCreateSubWindow(int parent, int x, int y, int largeur, int hauteur);
La sous-fenêtre devient la fenêtre courante; son origine est à (x, y)
tandis que largeur et hauteur désignent resp. la largeur et la hauteur
de la sous-fenêtre en pixels.
(x, y) désigne le coin supérieur gauche de la sous-fenêtre par rapport
à l’origine de la fenêtre.
7
Fonction d’affichage
La primitive
void glutDisplayFunc(void (*f)(void))
permet de spécifier le nom de la fonction qui renfermera les primitives
d’affichage. f sera appelée si le système de gestion des fenêtres détermine
que la fenêtre courante a besoin d’être réaffichée.
8
Ébauche de programme en OpenGL
Voici la forme la plus simple d’un programme OpenGL où une fenêtre vide est
affichée :
#include <windows.h>
#include <GL/glut.h>
void Affichage()
{
// Permet d’effacer la fenêtre active et de l’afficher avec la couleur de fond
// par défaut.
glClear(GL_COLOR_BUFFER_BIT);
// Force les commandes OpenGL émises précédemment à s’exécuter.
glFlush();
}
9
void main(int argc, char** argv)
{
// Permet d'initialiser des paramètres de certains utilitaires d’OpenGL.
glutInit(&argc, argv);
/*
Permet de fixer la dimension de la fenêtre à l'écran
soit, 800 pixels de largeur par 500 de hauteur.
*/
glutInitWindowSize(800, 500);
//
Permet de fixer la position du coin supérieur gauche de la fenêtre.
glutInitWindowPosition(500, 100);
/*
Créer une fenêtre avec comme titre celui passé en paramètre.
Les paramètres de la fenêtre doivent avoir été fixés au préalable.*/
glutCreateWindow("Fenetre principale");
/*
Spécifie la fonction "Affichage" qui sera appelée chaque fois
que la fenêtre doit être affiché.
*/
glutDisplayFunc(Affichage);
/*
Permet de conserver l'image produite à l'écran. Le programme
exécuté est mis en attente jusqu'à ce qu'un événement se produise,
l'image demeurant affichée à l'écran.
*/
glutMainLoop();
}
10
TYPES DE DONNÉES OPENGL
OpenGL manipule des types de données propres à OpenGL.
Puisqu’il n’y a pas de standard pour le type int (16 ou 32 bits), le type float ou le type
double, nous devons utiliser des types de données propres à OpenGL.
Suffixe
b
s
i
f
d
ub
us
ui
Type de donnée
entier 8 bits
entier 16 bits
entier 32 bits
réel 32 bits
réel 64 bits
nb. sans signe 8 bits
nb. sans signe 16 bits
nb. sans signe 32 bits
C++
signed char
short
int ou long
float
double
unsigned char
unsigned short
unsigned int ou
unsigned long
OpenGL
GLbyte
GLshort
GLint, GLsizei
GLfloat, GLclampf
GLdouble, GLclampd
GLubyte, GLboolean
GLushort
GLuint, GLenum,
GLbitfield
11
Tracé de formes géométriques de base
Pour afficher différentes formes géométriques, nous devrons procéder à
l’intérieur d’une fonction « Affichage » :
glutDisplayFunc(Affichage);
OpenGL définit des objets géométriques de base à partir d’une liste de
sommets.
En OpenGL, on réfère plutôt au concept de sommet de préférence à celui de
point :
forme générale : glVertex*()
où * représente 2 ou 3 caractères de la forme nt ou ntv où
n : dimension (2, 3 ou 4),
t : type de donnée : :
i pour entier
f pour réel en point flottant
d pour réel double précision.
v : indique la présence d’un pointeur à un vecteur plutôt qu’une
liste d’éléments.
12
Exemples de définition de sommets :
(i)
entiers dans l’espace à 2 dimensions
glVertex2i(GLint xi, GLint yi);
(ii)
nombres réels dans l’espace à 3 dimensions
glVertex3f(GLfloat x, GLfloat y, Glfloat z);
(iii)
vecteur de 3 nombres réels
typedef GLfloat vertex[3];
nous avons ainsi : glVertex3fv(vertex);
(iv)
vecteur de 3 points dans l’espace à 2 dimensions
typedef GLfloat point2[2];
point2 triangle[3];
13
FORME GÉNÉRALE POUR
DÉFINIR UN OBJET
Pour regrouper les sommets définissant un objet, on utilise les fonctions :
glBegin et glEnd.
Le paramètre de glBegin définit le type d’objet géométrique que l’on veut
représenter.
La forme générale pour définir des objets géométriques :
glBegin(type);
glVertex*(…);
.
.
.
glVertex*(…);
glEnd();
La valeur de « type » permet de définir le type d’objet géométrique représenté
par ces sommets.
14
EXEMPLES DE DÉFINITION
(i) un segment de droite
glBegin(GL_LINES);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glEnd();
(ii) une paire de points
glBegin(GL_POINTS);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glEnd();
(iii) un polygone
glBegin(GL_POLYGON);
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(0.0, 1.0, 0.0);
glVertex3f(0.0, 0.0, 1.0);
glEnd();
D’autres fonctions peuvent être appelées
entre glBegin et glEnd :
modification des attributs
calcul de nouveaux sommets
etc.
15
Types d’objets géométriques
Plusieurs types d’objets géométriques sont disponibles :
P2
P0
P2
P0
P5
GL_POINTS
P5
GL_LINES
P2
P0
P2
P0
P5
GL_LINE_STRIP
P5
GL_LINE_LOOP
16
P2
P2
P0
P5
P6
P7
P2
P0
P0
P7
GL_POLYGON
P5
P6
P7
GL_QUADS
GL_TRIANGLES
P1
P1
P3
P5
P7
P1
P3
P5
P5
P6
P2
P7
P3
P0
P0
P2
P4
P6
GL_TRIANGLE_STRIP
P0
P2
P4
P6
GL_QUAD_STRIP
P4
GL_TRIANGLE_FAN
17
PRÉSENCE DE VARIABLES D’ÉTAT
OpenGL possède plusieurs variables d’état : taille courante d’un point,
la couleur d’affichage courante, la couleur d’arrière-plan courante, etc.
La valeur d’une variable d’état demeure la valeur courante ou la valeur par défaut
jusqu’à ce qu’une nouvelle valeur soit spécifiée.
Exemples :
glPointSize(3.0);
Spécifie la taille d’un point (réel) à l’affichage.
glColor3f(rouge, vert, bleu);
Spécifie la couleur d’affichage courante où rouge, vert et bleu
sont des réels entre 0.0 et 1.0.
glClearColor(rouge, vert, bleu, alpha);
Spécifie la couleur d’arrière-plan courante où alpha désigne
un niveau de transparence. (0, 0, 0, 0) est la valeur par défaut (noir).
glClear(GL_COLOR_BUFFER_BIT);
Efface la fenêtre au complet avec la couleur d’arrière-plan.
18
Choix d’un système de coordonnées
Nous devons aussi établir le système de coordonnées à partir duquel nous
définirons des objets géométriques et à quel endroit ces objets apparaîtront
dans la fenêtre.
À éviter : Se contraindre à définir les données dans le système de coordonnées de
l’unité d’affichage.
Le concepteur doit travailler dans son propre système de référence
indépendant de l’unité d’affichage (système de coordonnées du monde).
Chaque unité d’affichage possède son propre système de référence
(système de coordonnées écran).
Le logiciel d’infographie doit renfermer les outils pour passer d’un système
à l’autre.
Pour le moment, nous débuterons avec un système de coordonnées 2D très
proche de celui de la fenêtre et des distances mesurées en « pixels ».
19
Choix d’un système de coordonnées
Pour définir dans quelle plage se retrouve les coordonnées des sommets qui
définissent les objets de la scène, on peut utiliser :
void gluOrtho2D(GLdouble gauche, GLdouble droite,
GLdouble bas, GLdouble haut)
Exemple :
À préciser
ultérieurement.
Défn d’un rectangle à l’intérieur duquel les objets sont définis.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 500.0, 0.0, 500.0);
Après avoir défini la fenêtre courante, OpenGL fait le passage automatiquement
entre le système de coordonnées du monde et celui de l’unité d’affichage.
20
Exemple : Affichage de 2 objets resp. dans une fenêtre et une sous-fenêtre.
#include <windows.h>
#include <GL/glut.h>
void Affichage_principal()
{
glClear(GL_COLOR_BUFFER_BIT);
}
void Affichage_secondaire()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
glColor3f(0.0, 1.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, 500, 0, 500);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, 500, 0, 500);
glBegin(GL_POLYGON);
glVertex2i(10, 20);
glVertex2i(100, 10);
glVertex2i(20, 200);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex2i(10, 20);
glVertex2i(100, 10);
glVertex2i(20, 200);
glEnd();
glFlush();
glFlush();
}
21
void main(int argc, char** argv)
{
int id;
glutInit(&argc, argv);
/*
Permet de fixer la dimension de la fenêtre à l'écran
soit, 800 pixels de largeur par 500 de hauteur.
*/
glutInitWindowSize(800, 500);
//
Permet de fixer la position du coin supérieur gauche de la fenêtre.
glutInitWindowPosition(500, 100);
/*
Créer une fenêtre avec comme titre celui passé en paramètre. Les
paramètres de cette fenêtre doivent avoir été fixés au préalable.
*/
id = glutCreateWindow("Fenetre principale");
/*
Spécifie la fonction "Affichage_principal" qui sera appelée chaque fois
que la fenêtre doit être affichée.
*/
glutDisplayFunc(Affichage_principal);
/*
Permet de créer une sous-fenêtre 300 x 200 où (400, 250) désigne
le coin supérieur gauche par rapport à la fenêtre principale.
*/
glutCreateSubWindow(id, 400, 250, 300, 200);
/*
Spécifie la fonction "Affichage_secondaire" qui sera appelée chaque fois
que la sous-fenêtre doit être affichée.
*/
glutDisplayFunc(Affichage_secondaire);
/*
Permet de conserver l'image produite à l'écran. Le programme exécuté
est mis en attente jusqu'à ce qu'un événement se produise, l’image
demeurant affichée à l'écran.
22 */
glutMainLoop(); }
Primitives de sortie
Pour débuter, nous allons opter pour les constructions les plus simples
d’OpenGL en choisissant implicitement plusieurs options par défaut.
Toutes ces options seront approfondies au fur et à mesure que nous
assimilerons la matière du cours.
Nous pouvons distinguer 4 types de primitives de sortie :
a)
« polyline »
b)
texte
c)
région de remplissage
d)
images point à point
À chaque primitive de sortie est associée différents attributs
(couleur, épaisseur de trait, etc. ).
Ces attributs possèdent des valeurs par défaut et des valeurs courantes.23
Polyline
« polyline » : suite de segments de droite reliés deux à deux.
Des images obtenues à partir de « polylines » sont appelées quelquefois des dessins
au trait.
Chaque segment d’un « polyline » est appelé arête et 2 segments adjacents
ont un sommet commun.
Les arêtes d’un « polyline » peuvent se croiser.
Un « polyline » est défini à partir d’une liste de sommets :
(x0, y0), (x1, y1), …, (xn, yn).
Attributs :
- couleur des arêtes
- épaisseur des arêtes
- trait continu ou discontinu
- la manière avec laquelle les extrémités des arêtes
24
adjacentes sont représentées.
Polygone
polygone : un « polyline » fermé (l’extrémité initiale du 1e segment coïncide
avec l’extrémité terminale du dernier segment)
polygone simple : un polygone où aucune paire d’arêtes ne se croise.
polygone simple
polygone
Note : GL_POLYGON désigne dans la structure glBegin() et glEnd() un
polygone convexe simple.
25
Polygone
Pour déterminer de quelle façon le contour polygonal et l’intérieur du polygone
seront affichés, cela dépend de la valeur des attributs courants :
glPolygonMode(,);
Premier paramètre: GL_FRONT face du polygone du côté de l’observateur
GL_BACK
autre face
ou
GL_FRONT_AND_BACK
les deux.
Deuxième paramètre :
Exemple :
GL_POINT
GL_LINE
GL_FILL
affichage des sommets
affichage du contour
remplissage.
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_LINE);
26
Texte
Il existe habituellement 2 modes d’affichage distincts :
(a) le mode texte :
dans des applications non graphiques, le texte est la norme;
le jeu de caractères est toujours affiché de la même façon.
(b) le mode graphique :
dans des applications graphiques, le texte peut être affiché de
multiples façons :
style, taille, fontes, couleurs, orientation, transformations, etc.
27
2 modes de représentation des
caractères dans une
application graphique
(i) Les caractères sont définis comme d’autres objets graphiques :
Le contour du caractère est approximé par des segments de droite ou courbes.
Si le contour est fermé, l’intérieur du caractère est accessible.
Le caractère peut alors être manipulé comme n’importe quel autre objet graphique
avec tous les avantages qui s’y rattachent.
Une fonte de 256 caractères peut être complexe à réaliser et exiger beaucoup de
mémoires et de temps de calcul.
28
(ii)
Matrice rectangulaire de points (masque)
Chaque caractère est défini à partir d’un bloc rectangulaire de bits 0 ou 1.
L’opération bit-block – transfert « bitblt » permet de transférer le bloc (le masque)
d’un caractère dans la mémoire d’entretien.
- Les transformations sont difficiles à effectuer.
- glRasterPos*() et glBitmap() fixe la position et
affiche un masque sur l’écran.
Masques de caractères
- glReadPixels() lit un bloc rectangulaire de « pixels » de la mémoire d’entretien
à la mémoire du processeur.
- glDrawPixels() lit un bloc rectangulaire de pixels de la mémoire du processeur
à la mémoire d'entretien à la position courante glRasterPos*().
- glCopyPixels() copie un bloc rectangulaire de « pixels » d’une position à l’autre
de la mémoire d’entretien.
29
Images point à point
Chaque image est constituée d’un ensemble de petites cellules appelées « pixels ».
Une image est rangée dans un tableau rect. renfermant des valeurs numériques.
Chaque valeur numérique désigne une teinte de gris ou une couleur quelconque.
Comment ces images sont-elles créées ?
1)
2)
3)
4)
des images conçues manuellement à l’aide de programmes de dessins interactifs
des images générées par un système graphique,
des images numérisées avec un scanner,
des modèles de génération de textures.
30
NOTION DE « VIEWPORT » EN OPENGL
Surface rectangulaire à l’intérieur de la fenêtre de l’écran.
void glViewport(GLint x, GLint y, GLsizei w, GLsizei h);
où
x, y est le coin inférieur gauche du « viewport » déterminé à partir
du coin inférieur gauche de la fenêtre
w, h : hauteur et largeur du rectangle.
Les positions sont spécifiées en entier et les distances en « pixels ».
Par défaut, il s’agit de la fenêtre entière.
L’usage des « viewports » permet :
-
d’éliminer les distorsions à l’écran
(ratios différents pour la fenêtre et le volume de visionnement)
permet d’avoir plusieurs « viewports »
31
(pour afficher des images différentes à des parties différentes de la fenêtre).
Systèmes de coordonnées :
fenêtre et « viewport » 2D
Les objets qui nous intéressent sont définis plus naturellement dans un système de
coordonnées « du monde », propre à votre application.
À l’intérieur de ce système de coordonnées « du monde », une fenêtre rectangulaire
est définie en coordonnées « du monde ».
Les objets ou les parties d’objets à l’intérieur de la fenêtre sont conservés et tout ce
qui est à l’extérieur de la fenêtre est exclu.
On définit aussi un « viewport » (une surface rectangulaire à l’intérieur de la fenêtre
à l’écran) en coordonnées « écran ». C’est à l’intérieur de ce « viewport » que
l’image est affichée.
32
Passage d’un système de
coordonnées à un autre
y
yhaut
syhaut
x
xgauche
sybas
xdroite
sxgauche sxdroite
ybas
Une fenêtre dans le système de
coordonnées du « monde ».
Note :
Un « viewport » en coordonnées « écran ».
Lorsque le rapport « hauteur / largeur » n’est pas le même pour une fenêtre
que pour un « viewport », des distortions peuvent survenir.
33
Transformation linéaire :
un point (x, y) en coordonnées du monde à un point (sx, sy)
en coordonnées écran
Soit
sx = A x + C
et
sy = B y + D.
En respectant les règles de proportionnalité, on obtient :
sx – sxgauche
=
x – xgauche
sxdroite – sxgauche
xdroite - xgauche
ce qui donne
sx =
sxdroite – sxgauche x +
sxgauche - sxdroite – sxgauche xgauche
xdroite – xgauche
xdroite - xgauche
d’où,
A=
sxdroite – sxgauche
xdroite – xgauche
B=
syhaut – sybas
yhaut – ybas
C=
sxgauche – A xgauche
D=
sybas – B ybas
Note : N’importe quel point (x, y) à l’intérieur de la fenêtre nous donne
après transformation un point (sx, sy) à l’intérieur du « viewport ».
Procédé en OpenGL
En OpenGL, ce processus est effectué automatiquement comme suit :
la transformation précédente est appliquée automatiquement à tous les sommets
définis via glVertex2*();
le découpage des objets se fait automatiquement.
Nous avons besoin seulement de spécifier les paramètres de transformation :
void gluOrtho2D(GLdouble gauche,
GLdouble droite,
GLdouble bas,
GLdouble haut);
Permet de définir la fenêtre dont le coin inférieur gauche est (gauche, bas)
et le coin supérieur droit est (droite, haut).
void glViewport( GLint x,
GLint y,
GLint Largeur, GLint Hauteur);
Permet de définir un «viewport» dont le coin inférieur gauche est (x, y)
et le coin supérieur droit est (x + Largeur, y + Hauteur).
Note :
Le « viewport » par défaut est l’écran au complet.
35
Exemple : Tracé de f(x) = sin(x) / (x), 1 x 8
#include <windows.h>
#include <GL/glut.h>
#include <cmath>
void Affichage()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 10.0, -1.0, 1.0);
glViewport(0, 0, 640, 480);
glBegin(GL_LINE_STRIP);
for (GLfloat x = 1.0; x < 8.0; x += 0.1)
glVertex2f(x, sin(3.14159 * x) / (3.14159 * x));
glEnd();
glFlush();
}
36
void main(int argc, char** argv)
{
...
/*
Permet de fixer la dimension de la fenêtre à l'écran
soit, 800 pixels de largeur par 500 de hauteur.
*/
glutInitWindowSize(800, 500);
/*
Permet de fixer la position du coin supérieur gauche
de la fenêtre.
*/
glutInitWindowPosition(500, 100);
...
}
En OpenGL, le découpage est fait automatiquement p/r à la fenêtre du monde.
Ex. :
En optant pour gluOrtho2D(4.0, 6.0, -0.5, 0.5) dans l’exemple précédent,
37
Une portion du tracé ne sera pas affichée.
Effets de zoom
En modifiant les dimensions de la fenêtre du monde, on peut créer des effets de zoom.
Par ex., une fenêtre plus petite permet de mettre l’emphase sur une partie d’un objet;
de plus, si le « viewport » ne change pas, cette partie de l’objet se trouve agrandie.
On peut ainsi créer des animations.
Comment fixer la dimension d’une fenêtre pour inclure un objet au complet ?
Une façon de faire est de déterminer une surface rectangulaire d’aire minimale
renfermant l’objet.
1558 polygones
38
Exemple en OpenGL
Affichage de plusieurs motifs de dinosaures
void Affichage()
{
...
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
{
glViewport(i * 64, j *44, 64, 44);
--Affichage d’un motif de dinosaure –
F.S. Hill, 2001
}
glFlush();
}
39
Distorsion d’une figure
Pour éviter les distorsions d’une figure en passant d’un système de coordonnées du
monde à celui de l’écran tout en occupant la plus grande surface de l’écran,
considérons les 2 cas suivants :
soient
R = largeur de la fenêtre du monde / hauteur de la fenêtre du monde,
L = largeur de la fenêtre de l’écran
H = hauteur de la fenêtre de l’écran,
1er cas :
R>L/H
Fenêtre du monde (R)
viewport
H
L/R
L
glViewport(0, 0, L, L / R);
2ième cas : R < L / H
Fenêtre du monde (R)
H
HR
glViewport(0, 0, H * R, H);
viewport
L
40
Il peut arriver aussi que l’usager modifie les dimensions de la fenêtre à l’écran en
faisant glisser un de ses sommets à l’aide de la souris.
L’événement généré est pris en compte grâce à la commande :
glutReshapeFunc(Reconfiguration);
que l’on retrouve dans la fonction main().
La fonction « Reconfiguration » doit permettre d’identifier un nouveau « viewport »
à l’intérieur de la nouvelle fenêtre à l’écran tout en conservant le ratio R de la fenêtre
du monde.
void Reconfiguration (GLsizei L, GLsizei H)
{
if (R > L / H)
glViewport(0, 0, L, L / R);
else
glViewport(0, 0, H * R, H);
}
Lorsque la fonction « Reconfiguration » est exécutée, le système lui transmet
automatiquement la nouvelle largeur (L) et hauteur (H) de la fenêtre à l’écran.
------------------------------------------------------------------------------------41
Exemple : Reprise de l’exemple précédent.
GLfloat R = (10.0 - 0.0) / (1.0 - -1.0);
void Affichage()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 10.0, -1.0, 1.0);
glBegin(GL_LINE_STRIP);
for (GLfloat x = 1.0; x < 8.0; x += 0.1)
glVertex2f(x, sin(3.14159 * x) / (3.14159 * x));
glEnd();
glFlush();
}
void Reconfiguration (GLsizei L, GLsizei H)
{
if (R > L / (GLfloat) H) glViewport(0, 0, L, L / R);
else
glViewport(0, 0, H * R, H);
}
42
Définition d’une fonction « Initialisation »
Dans un programme d’application, on retrouve habituellement une fonction
« Initialisation » permettant d’initialiser les variables d’état d’OpenGL si les
valeurs par défaut ne nous satisfont pas.
Celles-ci peuvent aussi être modifiées dans la fonction « Affichage » à chaque fois
que la fenêtre est redessinée.
Lorsque ces valeurs des variables d’état ne sont pas modifiées au cours de
l’application, il est souhaitable de procéder à l’intérieur d’une telle fonction
d’initialisation pour des raisons de modularité et d’efficacité.
43
Exemple :
#include <windows.h>
#include <GL/glut.h>
#include <math.h>
/*
Tracé de la courbe de Sierpinski.
3 points (x1, y1), (x2, y2) et (x3, y3) dans le plan sont spécifiés.
Procédé :
(1) générer aléatoirement un point à l'intérieur du triangle.
(2) choisir l'un des 3 sommets du triangle aléatoirement.
(3) Trouver le point-milieu entre le point initial et le sommet choisi aléatoirement.
(4) Afficher ce nouveau point à la position obtenue (un petit cercle par exemple).
(5) Remplacer le point initial généré par ce nouveau point.
(6) Retourner à l'étape 2.
*/
44
void Initialisation(void)
{
//
Permet d'initialiser la couleur de fond RVB noire.
glClearColor(0.0, 0.0, 0.0, 0.0);
/*
Permet de fixer la couleur d'affichage rouge.
glColor3f(1.0, 0.0, 0.0);
/*
OpenGL possèdent 2 matrices, MODELVIEW et PROJECTION
pour représenter respectivement les transformations ponctuelles et
les transformations visuelles courantes.
On opte ici pour la matrice de projection.
*/
glMatrixMode(GL_PROJECTION);
/*
Permet d'initialiser la matrice de transformation courante soit, la
matrice de projection dans ce cas-ci à la matrice identité.
*/
glLoadIdentity();
/*
Permet de spécifier la surface de visualisation. La signification
des paramètres est :
gauche, droite, bas, haut.
*/
gluOrtho2D(0.0, 800.0, 0.0, 500.0);
}
45
void Affichage( void )
{
typedef GLfloat point2[2];
point2 vertices[3] = {{10.0,10.0},{300.0,30.0},{200.0,300.0}};
int j, k;
long random();
point2 p = {10.0, 10.0};
glClear(GL_COLOR_BUFFER_BIT);
for ( k=0; k<5000; k++)
{
j=rand()%3;
p[0] = (p[0]+vertices[j][0])/2.0;
glBegin(GL_POINTS);
p[1] = (p[1]+vertices[j][1])/2.0;
glVertex2fv(p);
glEnd();
}
glFlush();
}
46
void main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitWindowSize(800, 500);
glutInitWindowPosition(500, 100);
glutCreateWindow("Courbe de Sierpinski");
glutDisplayFunc(Affichage);
Initialisation();
glutMainLoop();
}
47
Modélisation 3D et visualisation
Le passage du 2D au 3D nécessite 2 matrices en OpenGL représentant les
transformations courantes suivantes à appliquer aux objets :
GL_MODELVIEW : (par défaut)
elle est unique même si on peut la voir comme le produit de 2 matrices :
° les transformations à appliquer aux objets dans l’espace du « monde »,
° celles permettant de passer du repère des objets à celui de la caméra.
GL_PROJECTION :
° les transformations permettant de définir la forme et l’orientation
du volume de visualisation et de projeter les objets dans le plan de vue.
Chaque changement à l’une de ces matrices demeure actif tant et aussi
longtemps qu’un autre changement n’a pas eu lieu.
Initialement, ce sont des matrices identité. Celles-ci sont mises à jour à partir
d’une suite de transformations (fonctions d’OpenGL).
Lorsque nous voulons appliquer une transformation, nous devons choisir le
mode « GL_MODELVIEW » ou « GL_PROJECTION ».
48
Projections parallèles
orthographiques
La projection parallèle orthographique d’un point (x, y, z) nous donne
(x, y, 0).
Le volume de visualisation est un parallélépipède.
y
x
z=0
z
49
Projections parallèles orthographiques
En OpenGL, cette projection est spécifiée comme suit :
void glOrtho(
GLdouble gauche, GLdouble droite,
GLdouble bas, GLdouble haut,
GLdouble proche, GLdouble loin)
Le plan z = 0 doit se situer entre proche et loin.
Ce volume de visualisation (parallélépipède) sert aussi de volume de
découpage. La procédure de découpage est alors effectuée automatiquement
par OpenGL.
On peut aussi utiliser
void gluOrtho2D(GLdouble gauche, GLdouble droite,
GLdouble bas, GLdouble haut)
laquelle est équivalente à la fonction 3D avec proche = -1 et loin = 1.
50
Exemple :
#include <windows.h>
#include <GL/glut.h>
#include <math.h>
const int L = 800;
const int H = 500;
const GLfloat xmin = 1.0f;
const GLfloat xmax = 5.0f;
const GLfloat ymin = 1.0f;
const GLfloat ymax = 5.0f;
const GLfloat zmin = -1.0f;
const GLfloat zmax = 1.0f;
const GLfloat precision = 0.03f;
// Largeur de la fenêtre en pixels.
// Hauteur de la fenêtre en pixels.
// Intervalle de variation de x.
// Intervalle de variation de y.
// Intervalle de variation de F(x, y).
GLfloat F(GLfloat x, GLfloat y)
{
return sin(2 * 3.14159265 * x) * cos(2 * 3.14159265 * y);
}
51
void Initialisation(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glColor3f(1.0, 0.0, 0.0);
// Une rotation de 45° autour de l'axe (1,1,0) est appliquée à la surface.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(45.0, 1.0, 1.0, 0.0);
/*
Permet de spécifier une projection orthographique où le volume de
visualisation est un parallélépipède. La signification des paramètres
est :
gauche, droite, bas, haut, proche et loin.
*/
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(xmin-1, xmax+1, ymin-1, ymax+1, zmin-1, zmax+1);
}
52
void Affichage( void )
{
glClear(GL_COLOR_BUFFER_BIT);
for (GLint i = 0; i <= (int)((xmax - xmin) / precision); i++)
for (GLint j = 0; j <= (int)((ymax - ymin) / precision); j++)
{
GLfloat x, y;
x = xmin + i * precision; y = ymin + j * precision;
glBegin(GL_LINE_LOOP);
glVertex3f(x, y, F(x, y));
glVertex3f(x, y + precision, F(x, y + precision));
glVertex3f(x + precision, y + precision,
F(x + precision, y + precision));
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3f(x, y, F(x, y));
glVertex3f(x + precision, y + precision,
F(x + precision, y + precision));
glVertex3f(x + precision, y, F(x + precision, y));
glEnd();
};
glFlush();
53
}
Élimination des parties cachées
Dans l’exemple précédent, l’affichage de la fonction z = F(x, y) a été réalisé en
mode « fil de fer ».
En optant plutôt pour un mode par remplissage « GL_FILL », il faut procéder à
l’élimination des parties cachées de la scène.
Au départ, un tableau doit être initialisé à la distance maximale possible en se référant
au plan de découpage le plus éloigné de l’observateur. Cette initialisation est faite
grâce à la commande glClear() avec le paramètre GL_DEPTH_BUFFER_BIT.
Pour pouvoir utiliser ce tableau de distances, les déclarations initiales suivantes sont
aussi nécessaires :
glutInitDisplayMode(GLUT_DEPTH);
choix d’une matrice de profondeurs pour l’élimination
des parties cachées
glEnable(GL_DEPTH_TEST);
l’élimination des parties cachées est réalisée.
54
Exemple précédent en mode remplissage :
La fonction « Initialisation » demeure
la même.
void Affichage( void )
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for (GLint i = 0; i < (int)((xmax - xmin) / precision); i++)
for (GLint j = 0; j < (int)((ymax - ymin) / precision); j++)
{
GLfloat x, y;
x = xmin + i * precision; y = ymin + j * precision;
glBegin(GL_POLYGON);
glVertex3f(x, y, F(x, y));
glVertex3f(x, y + precision, F(x, y + precision));
glVertex3f(x + precision, y + precision,
F(x + precision, y + precision));
glEnd();
glBegin(GL_POLYGON);
glVertex3f(x, y, F(x, y));
glVertex3f(x + precision, y + precision,
F(x + precision, y + precision));
glVertex3f(x + precision, y, F(x + precision, y));
glEnd();
};
55
glFlush();
}
void main(int argc, char** argv)
{
glutInit(&argc, argv);
/*
On réserve un bloc de mémoire nécessaire pour résoudre
le problème d'élimination des parties cachées.
*/
glutInitDisplayMode (GLUT_DEPTH);
/*
Cette fonction permet de rendre active une option d'OpenGL
spécifiée en paramètre. Dans le cas présent, l'élimination des parties
cachées sera effectuée.
*/
glEnable(GL_DEPTH_TEST);
glutInitWindowSize(L, H);
glutInitWindowPosition(500, 100);
glutCreateWindow("Trace de la fonction F(x, y)");
glutDisplayFunc(Affichage);
Initialisation();
glutMainLoop();
}
56
Effets d’animation
Pour créer une animation, on peut procéder en répétant les étapes suivantes :
(a)
on efface l’écran
(b)
on affiche de nouveau la figure modifiée.
Or, l’étape (b) peut être lente et l’usager peut voir l’affichage se faire graduellement.
Il serait préférable de procéder comme suit :
(a) Tracer ailleurs une nouvelle figure tandis que l’usager observe la figure courante.
(b) Remplacer instantanément la figure courante par la nouvelle figure complétée.
Pour y arriver, OpenGL utilise une mémoire tampon supplémentaire lors des
communications (« double buffering »).
Cela est rendu possible grâce au paramètre GLUT_DOUBLE qui remplace le
paramètre GLUT_SINGLE lors de l’initialisation du mode d’affichage :
glutInitDisplayMode(GLUT_DOUBLE);
57
Effets d’animation
Tous les tracés se font sur cette mémoire tampon supplémentaire lesquels sont
invisibles à l’écran.
La commande
glutSwapBuffers();
transfert l’image à l’écran et l’image devient alors visible à l’usager.
Exemple :
for(int i = 0; i < Longueur_sequence; i ++)
{
Effacer l’écran;
Définir la fenêtre et le viewport;
Tracé de la figure;
glutSwapBuffers();
}
58