44
Synthèse d’images en temps réel Programmation OpenGL en C et C++ Livret pour les formations Master Ingénierie Informatique Benoît Piranda Version 3.109 du 6 octobre 2006

Programmation OpenGL en C et C++ Livret pour les ...piranda/CMS/supportOpengl.pdf · Ce document n'est en aucun cas un guide de ... Perl et Java). La réalisation d’une image de

Embed Size (px)

Citation preview

Synthèse d’images en temps réel

Programmation OpenGL en C et C++

Livret pour les formations Master Ingénierie Informatique

Benoît Piranda

Version 3.109 du 6 octobre 2006

iii

Tables des matières

Chapitre I Introduction ................................................................................................................. 5 Chapitre II La bibliothèque OpenGL............................................................................................ 7 Chapitre III Choix technologiques de OpenGL ............................................................................. 8 Chapitre IV Structuration système ................................................................................................. 9 Chapitre V Les fonctions de la bibliothèque OpenGL............................................................... 10 Chapitre VI La modélisation sous OpenGL................................................................................. 11 Chapitre VII Modélisation géométrique des objets....................................................................... 12 VII.1 Description des primitives géométriques ........................................................................................... 12 VII.2 Notions de normales .......................................................................................................................... 13 VII.2.1 Calcul de la normale mathématique ......................................................................................... 14 VII.2.2 Exemple d'application .............................................................................................................. 14

VII.3 Utilisation des tableaux de coordonnées (Vertex Array) ................................................................... 15 VII.3.1 Principe..................................................................................................................................... 15 VII.3.2 Utilisation ................................................................................................................................. 15 VII.3.3 Exemple.................................................................................................................................... 16

VII.4 Les objets mémorisés ......................................................................................................................... 17 Chapitre VIII Modélisation radiométrique ..................................................................................... 19 VIII.1 Modèle sans illumination .............................................................................................................. 19 VIII.2 Le modèle d’illumination de OpenGL .......................................................................................... 19 VIII.2.1 Modèle théorique...................................................................................................................... 19 VIII.2.2 Utilisation du modèle d’illumination OpenGL......................................................................... 20

VIII.3 Lissage des couleurs...................................................................................................................... 22 Chapitre IX Modélisation géométrique de la scène ..................................................................... 23 IX.1 Les matrices homogènes de transformation ....................................................................................... 23 IX.2 Fonctions de manipulation des matrices ............................................................................................ 24 IX.3 Modélisation de la caméra ................................................................................................................. 24 IX.3.1 La définition de l’écran ............................................................................................................ 25 IX.3.2 Le placement de la caméra OpenGL ........................................................................................ 25 IX.3.3 Utilisation de plusieurs caméras pour afficher des informations à l’écran ............................... 26

Chapitre X Les textures ................................................................................................................ 27 X.1 Les textures simples 2D ..................................................................................................................... 27 X.2 Les textures multi-échelles................................................................................................................. 28 X.3 Les textures d’environnement ............................................................................................................ 29

Chapitre XI Le brouillard.............................................................................................................. 30 XI.1 Notion de milieux participants ........................................................................................................... 30 XI.1.1 Les phénomènes d’absorption et de diffusion .......................................................................... 30 XI.1.2 L’émission propre..................................................................................................................... 30 XI.1.3 La diffusion entrante................................................................................................................. 31

XI.2 Simulation du brouillard par OpenGL ............................................................................................... 31 XI.3 Programmation du brouillard ............................................................................................................. 32

Chapitre XII Lecture et écriture dans les buffers ......................................................................... 33 XII.1 Exemple de sauvegarde de l'écran...................................................................................................... 33 XII.2 Exemple d'affichage d'une image à l'écran......................................................................................... 33 XII.3 Utilisation du buffer d’accumulation ................................................................................................. 34 XII.4 Utilisation du buffer de profondeur.................................................................................................... 35 XII.4.1 Exemple de récupération du buffer de profondeur ................................................................... 35

Chapitre XIII Les effets spéciaux ..................................................................................................... 36 XIII.1 La transparence ............................................................................................................................. 36 XIII.1.1 Fonctions OpenGL pour la transparence .................................................................................. 36 XIII.1.2 Exemple de transparence .......................................................................................................... 37 XIII.1.3 Transparence et texture............................................................................................................. 37

Chapitre XIV Sélection d’un objet à l’écran................................................................................... 38 Chapitre XV Utilisation de la bibliothèque GLUT ....................................................................... 40 XV.1 Introduction........................................................................................................................................ 40 XV.2 Gestion des fenêtres. .......................................................................................................................... 40

Benoît Piranda 06/10/2006

iv

XV.3 Gestion des événements émis par les périphériques d'entrées............................................................ 41 XV.4 Les objets simples définis dans GLUT. ............................................................................................. 42 XV.5 Un programme d'exemple. ................................................................................................................. 42 XV.6 Compilation de logiciel OpenGL / Glut sous système Linux............................................................. 44

Benoît Piranda 06/10/2006

5

Chapitre I Introduction

Ce document n'est en aucun cas un guide de référence sur OpenGL mais constitue un regroupement des informations de bases permettant de réaliser des applications graphiques répondant à la plupart des problèmes classiques de visualisation d'objets en 3D temps réel.

Nous présentons au chapitre II quelques bases de synthèse d’images sur la modélisation géométrique, la modélisation radiométrique et quelques algorithmes de rendu que nous illustrerons par des exemples sous OpenGL.

Le chapitre XV est consacré à la gestion des animations interactives en temps réel exploitant la bibliothèque GLUT.

Benoît Piranda 06/10/2006

6

Benoît Piranda 06/10/2006

7

Chapitre II La bibliothèque OpenGL

La bibliothèque graphique OpenGL regroupe des fonctions et des paramètres permettant d'effectuer le rendu de primitives graphiques 2D et 3D simples (lignes, polygones, surfaces paramétriques, ...) en faces cachées éclairées, texturées et lissées. Elle est une des bibliothèques graphiques les plus utilisées pour la représentation de scènes 2D ou 3D en temps réel.

Cette bibliothèque propose des versions pour la plupart des systèmes d'exploitation (Unix, Linux, Windows, Mac OS, OS/2, OPENStep, et BeOS) et peut être exploitée à partir des principaux langages de développement (Ada, C, C++, Fortran, Python, Perl et Java).

La réalisation d’une image de synthèse à partir d’une idée de la scène incluant les paramètres géométriques, radiométriques (participants aux couleurs), et temporels, suit le schéma suivant : − Une étape de modélisation géométrique, permettant au concepteur de décrire la géométrie de la scène, pour

obtenir une structure géométrique nue. − Une étape de modélisation radiométrique dévolue à décrire comment les éléments de la scène renvoient la

lumière, permettant de connaître une scène habillée et colorée. − Une étape de rendu, réalisée par un logiciel pour projeter la scène tridimensionnelle sur un écran à deux

dimensions en tenant compte des faces opaques et calculer la couleur de chacun de ses points visible depuis un point de vue : la caméra.

− Dans un contexte d’animations, ce traitement global est répété pour différents instants, le paramètre temporel pouvant intervenir sur le modèle géométrique (articulation, déplacement), sur le modèle radiométrique (bump mapping, changement d’état).

Idée de Scène

(fonction du temps)

Modélisation géométrique

Description :• de la forme des objets• de l’organisation des objets dans la scène

• placement de la caméra

Modélisation radiométrique

Description :• de la matière constituantl’objet

• des interactions entre laet la lumière

Vue descriptivede la scène« fils de fer »

Vue habilléeet colorée

Algorithme de rendu

Projection en 2DSuppression des faces cachéesCalcul des interactionsmultiples

Image 2DInstant courant : t+∆t

Idée de Scène

(fonction du temps)

Modélisation géométrique

Description :• de la forme des objets• de l’organisation des objets dans la scène

• placement de la caméra

Modélisation radiométrique

Description :• de la matière constituantl’objet

• des interactions entre laet la lumière

Vue descriptivede la scène« fils de fer »

Vue habilléeet colorée

Algorithme de rendu

Projection en 2DSuppression des faces cachéesCalcul des interactionsmultiples

Image 2DInstant courant : t+∆t

Benoît Piranda 06/10/2006

8

Chapitre III Choix technologiques de OpenGL

La bibliothèque OpenGL a pour but de proposer une solution permettant la visualisation en temps réel de scène en 3 dimensions. La contrainte temps réel impose de pouvoir produire une nouvelle image au moins tous les 25ème de seconde afin d’obtenir des animations visuellement fluides sur un écran d’ordinateur. De telles contraintes peuvent apparaître comme rédhibitoire lorsque l’on connaît la complexité des algorithmes de la synthèse d’image mais peuvent être atteinte avec le matériel informatique actuel sous réserve de quelques restrictions et de quelques choix technologiques judicieux. Ces choix s’appuient sur la sélection : − d’un modèle de description géométrique des scènes simplifié et associé à un algorithme de traitement des faces

cachées efficace et rapide, − d’algorithme d’illumination permettant de calculer rapidement la couleur visible des objets éclairés dans la

scène.

L’algorithme de visualisation des scènes en temps réel utilisé par la bibliothèque OpenGL repose sur un modèle de description des objets à l’aide d’un ensemble de facettes polygonales planes. La projection de la scène à l’écran en supprimant les faces cachées est réalisée par le modèle du Z-Buffer. Ce modèle consiste à projeter successivement sur l'écran chacune des facettes composant les objets en tenant compte de leur distance au point d'observation. La suppression des faces cachées est obtenue au niveau de chaque pixel de l'image en comparant la distance d’éloignement de l’objet à ajouter à la distances des objets déjà projetés en ce point et en n'affichant que les objets les plus proches. Cet algorithme impose de définir une mémoire (appelée Z-Buffer ou depth buffer) contenant pour chaque pixel un codage de la distance séparant l’objet projeté en ce point et l’écran.

Le second point porte sur le choix d’un modèle d’illumination rapide et ‘réaliste’. Le comportement réflectif d’une surface peut être représenté par une fonction de répartition spatiale de l’énergie reçue en fonction de son angle d’incidence. Cette fonction très générale est appelée fonction de réflexion bidirectionnelle (ou BRDF pour Bi-directional Reflection Distribution Function). Sa forme réelle dépend des caractéristiques physiques et géométriques du matériau constituant la surface de l’objet (propriétés électriques, indice de réfraction, température, rugosité,…) [HT91][TS66]. De plus cette fonction est différente suivant la fréquence de la lumière incidente. Le modèle d’illumination de Phong [Ph75] propose une décomposition simplifiant l’expression de la BRDF comme la somme de trois fonctions mathématiques.

Ce modèle d’illumination doit théoriquement être appliqué à chaque point des facettes éclairées, mais pour des raisons d’efficacité, OpenGL propose un modèle d’illumination couplant le modèle de Phong avec un lissage de Gouraud. Le lissage de Gouraud [Go71] est omniprésent dans les images OpenGL. Considérant une couleur associée à chacun de ses sommets, un polygone est lissé par interpolation bilinéaire des couleurs de façon à obtenir un dégradé de couleurs entre les valeurs aux sommets.

Benoît Piranda 06/10/2006

9

Chapitre IV Structuration système

OpenGL est indépendant de tout système d'exploitation et de tout système de gestion de fenêtres, il ne comporte donc aucune fonction de gestion d'écran ou de périphériques tels que la souris ou le clavier. Ces opérations sont déléguées aux environnements d'accueil tels que XWindow ou MS-Windows.

Sous l'environnement UNIX, OpenGL a été conçu comme une extension de XWindow. La principale conséquence est que la spécification OpenGL ne comporte aucune des fonctionnalités de gestion de fenêtres et des évènements, dans la mesure où XWindow les prend en charge. Un programme OpenGL utilisera donc conjointement des fonctions OpenGL reconnaissables par leur préfixe gl et des fonctions XWindow. Cependant, pour éviter aux utilisateurs OpenGL d'apprendre XWindow, un ensemble de fonctions simplifiées a été conçu, et est disponible dans la plupart des livraisons OpenGL. Ces utilitaires sont regroupés dans une librairie appelée AUX, ils permettent principalement d'ouvrir des fenêtres contenant les images calculées par OpenGL et quelques fonctions de créations d'objets génériques simples comme le cube, la sphère ou le tore. Nous consacrerons une partie de ce document à GLUT, qui est une bibliothèque de fonctions de gestion de fenêtres plus riche que AUX. Les principes de fonctionnement de GLUT sont semblables à ceux de AUX mais GLUT offre de nouvelles fonctionnalités comme entre-autres la possibilité de gérer plusieurs fenêtres simultanément, un système de traitement de menus sous le pointeur de la souris et la prise en compte d'un plus grand nombre de périphériques d'entrée-sortie (joystick,…).

Benoît Piranda 06/10/2006

10

Chapitre V Les fonctions de la bibliothèque OpenGL

Dans le but de faciliter la manipulation des fonctions OpenGL et éviter des erreurs d'exécutions difficiles à détecter, les noms de fonctions sont construits de façon à indiquer le type des données qu’il faut passer en paramètres. Une même fonction est souvent écrite avec plusieurs types de paramètres différents (b, s, i, f, d, ub, us, ui) de façon à être optimisée pour les paramètres du programme qui les utilise. Les fonctions qui admettent des vecteurs en paramètres existent souvent aussi avec un passage de paramètres sous forme de tableau des composantes (v).

Les noms de fonctions OpenGL sont construits sur le modèle suivant : − le préfix gl, − le nom de la fonction elle-même (première lettre en majuscule), − un suffixe indiquant le type des paramètres, − la lettre v si les paramètres sont placés dans un tableau.

Le tableau ci-dessous précise les types des paramètres possibles et les lettres associées dans la définition des fonctions OpenGL.

Type OpenGL Type correspondant Taille en octets Lettres associées

GLbyte signed char 1 b

GLshort short 2 s

GLint int 4 i

GLfloat float 4 f

GLdouble double 8 d

GLubyte unsigned char 1 ub

GLushort unsigned short 2 us

GLuint unsigned int 4 ui

Dans la suite du document nous écrirons les fonctions OpenGL suffixées par la liste des types possibles placés entre crochets sur le modèle glVertex3[dfi].

Par exemple : glVertex3d(double,double,double);

glVertex3i(int,int,int);

glVertex3fv(float*);

Benoît Piranda 06/10/2006

11

Chapitre VI La modélisation sous OpenGL

Une scène virtuelle à modéliser contient un certain nombre d'éléments bien différentiables qui peuvent être regroupé en trois catégories : des objets géométriques, des sources de lumière, le point de vue (ou caméra). Nous étudions dans cette partie comment décrire ces différents objets sous OpenGL.

Un objet de l'espace virtuel dont on veut synthétiser une image admet des composantes géométriques et radiométriques. Les composantes géométriques indiquent quelle est la forme propre de l’objet (souvent décrite par une liste de facettes) mais aussi comment il est placé et orienté dans l'espace 3D de la scène globale. Les composantes radiométriques interviennent dans le modèle d'illumination de façon à déterminer l'énergie reçue en un point de l'objet et en déduire sa couleur apparente. Cette couleur apparente est fonction des propriétés propre de l’objet (le matériau qui le recouvre et lui donne sa couleur) et de son éclairement (la couleur et la position des sources de lumière relativement à l’objet et à l’observateur). La modélisation de scène 3D (ou 4D pour intégrer la notion d’animation) se fait en plusieurs étapes : 1. La modélisation géométrique des objets qui constituent la scène. Cette action a pour but d’énumérer toutes les facettes (et toutes les autres primitives graphiques pouvant être traitées par OpenGL) qui constituent ces objets. Cette étape est détaillée au Chapitre VII

2. La modélisation radiométrique des objets simples présentée au VII.3. Les objets doivent être habillés de caractéristiques radiométriques intervenant dans le calcul de leur couleur apparente par le modèle d’illumination.

3. Ensuite, les objets ainsi habillés pourront être placés relativement les uns par rapport aux autres dans la scène finale. Cette étape, présentée au Chapitre IX doit être réalisée en tenant compte du paramètre temporel dans le cas d’une animation ou d’une scène interactive.

4. Enfin, quelques effets spéciaux tels que des textures, des effets de brumes, etc. peuvent être ajoutés pour accroître le réalisme des scènes.

Benoît Piranda 06/10/2006

12

Chapitre VII Modélisation géométrique des objets

Le modèle géométrique utilisé sous OpenGL repose sur un modèle de représentation des surfaces par leurs sommets, arêtes et faces : Brep [FV90] (Boundary Representation).

La modélisation d’objets sous OpenGL est obtenue uniquement par la définition d’une liste de primitives (facettes, lignes et points). Cette contrainte impose de bien connaître la géométrie de l’objet à construire, (par exemple en le définissant par une ou plusieurs équations mathématiques), ou à utiliser des logiciels spécialisés pour produire la liste des sommets, arêtes et faces des objets.

VII.1 Description des primitives géométriques

La description d’objets sous OpenGL repose sur l’énumération des sommets formant des primitives géométriques simples. Ces primitives simples sont les points (GL_POINTS), les lignes (GL_LINES) et les facettes à trois (GL_TRIANGLES), quatre (GL_QUADS) ou n cotés (GL_POLYGON).

Remarque : La plupart des primitives simples permettent de définir plusieurs objets de la forme décrite par la primitive dans un seul bloc de commande glBegin()…glEnd(). Si la primitive choisie nécessite n sommets pour être déclarée, n*m points placés dans le bloc glBegin()…glEnd() construisent m primitives comme indiqué sur la figure suivante.

GL_POINTS

V0

V1

V2

V3

V4

V5

GL_LINES

V0

V1

V2

V3

V4

V5

GL_TRIANGLES

V0

V1

V2

V3

V4

V5

GL_QUADS

V0

V1

V2

V4

V7

V5

V3

V6GL_POLYGONS

V0

V1

V2

V4V5

V3

Chaque sommet d'une primitive est placé dans l'espace tridimensionnel de l'objet par un appel de la fonction glVertex3[dfi](x,y,z). Placer un point aux coordonnées (5,6,2) dans l'espace représenté par OpenGL est simplement réalisé par les commandes suivantes :

glBegin(GL_POINTS)

glVertex3i(5,6,2);

glEnd();

De même, les éléments de surface sont placés dans l'espace de la scène en précisant la position de chacun de leurs sommets, par exemple, le code ci-dessous construit le quadrilatère présenté sur la figure ci-contre.

glBegin(GL_QUADS);

glVertex3d(-1.0, 0.5,0.0);

glVertex3d( 0.5, 1.0,0.0);

glVertex3d( 1.0, 0.5,0.0);

glVertex3d( 0.5,-0.5,0.0);

glEnd();

X

Y

X

Y

Benoît Piranda 06/10/2006

13

La déclaration d'un ensemble de facettes partageant des arêtes ou des sommets communs peut être faite en répétant l’utilisation des primitives simples ou bien de façon plus économique (car évitant des redondances de points), par l'utilisation des primitives multiples GL_TRIANGLE_STRIP (faces triangulaires partageant la dernière arête), GL_TRIANGLE_FAN (faces triangulaires partageant le premier sommet), GL_QUAD_STRIP (faces quadrilatères placées sur une grille de sommets) représentées sur la figure ci-dessous.

GL_TRIANGLE_STRIP

V0

V1

V2

V3

V4

V5

GL_TRIANGLE_FAN

V0

V1

V2

V3V4

GL_QUAD_STRIP

V0

V1

V2

V3

V4

V5

V6

V7

GL_LINE_STRIP

V0

V1

V2

V3

V4

V5

GL_LINE_LOOP

V0

V1

V2

V3

V4

V5

V5

GL_TRIANGLE_STRIP

V0

V1

V2

V3

V4

V5

GL_TRIANGLE_FAN

V0

V1

V2

V3V4

GL_QUAD_STRIP

V0

V1

V2

V3

V4

V5

V6

V7

GL_LINE_STRIP

V0

V1

V2

V3

V4

V5

GL_LINE_LOOP

V0

V1

V2

V3

V4

V5

V5

Ces outils permettent de définir des maillages de facettes en ne répétant pas certains sommets partagés par plusieurs facettes. Ces primitives permettent un gain de temps de communication entre la mémoire centrale et la carte graphique en comparaison avec la modélisation d'une scène équivalente exploitant les primitives simples.

Par exemple, si on appelle n le nombre de facettes obtenues à partir de la définition de p points, l’utilisation de primitives GL_QUAD_STRIP permet de définir la relation suivante : n=2p+2 ce qui apporte un gain appréciable si on compare à la relation obtenue dans le cas de l’utilisation de primitives GL_QUADS : n=4p.

Remarque : L’ordre de déclaration des points est important dans l’utilisation des primitives multiples, il doit être respecté pour éviter de construire des polygones croisés. En particulier, il est important de remarquer dans le cas de la primitive GL_QUAD_STRIP que la ligne supérieure est formée par les points d’ordre pair alors que la ligne inférieure est constituée des points d’ordre impair.

Un méthode encore plus économique dans le cas de grands ensembles de données redondantes est l’utilisation des tableaux de paramètres (ou vertex array) présentée au paragraphe VII.3.

VII.2 Notions de normales

La définition mathématiques du vecteur normal à une surface au point P correspond au vecteur orthogonal à tout vecteur du plan tangent à la surface en P. Le vecteur normal utilisé par OpenGL, permet de préciser la direction orthogonale à une surface en un point, il peut suivre ou non cette règle mathématique. Le modèle d'illumination de OpenGL utilise ce vecteur pour calculer l'éclairement de la surface et donc en déduire la couleur visible des points qui la constituent. Intuitivement, une surface très exposée à une source apparaît plus illuminée qu’une surface

admettant un éclairement rasant, cette dimension est estimée par le produit scalaire VNrr

⋅ où Nr est le vecteur

normal à la surface et Vr est le vecteur portant la direction d’éclairement.

Remarque : Utiliser un vecteur normal OpenGL différent du vecteur normal mathématique permet de réaliser des effets d’optique sur les surfaces. Par exemple, le bump mapping utilise cette technique pour donner l’impression qu’une surface géométriquement plane est bosselée en faisant varier son éclairement et donc sa couleur.

La fonction glNormal3[dfi] permet de définir le vecteur normal d’une surface OpenGL au cours de sa définition géométrique. L'affectation d'un vecteur normal à une facette est obtenue en plaçant l'appel à la fonction glNormal3[dfi] avant la fonction glBegin(...). Cette information n’étant pas toujours connue, ou nécessitant un calcul complexe, il est possible de préciser une information de vecteur normal au niveau de chaque sommet lors de leur déclaration à l'intérieur du bloc glBegin(...)...glEnd().

Remarque : Attention, le modèle d’illumination qui exploite les normales a besoin que celles-ci soient des vecteurs normés. Or, certaines transformations tels que le changement d’échelle modifient la norme de ces vecteurs

Benoît Piranda 06/10/2006

14

et donc la couleur calculée par le modèle d’illumination. Il est cependant possible de demander au logiciel de normer automatique ces vecteurs en activant le mode GL_NORMALIZE, mais ce traitement a un coup.

L’exemple ci-contre montre une surface définie par un ensemble de points auxquels on associe un vecteur normal (représenté ici par des petits segments noirs).

VII.2.1 Calcul de la normale mathématique

Le calcul de la normale à une surface en un ensemble de ses points est une opération très courante dans un programme de synthèse d’image, nous rappelons ici quelques notions mathématiques permettant de l’obtenir.

VII.2.1.1 La normale d’une surface paramétrique continue

Le principe de cette méthode consiste à calculer deux vecteurs tangents à la surface, et à déduire la normale comme le produit vectoriel à ces deux vecteurs.

Soit une surface V définie sous la forme paramétrique suivante, tout couple de paramètres (s,t) donne la position un point de la surface, la normale à la surface S existe en tout point où elle est définie et continue :

( ) ( )),(),,(),,(, tsZtsYtsXtsS =

Deux tangentes à la surface au point de paramètres (s,t) sont données par les expressions suivantes :

( ) ( ) ( )

∂∂

∂∂

∂∂

=∂∂=

stsZ

stsY

stsX

sVJ ,,,,,

r

( ) ( ) ( )

∂∂

∂∂

∂∂

=∂∂=

ttsZ

ttsY

ttsX

tVK ,,,,,

r

On en déduit la normale Nr par la relation :

( )xyyxzxxzyzzy

KJKJKJKJKJKJKJN −−−=∧= ,,rrr

Pour normaliser ce vecteur, il suffit de diviser chacune de ses composantes par sa norme d :

222zyx

NNNdrrr

++=

VII.2.1.2 La normale à une facette

Dans le cas d’une facette, les vecteurs tangents correspondent à deux des arêtes de la facette. Il suffit donc de faire le produit vectoriel entre ces vecteurs pour obtenir la normale à la facette.

VII.2.2 Exemple d'application

Par exemple, si on considère la surface d’un tore définie par l’expression paramétrique suivante :

( )( )

=+=+=

θφθφθ

sinsincoscoscos

rzrRyrRx

où R, r, θ et φ sont respectivement le grand rayon, le petit rayon, et les deux paramètres du tore.

On obtient, à partir de cette expression, les deux tangentes suivantes :

=∂∂

−=∂∂

−=∂∂

θθ

θφθ

θφθ

cos

sinsin

sincos

rz

ry

rx

( )

( )

=∂∂

+=∂∂

+−=∂∂

0

coscos

sincos

φ

φθφ

φθφ

z

rRy

rRx

Benoît Piranda 06/10/2006

15

Ces tangentes permettent de trouver la normale à la surface en un point P du tore.

( )( )

( )[ ]

=

++−

+−+−

φφθφθ

θφθφθφθθφθθ

sinsincoscoscos

sinsinsincoscossincoscoscoscoscos

22rRr

rRrrRr

VII.3 Utilisation des tableaux de coordonnées (Vertex Array)

VII.3.1 Principe

Les tableaux de coordonnées (ou Vertex Array) permettent une automatisation de la création de la liste des facettes composant les différents objets de la scène en une seule opération. Cet outil admet deux principaux avantages : − Eviter les redondances d’envois des informations concernant un sommet partagé par de nombreuses facettes, − Accélérer le traitement de transmission de la scène à la carte graphique.

Le principe d’utilisation de ces tableaux de coordonnées consiste à placer dans une table toutes les informations concernant tous les sommets des structures de la scène avant de transmettre ce tableau à la carte graphique. Les informations mémorisées sont bien évidemment les coordonnées spatiales dans la scène mais peuvent aussi être la direction de la normale en ces points, les coordonnées d’éventuelles textures ou bien encore leur couleur.

Une seconde table regroupe les informations topologiques en énumérant la liste des sommets des différentes primitives.

VII.3.2 Utilisation

Pour activer l’utilisation des vertex, il faut appeler la fonction suivante : glEnableClientState(GL_VERTEX_ARRAY);

Ensuite, il est possible de transmettre un tableau de coordonnées spatiales via la fonction glVertexPointer, un tableau de normales (glNormalPointer) de coordonnées de texture (glTexCoordPointer) ou encore de couleur (glColorPointer) sur le modèle suivant :

glVertexPointer(n,type,écart,tableau)

où n correspond au nombre de coordonnées transmises, type est un énuméré qui précise le type des valeurs du tableau transmis (GL_INT, GL_FLOAT, GL_DOUBLE…), écart permet de préciser le nombre de bits séparant deux données consécutives (habituellement 0) et tableau donne l’adresse du premier élément du tableau qui doit contenir au moins n données de type type, plus n*ecart bits.

L’appel de ces fonctions doit être fait à la place des appels aux fonctions telles que glVertex[] permettant de transmettre les informations sur un point.

Ces fonctions apportent déjà un avantage indéniable pour définir chacun des paramètres OpenGL des points d’une scène mais il est possible de généraliser cette capacité en transmettant dans un seul tableau une combinaison de ces informations. Pour cela, il faut construire un tableau dans lequel les informations sur chacun des sommets se succèdent dans un ordre précis. Cet ordre est imposé par OpenGL, il est néanmoins possible de choisir parmi les combinaisons suivantes (variable format) :

GL_V2F, GL_V3F, GL_C4UB_V2F, GL_C4UB_V3F, GL_C3F_V3F, GL_N3F_V3F, GL_C4F_N3F_V3F, GL_T2F_V3F, GL_T4F_V4F, GL_T2F_C4UB_V3F, GL_T2F_C3F_V3F, GL_T2F_N3F_V3F, GL_T2F_C4F_N3F_V3F, et GL_T4F_C4F_N3F_V4F

Où les lettres V indiquent des coordonnées (Vertex), N un vecteur normal, T des coordonnées de texture et C une couleur. Les autres informations sont le nombre et le type de données, F pour float et UB pour unsigned byte. L’ordre des paramètres doit correspondre à l’ordre des lettres correspondante dans le format sélectionné.

La transmission du tableau de données est réalisée par un appel à la fonction glInterleavedArrays(format, écart, tableau).

Une fois ce tableau défini, il faut y ajouter un second tableau d’indices qui précise dans quel ordre parcourir le tableau de données pour construire les différentes facettes de la scène. Par exemple, imaginons une grille de 3 lignes par 4 colonnes formant 6 facettes sur le modèle de la figure ci-contre, il faut indiquer à l’algorithme qui

0 1 2 3

4 5 6 7

8 9 10 11

0 1 2 3

4 5 6 7

8 9 10 11

Benoît Piranda 06/10/2006

16

construit toutes les facettes, les sommets associés à chacune des facettes quadrilatères, dans notre cas le tableau d’indices : (0,1,5,4, 1,2,6,5, 2,3,7,6, 4,5,9,8, 5,6,10,9, 6,7,11,10).

La fonction glDrawElements(primitive,n,type,indices) transmet le tableau d’indices, en précisant la primitive à utiliser (GL_POINTS, GL_LINES, GL_TRIANGLES, GL_QUADS…), le nombre d’indices du tableau et le type de données du tableau parmi GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT). Dans le cas de la grille précédente :

GLuint indices[]={0,1,5,4, 1,2,6,5, 2,3,7,6, 4,5,9,8, 5,6,10,9, 6,7,11,10};

glDrawElements(GL_QUADS,24,GL_UNSIGNED_INT,indices);

Remarque : Les tableaux de coordonnées permettent de ne pas envoyer plusieurs fois les coordonnées d’un même point à la carte graphique, il existe cependant une situation ou cette duplication est nécessaire. La duplication d’un point qui admet deux positions différentes dans le repère texture pour une position unique dans le repère tridimensionnel permet d’éviter les artéfacts apparaissant sur la figure ci-contre. En effet, les facettes concernées admettent des coordonnées de textures très éloignées (correspondant à un tour du tore), d’ou l’effet de compression de la texture.

VII.3.3 Exemple

L’exemple suivant présente une surface texturée construite par un seul appel OpenGL à partir d’un tableau de coordonnées contenant la position, la normale et les coordonnées de texture en chaque point du maillage formant cette surface.

#define REP_X 20.0

#define REP_Y 8.0

#define ECH_X 200.0

#define ECH_Y 80.0

void Surface::position(double alpha,double y,GLfloat *ptr)

{ double v = rayon*alpha + ampl*cos(alpha),

z = ampl*sin(alpha); double dx,dy,dz,d;

dx = -ampl*cos(alpha);

dy = 0;

dz = rayon-ampl*sin(alpha);

d = sqrt(dx*dx+dy*dy+dz*dz);

dx/=d;

dy/=d;

dz/=d;

*ptr=(GLfloat)dx;

ptr++;

*ptr=(GLfloat)dy;

ptr++;

*ptr=(GLfloat)dz;

ptr++;

*ptr=(GLfloat)v;

ptr++;

*ptr=(GLfloat)y;

ptr++;

*ptr=(GLfloat)z;

}

Surface::Surface()

{

nx = 200;

ny = 40; rayon = 0.3;

ampl = rayon*0.6;

// tableau contenant toutes les informations géométriques

// GL_T2F_N3F_V3F texture, normale, position

tabGeom = new GLfloat[8*nx*ny];

tabIndices = new GLuint[4*(nx-1)*(ny-1)];

Benoît Piranda 06/10/2006

17

// remplissage du tableau de données géométriques

int ix,iy;

GLfloat *ptr=tabGeom;

GLuint ind,*ptri=tabIndices;

for (iy=0; iy<ny; iy++)

{ for (ix=0; ix<nx; ix++)

{ // coordonnee de texture

*ptr=(GLfloat)(REP_X*(double)ix/(double)nx);

ptr++;

*ptr=(GLfloat)(REP_Y*(double)iy/(double)ny);

ptr++;

// position et normale

position(ECH_X* ix/(double)nx,ECH_Y*(-0.5+ iy/(double)ny) ,ptr);

ptr+=6;

if (iy!=ny-1 && ix!=nx-1)

{ ind = ix+iy*nx;

*ptri=ind;

ptri++;

*ptri=ind+1;

ptri++;

*ptri=ind+1+nx;

ptri++;

*ptri=ind+nx;

ptri++;

} }

}

initTexture();

}

void Surface::dessiner()

{ glPushMatrix();

glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D,gltextureid);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

glEnableClientState ( GL_VERTEX_ARRAY );

glInterleavedArrays(GL_T2F_N3F_V3F,0,tabGeom);

glDrawElements(GL_QUADS,4*(nx-1)*(ny-1),GL_UNSIGNED_INT,tabIndices);

glPopMatrix();

}

VII.4 Les objets mémorisés

Il est possible de construire des objets indéformables associant un identificateur à une liste de primitives graphiques. L’objet ainsi réalisé conserve les paramètres géométriques et radiométriques utilisés lors de sa définition pour les utilisations futures. Dans le cas où une information n’est pas affectée à la déclaration de l’objet (par exemple sa couleur) l’information courante au moment du dessin de l’objet sera utilisée.

Remarque : Comme dans toute définition d’objet canonique, la définition d’objets mémorisés sous OpenGL doit être faite dans un repère qui sera le repère propre de l’objet lors de sa réutilisation.

Le listing suivant présente un exemple de pré-compilation d'un objet composé de deux facettes, puis deux utilisations de cet objet.

/* construction de l'objet id_plan */

id_plan = glGenLists(1);

glNewList(id_plan, GL_COMPILE);

glNormal3d(0.0,0.0,1.0);

/* construction d'un quadrilataire */

glBegin(GL_QUADS);

glVertex3d(0.0,0.0,0.0);

glVertex3d(0.0,1.0,0.0);

glVertex3d(1.0,1.0,0.0);

glVertex3d(1.0,0.0,0.0);

glEnd();

/* construction d'un triangle /

glBegin(GL_TRIANGLES);

glVertex3d(0.0,0.0,0.0);

Benoît Piranda 06/10/2006

18

glVertex3d(0.0,1.0,0.0);

glVertex3d(0.0,0.5,0.5);

glEnd();

glEndList();

: : : : : : :

/* déformations et déplacements de deux instances de l'objet */

/* définition de paramètres de couleur différents pour les */

/* deux instances */

glDisable(GL_LIGHTING) ;

glPushMatrix();

glTranslated(-2.0,0.0,2.0);

glRotated(-90.0,1.0,0.0,0.0);

glScalef(4.0,4.0,1.0);

glColor3d(1.0,0.0,0.0);

glCallList(id_plan);

glPopMatrix();

glPushMatrix();

glTranslated(0.0,0.0,-5.0);

glScalef(5.0,5.0,1.0);

glColor3d(0.0,1.0,1.0);

glCallList(id_plan);

glPopMatrix();

Quatre objets ont été définis pour réaliser la bielle présentée sur la figure ci-contre. Ces objets correspondent aux éléments indéformables de la scène : − le bras, qui admet un mouvement de rotation, − l’arbre de transmission, − le piston, qui admet un mouvement de translation, − le support.

Les repères propres de chacun de ces objets simples sont choisis de façon à simplifier leur animation, ce choix correspond à un invariant de mouvement de l’objet par rotation. Par exemple, le repère du bras est centré sur son axe de rotation, alors que le repère de l’arbre de transmission est placé sur l’une de ses extrémités.

Benoît Piranda 06/10/2006

19

Chapitre VIII Modélisation radiométrique

A l’instar de la modélisation géométrique qui consiste à transmettre les données spatiales de positionnement et d’orientation des éléments de la scène, la modélisation radiométrique a pour but d’indiquer à la machine les valeurs des paramètres intervenant dans le modèle d’illumination qui détermine l’éclairement des objets en cohérence avec la position des sources de lumière et de l’observateur. Cet éclairement sert ensuite à déterminer la couleur visible de couverture des objets.

OpenGL donne la possibilité à l’utilisateur soit d’imposer la couleur couvrant les facettes soit de donner des paramètres permettant de calculer cette couleur. L’activation de ces modes se fait par les commandes glDisable(GL_LIGHTING) pour désactiver l’usage du modèle d’illumination ou glEnable(GL_LIGHTING) pour l’activer.

VIII.1 Modèle sans illumination

Le mode d’affichage sans illumination est le plus simple et le plus rapide pour OpenGL. Il est utilisé dans deux cas principaux. La première situation correspond à l’affichage d’informations comme des repères, des textes, ou des objets représentant des sources de lumières qui ne doivent pas subir de modification de couleur quel que soit le lieu dans la scène ou la position de l’objet relativement à la source de lumière.

La seconde situation correspond à un affichage d’une scène dont l’illumination a déjà été calculée précédemment par un autre modèle d’illumination (tel qu’un algorithme de radiosité par exemple). Dans ce cas, il faut que OpenGL se contente d’afficher les facettes associées aux couleurs ainsi pré-calculées.

Dans le cas où le modèle d'illumination est désactivé pour une surface, sa couleur apparente est indépendante de la position des sources, et est définie par la fonction glColor[3][4][dfi].

VIII.2 Le modèle d’illumination de OpenGL

VIII.2.1 Modèle théorique

Le modèle radiométrique exploité par OpenGL repose sur une adaptation du modèle d’illumination de Phong [Ph75]. Ce modèle a pour but de faire apparaître les tâches brillantes correspondant aux reflets des sources de lumière sur la surface des objets. Il permet ainsi de différentier des objets mats ou brillants, c’est-à-dire reflétant les sources de lumière. Pour l’aspect mat, le modèle d’illumination de OpenGL reprend le modèle de Lambert, ce modèle indique que l’éclairement de la surface suit une fonction cos(α) ou α est l’angle formé par la direction incidente et la normale à la surface.

La brillance d’un matériau est un phénomène complexe qui fait l’objet d’une grande approximation par le modèle de Phong qui simule la variation d’éclairement par une fonction de la forme (cosβ)NPhong ou β est l’angle formé par la direction d’observation et la direction spéculaire et NPhong est un coefficient réel propre au matériau, faible il diffuse la tache spéculaire, fort il la concentre dans la direction spéculaire.

Dans le cas d’un matériau donné, l’influence de chacun de ces modèles sur son aspect est pondéré par des coefficients réels compris entre 0 et 1 (Kd pour diffus, Ks pour spéculaire). La modélisation d’un matériau mat consiste à lui associer un coefficient Kd fort et Ks faible, alors qu’un matériau brillant admettra un coefficient Kd faible et un coefficient Ks fort.

N

αααα ααααββββ

directionspéculaire

N

αααα ααααββββ

directionspéculaire

Benoît Piranda 06/10/2006

20

L’exemple ci-contre montre trois tores dont l’éclairement est déterminé par le modèle de Phong avec respectivement de gauche à droite les paramètres (Kd=40%,Ks=0), (Kd=20%,Ks=100%,NPhong=5), et (Kd=20%,Ks=100%,NPhong=30).

L’expression mathématique de ce modèle sous forme d’énergie lumineuse est la suivante :

( )( )i

Nsource

i

NPhong

sdsLkkEE ∑

=

++=1

coscos βα

Où : − Es est l’émission propre du matériau, non nulle dans le cas d’un matériau qui produit de lui-même de la lumière

(phosphorescence). − Nsource est le nombre de sources. − Li est l’énergie lumineuse émise par la i

ème source.

VIII.2.2 Utilisation du modèle d’illumination OpenGL

Le modèle d’illumination utilisé par OpenGL ajoute quelques effets à ce modèle théorique dont il s’inspire. Les coefficients Kd, Ks sont affinés en trois composantes de couleurs permettant d’associer une couleur à chaque phénomène, par exemple un matériaux peut avec le modèle OpenGL diffuser dans une couleur et présenter une tache spéculaire d’une autre couleur. La simulation de l’émission propre est un peu différente, elle est combinée à des paramètres de la source. La source de lumière admet elle aussi plusieurs paramètres permettant de préciser l’émission destinée à la diffusion ou à la réflexion spéculaire ou encore à l’émission propre. Une telle décomposition n’a aucun sens physique mais permet de faire apparaître des effets particuliers sur les surfaces.

Le modèle d’illumination considère donc deux grandes catégories d'objets : les sources de lumière et les surfaces recouvertes de matériaux.

VIII.2.2.1 Définition des matériaux

OpenGL définit la notion de matériaux, c'est-à-dire un ensemble de composantes radiométriques qui peuvent être associées à chacune des surfaces. Pour associer un matériau à la surface courante on utilise la fonction glMaterial[fi][v](faces, paramètre, valeurs)) où le paramètre faces précise quels cotés de la facette reçoivent ce paramètre radiométrique (GL_FRONT, GL_BACK ou GL_FRONT_AND_BACK). Les valeurs affectées peuvent être soit un scalaire, soit un tableau (avec l'option [v]) de réels ([df]) ou d'entiers ([i]). Les paramètres radiométriques possibles sont : − Un coefficient Ca de diffusion de la lumière ambiante (GL_AMBIENT), − Un coefficient Cd de ré-émission diffuse de la lumière provenant des sources (GL_DIFFUSE), suivant le modèle

d'illumination de Gouraud, − Un coefficient Cs de ré-émission spéculaire de la lumière provenant des sources (GL_SPECULAR), sur le modèle

d'illumination de Phong. Le coefficient NPhong étant déterminé en utilisant le paramètre GL_SHININESS.

VIII.2.2.2 Exemple de matériau

L’exemple suivant construit une sphère dont on précise les paramètres du modèle d’illumination : ::

GLfloat ambiance[]={0.7,0.7,0.7,1.0};

GLfloat diffusion[]={0.1,0.5,0.8,1.0};

GLfloat speculaire[]={1.0,1.0,1.0,1.0}; GLfloat concentre=5.0;

glPushMatrix();

: : : : : : : :

/* définition du matériaux courant */

glMaterialfv(GL_FRONT,GL_AMBIENT,ambiance);

glMaterialfv(GL_FRONT,GL_DIFFUSE,diffuse);

glMaterialfv(GL_FRONT,GL_SPECULAR,speculaire);

glMaterialf(GL_FRONT,GL_SHININESS,concentre);

/* application sur une sphère */

glutSolidSphere(1.0,16,16);

Benoît Piranda 06/10/2006

21

glPopMatrix();

:

VIII.2.2.3 Les sources de lumière

OpenGL gère jusqu'à 8 sources de lumières (GL_LIGHT[0-7]). Ces sources peuvent être de trois sortes différentes : les sources directionnelles, les sources ponctuelles et les spots.

Les sources directionnelles simulent un éclairage solaire. Elles sont placées à l'infini et leurs rayons lumineux sont parallèles. La direction d'éclairement et précisée par la fonction glLightfv(GL_LIGHT0, GL_POSITION,

position[x,y,z,h]) où la composante h doit être égale à 0.

Les sources ponctuelles décrivent un éclairage de type ampoule électrique. On précise la position de ces sources à l'aide de la fonction glLightfv(GL_LIGHT0, GL_POSITION, position[x,y,z,h]) où la composante h doit être égale différente de 0.

Les spots permettent de modéliser des éclairages de type lampes de bureau. Les paramètres sont les suivant : − La position est précisée par la fonction glLightfv(GL_LIGHT0, GL_POSITION,position), − alors que la direction principale d'éclairement Vi est indiquée par la fonction glLightfv(GL_LIGHT0,

GL_DIRECTION, direction[vx,vy,vz]). − L'angle d'ouverture du cône d'éclairement est donné par la fonction

(glLighti(GL_LIGHT0,GL_SPOT_CUTOFF,angle)), − une fonction glLighti(GL_LIGHT0, GL_SPOT_EXPONENT, 0..128) permet de faire varier la densité de lumière

entre le centre du cône d'éclairement et sa périphérie.

L’ensemble des paramètres du spot permet de définir pour la source n°i la grandeur spoti. Ce paramètre intervient dans le calcul de l’illumination d’une surface pour simuler la zone effectivement éclairée par le spot et la zone d’atténuation. Les valeurs de ce paramètres sont : − Dans le cas ou la source n’est pas un spot, spoti vaut 1, − si le produit scalaire Vi.Li est négatif ou si l’angle est supérieur à CUT_OFF, spoti est nul.

− Dans les autres cas : ( )Exponent

iiiLVspot ⋅=

Pour la source de lumière n°i, on précisera les paramètres radiométriques suivants : − une couleur d'ambiance Ai (GL_AMBIANT), − une couleur de diffusion Di (GL_DIFFUSE), − une émission pour les composantes spéculaires Si des objets (GL_SPECULAR).

VIII.2.2.4 Exemple de source de lumière

L’exemple suivant définit une source de lumière ponctuelle et de type spot dont on précise les paramètres radiométriques :

GLfloat ambiance[]={0.1,0.1,0.1,1.0};

GLfloat diffusion[]={0.6,0.6,0.8,1.0};

GLfloat speculaire[]={0.8,0.8,0.8,1.0};

GLfloat position[]={0.0,0.0,2.0,1.0};

GLfloat direction[]={0.0,0.0,-1.0};

GLfloat angle=45.0f;

/* définition des paramètres de la source n°0 */

glLightfv(GL_LIGHT0,GL_AMBIENT,ambiance);

glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuse);

glLightfv(GL_LIGHT0,GL_SPECULAR,speculaire);

glLightfv(GL_LIGHT0,GL_POSITION,position);

glLighti(GL_LIGHT0,GL_SPOT_CUTOFF,angle);

VIII.2.2.5 Fonctions d'atténuation des sources de lumières

Un facteur d'atténuation permet de simuler la diminution d'énergie reçue en fonction de l'éloignement d de la source. Il repose sur trois paramètres GL_CONSTANT_ATTENUATION kc, GL_LINEAR_ATTENUATION kl, GL_QUADRATIC_ATTENUATION kq passés à la fonction glLight[fv]. L’atténuation de la lumière en fonction de l’éloignement est donné par la fonction suivante :

21

dkdkkA

qlc++

=

Benoît Piranda 06/10/2006

22

VIII.2.2.6 Le modèle d’illumination complet de OpenGL

L'équation suivante correspond à l'adaptation du modèle de Phong utilisée par OpenGL pour calculer la couleur des sommets des primitives graphiques.

[ ]∑−

=

××+××+×

+++×+=

1

02

1N

iimi

emimii

iqlcmLMm

SSpssDDpsdAAspotdkdkk

AAEE

Avec : − Em, Am, Dm et Sm resp. les composantes d'émission, d'ambiance, de diffusion et de réflexion spéculaire du

matériaux, − Ai, Di et Si respectivement les composantes d'ambiance, de diffusion et de réflexion spéculaire associées à la

source i, − ALM est le l'ambiance globale de la scène, − kc, kl et kq permettent de définir l'atténuation, − spoti simule l'angle d'ouverture du spot de la source i, − psd est le produit scalaire entre la direction d'incidence et la normale (nul si négatif), − pss est le produit scalaire entre la direction spéculaire et la direction d'observation (nul si négatif), − e est le coefficient du modèle de Phong permettant de simuler la concentration de la tache spéculaire.

VIII.3 Lissage des couleurs

Le lissage de Gouraud [Go71] est omniprésent dans les images OpenGL. Considérant une couleur associée à chacun de ses sommets, un polygone est lissé par interpolation bilinéaire des couleurs de façon à obtenir un dégradé de couleurs entre les valeurs aux sommets.

Sous OpenGL, si les informations radiométriques sont associées aux sommets des polygones, cette méthode d'interpolation est utilisée pour obtenir une valeur pour ces coefficients radiométriques en chaque point des polygones.

L'activation (mode par défaut) et la désactivation de ce lissage se fait par la fonction : glShadeModel(GL_FLAT / GL_SMOOTH).

Benoît Piranda 06/10/2006

23

Chapitre IX Modélisation géométrique de la scène

IX.1 Les matrices homogènes de transformation

Les objets sont construits dans leur repère propre (on parle d'objets canoniques), ils peuvent ensuite être déformés, déplacés et orientés en appliquant successivement des matrices de transformations géométriques. OpenGL implémente trois transformations géométriques : − L’homothétie (ou étirement, ou changement d'échelle) suivant les axes du repère (glScale[fd](sx,sy,sz)). − La rotation d'angle alpha (en degré) autour de l'axe passant par l'origine et de vecteur directeur V(vx,vy,vz)

(glRotate[fd](alpha,vx,vy,vz)), − la translation de vecteur T(tx,ty,tz) (glTranslate[fd](tx,ty,tz)). L'ordre d'application des matrices de transformation dans un programme est très important, pour bien comprendre le fonctionnement d’OpenGL, il faut tenir compte du fait qu’il utilise des matrices courantes (GL_PROJECTION, GL_MODELVIEW). La première sert à définir les paramètres propres à la caméra la seconde à placer les objets dans la scène. Lorsque l’on veut placer des objets dans une scène, on active la matrice GL_MODELVIEW puis on l’initialise généralement à l’unité. Ensuite chaque appel d’une fonction de transformation géométrique a pour effet de multiplier la matrice courante par la matrice correspondante à cette transformation géométrique. Ainsi, si on veut tourner (R) puis déplacer un objet (T) canonique dans la scène, il faut lui appliquer la transformation géométrique (ToR). La matrice correspondant à cette composition de transformations et la matrice (Mt x Mr) et donc dans le programme les deux fonctions doivent être appelées dans l’ordre suivant :

GlMatrixMode(GL_MODELVIEW);

glLoadIdentity();

glTranslate(...);

glRotate(...);

glCallList(...);

Par exemple, pour appliquer la rotation R puis la translation T sur un point A, il faut multiplier les matrices dans l'ordre Mt x Mr, ce qui se programme de la forme suivante :

glTranslatef(Tx,Ty,Tz);

glRotatef(Ra,Rx,Ry,Rz);

glBegin(GL_POINTS);

glVertex3d(Ax,Ay,Az);

glEnd();

Autre exemple : les deux codes suivants sont équivalents, dans la deuxième version la translation est appliéques à chacun des points de l'objet créé ensuite :

glBegin(GL_LINES);

glVertex3d(0.0,0.0,1.0);

glVertex3d(1.0,0.0,1.0);

glEnd();

glTranslatef(0.0,0.0,1.0);

glBegin(GL_LINES);

glVertex3d(0.0,0.0,0.0);

glVertex3d(1.0,0.0,0.0);

glEnd();

Lorsque l'on déclare ensuite une primitive géométrique par ses sommets, la matrice courante est appliquée à ces positions pour déterminer leur position dans la scène.

Remarque : Attention, le produit matriciel n’est pas commutatif, A.B ≠ B.A. La figure présentée ci-dessous illustre cette propriété en montrant le résultat de l’application de deux transformations géométriques non commutatives dans des ordres différents.

Benoît Piranda 06/10/2006

24

Un canard dans son repère propre, posé sur l’axe x et de verticale z.

Résultat de l’application de la translation T(2x) sur le canard de la figure de droite.

Résultat de l’application de la rotation R(45°,z) sur le canard de la figure de droite.

Résultats de l’application des transformations TxR (en haut) et RxT (en bas) sur le canard de la figure de droite.

IX.2 Fonctions de manipulation des matrices

Les matrices courantes (GL_PROJECTION, GL_MODELVIEW) sont sélectionnées à l’aide de la fonction glMatrixMode, initialisée avec glLoadIdentity ou glLoadMatrix et peuvent être multipliées par une matrice passée en paramètre de la fonction glMultMatrix.

Il est souvent nécessaire de mémoriser une matrice de transformation pour pouvoir la réutiliser ultérieurement. OpenGL propose une gestion de piles de matrices pour réaliser ces opérations. La fonction glPushMatrix() permet d'empiler la matrice courante et glPopMatrix() permet de récupérer la dernière matrice mémorisée comme matrice courante.

Cette pile est une mémoire souvent gérée directement par la carte graphique et d’accès très rapide mais de dimension limitée (dépend du système mais souvent 64 emplacements par défaut). Dans un soucis d’efficacité, le système ne réalise pas de vérification de l’intégrité de cette pile, il est donc à la charge de l’utilisateur de bien dépiler toutes les données empilées en respectant bien l’ordre. Les erreurs de maniement de la pile de matrice provoque souvent des erreurs graves tels que l’arrêt de XWindow, ou le blocage de la carte graphique.

Dans l'exemple suivant, on utilise ces fonctions pour placer deux objets dans un repère commun, la rotation est appliquée aux deux objets alors que les translations sont appliquées spécifiquement à chaque objet.

GlMatrixMode(GL_MODELVIEW); /* matrice de description de la scène /

glRotated(45.0,0.0,0.0,1.0); /* rotation autour de Z /

glPushMatrix(); /* mémorisation de la matrice (1) */

glTranslated(0.0,1.0,0.0); /* modification de la matrice (1) /

glBegin(GL_QUADS);

glVertex3d(-1.0,-0.5,0.0);

glVertex3d( 0.5, 1.0,0.0);

glVertex3d( 0.5, 0.5,0.0);

glVertex3d( 0.5,-0.5,0.0);

glEnd(); glPopMatrix(); /* récupération de matrice (1) comme matrice courante */

glTranslated(1.0,1.0,0.0); /* modification de la matrice (1) */

glBegin(GL_QUADS);

glVertex3d(-1.0,-0.5,0.0);

glVertex3d( 0.5, 1.0,0.0);

glVertex3d( 0.5, 0.5,0.0);

glVertex3d( 0.5,-0.5,0.0);

glEnd();

IX.3 Modélisation de la caméra

En synthèse d’image, la caméra est un objet non visible dans la scène qui permet de définir la position d’observation à partir de laquelle est produit l’image. Elle regroupe un point de vue et un écran placé dans la direction d’observation.

-Z

zmin

xmin,yminxmax,ymax

-X

-Y

zmax

-Z

zmin

xmin,yminxmax,ymax

-X

-Y

zmax

Benoît Piranda 06/10/2006

25

IX.3.1 La définition de l’écran

Les paramètres propres de l’écran correspondent aux coordonnées des coins inférieur gauche (xmin,ymin) et supérieur droit (xmax,ymax) de la fenêtre de projection dans le repère de la caméra ainsi que les distances zmin et zmax représentant respectivement la distance entre l’œil et l'écran et la distance maximale d'observation d'un objet. Tout objet placé en dehors du volume ainsi défini ne sera pas projeté sur l'écran (clipping).

Les distances zmin et zmax sont utilisées par le Z-Buffer pour définir la précision d'échantillonnage en Z, l'écart entre ces deux valeurs doit donc être le plus petit possible pour réduire les imprécisions au niveau des limites des intersections d'objets tout en étant suffisamment grand pour englober tous les objets de la scène.

Ces paramètres de projection en perspective sont définis par l'appel de la fonction glFrustum(xmin, xmax, ymin, ymax, zmin, zmax). La bibliothèque GLu propose une fonction plus intuitive permettant de calculer ces paramètres en passant comme paramètres l'angle d'ouverture de l'objectif de la caméra (a) et le rapport (r) entre la longueur et la largeur de l'écran gluPerspective(a, r, zmin, zmax).

IX.3.2 Le placement de la caméra OpenGL

Placer et orienter une caméra dans une scène tridimensionnelle implique de préciser : − la position de son centre (que l’on appellera centre optique ou point de vue), − la direction d’observation, c’est-à-dire la direction principale suivant laquelle la caméra est tournée, − la direction de verticale permettant d’orienter la caméra autour de l’axe d’observation.

Pour réaliser ce placement de la caméra, deux techniques peuvent être utilisées : placer la scène dans le repère caméra ou placer la caméra dans le repère de la scène. La première solution consiste à considérer la caméra comme l’origine de la scène et les directions d’observation et de vertical comme des axes du repère de la scène. Sous cette hypothèse, les objets de la scène doivent être placés dans ce repère de façon à être visible. Cette solution permet de simplifier certains calculs de projection mais devient vite difficile à exploiter dans le cas d’une caméra mobile. La seconde solution consiste à placer la caméra comme n’importe quel autre objet de la scène en précisant les paramètres de celle-ci.

OpenGL utilise deux systèmes de coordonnées, le système associé à la scène (GL_MODELVIEW) et le système associé au point de vue (GL_PROJECTION). On choisit la matrice courante à l'aide de la fonction glMatrixMode(). Le repère de la caméra canonique est tel que les axes X et Y correspondent aux axes de l'image, et la direction de visée correspond à l'axe -Z (pour construire un trièdre direct) comme indiqué sur la figure ci-contre. Les transformations de rotation et de translation permettent de placer et d'orienter la caméra de façon similaire aux objets de la scène.

Le programme suivant donne un exemple de placement d'une caméra. GLfloat h = height / width;

/* dimension de l'écran GL */

glViewport(0, 0, (GLint)width, (GLint)height);

/* construction de la matrice de projection */

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

/* définition de la camera */

glFrustum( -1.0, 1.0, -h, h, 5.0, 60.0 );

/* recul de la caméra */

glTranslatef( 0.0, 0.0, -15.0 );

/* initialisation de la matrice de la scène */

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

/* description de la scène ci-dessous ... */

Une autre solution pour placer les objets relativement à la caméra canonique consiste à appliquer la translation de recul de la caméra à l'ensemble des objets de la scène.

La fonction gluLookat(ox,oy,oz, cx,cy,cz, ux,uy,uz) propose un moyen plus intuitif de placer la scène relativement à la caméra canonique en précisant le point de vue O(ox,oy,oz), le point visé C(cx,cy,cz) et l'axe vertical U(ux,uy,uz) de la caméra dans les coordonnées de la scène.

/* modification de la matrice de la scène */

glMatrixMode(GL_MODELVIEW);

glPushMatrix();

/* placement de la caméra */

gluLookAt(-10.0,0.0,3.0,0.0,0.0,0.0,0.0,0.0,1.0);

Benoît Piranda 06/10/2006

26

/* placement des objets dans la scène : */

glPushMatrix();

glRotated(rotz,0.0,0.0,1.0);

glCallList(id_plan);

glPopMatrix();

glPopMatrix();

IX.3.3 Utilisation de plusieurs caméras pour afficher des informations à l’écran

Il est possible d’afficher des informations à l’écran tels que des dessins schématiques en 2D, ou des textes avec OpenGL. Cette bibliothèque propose pour cela de placer des informations graphiques sur l’écran de projection de la scène, qui correspond à l’écran recevant l’image finale.

Le problème réside donc dans le changement des paramètres de la caméra de projection au cours du dessin de la scène, le traitement est décomposé en plusieurs étapes : − La création de la matrice de projection centrale de la caméra 3D est calculée une fois lors de la définition de la

taille de l’écran. − Dans la fonction de dessin, la scène 3D est tout d’abord décrite. − Puis la matrice de projection courante est mémorisée dans la pile et remplacée par une matrice de projection

parallèle pour le plaquage des informations 2D. − Après être repassé en mode modélisation de la scène, on place à l’écran les informations 2D. − Enfin on replace la matrice de projection centrale mémorisée précédemment.

L’exemple ci-dessous illustre cette décomposition : // fonction permettant d’afficher une chaine de caractère à l’écran

// attention : doit être terminée par le caractère \0

void drawString(GLfloat x,GLfloat y,char *ptr)

{ glRasterPos2f(x,y);

while (*ptr)

{ glutBitmapCharacter(GLUT_BITMAP_8_BY_13, *ptr);

ptr++;

}

}

:

// modélisation de la scene 3D

glPushMatrix();

glRotated(30.0,0.0,1.0,0.0);

surf->dessiner();

glPopMatrix();

:

// passage en mode orthogonale

glMatrixMode(GL_PROJECTION);

glPushMatrix();

glLoadIdentity();

glOrtho(0,1024,0,768, -1, 1);

glMatrixMode(GL_MODELVIEW);

glPushMatrix();

glLoadIdentity(); // dessin de la scène 2D

glDisable(GL_LIGHTING);

glColor3f(1.0,1.0,1.0);

drawString(20.0,20.0,chainevm);

glEnable(GL_LIGHTING);

// retour au mode caméra 3D

glMatrixMode(GL_PROJECTION);

glPopMatrix();

glMatrixMode(GL_MODELVIEW);

glPopMatrix();

Benoît Piranda 06/10/2006

27

Chapitre X Les textures

L'application de textures sur les surfaces apporte du réalisme aux images synthétisées. Le principe des textures est de faire varier le résultat de la fonction d’illumination localement à la surface pour en rendre l’aspect plus irrégulier. Une texture peut être linéaire, surfacique (papier peint) ou volumique (bois). Les textures ont la possibilité d’habiller une surface avec un ensemble de couleurs comme pour un papier peint, mais elles représentent aussi un premier niveau d’effets spéciaux lorsqu’elles utilisent un effet d’optique pour donner l’impression qu’une surface géométriquement lisse est rugueuse voire bosselée avec la technique du Bump Mapping. Les textures d’environnement (Environment mapping) proposent une autre astuce apportant une solution rapide aux traitements des réflexions spéculaires du décor lointain sur la surface d’un objet.

X.1 Les textures simples 2D

Les textures les plus utilisées sont les textures à deux dimensions, elles reposent sur une utilisation d’une image qui doit être déformée (étirée ou compressée) pour être appliquée (on dit aussi plaquée) sur la surface.

Remarque : OpenGL utilise, pour définir ses textures, des images de dimension (longueur et largeur) s’exprimant sous la forme 2n et contenant des couleurs sur 3 ou 4 composantes (la quatrième composante faisait office de coefficient de transparence).

Remarque : Il est possible de définir des facettes aux contours irréguliers en combinant une facette classique avec une texture en partie transparente. Cette astuce peut s’avérer intéressante pour, par exemple, définir une feuille d’érable à partir d’une simple facette rectangulaire.

L'utilisation de textures se déroule en deux étapes, tout d'abord l'affectation de la matrice de pixels comme une texture de OpenGL, puis le plaquage de cette texture implique d'associer à chaque sommet de la surface ses coordonnées dans le repère texture en plus de ses coordonnées dans le repère de la scène.

texture

(0,2) (1,2)

(0,0) (1,0)

facette avec coordonnées de texture facettes illuminées et texturéestexture

(0,2) (1,2)

(0,0) (1,0)

facette avec coordonnées de texture facettes illuminées et texturées

Les paramètres transmis à la fonction glTexParameter[ifv] permettent de préciser le mode de répétition de la texture sur la surface (paramètres GL_TEXTURE_WRAP_S et GL_TEXTURE_WRAP_T avec les valeurs GL_CLAMP ou GL_REPEAT).

Le mode de combinaison de la texture avec la couleur issue du modèle d’illumination est choisi par la fonction glTexEnv[fv] (paramètres GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE avec les valeurs GL_MODULATE pour mélanger, GL_DECAL pour imposer la couleur de texture).

Le code suivant est un exemple de construction d'une texture à partir d'un tableau image contenant des couleurs sur 3 octets.

// demande d'un identificateur de texture libre glGenTextures(1,&id);

// choix de la texture courante

Benoît Piranda 06/10/2006

28

glBindTexture (GL_TEXTURE_2D,id);

// organisation des plans dans le tableau

glPixelStorei (GL_UNPACK_ALIGNMENT, 1);

// paramètre de répétition de la texture

glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

// la texture sera multipliée par la couleur du modèle d'illumination

glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

// copie de l’image dans la texture courante

glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,dx,dy,0,GL_RGB,GL_UNSIGNED_BYTE, image);

L'affectation de coordonnées dans le repère texture est réalisée par la fonction glTexCoord2[dfi] sur l'exemple suivant :

glEnable(GL_TEXTURE_2D);

// choix de la texture courante glBindTexture (GL_TEXTURE_2D,id);

glBegin(GL_QUADS);

glTexCoord2i(0,0);

glVertex3i(0,0,0);

glTexCoord2i(1,0);

glVertex3i(10,0,0);

glTexCoord2i(1,1);

glVertex3i(10,10,0);

glTexCoord2i(0,1);

glVertex3i(0,10,0);

glEnd();

glDisable(GL_TEXTURE_2D);

X.2 Les textures multi-échelles

Le mipmapping ou le plaquage de texture multi-échelle apporte un gain en terme de qualité et de rapidité dans l’utilisation des textures sous OpenGL. En effet, les principaux inconvénients des textures issues du plaquage d’une image sur les facettes sont les suivants : − l’effet de pixélisation qui apparaissent lorsque l’on observe une surface de trop près, ou lorsque l’on

plaque une texture de faible résolution sur une grande facette, − et à l’opposé la perte de temps due au plaquage d’une texture de grande dimension sur une facette de

quelques pixels.

Les textures multi-échelle apportent une adaptation automatique de la taille de la texture plaquée en fonction de la taille des facettes qui la reçoivent. Chaque texture est définie pour un ensemble de résolution correspondant aux exposants de 2. Par exemple, si la texture la plus précise est de résolution 256x256 pixels, il faut fournir toutes les textures de résolutions inférieures (128x128, 64x64, 16x16, 8x8, 4x4 et 2x2). Ces textures sont ensuite automatiquement sélectionnées par OpenGL en fonction de la distance d’observation d’une facette recouverte par la texture de plus haute résolution.

La génération de textures intermédiaires spécifiques étant peu courant, elles sont classiquement produites à partir d’un changement de résolution d’une texture initiale. La bibliothèque GLU propose une fonction (gluBuild2DMimaps) permettant d’automatiser ce traitement à partir de la texture la plus précise (de taille maximale) sur le modèle de l’exemple suivant :

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

glGenTextures(1,&gltextureid);

glBindTexture(GL_TEXTURE_2D,gltextureid);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);

gluBuild2DMipmaps(GL_TEXTURE_2D,GL_RGB,lx,ly,GL_RGBA,GL_UNSIGNED_BYTE,image);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

glBindTexture(GL_TEXTURE_2D,0);

delete [] image; // la mémoire peut être libérée, une copie a été faite par OpenGL

Benoît Piranda 06/10/2006

29

X.3 Les textures d’environnement

L’utilisation des textures d’environnement propose une solution à la simulation des réflexions spéculaires en temps réel sous OpenGL. En effet, ces textures vont permettre de plaquer sur un objet une image du décor extérieur de la scène. OpenGL ne propose pas de solution complète mais permet de gérer l’utilisation d’images particulières représentant une projection d’une vue de l’environnement en coordonnées sphériques sur une texture plane.

Le principe consiste à calculer préalablement une image de l'environnement entourant l'objet texturé, en supposant que cette image se situe sur la surface intérieure d'une sphère infiniment grande centrée sur cet objet. Sous cette hypothèse, deux rayons réfléchis parallèles atteignent le même point de cette sphère, ce qui revient à dire que seule la direction de réflection importe et non la position du point d'origine du rayon.

Pour faire de telles textures de reflets, il faut transmettre à OpenGL pour chaque sommet de l'objet, les coordonnées de texture correspondant à la position du point de la sphère vue depuis ce sommet, simplement en calculant la direction de réflection qui est le symétrique de la direction de l'observateur par rapport à la normale au sommet, ces deux informations étant connues au moment où est effectué l'appel à la fonction glVertex(). Ce plaquage particulier est obtenu par:

glEnable(GL_TEXTURE_GEN_S);

glEnable(GL_TEXTURE_GEN_T);

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

Benoît Piranda 06/10/2006

30

Chapitre XI Le brouillard

Nous présentons ici le phénomène du brouillard comme un cas particulier des milieux participant et la méthode de simulation utilisée par la bibliothèque OpenGL.

XI.1 Notion de milieux participants

Un milieu participant est un ensemble de particules en suspension dans l’air de taille suffisamment fines pour entrer en interaction avec la lumière qui la traverse. Ces particules peuvent être transparentes comme des gouttelettes d'eau ou opaques comme de la poussière ou de la pollution, ce qui amplifie encore les phénomènes d'interactions avec la lumière.

Lorsqu’un rayon de lumière traverse un milieu participant, il apparaît des interactions visant soit à réduire soit à augmenter l’énergie qu’il transporte [La94] [SP94] [SH72]. Ces phénomènes peuvent être classés en quatre catégories : − Deux phénomènes réduisent l’énergie transportée par un rayon. Le long d’un chemin de la lumière, une

fraction de l’énergie est absorbée par les molécules du milieu (absorption), et une autre fraction est perdue par diffusion dans toutes les directions (diffusion sortante).

− Deux phénomènes inverses augmentent l’énergie transportée par un rayon à travers un milieu participant : l’éclairement provenant de l’émission propre des molécules du milieu (émission) et la réception des énergies diffusées (diffusion entrante) dans les zones voisines du milieu traversé.

XI.1.1 Les phénomènes d’absorption et de diffusion

L’absorption et la diffusion (out-scattering) dans les milieux participants sont deux phénomènes intimement liés. Lorsqu’un rayonnement électromagnétique traverse le milieu, les molécules de gaz qui le compose passent d’un état de repos à un état excité, en contrepartie le rayonnement est en partie absorbé. Cet état excité est instable, le retour à un état de repos s’accompagne d’une émission d’énergie considérée de même fréquence que le rayonnement excitateur dans toutes les directions. Cette émission est une des composantes de la diffusion, l’autre partie, plus directionnelle est provoquée par la déviation des photons sur les molécules composant le milieu. Ce phénomène dépend fortement de la longueur d’onde et de la taille des particules composant le milieu.

La modélisation de ces phénomènes passe par la définition de deux coefficients de diminution de l’énergie au cours de sa propagation dans le milieu, Ka pour l’absorption et Kd pour la diffusion (scattering en anglais). Le phénomène global d’absorption / diffusion est simulé en définissant le coefficient Kt = Ka + Kd appelé coefficient d’extinction du milieu. Il dépend de la température, de la pression du milieu ainsi que de la longueur d’onde de la radiation qui le traverse.

La loi de Bouguer Lambert nous donne une relation exprimant l’énergie reçue en un point S1 depuis un point S0 séparés par un milieu participant de coefficient d’extinction Kt :

( ) ( ) ( )

−= ∫

1

0

01

s

s

T dssKexpSLSL (1.4)

XI.1.2 L’émission propre

Les milieux participants comme les surfaces peuvent émettrent spontanément une énergie propre, c’est le cas des gaz tels que le néon sous certaines conditions de température et de pression.

Benoît Piranda 06/10/2006

31

XI.1.3 La diffusion entrante

La diffusion classique en un point d’un milieu semi-transparent affecte toutes les zones voisines. Par conséquent, considérant un point du milieu, il reçoit de l’énergie diffuse entrante (in-scattering) depuis toutes les directions. Cette énergie contribue à augmenter la luminance dans la direction considérée.

XI.2 Simulation du brouillard par OpenGL La bibliothèque OpenGL propose trois modèles permettant de simuler l’absorption et la diffusion de la lumière

dans les milieux participants. Ces modèles reposent sur trois fonctions de variations de l’énergie lumineuse en fonction de l’épaisseur de gaz traversé.

Ces fonctions sont identifiées par OpenGL par GL_EXP, GL_EXP2 et GL_LINEAR. Elles admettent comme paramètres la densité d (GL_FOG_DENSITY), la couleur Cf (GL_FOG_COLOR), et pour GL_LINEAR des distances d’entrée z0 (GL_FOG_START) et de sortie du milieu z1 (GL_FOG_END). Ces fonctions dépendent donc de la profondeur z du point de la facette affichée, elle admettent les expressions suivantes :

− GL_EXP : ( )zdef ×−=

− GL_EXP2 : ( )2zdef ×−=

− GL_LINEAR : 10

1

zzzz

f−

−=

La figure ci-contre présente les variations de ces fonctions pour quelques valeurs de paramètres :

Ces fonctions sont ensuite utilisées pour déterminer la couleur d’un pixel à partir de la couleur Ci de la facette qui l’éclaire et de la couleur Cfog du brouillard par l’expression :

( )fogi

CfCfC ×−+×= 1

Intuitivement, plus le brouillard est dense ou épais, plus le paramètre f est proche de 0 et donc plus la couleur affichée correspond à la couleur propre du brouillard. Pour les facettes proches ou placées dans un brouillard de densité faible, la couleur de la facette ou du fond est prédominante.

Par exemple, la figure ci-dessous montre la variation de couleur du brouillard en fonction de l’épaisseur de milieu traversé pour les différents modèles et paramètres proposés par OpenGL. On peut remarquer deux phénomènes : l'absorption qui rend les objets distant moins visible dans le brouillard et la diffusion qui ajoute un voile coloré (ici bleu clair) sur les objets distants.

0

0,1

0,2

0,3

0,4

0,5

0,6

0,7

0,8

0,9

1

0 1 2 3 4 5 6 7 8 9 10

GL_EXP(0.25)

GL_EXP(0.5)

GL_EXP2(0.25)

GL_EXP2(0.5)

GL_LINEAR

Benoît Piranda 06/10/2006

32

Remarque : La fonction GL_EXP2 permet de réaliser des brouillards qui deviennent très rapidement opaques, ce qui permet de réduire la profondeur de visibilité de l’environnement. Cette astuce est souvent utilisée dans les logiciel tels que les simulateurs de vol ou de conduite de façon à ce que le décor se perde rapidement dans le brouillard. Elle permet de simplifier la scène à afficher effectivement et d'éviter les problèmes produits par le Zbuffer dans le cas de scènes très profondes.

XI.3 Programmation du brouillard

L’activation du mode brouillard est réalisée par la fonction glEnable(GL_FOG), il faut ensuite définir la fonction d'atténuation choisie, de couleur, et éventuellement les paramètres de densité, et de distances d'effet du brouillard à l’aide de la fonction glFog[ifv] sur le modèle suivant :

GLfloat fcolor[]={0.5f,0.5f,0.8f};

glEnable(GL_FOG);

glFogfv(GL_FOG_COLOR,fcolor); // couleur du brouillard

glFogi(GL_FOG_MODE,GL_EXP); // choix de la fonction d'atténuation

// dans le cas de l'utilisation des fonctions GL_EXP et GL_EXP2

glFogf(GL_FOG_DENSITY,0.01f); // densité

// dans le cas de l'utilisation de la fonction GL_LINEAR

glFogf(GL_FOG_START,50.0f);

glFogf(GL_FOG_END,100.0f);

Remarque : Pour obtenir un meilleur aspect visuel sur le fond de l'écran choisissez la même couleur pour le fond et pour le brouillard. Plus précisément, si le fond sans brouillard est de couleur Ci, la couleur de fond Cfond à appliquer avec le brouillard activé est donnée par la formule suivante :

( )fogfarifarfond

CfCfC ×−+×= 1

Benoît Piranda 06/10/2006

33

Chapitre XII Lecture et écriture dans les buffers

La communication avec la carte graphique pour y lire des données ou lui transmettre des images se fait par l'intermédiaire de fonctions d'accès aux buffers. Ces besoins sont très classiques, ils consistent par exemple à : − récupérer l'image produite par OpenGL pour l'écrire sur disque et faire ainsi une copie d'écran, − afficher directement l'image stockée dans un tableau à l'écran, par exemple provenant d'un flux vidéo, − exploiter la profondeur de chaque pixel pour réaliser des effets spéciaux sur l'image de synthèse. − Mémoriser le résultat d'un calcul dans une image intermédiaire.

Les fonctions OpenGL qui permettent d'accéder aux buffers sont principalement glReadPixels et glDrawPixels.

XII.1 Exemple de sauvegarde de l'écran

La fonction suivante permet de sauvegarder l'image de l'écran OpenGL sous la forme d'un fichier au format PPM dont le nom est passé en paramètre à la fonction. Ce format très simple peut ensuite être exploités par des logiciels tels que The Gimp ou display (ImageMagick) sous linux ou Photoshop sous Windows avec l'extension .raw).

void sauverEcran(char *titre)

{ FILE *fichier = fopen(titre,"wt");

unsigned char *pixels;

int w,h;

/* dimensions de la fenêtre */

w = glutGet(GLUT_WINDOW_WIDTH);

h = glutGet(GLUT_WINDOW_HEIGHT);

pixels = (unsigned char*) malloc(3*w*h);

/* lecture des pixels dans le buffer d'affichage */

glReadPixels(0,0,w,h,GL_RGB,GL_UNSIGNED_BYTE,(GLvoid*) pixels);

/* écriture de l'entete du fichier PPM */

fprintf(fichier,"P6\n%d %d\n255\n",w,h);

/* écriture de l'image */

fwrite(pixels,w*h*3,1,fichier);

fclose(fichier);

free(pixels);

}

XII.2 Exemple d'affichage d'une image à l'écran

L'affichage d'une image à l'écran est une action qui peut être réalisée en par une seule instruction glDrawPixels, cependant ce programme affiche l'image de bas en haut (les premières lignes du tableau de données sont affichées en bas à l'écran. Pour compenser ce problème, l'image peut être retournée dans le tableau en mémoire ou bien être affichée ligne après ligne dans l'ordre inverse. C'est cette solution qui a été retenue dans l'exemple suivant qui visualise une image couleur vidéo de résolution 720x576 mémorisée dans le tableau video :

unsigned char *ptr=video;

int i=576;

while (i--)

{ glRasterPos2i(0,i);

glDrawPixels(720,1,GL_RGB,ptr);

ptr+=720*3;

}

Benoît Piranda 06/10/2006

34

XII.3 Utilisation du buffer d’accumulation

Nous présentons ici l'utilisation du buffer d'accumulation à travers un exemple simple : l'anti-aliasing. L’effet de crénelage (ou aliasing) est un phénomène courant en synthèse d’image, il apparaît principalement aux frontières des objets lorsque ceux-ci se découpent sur un fond de couleur fortement différente sous la forme d’escaliers de pixels.

Deux méthodes peuvent être utilisées pour corriger ce phénomène : − La première, assez exigeante en ressources, consiste à calculer plusieurs images décalées les unes par rapport

aux autres d’une distance inférieure à celle du pixel. Puis l’image anti-aliasée résultante est construite comme la moyenne des couleurs des différentes images précédentes.

− La seconde, appliquée sur les bords des facettes uniquement utilise la couleur de transparence des pixel pour simuler un recouvrement partiel de la facette sur le pixel qui sera mélangé aux couleurs placées dessous.

L’utilisation du buffer d’accumulation (accumulation buffer) permet de mettre en œuvre la première méthode. Chacune des n images est ajoutée aux précédentes, pondérées par le coefficient 1/n dans une mémoire particulière adaptée à ce traitement.

La fonction de dessin de l’exemple suivant permet de dessiner NAA² fois la scène qui doit être décrite dans la fonction drawScene(). Les images sont pondérées et ajoutées dans le buffer d’accumulation (par la commande glAccum(GL_ACCUM,1.0/(NAA*NAA)). En fin de création, l’image résultante est affichée dans le buffer de couleur par la commande glAccum(GL_RETURN,0).

#define NAA 2

#define ECH 1.5

float r = ECH*height/width,decalx=ECH/(NAA*width),decaly=ECH/(NAA*height);

double x,y;

int i,j;

glEnable(GL_ACCUM);

glClear(GL_ACCUM_BUFFER_BIT);

for (i=0,x=-decalx/2; i<NAA; i++,x+=decalx)

{ for (j=0,y=-decaly/2; j<NAA; j++,y+=decaly)

{ /* initialisation des buffers : couleur et ZBuffer */

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

/* définition de la camera */

glFrustum(-ECH+x,ECH+x,-r+y,r+y,1.0,20.0 );

/* modification de la matrice de la scène */

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

drawScene();

glFlush();

glAccum(GL_ACCUM,1.0/(NAA*NAA));

}

}

glAccum(GL_RETURN,1.0);

L’exemple ci-contre présente deux images agrandies (2x) de la même scène sans anti-crénelage en haut et avec anti-crénelage 2x2 en bas, calculées avec le buffer d’accumulation. Les effets d’escalier s’estompent dans la deuxième image, même avec un nombre d’images réduit (quatre dans notre cas).

Remarque : Le buffer d’accumulation n’est géré matériellement que par les cartes graphiques les plus performantes, cette fonction est souvent réalisée par logiciel sur le processeur central ce qui fait chuter très rapidement les performances du Z-Buffer.

De nombreux effets spéciaux peuvent être réalisés en exploitant simultanément plusieurs résultats de calcul d'image de synthèse mémorisé dans des textures. Par exemple, il est possible de réaliser simplement un programme

Benoît Piranda 06/10/2006

35

faisant apparaître un effet de flou de déplacement (motion blur) permettant de mettre en évidence la vitesse de déplacement d'un objet. Pour cela, il suffit de mémoriser plusieurs images obtenue à des instants différents que l'on mélange par un effet de transparence.

XII.4 Utilisation du buffer de profondeur

Le buffer de profondeur est une mémoire utilisée par l'algorithme du Z-Buffer pour mémoriser la distance d'un objet par rapport à l'écran pour chacun de ses pixels. L'exploitation de ce buffer permet de réaliser des traitements fins qui sont fonctions de la distance d'observation tels que l'ajouts d'effets de milieux participants (effets spéciaux atmosphériques).

XII.4.1 Exemple de récupération du buffer de profondeur

//creation de la texture avec texWidth=1024 et texHeight=1024

glGenTextures(1, &depthTexture);

glBindTexture(GL_TEXTURE_2D, depthTexture);

glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texWidth, texHeight,

0,GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

//Recuperation du zbuffer dans la texture courante glBindTexture(GL_TEXTURE_2D, depthTexture);

glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, texWidth, texHeight );

glBindTexture( GL_TEXTURE_2D, 0 );

Benoît Piranda 06/10/2006

36

Chapitre XIII Les effets spéciaux

XIII.1 La transparence

OpenGL gère quatre plans de couleurs de façon à intégrer la notion de transparence dans la quatrième composante appelée alpha : plus cette valeur est proche de 0, plus l'objet est transparent, et plus elle est proche de 1 plus l’objet est opaque.

La notion de transparence est ici simplement un mélange entre les couleurs des facettes se projetant sur le même pixel. Le modèle d'illumination ne tient pas compte de coefficients de transmission ni de la loi de Descartes, les effets obtenus sont similaires à des calques semi-transparents dont l’ordre et le coefficient de transparence sont les paramètres du résultat.

XIII.1.1 Fonctions OpenGL pour la transparence

La gestion de la transparence est activée par les commandes : glEnable(GL_BLEND);

et désactivée par la commande : glDisable(GL_BLEND);

La couleur résultante de la combinaison d’une couleur source S(Rs,Vs,Bs,As), de la facette en cours de dessin et d’une couleur destination D(Rd,Vd,Bd,Ad) du pixel déjà éclairé est obtenue par la formule suivante :

DCSCC ×+×=21

Les deux coefficients C1 et C2 sont les paramètres de l’algorithme de combinaison proposé par OpenGL. Leur expression peut être sélectionnée dans une liste par la fonction glBlendFunc. Deux paramètres doivent être passés à cette fonction, le premier détermine la valeur de C1 et le second la valeur de C2. Le tableau suivant présente les différentes valeurs de ces paramètres :

Paramètre C1 ou C2

GL_ZERO (0, 0, 0, 0)

GL_ONE (1, 1, 1, 1)

GL_SRC_COLOR (Rs, Vs, Bs, As)

GL_ONE_MINUS_SRC_COLOR (1-Rs, 1-Vs, 1-Bs, 1-As)

GL_DST_COLOR (Rd, Vd, Bd, Ad)

GL_ONE_MINUS_DST_COLOR (1-Rd, 1-Vd, 1-Bd, 1-Ad)

GL_SRC_ALPHA (As, As, As, As)

GL_ONE_MINUS_SRC_ALPHA (1-As, 1-As, 1-As, 1-As)

GL_DST_ALPHA (Ad, Ad, Ad, Ad)

GL_ONE_MINUS_DST_ALPHA (1-Ad, 1-Ad, 1-Ad, 1-Ad)

GL_SRC_ALPHA_SATURATE (i, i, i, 1) où i=min(As,1-Ad)

Le couple de fonctions utilisé le plus couramment pour simuler des objets transparents est (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) associé à un affichage des objets dans l’ordre du plus éloigné au plus proche de l’observateur. La valeur de couleur obtenue suit la formule suivante :

( )DASACss

−+×= 1

Benoît Piranda 06/10/2006

37

Formule que l’on peut expliciter par : la couleur obtenue est la couleur intermédiaire entre la couleur source et la couleur destination réglée linéairement par le paramètre As.

XIII.1.2 Exemple de transparence

Le code suivant présente les étapes de définition de la scène permettant de réaliser l’image ci-contre, où l’on peut observer un repère construit à base de cônes (rouge, vert et bleu) opaques et une théière jaune légèrement transparente.

Définition des tableaux contenant les composantes de couleurs. GLfloat c1[4] = { 0.8f, 0.8f, 0.2f, 0.7f}; // ocre transparent (30%)

GLfloat cr[4] = { 1.0f, 0.0f, 0.0f, 1.0f}; // rouge opaque

GLfloat cv[4] = { 0.0f, 1.0f, 0.0f, 1.0f}; // vert opaque

GLfloat cb[4] = { 0.0f, 0.0f, 1.0f, 1.0f}; // bleu opaque

Dessin des objets opaques /* construction du repère id_fleche */

glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,cr);

glPushMatrix();

glRotated(90,0.0,1.0,0.0);

glutSolidCone(0.15,2.5, 50, 10);

glPopMatrix();

glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,cv);

glPushMatrix();

glRotated(-90,1.0,0.0,0.0);

glutSolidCone(0.15,2.5, 50, 10);

glPopMatrix();

glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,cb);

glutSolidCone(0.15,2.5, 50, 10);

Dessin des objets transparents par dessus les objets opaques déjà dessinés, de façon à les faire apparaître par transparence.

/* activation de la transparence */

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

/* construction de l'objet théière */

glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,c1);

glPushMatrix();

glRotated(90,1.0,0.0,0.0);

glutSolidTeapot(1.0);

glPopMatrix();

XIII.1.3 Transparence et texture

L’utilisation de la transparence dans les textures permet de réaliser des masques. Ces masques proposent une solution permettant de limiter la contrainte de modélisation géométrique en lui substituant des contraintes de modélisation radiométriques.

En contrepartie, le Z-Buffer doit être désactivé pour éviter que des zones transparentes « cachent » des objets placés derrière.

Par exemple la modélisation géométrique d’une feuille d’arbre est une action beaucoup plus compliquée que l’application d’une texture de masque découpant la forme de la feuille. Dans certaines situations, où l’on connaît la position des feuilles les unes par rapport aux autres et dans la scène globale, il est pratique d’utiliser des textures en partie transparentes comme sur l’image ci-contre.

Benoît Piranda 06/10/2006

38

Chapitre XIV Sélection d’un objet à l’écran

De nombreuses applications multimédia utilisent les éléments graphiques de la scène 3D comme interface entre l’utilisateur et le programme. Par exemple, le fait de cliquer sur la poignée d’une porte visualisée en 3D à l’écran indique au logiciel d’ouvrir cette porte.

Une telle interface utilise la capacité à récupérer l’information suivante : quel objet a été projeté sur la zone de l’image placée sous le curseur de la souris au moment du clic. Si plusieurs objets sont projetés sur le même pixel, il est nécessaire de différencier leur distance d’éloignement de l’écran pour sélectionner, par exemple, le plus proche.

Ce traitement est proposé par la bibliothèque OpenGL par l’utilisation d’un mode de dessin de la scène particulier (GL_SELECT). Il consiste à réaliser le même travail que lors de la création d’une image mais en mémorisant un identifiant de l’objet projeté sur chaque pixel. On remarquera que dans ce mode, l’aspect rendu délivré par le modèle d’illumination n’est pas pris en compte, il n’est pas nécessaire de calculer la couleur visible de l’objet, seule sa géométrie compte.

L’utilisation du mode GL_SELECT consiste à nommer les objets que l’on désire rendre sélectionnables durant leur affichage à l’écran, c’est-à-dire ceux qui présentent un intérêt dans l’interface. La liste de noms ainsi crée est placée dans une pile.

La zone d’affichage est ensuite réduite à quelques pixels placés autour du curseur. On utilisera pour cela une fonction de la bibliothèque GLu qui produit un masque placé sur la matrice de modélisation courante. Uniquement les objets placés dans la zone de pixels ainsi définie seront effectivement ‘dessinés’ pour la phase de sélection. Cette étape d’affichage est un peu particulière, elle est réalisée dans une mémoire (buffer) que l’on n’affiche pas à l’écran. Ici la radiométrie n’a aucun intérêt, seule les noms sont mémorisées dans le buffer. Ainsi, on pourra désactiver tous les paramètres du modèle d’illumination pour effectuer une rechercher plus rapide de l’objet cliqué.

Exemple de fonction de sélection : void selection(int x,int y)

{ GLuint selectBuf[BUFSIZE];

GLint hits;

GLint viewport[4];

glGetIntegerv(GL_VIEWPORT,viewport);

glSelectBuffer(BUFSIZE,selectBuf); glRenderMode(GL_SELECT);

// initialisation de la pile de nom (-1 nom par défaut)

glInitNames();

glPushName(-1);

// mise en place de la matrices de projection (cohérente avec l’affichage)

glMatrixMode(GL_PROJECTION);

glPushMatrix();

glLoadIdentity();

// filtrage de la zone de sélection (ici un bloc de 9 pixels centrés sur x,y)

gluPickMatrix((float) x,(float)(viewport[3]-y),3.0,3.0,viewport);

gluPerspective(40.0,rapportw_h,1.0,20.0 );

// mise en place de la matrices de modélisation (cohérente avec l’affichage)

glMatrixMode(GL_MODELVIEW);

// il faut ici dessiner la scène en nommant les éléments sélectionnables

gluLookAt(oeilx,oeily,oeilz,oeilx+visex,oeily+visey,oeilz+visez,0.0,0.0,1.0);

glLoadName(1);

// dessin de l’objet de nom 1

glLoadName(2);

// dessin de l’objet de nom 2

glPopMatrix();

glMatrixMode(GL_PROJECTION);

Benoît Piranda 06/10/2006

39

glPopMatrix();

glFlush();

hits = glRenderMode(GL_RENDER);

processHits(hits,selectBuf);

}

La fonction suivante permet le traitement du résultat de la sélection. Elle a pour tâche d’analyser le contenu d’un tableau d’entiers regroupant, pour chacun des objets nommés identifiés sur l’ensemble des pixels de la zone de sélection, leur nom et le codage de la profondeur minimale et maximale de l’objet lors de sa projection.

void processHits(GLint hits, GLuint buffer[]) { int i;

GLuint names,*ptr,zmini,nmini=-1;

ptr =(GLuint *) buffer;

names = ptr[0];

zmini = ptr[1];

// ptr[2] contient la profondeur maximale

nmini = ptr[3];

ptr+=4;

for ( i = 1 ; i < hits ; i++ )

{ names = ptr[0];

if (ptr[1]<zmini)

{ zmini = ptr[1];

// ptr[2] contient la profondeur maximale

nmini = ptr[3];

}

ptr+=4;

}

// traitement d’une selection

// nmini contient le numéro de l’élément sélectionné

// celui de z minimum

}

Exemples d’applications utilisant la sélection d’un objet à la souris, la porte s’ouvre puis se ferme lorsque l’on clique sur la poignée (image de gauche), l’objet posé sur le plateau devient vert lorsqu’il a été cliqué, les plateau dentés s’animent et viennent placer l’objet vert entre les marques rouges.

Benoît Piranda 06/10/2006

40

Chapitre XV Utilisation de la bibliothèque GLUT

XV.1 Introduction

La bibliothèque GLUT (pour OpenGL Utility Toolkit) propose des fonctions permettant la gestion simplifiées du système de fenêtre. Contrairement à la plupart des bibliothèques d’Interface Homme/Machine (IHM), GLUT n’a pas pour but de fournir des éléments graphiques (Widget) nombreux mais de proposer un support simple et rapide pour l’affichage d’une ou plusieurs fenêtres OpenGL avec un soucis de maîtrise du temps, pour permettre son utilisation dans un contexte de développement logiciel temps réel.

Remarque : Étant indépendante du système sur lequel elle fonctionne, la bibliothèques GLUT permet d'écrire des applications OpenGL très facilement portables, par exemple le même source GLUT peut être compilé sans modification sous Linux ou Windows.

Ce système de gestion de fenêtres permet de créer des IHM très simples. La gestions des événements liés à la fenêtre est réalisée par l'association de fonctions à la charge de l'utilisateur pour chaque catégorie d'événement (déplacement souris, re-dessin de la fenêtre, action clavier...). Certaines fonctions sont dédiées à l'animation en temps réel, en permettant de réaliser des traitements entre les actions de gestions des événements.

L'intervention de l'utilisateur sur les principaux périphériques d'entrée de la machine (clavier, souris, joystick...) pourra, elle aussi, être traitée par l'association de fonction à chaque catégorie d'événement.

De plus, pour palier au manques d'outils graphiques, GLUT propose de définir facilement des menus flottants que l'on associe au clique souris sur la fenêtre OpenGL.

Enfin, GLUT définit aussi quelques objets canoniques associés à des géométries simple ou à des objets d’utilisation courante dans les scènes de synthèse d’image, ces outils permet de construire rapidement des scènes simples.

XV.2 Gestion des fenêtres.

La programmation d'un système de gestion de fenêtres tels que Xwindow ou MS/Windows nécessite traditionnellement un travail de développement important et peu valorisant pour le programmeur. Un tel programme fait appel à de nombreuses fonctions spécifiques à chacune des bibliothèques et à la prise en compte de traitements spécifiques pour chacune des configurations du matériel pouvant supporter le logiciel (résolution de l'écran, nombre de plans de couleur, capacité d'accélération graphique …).

La librairie GLUT propose une simplification de deux points importants : − la gestion des fenêtres d'affichage, par l'utilisation de quelques fonctions (création, ouverture, affichage…), − l'association des événements pouvant intervenir dans les fenêtres à des fonctions simples définies par

l'utilisateur.

Le tableau suivant présente les principales fonctions de gestion des fenêtres par GLUT. Fonctions Traitements

glutInit(&argc, argv); Initialisation du système de fenêtre en tenant compte des paramètres passés au programme.

glutInitDisplayMode(mode); Définition des buffers définis dans la fenêtre. Mode ∈∈∈∈ {GLUT_RGB, GLUT_DOUBLE, GLUT_ALPHA, GLUT_DEPTH, GLUT_STEREO}

glutInitWindowPosition(x,y); Placement initial de la fenêtre. glutInitWindowSize(x,y); Dimension initial de la fenêtre.

glutCreateWindow(titre); Ouvre une fenêtre dont on précise le titre. Cette fonction retourne GL_TRUE si l'opération s'est bien passée.

Benoît Piranda 06/10/2006

41

Le tableau ci-dessous énumère les principales fonctions de gestion des évènements par la bibliothèque GLUT. Fonctions Traitements

glutDisplayFunc(drawFunc);

void drawFunc(void); Associe la fonction drawFunc à l'événement de dessin du contenu de la fenêtre.

glutReshapeFunc(reshapeFunc);

void reshapeFunc(int width,

int height);

Associe la fonction reshapeFunc à l'événement de changement de dimension de la fenêtre. (width, height) nouvelles dimensions de la zone de dessin.

glutIdleFunc(idleFunc);

void idleFunc(void);

La fonction idleFunc sera appelée lorsque aucun autre événement n'est à traiter. Utile pour la gestion de traitements temps réel.

glutTimerFunc(unsigned int

delais,timerFunc,int v);

void timerFunc(int x);

La fonction timerFunc sera appelée une seule fois dès que le délais exprimé en miliseconde se sera écoulé. La valeur v est transmis à la fonction timerFunc lors de l’appel.

Remarque : La fonction glutTimerFunc permet de réaliser des traitements séparés par un pas de temps régulier. Le traitement de la fonction associée sera cependant exécuté lorsque tout autre traitement (fonction) en cours dans une fonction sera terminé. En effet, le lancement de la fonction de timer est géré comme un évènement, il faut donc que les traitements des événements en cours soit terminé, cet évènement ne vient pas interrompre des traitements en cours ou ne sont pas démarré simultanément comme le ferait un programme multitâche.

XV.3 Gestion des événements émis par les périphériques d'entrées.

La gestion des périphériques d'entrées tels que le clavier, la souris, ou un joystick se fait par l'association de fonctions à chaque catégorie de périphérique. Ces fonctions doivent traiter chaque événement pouvant être émis par le périphérique associé.

Fonction Événements traités

glutMouseFunc(mouseFunc);

void mouseFunc(int button, int state,

int x, int y);

Enfoncement et relâchement des boutons de la souris. Button ∈∈∈∈ {GLUT_LEFT_BUTTON,

GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON}. state ∈∈∈∈ {GLUT_DOWN, GLUT_DOWN}. (x,y) coordonnées du pointeur.

glutMotionFunc(motionFunc);

void motionFunc(int x, int y);

Déplacement de la souris bouton enfoncé . (x,y) coordonnées du pointeur.

glutKeyboardFunc(kbdFunc);

void kbdFunc(unsigned char c, int x,

int y);

Enfoncement d'une touche standard du clavier. c caractère associé à la touche. (x,y) coordonnées du pointeur.

glutSpecialFunc(kbdSpecialFunc);

void kbdSpecialFunc(int code, int x,

int y);

Enfoncement d'une touche spéciale du clavier. code ∈∈∈∈ {GLUT_KEY_F[1..12], GLUT_KEY_LEFT, GLUT_KEY_UP, GLUT_KEY_RIGHT, GLUT_KEY_DOWN,

GLUT_KEY_PAGE_UP, GLUT_KEY_PAGE_DOWN, GLUT_KEY_HOME, GLUT_KEY_END,

GLUT_KEY_INSERT}

(x,y) coordonnées du pointeur.

GlutJoystickFunc(jsFunc, int t);

void jsFunc( unsigned int buttonMask,

int x, int y, int z);

État du joystick toutes les t millisecondes. buttonMask ∈∈∈∈ {GLUT_JOYSTICK_BUTTON_A, GLUT_JOYSTICK_BUTTON_B,

GLUT_JOYSTICK_BUTTON_C,

GLUT_JOYSTICK_BUTTON_D}

(x,y,z) coordonnées des axes du joystick. -1000 < (x,y,z) < 1000

Benoît Piranda 06/10/2006

42

XV.4 Les objets simples définis dans GLUT.

GLUT définit quelques objets canoniques simples que l'on peut directement utiliser par l'appel des fonctions suivantes, ces objets existent sous forme de maillage « fils de fer » et modélisés par des facettes : − la sphère de rayon r découpée en subh facettes suivant l'axe horizontal et subv facettes suivant l'axe vertical,

glutWireSphere(GLdouble r, GLint subh, GLint subv) et glutSolidSphere(GLdouble r, GLint

subh, GLint subv), − le cône de rayon de base r, de hauteur h découpé en subh facettes suivant l'axe horizontal et subv facettes suivant

l'axe vertical, glutWireCone(GLdouble r, GLdouble h, GLint subh, GLint subv) et glutSolidCone(GLdouble r, GLdouble h, GLint subh, GLint subv),

− le cube de coté a, glutWireCube(GLdouble a) et glutSolidCube(GLdouble a), − le tore de rayon intérieur ri, de rayon extérieur re, découpé en subh facettes suivant l'axe horizontal et subv

facettes suivant l'axe vertical, glutWireTorus(GLdouble ri, GLdouble re, GLint subh, GLint subv) et glutSolidTorus(GLdouble ri, GLdouble re, GLint subh, GLint subv),

− la théière de taille t, glutWireTeapot(GLdouble t) et glutSolidTeapot(GLdouble t).

XV.5 Un programme d'exemple.

Le programme suivant fait appel à la librairie GLUT pour ouvrir une fenêtre OpenGL, configurée pour utiliser le Z-buffer, le double buffer, et 4 plans de couleur. Chacune des interruptions classiques sur les périphériques est associée à une fonction. Le double buffer est activé, il consiste en un outils permettant de rendre les animations plus fluides en calculant chaque image en mémoire avant de l'afficher à l'écran en une seule action. L'inversion du buffer d'affichage et du buffer caché se fait à l'aide de la fonction glutSwapBuffers().

La réalisation d'animations est facilité par la possibilité de créer une fonction (idleFunc) qui va être exécutée durant les périodes d'inactivité de OpenGL. En effet, nous pourrons dans cette fonction calculer les évolutions de la scène au cours du temps et redemander la mise à jour de la scène en appelant la fonction glutPostRedisplay().

#include <GL/glut.h>

/*********************************************************/

/* fonction de dessin de la scène à l'écran */

static void drawFunc(void)

{ /* effacement de l'écran en gris */

glClearColor(0.2,0.2,0.2,0.2);

glClear(GL_COLOR_BUFFER_BIT);

/* inversion des deux buffers */

glutSwapBuffers();

}

/*********************************************************/

/* fonction appelée entre les traitements OpenGL */

static void idleFunc(void)

{ }

/*********************************************************/

/* fonction de changement de dimension de la fenêtre */

/* (exécutée au moins une fois à l'ouverture). */

/* - width : largeur (x) de la zone de visualisation */

/* - height : hauteur (y) de la zone de visualisation */

static void reshapeFunc(int width,int height)

{ }

/*********************************************************/

/* fonction associée au clique de la souris. */

/* - button : nom du bouton pressé GLUT_LEFT_BUTTON, */

/* GLUT_MIDDLE_BUTTON ou GLUT_RIGHT_BUTTON */

/* - state : état du bouton button GLUT_DOWN ou GLUT_UP */

/* - x,y : coordonnées du curseur dans la fenêtre */

static void mouseFunc(int button, int state, int x, int y)

{ }

/*********************************************************/

/* fonction liée au déplacement de la souris bouton pressé*/

Benoît Piranda 06/10/2006

43

/* - x,y : coordonnées du curseur dans la fenêtre */

static void motionFunc(int x, int y)

{ }

/*********************************************************/

/* fonction associée aux interruptions clavier standards */

/* - c : caractère saisi */

/* - x,y : coordonnée du curseur dans la fenêtre */

static void kbdFunc(unsigned char c, int x, int y)

{ }

/*********************************************************/

/* fonction associée aux interruptions clavier étendu. */

/* - code : code de la touche enfoncée */

/* - x,y : coordonnée du curseur dans la fenêtre */

static void kbdSpecialFunc(int code, int x, int y)

{ }

/*********************************************************/

/* fonction associée aux événements de menu. */

/* - item : code associé au menu selecitonné */

static void menuFunc(int item)

{ if (item == 99) exit(0);

printf("menu : %d\n",item);

}

int main(int argc, char** argv)

{ GLint sousmenu;

/* traitement des paramètres du programme propres à GL */

glutInit(&argc, argv);

/* initialisation RVBA + ZBuffer + Double buffer. */

glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_DOUBLE);

/* placement et dimensions originales de la fenêtre */

glutInitWindowPosition(0,0);

glutInitWindowSize(500, 500);

/* ouverture de la fenêtre */

if (glutCreateWindow("Exemple 1") == GL_FALSE) return 1;

/* association de la fonction de dimensionnement */

glutReshapeFunc(reshapeFunc);

/* association de la fonction d'affichage */

glutDisplayFunc(drawFunc);

/* association de la fonction d'événement souris */

glutMouseFunc(mouseFunc);

/* association de la fonction de DRAG */

glutMotionFunc(motionFunc);

/* association de la fonction d'événement du clavier */

glutKeyboardFunc(kbdFunc);

/* association de la fonction spéciales du clavier */

glutSpecialFunc(kbdSpecialFunc);

/* association de la fonction de traitement par défaut */

glutIdleFunc(idleFunc);

/* création d'un popup menu */

/* sous menu */

sousmenu = glutCreateMenu(menuFunc);

glutAddMenuEntry("sousmenu1", 21);

glutAddMenuEntry("sousmenu2", 22);

/* menu principal */

glutCreateMenu(menuFunc);

glutAddMenuEntry("menu", 10);

glutAddSubMenu("sous-menu", sousmenu);

glutAddMenuEntry("Quit", 99);

/* association du menu au clique droit */

glutAttachMenu(GLUT_RIGHT_BUTTON);

/* boucle principale de gestion des événements */

glutMainLoop();

return 0;

}

Benoît Piranda 06/10/2006

44

XV.6 Compilation de logiciel OpenGL / Glut sous système Linux Sous Linux, les fichiers de la bibliothèque OpenGL se trouvent soit dans les répertoires standards

/usr/include/GL pour les fichiers d'en-têtes et /usr/lib pour les bibliothèques, soit dans les répertoires de Xwindow /usr/X11R6/include/GL et /usr/X11R6/lib. Le fichiers de bibliothèques (library) qui interviennent dans un programme OpenGL / Glut sont : GL, GLU et glut.

Dans la première situation, la compilation avec le compilateur GNU gcc se fera donc par la commande : gcc td1.c -o td1 -lglut -lGLU –lGL

Dans la seconde situation, il peut être nécessaire de préciser le chemin d’accès aux bibliothèques et d’ajouter d’autres bibliothèques en utilisant le format de commande suivant :

gcc td1.c -o td1 -I/usr/X11R6/include -L/usr/X11R6/lib -lglut -lGLU -lGL -lXmu

Un fichier Makefile peut de même être écrit pour automatiser le processus de compilation sur le modèle de l'exemple suivant :

prog: fichier.o

gcc -o prog fichier.o -L/usr/X11R6/lib -lglut -lGLU -lGL -lXmu

fichier.o: fichier.cpp fichier.h

gcc -c fichier.c -I/usr/X11R6/include