Infographie et bibliothèques graphiques

1
Chapitre II : Infographie et bibliothèques graphiques
Le premier chapitre a posé les bases perceptives et techniques de la production et de
l'affichage d'images, ou plus globalement d'objets graphiques. Nous nous penchons
maintenant sur les moyens concrets qui sont à notre disposition afin de mettre en place ces
manipulations de façon logicielle, plus précisément du point de vue de la programmation et de
l'interfaçage. Nous allons introduire les concepts généraux de l'infographie pour nos focaliser
sur des aspects bidimensionnels et la présentation de la bibliothèque OpenGL.
II.1 – Les outils et techniques de base en infographie
Le domaine technique que nous abordons s'appelle l'infographie, acronyme d'informatique
graphique. Cette discipline existe maintenant depuis plus de 30 ans et a été à la base de tous
les développements en image de synthèse, réalité virtuelle et multimédia
II.1.a : Le système graphique
On appelle système graphique, un ensemble de composantes matérielles et logicielles
permettant de produire des dessins et des images par ordinateur. Un tel système doit permettre
l'entrée, le traitement et la sortie d'informations de nature graphique. Il peut être représenté par
un modèle comme celui de la Figure 2.1. Un modèle de système graphique où les parties
matérielles et logicielles ne sont pas indiquées.
Module
d'application
Module
géométrique
Module
d'entrée
Affichage
Figure 2.1 : Un modèle de système graphique
Infographie et bibliothèques graphiques
2
Dans ce modèle, on distingue quatre modules principaux:
• le module d'entrée: il est responsable de l'entrée des dessins et comprend normalement
des dispositifs d'entrée tels qu'une tablette graphique, par exemple, et le logiciel de
contrôle de ces dispositifs.
• le module géométrique: il est formé essentiellement de logiciel et son rôle est de créer
et de manipuler des objets graphiques; il faut noter que les stations graphiques les plus
récentes offrent de plus en plus de facilités matérielles pour créer et manipuler des
objets graphiques: rotations matérielles, générateur de cercles.
• le module d'affichage: il est responsable de la sortie des dessins et comprend le
matériel de sortie, tel que les écrans, les imprimantes ainsi que le logiciel d'affichage
qui peut aller du simple ensemble de sous-programmes de traçage de lignes jusqu'au
logiciel super sophistiqué de synthèse d'images avec multiples sources de lumière,
ombre portée, transparence.
• le module d'application: ce module est celui orienté vers l'usager; il diffère
évidemment suivant le type d'application et se présente généralement comme un
programme interactif. Ce modèle est évidemment théorique et pratiquement, on peut
plutôt considéré qu'on a un ensemble de dispositifs matériels d'entrée et de sortie, un
logiciel graphique de base et un logiciel d'application. Le logiciel graphique de base
comprend généralement des opérations d'entrée, de sortie et de création et
manipulation d'objets graphiques.
II.1.b : Les dispositifs d'entrée graphique
Ces dispositifs ont deux fonctions principales: l'entrée des objets graphiques et la désignation
des objets graphiques. On distingue normalement 5 sortes de dispositifs d'entrée graphique:
• les locateurs qui permettent d'entrer une position ou une suite de positions. Les
principaux dispositifs de ce type sont :
- la tablette graphique, surface rectangulaire accompagnée d'un crayon qui
permet de donner à l'ordinateur la position de ce crayon sur la tablette.
- la souris, dispositif se déplaçant sur roulettes ou glissant sur une surface plane;
un ou plusieurs boutons permettent d'entrer une position correspondant à
l'emplacement de cette souris
- le "trackball" formé d'une balle que l'on peut faire tourner dans toutes les
directions avec la paume de la main pour indiquer les déplacements que l'on
souhaite
- le "joystick", sorte de tige que l'on peut aussi mouvoir dans toutes les
directions pour indiquer un déplacement
• les instruments de désignation (pick) qui permettent de pointer un objet; le plus connu
est le photostyle ou (light pen : crayon optique) qui est une sorte de crayon qui détecte
la lumière. Ainsi en le déplaçant sur la surface d'un écran, on peut pointer des objets
dessinés à l'écran et un signal est envoyé à l'ordinateur, ce qui permet de recueillir les
coordonnées de l'objet pointé.
• les valuateurs (ou boites à boutons) qui permettent d'entrer une valeur numérique
comme un angle, une taille ou un rayon; ce sont typiquement les potentiomètres qui se
Infographie et bibliothèques graphiques
3
présentent généralement sous la forme de boutons que l'on peut tourner pour faire
varier la valeur choisie.
• les claviers qui permettent d'entrer des objets en tapant des commandes. Leur
ergonomie interactive est discutable, mais certains modèles évolués (notamment au
niveau de la forme) sont particulièrement intéressants.
Signalons encore les possibilités d'entrer des images à partir d'une caméra vidéo. Néanmoins,
dans ce cas on se trouve confronté à des problèmes d'analyse d'images relevant du domaine du
traitement d'images et de la reconnaissance de formes.
Il existe également des dispositifs tridimensionnels très évolués basés sur des capteurs, qui
seront présentés en détail dans la partie consacrée à la réalité virtuelle.
II.1.c : Les dispositifs graphiques de sortie
Les principaux dispositifs de sortie sont les écrans de visualisation. Même s'il en existe
d'autres variétés comme les écrans à plasma, les deux principaux types sont les écrans
calligraphiques et les écrans à balayage récurrent (raster scan).
• Dans les écrans calligraphiques, les images sont produites par une suite de segments
de droite, ce qui a l'avantage de produire des lignes de très bonne qualité, mais rend
difficile le remplissage de polygones. La technologie repose sur les écrans à tube
cathodique dont le principe illustré à la figure 2.2 est le suivant :

Figure 2.2 : Principe du tube cathodique
Un canon à électrons (émission thermo-électronique) émet un faisceau d'électrons à
haute vitesse. Un dispositif de concentration permet d'assurer la convergence du
faisceau. Un dispositif de déviation permet de commander la position d'impact sur
l'écran. Ce dernier est fait de matériaux luminescents et fonctionne selon le principe
photoélectrique. Les électrons incidents provoquent une excitation des électrons de la
matière de l'écran qui en revenant au repos provoque l'émission d'un photon (émission
lumineuse).
• Les écrans à balayage récurrent, que nous nommerons plus simplement écrans raster,
sont proches d'un poste de télévision mais ils sont munis d'une mémoire d'image
(frame buffer) qui permet de stocker l'image. Cette mémoire se présente comme une
matrice d'informations. La taille de la matrice correspond à la résolution du terminal et
chaque information est une élément d'image ou pixel. Pour chaque pixel, on a un
Infographie et bibliothèques graphiques
4
nombre de bits, ce qui fixe les possibilités de couleurs pour ce pixel. Par exemple, si
on a 8 bits par pixel, on pourra colorier le pixel selon 256 couleurs différentes. En fait,
beaucoup de terminaux ont un grand nombre de couleurs à choix, et la valeur d'un
pixel est une adresse dans une table de couleurs choisies parmi toutes les couleurs
disponibles. Par exemple, la plupart des stations Silicon Graphics ont une résolution
de 1280x1024 avec 24 bits pour la couleur, ce qui permet environ 16.7 millions de
couleurs différentes.
Le principal défaut des terminaux raster est la mauvaise qualité du tracé de lignes
droites. En effet, comme on peut le voir à la figure 2.3, les droites sont formées d'une
suite de pixels, ce qui cause des effets d'escalier ou aliasing. On peut y remédier par
des techniques d'antialiasing, mais elles sont souvent coûteuses en temps de
traitement.
Figure 2.3 : Maison a) sur un terminal calligraphique b) sur un terminal raster
II.1.d : Les logiciels graphiques
Ces logiciels peuvent se présenter sous différentes formes :
• Les ensembles de sous-programmes pouvant être "appelés" depuis un langage de
programmation comme C. Il s'agit de bibliothèques ou librairies de programmation. Le
principal défaut est l'absence de syntaxe dans la construction des objets. La plus
connue à l’heure actuelle sont OpenGL, dérivée de la Graphics Library de Silicon
Graphics (SGI). Depuis quelques années et l'avènement de Windows 95, 98 et consort,
on lui connaît un sérieux concurrent en la "personne" de DirectX
• Langages purement graphiques. Pendant longtemps, aucun n'a eu de réel succès, car
cela nécessite d'apprendre un nouveau langage et se posait le problème de la mise en
place d'un "norme consensuelle". Le problème a été quasiment réglé avec l'apparition
de VRML, dont nous reparlerons dans le chapitre dédié à la réalité virtuelle.
• Systèmes interactifs: ils regroupent tous les programmes d'application, les systèmes de
modélisation et les éditeurs graphiques. On peut citer, par exemple, les logiciels de
synthèse d'images de Alias-Wavefront, de Softimage et de StudioMax (sans doute le
plus célèbre avec 3D StudioMax).
Infographie et bibliothèques graphiques
5
II.2 – Objets et transformations de base
Du monde réel (ou continu) en deux ou trois dimensions jusqu'à sa représentation (son image
?) sur un support quelconque, il est un long chemin dont les différentes étapes sont appelées
transformations. Nous allons – rapidement – les présenter ici. Nous y associerons également
les différentes entités qu'un système graphique doit savoir gérer : les objets.
II.2.a : 2D versus 3D
L'être humain vit dans un monde à trois dimensions, mais lorsqu'il dessine, il utilise
généralement des feuilles de papier qui n'ont que deux dimensions. Il se trouve donc
confronté à un problème de représentation en deux dimensions d'un monde à trois. Deux
solutions s'offrent alors:
• représenter seulement une face plane des objets, par exemple la façade avant d'une maison
ou le dessus d'une table
• tenter de dessiner la scène choisie en tenant compte de lois de projection telle que la
perspective.
En informatique graphique, comme les supports matériels (écrans) sont à deux dimensions,
ces deux approches se retrouvent et donnent lieu à deux types de modélisation, de systèmes
graphiques et d'applications.
On dira qu'un système graphique est à deux dimensions (2D) si la représentation interne de
l'information graphique dans l'ordinateur est à deux dimensions. Un système graphique sera à
trois dimensions (3D) lorsque l'ordinateur a connaissance de l'information tridimensionnelle.
Cette distinction est fondamentale. En effet, lorsqu'on voit une image produite par ordinateur
d'une maison en perspective, il est impossible de savoir si l'image a été produite avec un
système à 2 ou à 3 dimensions. En effet, la maison a pu être dessinée en perspective et fournie
ainsi à un système graphique à 2 dimensions qui s'est contenté de la restituer ou la vue en
perspective a été synthétisée par un système tridimensionnel à partir de données
tridimensionnelles. Ceci nous amène à préciser que lorsque nous parlerons d'images
tridimensionnelles, il s'agira toujours d'images produites à partir d'un modèle tridimensionnel
connu de l'ordinateur et non d'images réellement en trois dimensions, telles que celles
produites par des techniques comme l'holographie ou la stéréoscopie.
Il faut aussi remarquer que l'espace à deux dimensions peut être considéré comme un cas
particulier d'espace à trois dimensions dont la troisième dimension Z est toujours nulle.
Infographie et bibliothèques graphiques
6
Figure 2.4 : Systèmes de coordonnées a) 2D b) 3D
Néanmoins, au niveau de ce chapitre, et afin d'introduire plus sereinement la partie liée à
l'analyse d'images et afin d'appréhender simplement la bibliothèque OpenGL, nous nous
restreindrons à des univers 2D.
II.2.b : Points, vecteurs, segments, …
Le point (ou vertex)
Il s'agit évidemment de l'objet graphique le plus simple. Il est caractérisé par ses coordonnées
cartésiennes (x, y, z). Il est potentiellement à la base de tous les autres objets
Les vecteurs
Nous utiliserons une notation semblable aux vertex pour représenter les vecteurs qui jouent un
rôle fondamental en informatique graphique. Un vecteur sera considéré comme la direction
donnée par la flèche reliant l'origine du système d'axes au point donné par les composantes du
vecteur.
Il est toujours intéressant de rappeler les opérations de base sur les vecteurs, à savoir :
• Norme :
2 2 2
u u u
z y x u + + =
• Addition : ( )
v u v u v u
z z y y x x v u + + + = +
• Produit scalaire : ) cos(α ⋅ ⋅ = ⋅ v u v u où α est l'angle entre les 2 vecteurs il faut
noter que le résultat est un nombre réel et que dans un système orthonormé, on a :
v u v u v u
z z y y x x v u ⋅ + ⋅ + ⋅ = ⋅ , ce qui permet bien sûr de calculer α.
• Produit vectoriel : ( ) u v v u v u u v v u v u z x z x z x z x y z z y v u ⋅ − ⋅ ⋅ − ⋅ ⋅ − ⋅ = ∧ . L'intérêt principal du
produit vectoriel est qu'il fournit un vecteur perpendiculaire au plan des deux
vecteurs intervenant dans le produit. On peut encore noter que :
) sin(α ⋅ ⋅ = ∧ v u v u où α est l'angle entre les 2 vecteurs
Droites et segments
La droite est une figure très courante bien que l'on utilise plutôt le segment de droite. La
différence est simple, une droite passe par deux points, tandis qu'un segment de droite est
limité par 2 points. Pour tracer un segment de droite AB, nous utiliserons deux instructions,
une pour se positionner au point A et une pour tracer le segment de A à B.
Polygones
Que l'on travaille en deux dimensions ou en trois, le polygone joue un rôle extrêmement
important. Sa définition n'est pas toujours rigoureuse et varie selon les auteurs. Nous
entendons par polygone une figure plane définie par une liste de points (les sommets) reliés
par des segments de droite (les arêtes). Les sommets sont supposés tous différents, les arêtes
Infographie et bibliothèques graphiques
7
ne doivent pas se croiser et une arête relie le dernier sommet au premier. Un polygone est
concave s'il existe au moins un angle interne supérieur à 180°; il est convexe, s'il n'est pas
concave.
Des algorithmes particuliers existent permettant de "remplir" (colorier) un polygone.
Cependant, il n'ont pas d'intérêt particulier dans le cadre de ce cours. On se réfèrera donc aux
ouvrages de base cités en référence.
Les objets complexes
Il s'agit le plu souvent de courbes et surfaces de forme libre. Leur définition sera vue dans un
chapitre qui y sera entièrement consacré.
II.2.c : Les transformations de base
Une fois que l'on a construit des objets graphiques, on désire généralement les manipuler,
c'est-à-dire changer leurs attributs. Considérons donc les attributs d'un objet graphique, on
peut relever :
• la position
• l'orientation
• la taille
• la forme
• la couleur
• la transparence
• la texture
La forme est un attribut particulier et sa modification peut s'avérer très compliquée. Nous
avons déjà parlé de la couleur. La transparence et la texture sont des attributs seulement
présents dans les images 3D "réalistes" qui seront dans le chapitre 8.
Les trois premiers attributs (position, orientation, taille) ont en commun qu'ils peuvent être
modifiés par des transformations dites ponctuelles. Ainsi :
• on modifie la position par des translations
• on modifie l'orientation par des rotations
• on modifie la taille par des transformations d'échelle
Ces transformations sont ponctuelles car elles s'appliquent sur tout point (vertex) P de l'objet
pour
donner un nouveau point P'. On peut donc définir chaque transformation par la relation
permettant de passer de P à P' en fonction de leur coordonnées respectives.
OpenGL implémente largement ces transformations ponctuelles. Aussi, nous passerons
rapidement sur celles-ci et il suffit de savoir qu'elles sont pratiquement réalisées par le biais
de produits matriciels.:
Infographie et bibliothèques graphiques
8
II.2.d : Les transformations visuelles
Même si on est capable de modéliser un objet dans l'ordinateur et de le transformer, il n'en
reste pas moins que pour le voir sur un écran graphique, il faut passer de l'espace de l'usager
(généralement tridimensionnel) à l'espace de l'écran. Non seulement ce dernier est à deux
dimensions, mais les constructeurs de matériel graphique se sont ingéniés à définir des
espaces d'adresses compliqués et très différents d'un modèle à l'autre.
Le but des transformations visuelles est donc de passer de l'espace de l'usager à celui du
dispositif graphique. Nous commencerons par le cas simple d'un monde de l'usager à deux
dimensions. Ceci nous amène à définir deux concepts fondamentaux: la fenêtre et la clôture.
Pour ne pas s'occuper de l'espace d'adresses d'un terminal particulier. nous allons considérer
que l'écran se représente par un rectangle dont le sommet inférieur gauche est à <0,0> et le
sommet supérieur droit est à <1,1>. Ainsi toute portion de l'écran que nous utiliserons sera
nécessairement limitée par des vecteurs de composantes comprises entre 0 et 1. Cette portion
du dispositif graphique est appelée la clôture.
Dans la pratique, on utilise des cordonnées entières, liées à la résolution de l'écran ou la taille
de la fenêtre (sorte d'écran virtuel) dans laquelle on souhaite afficher la scène. Notre monde
réel, supposé à deux dimensions, est évidemment illimité et nous devons spécifier quelle
portion du monde réel nous voulons représenter. Cette portion est un rectangle, nommé
fenêtre, et nous le donnerons par son sommet inférieur gauche et son sommet supérieur droit.
C'est ainsi que le contenu de la fenêtre sera toujours représenté dans la clôture.
Figure 2.5 : Passage de la fenêtre à la cloture
En considérant la figure 2.5, on peut montrer comment on passe d'un point P dans la fenêtre à
un point P' dans la clôture :
Les points FB et FH représentent les points extrêmes, respectivement haut et bas, de la
fenêtre. De façon similaire, le points CB et CH représentent les points extrêmes,
respectivement haut et bas, de la clôture. On a les relations suivantes :
P'
x
= (CH
x
- CB
x
).(P
x
- FB
x
) / (FH
x
- FB
x
) + CB
x
P'
y
= (CH
y
- CB
y
).(P
y
- FB
y
) / (FH
y
- FB
y
) + CB
y
Infographie et bibliothèques graphiques
9
II.2.e : Coupage selon la fenêtre
Nous avons encore un problème à résoudre: comment couper les objets qui sortent de la
fenêtre? Il s'agit, dans le cas de dessins en lignes, de couper chaque segment selon les bords
de la fenêtre comme le montre la figure 2.6.
Figure 2.6 : Coupage d'un segment a) avant b) après
La méthode la plus connue pour effectuer cette opération est la méthode de Cohen-
Sutherland. Cet algorithme est basé sur une division du plan en 9 régions dont la région
centrale est la fenêtre. La figure 2.7 nous montre cette division.
Figure 2.7 : Les 9 régions dans l'algorithme de Cohen-Sutherland
Nous avons donc passé en revue les outils de base qui vont nous permettre d'afficher des
scènes 2D sur un écran informatique. Si il est possible, à partir de primitives très simples
(allumer un point, tracer un segment) de programmer sa propre librairie de fonctions
graphiques (fenêtre, clôture, transformations, …), le plus simple reste d'utiliser une
bibliothèque existante et robuste. C'est encore plus vrai si celle-ci est devenu un standard dans
le domaine. C'est le cas de la bibliothèque OpenGL que nous allons présenter ici.
II.3 – La bibliothèque graphique OpenGL
II.3.a : Présentation - Historique
OpenGL est sans aucun doute l'interface de programmation industrielle prédominante pour le
développement d'application graphique en 2 et 3D. Elle peut être considérée comme le
successeur de la formidable bibliothèque Silicon Graphics IRIS GL qui a rendu si populaire
les stations de travail SGI en tant que plate-formes de prédilection pour les scientifiques, les
ingénieurs et les effets spéciaux. SGI a amené dans OpenGL une grande part de son
expérience pour en faire une interface de programmation du futur, facile à utiliser, intuitive,
Infographie et bibliothèques graphiques
10
portable et ouverte aux réseaux. On peut aussi remercier SGI d'avoir réalisé l'importance des
standards ouverts. Plusieurs fabricants de logiciels et de matériels ont participé à l'élaboration
de spécifications d'OpenGL et continuent à la supporter. Grâce à cela, les applications
OpenGL peuvent facilement être portées sur n'importe quelle plate-forme du marché, depuis
les PC Windows 95 en passant par notre glorieux système Linux, les stations de haut de
gamme UNIX jusqu'aux puissants supers-ordinateurs. Le Bureau Architectural de Revue
supervise les spécifications OpenGL en acceptant ou rejetant des changement et en proposant
des tests de conformité.
Par opposition à l'ancienne bibliothèque GL de SGI, OpenGL est par conception,
indépendante de la machine et du système d'exploitation. Elle reconnaît les réseaux ce qui
permet de séparer notre application OpenGL en un serveur et un client qui est chargé
d'afficher le graphique. Il existe un protocole de transfert des commandes OpenGL sur le
réseau entre serveur et client. Grâce à son indépendance vis à vis du système d'exploitation,
serveur et client n'ont pas à fonctionner sur le même type de plate-forme. Assez souvent, le
serveur est un super-ordinateur qui exécute une simulation complexe, et le client une simple
station de travail dédiée à la visualisation graphique. OpenGL permet aux développeurs
d'écrire des applications qui peuvent facilement être réparties sur plusieurs plates-formes.
En dessous d'OpenGL, est située une bibliothèque de visualisation graphique très performante
et optimisée. Beaucoup de cartes graphiques accélérées et spécialisée en 3D comprennent les
primitives OpenGL au niveau matériel. Jusqu'à très récemment, ces cartes graphiques
évoluées étaient très chère et disponibles uniquement pour les stations SGI ou UNIX. Les
choses changent rapidement et grâce au licences généreuses et au kit de développement de
pilotes de Silicon Graphics, nous voyons de plus en plus de matériel OpenGL pour les
utilisateurs de PC.
Dans OpenGL, on ne trouve que des primitives géométriques (points, lignes, et polygones).
Le développeur doit construire son propre modèle à partir de ces primitives. Des
bibliothèques dépendantes d'OpenGL fournissent des modèles plus complexes et chaque
utilisateur peut construire sa propre bibliothèque à partir de celles là. Dans la cadre de ce
cours nous utiliseront l'interface C car c'est la plus populaire. Toutefois, il faut savoir que des
liens existent avec d'autres langages : FORTRAN, C++, Ada et Java.
II.3.b : Un aperçu des possibilités d'OpenGL
Sans trop entrer dans les détails, voici quelques possibilités d'OpenGL (nous réserverons les
fonctionnalités 3D pour la fin de ce cours):
• Les Primitives Géométriques : Elles permettent de construire des descriptions
mathématiques d'objets à partir de points, lignes, polygones, images et bitmaps.
• Le codage de couleur en RGBA (Rouge-Vert-Bleu-Alpha) ou en mode index de
couleur.
• La visualisation et le modelage permettent d'arranger les objets dans une scène
tridimensionnelle, de bouger les caméras dans l'espace et de choisir le point de vue
d'ou sera visualisé la scène.
• La projection de texture apporte du réalisme au modèle en donnant un aspect réaliste
au surfaces des polygones.
Infographie et bibliothèques graphiques
11
• L'éclairage des matériaux est une partie indispensable de tout infographie 3D.
OpenGL fournit les commandes pour calculer la couleur de n'importe quel point à
partir des propriétés des matériaux et des sources de lumières dans la pièce.
• Le double-tampon (également appelé double-buffer) élimine les clignotement dans les
animations. Chaque image de l'animation est construite dans une mémoire tampon
séparée et affichée quand le tracé est terminé.
• L'anti-repliement (ou antialiasing) réduit les lignes en zigzag sur les écrans. Elles
apparaissent surtout en basse résolution. L'anti-repliement est une technique classique
qui consiste à modifier la couleur et l'intensité des pixels en bordure de ligne pour
atténuer l'effet de zigzag.
• L'ombrage de Gouraud est une technique qui applique des ombrages réguliers aux
objets 3D et donne de subtiles différences de couleurs sur la surface.
• Le tampon de profondeur (Z-buffer) mémorise la coordonnée Z d'un objet 3D. Le
tampon sert à mémoriser la proximité d'un objet par rapport à l'observateur. Sa
fonction est aussi cruciale pour la suppression des parties cachées
• Les effets atmosphériques comme le brouillard, la fumée et le voilage rendent les
images numériques plus réalistes. Sans ces effets, les images apparaissent parfois trop
piquées et top bien définies. Fog est un terme qui décrit un algorithme qui simule la
brume, le crachin, la fumée, la pollution ou simplement l'air en ajoutant de la
profondeur à l'image.
• Le mélange Alpha utilise la valeur Alpha (valeur de diffusion du matériau) du code
RGBA afin de combiner la couleur d'un fragment en cours de traitement avec celle
d'un pixel déjà stocké dans le tampon d'image. Imaginez par exemple devoir tracer une
fenêtre transparente bleue devant une boite rouge. Le mélange Alpha permet de
simuler la transparence de la fenêtre de telle sorte que la boite vue à travers la vitre
soit violette.
• Les plans masqués restreignent le tracé à certaines portions de l'écran
• Les listes d'affichage permettent le stockage de commandes de tracé dans une liste
pour un affichage ultérieur. Avec une utilisation correcte, les listes d'affichages
peuvent améliorer nettement les performances.
• Les évaluateurs polynomiaux supportent les B-Splines non uniformes rationnelles.
Cela permet de tracer des courbes régulières avec quelques points ce qui évite d'en
stocker de nombreux intermédiaires.
• Les primitives d'affichage (Octets de l'affichage et rectangles de pixels)
• Les opérations sur les pixels
• Les transformations: rotation, échelles, translations, perspectives en 3D, etc.
II.3.c : Au dessus d'OpenGL : GLUT
Comme nous l'avons mentionné et afin de rendre OpenGL complètement portable, il a été
nécessaire de sacrifier toutes les commandes qui sont en interface avec le gestionnaire de
fenêtres. Par exemple, ouvrir, fermer, redimensionner ou changer l'échelle d'une fenêtre, lire
la position du curseur ou les entrées au clavier etc... Toutes ces actions dépendent très
fortement du système d'exploitation.
A l'origine, la bibliothèque GL avait ses propres commandes de fenêtres et pilotes de
périphériques mais ils étaient spécifiques à IRIX (le système d'exploitation UNIX de SGI). Il
Infographie et bibliothèques graphiques
12
appartient au développeurs OpenGL de connaître leurs machines et de prendre en compte la
manipulation des fenêtres avec des outils spécifiques.
Pour obtenir son indépendance matérielle, la spécification d'OpenGL a été écartée de toute
dépendance à un système de fenêtrage. L'interface qui en résulte est portable, cohérente et
efficace en termes de bibliothèque de tracés 2 et 3D. Il appartient au gestionnaire de fenêtres
du système d'ouvrir et de dessiner les fenêtres. La bibliothèque OpenGL communique avec le
système au travers de bibliothèques additionnelles auxiliaires. Par exemple, la bibliothèque
auxiliaire GLX décrit les interactions entre OpenGL et le système X windows. Heureusement,
nous avons GLUT, une bibliothèque additionnelle qui contourne ce problème!
GLUT (GL Utility Toolkit) a été écrite par Mark J. Kilgard de SGI. Cette bibliothèque
utilitaire remplace la vieille AUX (ne cherchez pas à savoir ce que c'était !). La bibliothèque
GLUT est gratuite. Elle est dépendante de la machine et offre des liens commun pour le
fenêtrage et les pilotes de périphériques. Ainsi, quand une application OpenGL souhaite
ouvrir une fenêtre pour une animation graphique, il utilise la commande GLUT et cela prend
en charge le gestionnaire de fenêtres sous-jacent.
Dans un sens, GLUT cache au programmeur les sales détails des systèmes graphiques fenêtrés
(X11, Windows, Motif, etc..) et lui permet de se concentrer sur sa tache - le code OpenGL.
Un autre grand avantage de l'utilisation de GLUT est qu'il rend le code indépendant de la
plate-forme de programmation
Pour terminer cette rapide introduction, nous ne pouvons oublier de mentionner Brian Paul,
qui avec constance et patience implante une bibliothèque similaire à OpenGL pour Linux et
nommée Mesa. Pour l'instant, Mesa ne travaille que par logiciel, ce qui signifie que c'est le
processeur qui effectue tous les calculs graphiques qui pourraient être sinon délégués au
matériel 3D. Mais Mesa contient des liens internes qui permettent à des pilotes de matériels
accélérés d'être écrits et utilisés.
Actuellement, les pilotes existent pour Mondello, S3 Virge (Win95 seulement), GLINT, et les
processeurs Voodoo 3Dfx. Grâce au pilote Voodoo (écrit par David Bucciarelli) Mesa peut
atteindre les même performances que de très chères stations SGI.
II.4 – Débuts en programmation OpenGL-MESA
Nous allons décrire ici les éléments les plus basiques permettant de commencer à travailler en
2D avec OpenGL. Le compilateur choisi peut être gcc sous Linux, couplé à la bibliothèqye
MESA (qui doit être installée par l'administrateur).
II.4.a : La syntaxe d'OpenGL
Les fonctions OpenGL de base commencent par le préfixe gl.
Les constantes (#define ou enum) sont données en majuscules et commencent par le
préfixe GL_.
Infographie et bibliothèques graphiques
13
Certaines instructions finissent par un suffixe (par exemple: glVertex2f). Ce suffixe
indique le nombre et le type des arguments de la fonction en question (cas particulier d'une
famille de fonctions). Le tableau suivant en illustre les typologies :
Type Type C Type OpenGL
b entier 8 bits signed char GLbyte
s entier 16 bits short GLshort
i entier 32 bits long, int GLint, GLsizei
f réel 32 bits float GLfloat, GLclampf
d réel 64 bits double GLdouble, GLclampd
ub entier non signé 8 bits unsigned char GLubyte, GLboolean
us entier non signé 16 bits unsigned short GLushort
ui entier non signé 32 bits unsigned long GLuint, GLenum, GLbitfield
On peut donc dicter certaines correspondances entre des utilisations de ces fonctions. Par
exemple, La "famille de fonctions" glVertex permettant d'allumer un point (en 2 ou 3-D)
peut être l'objet de ce type d'utilisation :
glVertex2i(1,3); <=> glVertex2f(1.0,3.0);
De façon similaire, la lettre terminale v indique que la fonction prend comme paramètre un
pointeur sur un tableau. On peut donc avoir, comme utilisation de la famille de fonctions
glColor (permettant de fixer la couleur courante) :
/* version 3 entiers */ /* version tableau d'entiers */
glColor3i(1,0,1); <=> int c[3]={1,0,1};
glColor3iv(c);
II.4.b : L'initialisation via GLUT
Tout programme OpenGL qui utilise GLUT doit commencer par initialiser la machine d'état
GLUT. Les fonctions d'initialisation GLUT sont préfixées par glutInit-. La routine principale
d'initialisation est glutInit:
glutInit(int **argcp, char **argv);
glutInit extrait de la ligne de commande les options adaptées à la bibliothèque GLUT, par
exemple :
sous l'environnement du système X Windows, toute option adaptée pour X windows et
associée à la fenêtre GLUT (geometry, display).
glutInitWindowPosition(int x, int **y);
glutInitWindowSize(int width, int **height);
x,y = position écran en pixels dans la fenêtre window (coin supérieur gauche)
width,height en pixels de la fenêtre.
glutInitDisplayMode(unsigned int mode);
Infographie et bibliothèques graphiques
14
mode est le mode d'affichage, un OU logique bit à bit du masque des modes d'affichage
GLUT. Les principales valeurs possibles du masque sont:
GLUT_RGBA Sélectionne une fenêtre en mode RGBA (choix par défaut).
GLUT_SINGLE Sélectionne une fenêtre simple tampon (choix par défaut).
GLUT_DOUBLE Sélectionne une fenêtre double tampon .
GLUT_DEPTH Sélectionne une fenêtre avec un tampon de profondeur.
int glutCreateWindow(char *nom);
nom est le tire de la fenêtre. Cette fonction crée une fenêtre selon les valeurs des
initialisations ci-dessus. Elle renvoi un entier unique qui identifie la fenêtre.
Un programme GLUT commence donc, par exemple, comme ceci :
#include <GL/glut.h>
void main(int argcp, char **argv)
{
/* Initialisation de GLUT */
glutInit(&argcp, argv);
/* Taille et emplacement de la fenêtre */
glutInitWindowSize(400, 400);
glutInitWindowPosition(50, 50);
/* Choix du type et d'affichage et d'un simple buffer */
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
glutCreateWindow(" Test" ) ;
.....encore du code
};
II.4.c : Le traitement des évènements
Comme mentionné auparavant, GLUT est une machine d'état, c'est aussi un processeur
d'évènements. Il existe une "horloge" ou une boucle continue qui traite, un par un, tous les
évènements déclarés à GLUT pendant la phase d'initialisation. Les évènements sont : un clic
de souris, une fenêtre fermée ou dimensionnée, un curseur déplacé, une frappe sur une touche
et, plus curieux, l'évènement "idle", c'est à dire que rien ne se passe ! Chacun de ces
évènements doit être enregistré dans une des variables d'état de GLUT pour que la boucle du
processeur de GLUT puisse les interroger périodiquement et déterminer si l'utilisateur les a
activés.
Infographie et bibliothèques graphiques
15
Par exemple, nous pourrions enregistrer : "cliquez un bouton de la souris", comme évènement
à surveiller par GLUT. Les évènements sont enregistrés au moyen de routines de rappel
(callback en anglais). Toutes suivent la syntaxe glut[unEvenement]Func. Dans le cas du
clic de souris ce sera glutMouseFunc. L'enregistrement d'un callback dit au processeur
GLUT quelle fonction, définie par l'utilisateur, doit être appellée quand l'évènement se
produit. Ainsi, si j'écris ma propre fonction : MyMouse, qui définit ce qu'il faut faire si le
bouton gauche de la souris est cliqué, je dois enregistrer mon callback après glutInit() dans
main() en utilisant l'instruction "glutMouseFunc(MyMouse);".
Pour finir, et après avoir enregistré tous les évènements de notre application, il faut appeler le
processeur d'évènement GLUT, soit la fonction glutMainLoop(). Cette fonction ne se
termine jamais, autrement dit, notre programme entre dans une boucle infinie. Elle appellera ,
tant que nécessaire, toutes les callback que nous avons précédemment enregistrées. La
fonction main(), dans une application OpenGL, doit donc se terminer par une instruction
glutMainLoop().
Pour tester tout ceci, essayez de compiler l'exemple ci-dessous, qui doit ouvrir une fenêtre et
enregistrer le callback Clic Gauche, ainsi que celui de la touche ESC pour quitter
l'application.
Bon voilà pour cette première partie. Nous verrons au fur et à mesure les différents callback
existants, la création de menus sans oublier les instructions OpenGL !
#include <sdtio.h>
#include <stdlib.h>
#include <GL/glut.h>
void affichage(void)
{
glClear ( GL_COLOR_BUFFER_BIT) ; /*Oups ! une instruction OpenGL*/
}
void clavier ( unsigned char key, int x, int y )
{
switch ( key ) {
case 27: /* ESC */
case 'q':
case 'Q':
exit (0);
break ;
}
}
void souris(int button, int state, int x, int y)
{
switch(button) {
case GLUT_LEFT_BUTTON:
if (state == GLUT_DOWN) printf("Clic gauche OK\n") ;
break;
}
}
void main(int argcp, char **argv)
{
/* Initialisation de GLUT */
glutInit(&argcp, argv);
Infographie et bibliothèques graphiques
16
/* Taille et emplacement de la fenetre */
glutInitWindowSize(400, 400);
glutInitWindowPosition(50, 50);
/* Choix du type et d'affichage et d'un simple buffer */
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
/* Creation de la fenetre */
glutCreateWindow("Test") ;
/* Association des callback */
glutDisplayFunc(affichage);
glutMouseFunc(souris);
glutKeyboardFunc(clavier);
/* On entre dans la boucle d'evenements */
glutMainLoop() ;
}
II.4.d : Les primitives d'OpenGL
L'utilisation d'OpenGL est orientée selon un principe de primitives : chaque objet est composé
d'éléments basiques comme les sommets, les faces, etc...., appelés primitives. Pour créer un
objet, il suffit donc de lui indiquer toutes ses primitives. Par exemple, une face carrée sera
générée de cette façon :
glBegin(GL_QUADS);
glVertex3f(1.0,1.0,0.0);
glVertex3f(0.0,1.0,0.0);
glVertex3f(0.0,0.0,0.0);
glVertex3f(1.0,0.0,0.0);
glEnd();
Plus précisément, OpenGL n'a que quelques primitives géométriques : points (vertex), lignes,
polygones. Tous sont décrits par des vecteurs. Un vecteur est caractérisé par 2 ou 3 points
réels, les coordonnées cartésiennes du vecteur, (x,y) en 2D et (x,y,z) en 3D. Il existe bien sur
également le système homogène de coordonnées dans lequel chaque point est décrit par 4
nombres réels (x,y,z,w).
Les points
Puisque avec OpenGL, tous les objets géométriques sont décrit en termes de vecteurs
ordonnés, il y a une famille de fonctions pour déclarer un vecteur. Sa syntaxe est :
void glVertex{234}{sifd}[v](TYPE coords);
La notation devra vous devenir familière. Les accolades indiquent une partie du nom de la
fonction. Cette dernière peut prendre 2,3 ou 4 paramètres soit de type entier, réels ou doubles.
Ces paramètres peuvent être fournis optionnellement sous forme de vecteurs, auquel cas, le
nom de la fonction comportera un v. Voici quelques exemples :
void glVertex2s(1, 3);
void glVertex2i(23L, 43L);
void glVertex3f(1.0F, 1.0F, 5.87220972F);
float vector[3];
void glVertex3fv(vector);
Infographie et bibliothèques graphiques
17
A titre de simplification, toutes ces fonctions sont appellées glVertex*.
OpenGL interprète une séquence quelconque de vecteurs selon son contexte. Le contexte est
déclaré par la paire de fonctions glBegin(GLenum mode) et glEnd(). Toute instruction
glVertex* exécutée entre les deux est interprétée en fonction de la valeur de mode, par
exemple:
glBegin(GL_POINTS);
glVertex2f(0.0, 0.0);
glVertex2f(1.0, 0.0);
glVertex2f(0.0, 1.0);
glVertex2f(1.0, 1.0);
glVertex2f(0.5, 0.5);
glEnd();
dessine 5 points en 2D avec les coordonnées spécifiées. GL_POINTS est une des constantes
définies dans le fichier d'entête <GL/gl.h>. Il existe de nombreux autres modes disponibles
mais nous les décrirons au fur et à mesure des besoins.
Chaque point est tracé avec la couleur mémorisée par la variable d'état OpenGL associée au
tampon des couleurs. Pour changer de couleur, on utilise la famille de fonctions glColor*; il
y a beaucoup à dire sur la sélection et la manipulation des couleurs. Pour l'instant, nous
utiliserons 3 nombres réels variants de 0.0 à 1.0. C'est le codage RVB (Rouge-Vert-Bleu):
glColor3f(1.0, 1.0, 1.0); /* White */
glColor3f(1.0, 0.0, 0.0); /* Red */
glColor3f(1.0, 1.0, 0.0); /* Yellow */ etc...
La taille d'un point peut-être spécifié en pixels en utilisant glPointSize:
void glPointSize(GLfloat size)
Par défaut la taille d'un point est de 1.0 et elle est toujours supérieure à zéro. Vous
remarquerez que la taille d'un point est donnée par un nombre réel; les portions de point et de
ligne sont autorisées. OpenGL interprète les portions de point en fonction du contexte de
rendu. Si l'anti-crénelage est actif alors OpenGL modifie les pixels voisins de la ligne pour lui
donner l'apparence de la largeur spécifiée. L'anti-crénelage est une technique visant à éliminer
les gros carrés que l'on voit lorsque l'on dessine des lignes à basse résolution. Si l'anti-
crénelage n'est pas actif, glPointSize arrondira la taille à l'entier le plus proche.
La largeur des lignes est spécifiée par la fonction glLineWidth qui doit être appelée avant le
bloc glBegin() - glEnd() qui dessine la (les) ligne(s). Voici la syntaxe complète de la
fonction :
void glLineWidth(GLfloat width)
Il se peut que l'implémentation d'OpenGL limite la largeur des lignes sans anti-crénelage à la
valeur maximale de la largeur avec anti-crénelage (arrondi à l'entier le plus proche). Gardez
également en mémoire que la largeur d'une ligne n'est pas mesurée perpendiculairement à
celle-ci, mais dans la direction des Y si la valeur absolue de la pente est inférieure à 1, dans la
direction des X dans le cas contraire.
Infographie et bibliothèques graphiques
18
Juste pour information et parce que nous venons d'aborder le sujet, l'anti-crénelage s'active
par l'instruction :
glEnable (GL_LINE_SMOOTH); et se désactive par l'instruction :
glDisable (GL_LINE_SMOOTH);
Nous allons compléter notre exemple afin d'obtenir quelques points à l'écran (voir à la fin).
Remarquez la fonction d'initialisation repere(). Elle utilise une instruction de la bibliothèque
d'Utilitaires OpenGL (GLU) : gluOrtho2D(). Cette fonction établit un système de
coordonnées 2D orthogonal. Les paramètres sont "x minimum, x maximum, y minimum, y
maximum".
Les polygones
Nous avons déjà mentionné que glBegin(GLenum mode) accepte divers modes et que les
séquences de vecteurs v0, v1,v2, v3,v4,... vn-1 déclarés à posteriori sont interprétés en
conséquence. Les valeurs possibles du mode et les actions associées sont :
GL_POINTS Trace un point pour chacun des n vecteurs.
GL_LINES Trace une série de lignes non connectées. Les segments sont tracés entre v0 et v1,
v2 et v3,...etc. Si n est impair vn-1 est ignoré.
GL_POLYGON Trace un polygone avec v0, v1,..,vn-1 comme vecteurs. n doit être au moins
égal à 3 ou rien n'est dessiné. De plus, le polygone ne peut se croiser lui et doit être convexe à
cause de limitations (dues aux algorithmes).
GL_TRIANGLES Trace une série de triangles avec les vecteurs v0, v1 et v2, puis v3, v4 et v5
etc. Si n n'est pas un multiple de 3, les points restants sont ignorés.
GL_LINE_STRIP Trace une ligne de v0 à v1, puis de v1 à v2 et ainsi de suite. Finalement de
vn-2 à vn-1 pour un total de n-1 segments. Il n'y a pas de restrictions sur les vecteurs
décrivant les tronçons de lignes, les lignes peuvent arbitrairement se croiser.
GL_LINE_LOOP Identique à GL_LINE_STRIP excepté qu'un segment final est dessiné de
vn-1 à v0, pour fermer la boucle.
GL_QUADS Dessine une série de quadrilatères avec les vecteurs v0, v1, v2, v3 et v4, v5, v6,
v7 et ainsi de suite.
GL_QUAD_STRIP Dessine une série de quadrilatères avec les vecteurs v0, v1, v3, v2 puis
v2, v3, v5, v4 et ainsi de suite.
GL_TRIANGLE_STRIP Dessine une série de triangles avec les vecteurs v0, v1, v2, puis v2,
v1, v3, puis v2, v3, v4, etc. L'ordre permet de s'assurer que les triangles ont l'orientation
correcte et l'a bande peut être utilisée pour former une partie de la surface
GL_TRIANGLE_FAN Similaire à GL_TRIANGLE_STRIP excepté que les triangles sont v0,
v1, v2, puis v0, v2, v3, puis v0, v3, v4, et ainsi de suite. . Tous les triangles ont v0 en
commun.
#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>
#define NB_POINTS 20 /* 20 points au plus */
int points[NB_POINTS][2] ; /* pour stocker les points */
int n=0 ; /* le nombre effectif de points */
Infographie et bibliothèques graphiques
19
void initPoints( void )
{
int i ;
for (i=0 ; i<NB_POINTS ; i++) points[i][0]= points[i][1]= 15*i ;
n= NB_POINTS ;
}
void affichage ( void )
{
int i ;
glClear ( GL_COLOR_BUFFER_BIT) ; /* Efface la fenetre */
glPointSize(5.0) ;
glBegin(GL_POINTS);
for (i=0;i<n;i++)
glVertex2i(points[i][0],points[i][1]) ; /*definition des
points*/
glEnd();
glFlush() ; /* lance l#affichage pour un simple buffer */
}
void repere ( )
{
gluOrtho2D(0, 400, 0, 400) ; /* definit un repere orthogonal*/
}/* les valeurs donnees sont x minimum, x maximum, y minimum, y maximum */
void clavier ( unsigned char key, int x, int y )
{
switch ( key ) {
case 27: /* ESC */
case 'q':
case 'Q': exit (0);
break ;
}
}
void souris(int button, int state, int x, int y)
{
switch(button) {
case GLUT_LEFT_BUTTON:
if (state == GLUT_DOWN) printf("Clic gauche OK\n") ;
break;
}
}
void main(int argcp, char **argv)
{
initPoints(); /* remplissage du tableau de points */
glutInit(&argcp, argv); /*Initialisation de GLUT*/
glutInitWindowSize(400, 400); /*Taille et emplacement de la fenetre*/
glutInitWindowPosition(50, 50);
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE); /*Choix mode d#affichage*/
glutCreateWindow("Test") ; /*Creation de la fenetre*/
glutDisplayFunc(affichage); /*Association des callback*/
glutMouseFunc(souris);
glutKeyboardFunc(clavier);
glutReshapeFunc(repere) ;
glutMainLoop() ; /*On entre dans la boucle d#evenements*/
}
Infographie et bibliothèques graphiques
20
II.4.e : Les bitmaps
OpenGL permet la gestion de bitmaps (tableau de pixels) en autorisant les opérations de
transfert entre le buffer couleur et la mémoire centrale. Les objectifs ces manipulations
s'articulent principalement autour de la lecture des fichiers d'image bitmap. Ceci dit, la gestion
des tableaux de pixels s'avère également utile pour la gestion de polices de caractères (non
traité de façon immédiate par OpenGL) ou l'utilisation des textures (dont nous ne traiterons
pas ici)
Lecture d'un bitmap
L'instruction suivante permet la lecture des pixels d'un frame-buffer et son stockage en
mémoire
void glReadPixels(GLint x,GLint y,GLsizei l,GLsizei h,GLenum format,GLenum
type,GLvoid *pixels);
avec :
x et y: position du coin supérieur gauche du rectangle de pixels
l et w: dimensions du rectangle de pixels
format: information à lire sur les pixels
type: type du résultat à fournir
pixels: tableau destiné à recevoir les résultats
Le format des pixels doit être choisi dans un de ceux du tableau suivant :
Format Type de données pour les pixels
GL_COLOR_INDEX Indexe de couleurs
GL_RGB Couleurs RVB
GL_RGBA Couleurs RVBA
GL_RED Composante rouge
GL_GREEN Composante verte
GL_BLUE Composante bleue
GL_ALPHA Composante alpha
GL_LUMINANCE Luminance
GL_LUMINANCE_ALPHA Luminance puis composante alpha
GL_STENCIL_INDEX Stencil
GL_DEPTH_COMPONENT Composante de profondeur
Infographie et bibliothèques graphiques
21
Quant au type du résultat à fournir, il figure sur le tableau suivant :
Type Type C
GL_UNSIGNED_BYTE Entier 8 bits non signé
GL_BYTE Entier 8 bits signé
GL_BITMAP Bits dans des entiers 8 bits non signés
GL_UNSIGNED_SHORT Entier 16 bits non signé
GL_SHORT Entier 16 bits signé
GL_UNSIGNED_INT Entier 32 bits non signé
GL_INT Entier 32 bits signé
GL_FLOAT Réel simple précision
Affichage d'un bitmap
L'instruction suivante permet l'affichage des pixels d'un frame-buffer de pixels stockés en
mémoire dans la fenêtre courante avec son coin supérieur gauche en position raster courante.
void glDrawPixels(GLsizei l,GLsizei h,GLenum f,GLenum type,GLvoid *pixels);
avec :
l et w: dimensions du rectangle de pixels
f: information à écrire sur les pixels
type: type des données transmises
pixels: tableau destiné à transmettre les pixels
Nous voilà donc prêts à pouvoir traiter des images bitmap et les afficher sur une fenêtre
OpenGL sous Linux.