OpenGL avec Delphi

Martin Beaudet

Présentation de l'utilisation d'OpenGL avec Delphi.

- Titre : OpenGL avec Delphi

- Auteur : Martin Beaudet

- Parution : 1 juin 2002

- Mise à jour : 3 janvier 2020

- Licence : Copyright ® 2002 Martin Beaudet. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Initialiser OpenGL

- I-A. Qu'est-ce que OpenGL ?

- I-B. Téléchargement/préalables

- I-C. OpenGL et ses types de données

- I-D. Programme relié

- II. Premières primitives

- II-A. Qu'est-ce qu'une primitive ?

- II-B. Événement OnPaint() du contrôle fenêtré

- II-C. Événement OnResize() du contrôle fenêtré

- II-D. Programme relié

- III. Transformations

- III-A. Le préalable ?

- III-B. Les transformations ?

- III-C. Événement OnPaint() du contrôle fenêtré

- III-D. Programme relié

- IV. Lumière fixe

- IV-A. Les objets prédéfinis de GLUT

- IV-B. double tampon

- IV-C. Comprendre l'importance de la lumière en 3D

- IV-D. Supprimer les surfaces cachées d'un objet

- IV-E. Les modèles de lumières

- IV-F. Démarche à suivre pour la création d'une lumière

- IV-F-1. Les normales de surfaces

- IV-F-2. Créer et positionner la ou les sources de lumière

- IV-F-3. Définir les propriétés des matières contenues par les objets de la scène

- IV-F-4. Création des sources de lumière

- IV-G. Un petit peu de code ?

- IV-H. Programme relié

- V. Lumière en mouvement

- V-A. Spécification de réflectivité de matière (petit truc)

- V-B. Créer une lumière éclairant un objet en mouvement

- V-C. Éclairage se déplaçant avec le point de vue

- V-D. Programme relié

- VI. Transparence

- VI-A. Survol du tutoriel

- VI-B. Fusionnement des couleurs

- VI-C. Programme relié

- VII. Listes d'affichage

- VII-A. Qu'est-ce qu'une liste d'affichage

- VII-B. À quoi servent plus précisément les listes d'affichage

- VII-C. Explication de l'exemple

- VII-D. Programme relié

- VIII. Lissage - (Antialiasing)

- VIII-A. Lissage ou antialiasing

- VIII-B. Programme relié

- IX. Texture

- IX-A. Pourquoi utiliser des textures ?

- IX-B. Étapes pour l'application de texture

- IX-C. Utiliser une texture gérée par programmation

- IX-D. Quelques petits trucs utiles

- IX-E. Source

- IX-F. Qu'est-ce qui se traque derrière le chargement d'une texture ?

- IX-G. Différentes bibliothèques pour charger des textures

- IX-H. Programme relié

- X. Brume

- X-A. Avantages de la brume

- X-B. Objets quadriques de l'utilitaire GLU

- X-C. Programme relié

- XI. Courbes de Bézier

- XI-A. Courbe de Bézier (définition)

- XI-B. Courbe de Bézier (OpenGL)

- XI-C. Compteur de cadre par seconde (FPS)

- XI-D. Programme relié

- XII. Fontaine de particules

- XII-A. Détail du projet

- XII-B. Captures d'écran

- XII-C. Programme relié

I - Initialiser OpenGL

I-A - Qu'est-ce que OpenGL ?

OpenGl est selon moi le meilleur outil mis au point pour faire de la 3D ou encore du 2D. Il est compatible avec Mac® OS, OS/2®, UNIX, Windows 95, Windows NT, Linux… et beaucoup d'autres encore. Il est plus que souvent le moteur principal dans les meilleurs shooting games tels que : Unreal, Dai-Katana, Half-Life, SpecOps… Son initialisation et son utilisation sont beaucoup plus faciles que celles de Direct3D et permettent une plus grande quantité de fonctions intéressantes. Il se compose d'environ 250 commandes distinctes pour travailler des objets et opérations permettant la troisième dimension. On peut construire des objets de plusieurs manières intéressantes à l'aide de formes basiques telles que : des points, lignes et polygones. OpenGL possède beaucoup de kits d'outils, mais j'aborderai le plus connu et facile qu'est : Glut (OpenGL Utility Library). Pour bien comprendre il est nécessaire de posséder une bonne petite base en langage Pascal ainsi que, le plus important, les mathématiques (algèbre linéaire et vectorielle, géométrie dans l'espace, etc.). Bonne chance, j'espère que vous apprécierez mes efforts.

Pour plus de détails sur ce puissant outil, allez jeter un coup d'œil sur le site officiel : https://www.opengl.org/

I-B - Téléchargement/préalables

1. Tout d'abord vous aurez besoin de Borland Delphi v3.0 ou ultérieur.

2. Il est nécessaire de posséder la bibliothèque d'OpenGL donc OpenGL32.dll. Qui est plus que souvent déjà fourni avec les pilotes de votre carte vidéo. Si toutefois vous ne disposez pas de celui-ci alors vous pourrez télécharger à l'adresse suivante : GLSetup un petit utilitaire très pratique pour l'installer sur votre ordinateur.

3. L'utilitaire Glut(Optionnel). Si vous désirez plus d'informations sur cet outil, voici son concepteur : Nate Robins ou encore directement sur le site d'OpenGL.

4. Pour ceux qui aimeraient avoir plus de détails sur les procédures, fonctions et instructions dont je me sers tout au long de ces tutoriels, il vous serait pratique de vous procurer le fichier OpenGL HLP (anglais). En ouvrant ce fichier on obtient une erreur d'index, mais ce n'est pas grave, dirigez-vous directement vers le bouton Index pour retrouver la commande sur laquelle vous désirez de l'aide.

5. De l'intérêt pour la 3D.

Ce fichier contient la plupart des types, fonctions et procédures permettant l'accès à la bibliothèque OpenGl32.Dll.

Pour glut suivez les instructions du fichier ReadMe.Txt. (Si vous avez des problèmes à l'installation alors, contactez-moi.) Ce fichier contient un Kit de développement qui facilite quelques commandes soit pour construire des primitives ou encore effectuer des actions qui nécessitent beaucoup de code.

I-C - OpenGL et ses types de données

Voici les types de données prédéfinis sous OpenGL

Suffixe

Type de données

Type correspondant à Delphi

Définition

b

entier 8 bits

SmallInt

GLbyte

s

entier 16 bits

Integer

GLshort

i

entier 32 bits

Integer

GLint, GLsizei

f

flottante 32 bits

Single

GLfloat, GLclampf

d

flottante 64 bits

Double

GLdouble, GLclampd

ub

entier non signé 8 bits

Byte

GLubyte, GLboolean

us

entier 16 bits

Cardinal

GLushort

ui

entier non signé 32 bits

Cardinal

GLuint, GLenum, GLbitfield

Vous devez probablement vous demander pourquoi je débute avec les types de données d'OpenGL. La réponse est assez simple, c'est que, pour construire des polygones, il faut spécifier des coordonnées de sommets et ces coordonnées seront spécifiées par des valeurs qui elles seront d'un type listé dans le tableau précédent. Ainsi il est possible de positionner des coordonnées de polygones ou ajuster leur couleur en se servant soit d'entiers ou de nombres réels. Mais avant de créer des polygones, nous allons commencer par initialiser OpenGL.

- Démarrez une nouvelle application comme à l'habitude.

- Spécifiez la couleur clBlack pour votre fenêtre (cela réduira les effets indésirés).

- Dans la clause Uses liez le fichier suivant : OpenGL, pour le fichier (OpenGl.pas).

Avant de commencer les bouts de code, vous devez comprendre deux concepts abstraits quant aux préalables de la liaison d'OpenGL et de votre fenêtre. Tout d'abord vous aurez besoin de conserver deux valeurs :

- DC : adresse d'un contexte graphique ou plutôt d'une surface dessinable. Dans notre cas la surface dessinable sera notre fenêtre. Cette adresse est nécessaire, car il faut mentionner à OpenGL sur quoi (surface) et où il doit dessiner ;

- HGLRC : adresse du contexte graphique de OpenGL. Cette valeur est comme une sorte de passerelle entre notre DC et OpenGL donc elle est très importante du fait qu'elle est en grande partie responsable du lien entre OpenGL et notre fenêtre.

Bon maintenant je vais vous bourrer de code

Ce code vous permettra seulement de connecter OpenGL à votre application Delphi. Au départ, ces extraits de code ne seront peut-être pas explicites pour vous, mais vous comprendrez mieux avec la suite. Le début :

Unit Utils;
Interface
Uses Windows, SysUtils, OpenGL;

...

Implementation
Var
hDCGlobal   :HDC;
GLContext   :HGLRC;

Voilà les deux seules variables globales privées de ce Unit :

- hDCGlobal : cette variable récupère le contexte graphique de la fenêtre. Comme le contexte graphique de la fenêtre est reçu comme paramètre dans la fonction InitOpenGL alors il faut la garder précieusement, car elle nous sera utile à la toute fin pour briser le lien entre OpenGL et notre contrôle fenêtré ;

- GLContext

Voici la procédure permettant d'initialiser OpenGL à notre composant fenêtré :

Function InitOpenGL( DC :HDC; ColorBits :Integer;
DoubleBuffer :Boolean ) :Boolean;

Voici la fonction qui initialisera OpenGL sur notre fenêtre. Cette fonction requiert trois paramètres que voici :

- DC : le DC est l'adresse graphique de la fenêtre qui doit être liée à OpenGL. Sous Delphi, l'adresse graphique d'un composant se récupère à l'aide de la propriété « Canvas.Handle ». Ce paramètre a été nommé DC, car en anglais on dit (Device Context) ;

- ColorBits : ce paramètre spécifie le nombre de bits utilisé pour spécifier une couleur. Ce paramètre devrait être à 16. Par contre ce paramètre pourrait prendre la valeur de 32 si nous désirons effectuer du Blending, cela sera expliqué plus tard… ;

- DoubleBuffer : désirons-nous travailler en mode double tampon ou en mode simple ? Cela sera expliqué dans le prochain tutoriel.

Voici les variables nécessaires à la procédure InitOpenGL :

Var
PixelFormat    :TPixelFormatDescriptor;
cPixelFormat   :Integer;

La première variable (PixelFormat) sera utilisée pour notre requête d'un format spécifique de tampon de pixels.
La deuxième variable (cPixelFormat) contiendra le numéro d'identification du format de tampon de pixels qui nous aura été attribué par Windows. Précisons que chaque surface dessinable a toujours un descripteur de tampon de pixels.

FillChar( PixelFormat, SizeOf(PixelFormat), 0 );
With PixelFormat Do
Begin
nSize      := Sizeof(TPixelFormatDescriptor);
If DoubleBuffer Then
dwFlags    := PFD_DOUBLEBUFFER OR PFD_DRAW_TO_WINDOW
OR PFD_SUPPORT_OPENGL
Else
dwFlags    := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL;
iLayerType := PFD_MAIN_PLANE;
iPixelType := PFD_TYPE_RGBA;
nVersion   := 1;
cColorBits := 16;
CdepthBits := 16;
End;

Tout d'abord la fonction FillChar remplit notre structure de tampon de pixels avec des 0. Ce remplissage de 0 n'est pas ultimement nécessaire, mais il peut parfois éviter des problèmes avec des valeurs non désirées. Ensuite il faut spécifier le format du tampon de pixels alors je ne vous expliquerai pas toute la structure en détail, car elle a beaucoup de membres. Ce que je vous conseille c'est de presser la touche F1 sur celle-ci. Les membres les plus importants pour ce tutoriel sont expliqués ici :

- dwFlags : ce membre spécifie les propriétés du tampon de pixels. Ici nous lui demandons de pouvoir dessiner sur une fenêtre, et de supporter OpenGL. La variable DoubleBuffer détermine si le tampon de pixels est doublé. Si ce n'est pas très clair alors vous comprendrez au deuxième tutoriel.

- cColorBits

cPixelFormat := ChoosePixelFormat(DC, @PixelFormat);

Une fois que la structure de spécification du tampon de pixels est spécifiée alors il suffit de la proposer à Windows qui lui déterminera quel type de tampon de pixels est le plus adapté pour votre fenêtre.

SetPixelFormat( DC, cPixelFormat, @PixelFormat);

Cette fonction applique le nouveau format de tampon de pixels à la fenêtre, le format est appliqué par l'entremise du contexte graphique (DC) de la fenêtre.

GLContext := wglCreateContext( DC );

C'est ici que la passerelle entre OpenGL et votre DC est créée. Par contre il faut faire attention, car elle n'est pas encore activée ou plutôt dire qu'elle est créée, mais le passage est bouché, alors le lien n'est pas fait. La fonction suivante démontre comment la rendre active (Débouché).

wglMakeCurrent( DC, GLContext );

Et pour finir, cette fonction rend la passerelle active entre les deux contextes graphiques. Eh! Ne partez pas ! Il reste juste une procédure, celle qui détruit le lien entre OpenGL et votre fenêtre.

Procedure Terminer;
Begin
wglMakeCurrent( hDCGlobal, 0 );
wglDeleteContext( GLContext );
End;

La première fonction rebouche la passerelle ou plutôt elle la désactive.
Ensuite il ne reste plus qu'à supprimer la passerelle.

Bien sûr le code que je vous ai montré ne gère vraiment pas les erreurs et je crois sincèrement que c'est primordial qu'il le fasse alors le code source que vous téléchargerez tantôt sera optimisé en ce sens.

I-D - Programme relié

Ce programme montre la plus petite application OpenGL sous Dephi. Pour l'instant rien de visuel ne se produit, mais si tout se déroule bien vous constaterez que OpenGL est lié à votre fenêtre. Source

P.-S. : ne vous en faites pas pour le code supplémentaire dans les autres événements de la fenêtre, ils seront expliqués dans le tutoriel suivant alors ne vous gênez pas, pressez la flèche de droite .

pour ceux que cela intéresse, voici l'autre manière de créer une application OpenGL avec Delphi. Source (à la C++).

P.-S. - 2 : si vous ne savez pas comment créer votre propre fenêtre (comme à la C++) alors je vous conseille d'aller jeter un coup d'œil ici avant.

II - Premières primitives

II-A - Qu'est-ce qu'une primitive ?

Une primitive est une forme géométrique simple, par exemple un carré, un triangle, un rectangle… C'est à l'aide de ceux-ci que nous serons en mesure de construire plusieurs formes géométriques épatantes. Ne perdons pas de temps et allons étudier quelques commandes de base.

II-B - Événement OnPaint() du contrôle fenêtré

Comme à l'habitude voici du code, celui-ci sera écrit dans la procédure OnPaint() de votre contrôle fenêtré.

glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.0, 0.0, 0.0, 0.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity;

gluLookAt( 0.0, 0.0, 7.0,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0 );

glBegin( GL_TRIANGLES );
glColor3f( 1.0, 1.0, 1.0 );
glVertex2d( 2.0, -2.0 );
glColor3f( 0.5, 0.5, 0.0 );
glVertex2d( -2.0, -2.0 );
glColor3f( 0.0, 0.5, 0.5 );
glVertex2d( 0.0, 2.0 );
glEnd();

glFlush();

Voilà des nouvelles commandes OpenGL que je ne vous ai pas expliquées, mais qui étaient présentes dans le premier tutoriel :

- glClear(GL_COLOR_BUFFER_BIT); : ici on vide le tampon de pixels où les valeurs chromatiques (couleurs) sont logées. La couleur de vidage sera spécifiée à l'aide de la commande suivante glClearColor(..) ;

- glClearColor(0.0, 0.0, 0.0, 0.0); : voilà la couleur de vidage. Elle se présente sous format RGBA donc (Rouge, Vert, Bleu, Alpha). Alpha est un taux de luminosité, mais nous en reparlerons plus tard ;

- glMatrixMode( GL_MODELVIEW ); / glLoadIdentity(); : aa commande glMatrixMode spécifie la matrice active, trois paramètres sont possibles (GL_MODELVIEW, GL_PROJECTION, GL_TEXTURE. Ici la matrice de modélisation-visualisation a été choisie parce que nous nous préparons à construire nos primitives. Donc les appels de transformations successives affecteront la matrice de modélisation-visualisation. Et c'est aussi le temps maintenant d'effectuer sur nos primitives des rotations, des déplacements et des redimensions. La commande glLoadIndentity(); vide la matrice active afin de prévoir les commandes de transformations suivantes ;

- gluLookAt( 0.0, 0.0, 7.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 ); : cette commande provient de la bibliothèque GLU, elle permet de placer le spectateur en fonction de la scène à visualiser. Les trois premiers paramètres spécifient où est placée la caméra dans un univers 3D donc X, Y et Z. Les trois autres paramètres spécifient où la caméra regarde ou plutôt quel point elle fixe. Et les trois derniers paramètres spécifient la direction du vecteur. Comme nous n'avons pas nécessairement de déplacement complexe dans ce petit programme, alors il aurait suffi d'utiliser la commande suivante glTranslated( 0.0, 0.0, -10 ). Cette commande aurait placé la caméra à -10 unités derrière la scène alors juste parfait pour visualiser le triangle de face. Pour ce qui est des transformations, il existe trois belles commandes que voici :

- glTranslate( X, Y, Z ) : spécifie un déplacement dans la scène ;

- glScale( X, Y, Z ) : spécifie un changement d'échelle dans la scène ;

- glRotate( X, Y, Z ) : spécifie une rotation dans la scène.

N'oubliez jamais de spécifier la position de votre vue avant de dessiner la scène, car sinon vous serez à l'origine de la scène et vous ne verrez rien.

- glBegin( GL_TRIANGLES ); / glEnd() : la commande glBegin( type ) débute une liste de sommets décrivant une primitive géométrique. Comme vous le constaterez, nous allons construire un triangle. le paramètre type peut prendre les différentes valeurs que voici :

Différents types pour la commande glBegin()

Valeur

Signification

GL_POINTS

Points isolés.

GL_LINES

Paires de sommets interprétées en tant que segments de ligne isolés.

GL_LINES_STRIP

Séries de segments de ligne joints.

GL_LINE_LOOP

Même chose, mais ajout d'un segment entre le dernier et le premier sommet.

GL_TRIANGLES

Regroupe par trois les sommets interprétés comme des triangles.

GL_TRIANGLE_STRIP

Bande liée de triangles.

GL_TRIANGLE_FAN

Éventail lié de triangles.

GL_QUADS

Regroupe par quatre les sommets interprétés comme des polygones à quatre côtés.

GL_QUAD_STRIP

Bande liée de quadrilatères.

GL_POLYGON

Contour externe d'un polygone convexe simple.

- glColor3f( 1.0, 1.0, 1.0 ); : spécifie la couleur des sommets. Remarquez ici la dernière lettre de cette commande, f et le nombre 3 si vous vous référez au tableau (début du premier tutoriel) vous remarquerez que la couleur est donnée sous format (Rouge, Vert, Bleu) et le type de données pour les couleurs sera float (entre 0.0 et 1.0). Tant et aussi longtemps qu'il n'y aura pas une nouvelle commande de sélection de couleur, les sommets seront tous blancs (1.0, 1.0, 1.0). Il est préférable de toujours spécifier la couleur des sommets avant leur création.

- glVertex2d( 2.0, -2.0 ); : encore une fois regarder le nombre 2 et la dernière lettre de la fonction d. Celle-ci dessine positionne un sommet à l'endroit voulu. Ici l'endroit est spécifié par glVertex2d( X, Y ). Ce sommet aura la couleur spécifiée par la commande glColor*.

- glFlush(); : exécution des commandes d'OpenGL. Donc toutes les commandes qui ont été envoyées à OpenGL seront exécutées après cette commande.

II-C - Événement OnResize() du contrôle fenêtré

Un exemple de code définissant le champ de vision en fonction de la dimension du contrôle fenêtré (la fenêtre).

glViewport(0, 0, ClientWidth, ClientHeight );
glMatrixMode( GL_PROJECTION );
glLoadIdentity;

gluPerspective( 45, Width / Height, 0.1, 10.0 );
glMatrixMode( GL_MODELVIEW );
Invalidate;

- glViewport(0, 0, TGLsizei(ClientWidth), TGLsizei(ClientHeight)); : spécifie l'espace disponible sur le composant où OpenGL dessine. Les deux paramètres suivants spécifient les coordonnées de la fenêtre située en haut à gauche donc (0, 0). Les deux derniers paramètres spécifient la grandeur de la fenêtre donc en bas à droite (largeur, hauteur). Cette commande est appelée ici parce que si la dimension de la fenêtre change alors OpenGL se résignera à dessiner sa future scène dans cet enclos défini.

- glMatrixMode( GL_PROJECTION ); / glLoadIdentity(); : active la matrice de projection, car nous allons définir un champ de vision à la ligne suivante. La commande glLoadIdentity() sert à initialiser la matrice de projection, car c'est elle qui est active maintenant.

- gluPerspective( 45, Width / Height, 0.0, 10.0 ); : crée une nouvelle matrice de projection permettant de créer un champ de vision pour notre scène. Quatre paramètres sont nécessaires :

- Angles de vue : spécifie l'angle du champ de vue, ici nous lui attribuons un angle de 45º. Sa valeur doit être comprise entre 0 et 180º ;

- Ratio : représente l'aspect du ratio, donc la largeur divisée par la hauteur. Si par exemple nous lui avions spécifié un ratio de 0.5 qui est égal à ½, donc la largeur serait 2 fois moins dense ;

- Proche : la distance la plus proche du champ de vision, en profondeur, est (0.0) alors on ne peut être plus dans l'action que cela !;

- Loin : la distance la plus éloignée donc 10.0. Si nous dessinons une primitive à 11.0 de profondeur et plus, il nous serait impossible de l'apercevoir, car elle ne ferait pas partie de notre champ de vision défini.

Il existe d'autres types de perspective comme glFrustum, glOrtho. À vous de les découvrir…

- Invalidate; : si l'on jette un petit coup d'œil dans l'aide en ligne de Delphi, on comprendra vite que cette commande sert à obliger le rafraîchissement d'un contrôle fenêtré. Par conséquent s'il est rafraîchi il est aussi repeint OnPaint().

II-D - Programme relié

Ah! le fameux triangle d'OpenGL. Je ne peux vous dire mieux que : « Essayez de dessiner d'autres formes géométriques… » Source

Pour ceux que cela intéresse, voici l'autre manière. Source (à la C++)

III - Transformations

III-A - Le préalable ?

Dès qu'on parle de transformation cela sonne aussi ANIMATION. Comme je ne suis pas très très fervent du TTimer de Delphi je vous propose un Timer beaucoup plus rapide. J'espère que vous comprendrez bien sinon ne vous gênez pas pour les questions.

Pour ceux qui utilisent la manière (à la C++) du premier tutoriel alors vous n'avez pas besoin de ce TIMER.

III-B - Les transformations ?

Qu'est-ce que les transformations, il s’agit par exemple de faire effectuer une rotation, une translation ou encore un changement d'échelle à une primitive. Dans ce tutoriel nous verrons les principales commandes qui permettent d'effectuer ces transformations.

III-C - Événement OnPaint() du contrôle fenêtré

Voici ce que vous devez inclure dans l'événement OnPaint() de votre fenêtre.

glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.0, 0.0, 0.0, 0.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity;

gluLookAt( 0.0, 0.0, 20.0,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0 );

glPushMatrix();
glScaled( Redim, 1.0, 1.0 );
glRotated( DegreRot, 0, 0, 1 );
glTranslated( Redim, 0.0, 0.0 );

glBegin( GL_TRIANGLES );
glColor3f( 1.0, 1.0, 1.0 ); glVertex2d(  2.0, -2.0 );
glColor3f( 0.5, 0.5, 0.0 ); glVertex2d( -2.0, -2.0 );
glColor3f( 0.0, 0.5, 0.5 ); glVertex2d(  0.0,  2.0 );
glEnd();
glPopMatrix();

glFlush;

Explication de ce bout de code :

glPushMatrix(); / glPopMatrix();

Vous verrez que ces deux commandes sont plus qu'utiles quand on possède plus d'une primitive dans notre scène. Tout d'abord il faut comprendre que chaque transformation que l'on effectue est stockée sous forme de matrice (4 x 4). glPushMatrix permet de sauvegarder la matrice active et glPopMatrix permet de récupérer la matrice précédemment sauvegarder. Ces deux commandes fonctionnent de la même manière que la pile d'un ordinateur donc comme une pile d'assiettes (LIFO : Last In First Out). Mais pourquoi cela peut être utile et bien voilà une petite exemple.

Voici deux primitives que vous devez utiliser pour construire une roue de voiture.

+ =

Attention : prenez par exemple un des triangles positionnés sur l'image résultat, constatez-vous qu'il ne possède plus le même angle que sur l'image 2 ? Si vous avez remarqué alors c'est que vous avez compris aussi que ces triangles ont tous subi un effet de rotation de x degrés. Cette rotation s'effectuera avec la commande glRotate*(). Maintenant je vous donne la manière de procéder pour dessiner cette roue tout en faisant effectuer une rotation triangle par triangle sans jamais affecter l'ensemble des autres éléments dans la scène ou plutôt de la roue elle-même. Malgré que la roue elle-même soit ronde alors une rotation sur celle-ci n'aurait pas grande importance, mais c'est la seule idée qu'y m'est venue en tête au moment où j'ai conçu ce tutoriel.

1. Dessiner la roue.

2. Sauvegarder la matrice.

3. Rotation du triangle #1.

4. Dessin du triangle #1 sur la roue.

5. Récupération de la matrice.

6. Sauvegarder la matrice.

7. Rotation du triangle #2.

8. Dessin du triangle #2 sur la roue.

9. Récupération de la matrice.

10. Sauvegarder la matrice.

11. Rotation du triangle #3.

12. Dessin du triangle #3 sur la roue.

13. Récupération de la matrice.

De cette manière vous vous assurez que les transformations effectuées sur des primitives entre ces deux commandes glPushMatrix() et glPopMatrix() n'affectent que les primitives c'est primitives et non ceux à l'extérieur. Pratique non ?

glTranslate*(x, y, z);

Comme son nom l'indique cette commande effectue une translation dans un axe donné selon X, Y ou Z. Même si l'image semble rétrécir ne croyez pas que le carré change d'échelle, mais il se translate simplement vers l'axe des Z donc en profondeur.
Voici l'explication graphique :

glRotate*(Angle, x, y, z );

Encore une fois son nom ne ment pas : cette commande sert à effectuer des rotations selon un axe et un angle donné. Mais ici il faut faire attention, car cela dépend si une translation a été appelée avant ou après, cela peut entrainer des transformations différentes. Voici la différence si l'on exécute une translation suivie d'une rotation et le cas contraire :

glScale*( x, y, z );

Commande permettant d'effectuer des redimensions sur une primitive selon l'axe donné. Si la valeur donnée est < 1 alors la primitive sera réduite sinon si la valeur donnée est > 1 alors la primitive sera grossie. Si la valeur est égale à 1 alors rien ne se produit. Explication graphique :

Capture d'écran :

III-D - Programme relié

Je m'excuse encore pour ma roue de voiture je sais que c'est vulgaire et dégueulasse, mais il faut dire que je ne suis pas un as en art plastique. Amusez-vous à interchanger les commandes de transformation et essayez de remarquer les différences que cela provoque. Si c'est trop facile alors vous pouvez toujours ajouter une autre primitive et contrôler les transformations d'une et l'autre avec les deux commandes magiques : glPushMatrix(); et glPopMatrix(); Source

Pour ceux que intéresse, voici l'autre manière. Source (à la C++)

IV - Lumière fixe

IV-A - Les objets prédéfinis de GLUT

Dans le premier tutoriel, je vous avais glissé un petit mot quant à ce petit utilitaire intéressant. Eh bien maintenant c'est le temps de s'en servir (pour ceux que cela intéresse…). La raison de son existence est de nous faciliter la vie avec des commandes qui sont souvent utilisées pour réaliser une ou des actions quelconques. Par exemple si nous désirons créer un cube et bien nous devrons créer 12 sommets et Dieu sait que c'est long… Pour pouvoir l'utiliser vous devez le lier à votre unit principal donc Uses Glut.

Voyons maintenant ce que l'utilitaire Glut peut nous offrir en tant que construction de primitive :

(Wire = Fil de fer) (Solid = Rempli)

Commande

Capture d'écran

glutWireSphere(GLdouble radius, GLint slices, GLint stacks);
glutSolidSphere(GLdouble radius, GLint slices, GLint stacks);

glutWireCone(GLdouble base, GLdouble height, GLint slices, GLint stacks);
glutSolidCone(GLdouble base, GLdouble height, GLint slices, GLint stacks);

glutWireCube(GLdouble size);
glutSolidCube(GLdouble size);

glutWireTorus(GLdouble innerRadius, GLdouble outerRadius, GLint sides, GLint rings);
glutSolidTorus(GLdouble innerRadius, GLdouble outerRadius, GLint sides, GLint rings);

glutWireDodecahedron(void);
glutSolidDodecahedron(void);

glutWireTeapot(GLdouble size);
glutSolidTeapot(GLdouble size);

glutWireOctahedron(void);
glutSolidOctahedron(void);

glutWireTetrahedron(void);
glutSolidTetrahedron(void);

glutWireIcosahedron(void);
glutSolidIcosahedron(void);

Bien sûr, l'utilisation de cet outil permet d'effectuer une tonne d'autres tâches, par exemple la gestion de votre fenêtre, des procédures d'affichage, captures d'événements… Mais je n'ai pas l'intention d'exploiter Glut à son maximum durant mes tutoriels alors vaut mieux s'arrêter aux choses les plus intéressantes…

IV-B - double tampon

Pourquoi il nous parle encore de TAMPON en plein milieu de ce tutoriel ?
Vous savez autant que moi qu'en graphisme, deux choses sont d'une importance capitale : rapidité + réalisme.

Pour gagner un peu plus de vitesse nous allons passer en mode (Double Buffering) donc double tampon. Rien de plus simple pour l'activer :

InitOpenGL( FrmOpenGL.Canvas.Handle, 16, TRUE );

Le principe du double tampon est simple, le but est de faire basculer le contenu d'un tampon dans l'autre. Mais pourquoi deux tampons ? J'explique, si vous pouviez faire deux choses en même temps au lieu d'une que choisiriez-vous ? Le graphisme est plus que souvent matière à rapidité et c'est pour cela qu'OpenGl propose de dessiner la prochaine scène à venir dans un tampon virtuel pendant qu'une scène est déjà en train de se dessiner sur votre fenêtre. Voilà d'où le nom de (Double Buffering) vient. Mais il est aussi possible de travailler en simple tampon par exemple dans les trois premiers tutoriels où il n'y a pas vraiment d'animations robustes continues. Donc un rappel rapide.

- simple tampon : pour afficher le contenu, on se sert de la fonction glFlush(): simplement ;

- double tampon : pour afficher, c'est glFlush(); suivi de SwapBuffers( DC );. Ici le DC c'est l'adresse graphique de votre fenêtre donc Canvas.Handle.

IV-C - Comprendre l'importance de la lumière en 3D

L'aspect 3D des objets n'est souvent visible que lorsqu'ils sont éclairés par une source de lumière. Par exemple, une sphère non éclairée n'est qu'un disque. La lumière permet aussi de changer l'univers d'une scène par exemple une belle journée d'hiver où la neige tombe sur le sol, mais laissant réfléchir chacun de ses flocons. C'est bien beau tout ça, mais imaginons la lumière réfléchissant sur un cube, est-ce qu'elle doit réfléchir les parties que l'on ne voit pas ?

IV-D - Supprimer les surfaces cachées d'un objet

Quand on dessine plusieurs objets dans une scène, certains sont cachés par d'autres et s'ils ne sont pas supprimés cela peut affecter la rapidité du rendu ou encore donner un effet non désirable. Ceux-ci peuvent être cachés selon certaines conditions, par exemple si l'on change de point de vue ou encore si nous effectuons une rotation, translation ou redimensionnement sur l'objet lui-même. La manière la plus efficace de supprimer les surfaces cachées est d'utiliser le tampon de profondeur. Qu'est-ce que le tampon de profondeur ?

Premièrement, voyons comment l'activer et le désactiver et je vous dirai ensuite en quoi il consiste :

{Activé}
glEnable( GL_DEPTH_TEST )

{Désactivé}
glDisable( GL_DEPTH_TEST )

Lors de l'activation du tampon de profondeur, toutes les surfaces existantes seront stockées et comparées une à une pour vérifier si une d'entre elles ne serait pas dissimulée par une autre. La surface traitée ne sera dessinée que si elle n'est pas dissimulée par une autre. À présent, chaque fois que l'on videra le tampon principal, il faudra aussi vider celui du test de profondeur : glClear( … GL_DEPTH_BUFFER_BIT ), et voilà c'est aussi simple que cela.

Mais dans ce cas qu'est-ce qu'il se produit lors de la commande glClear( GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT ) ?

- Chaque valeur chromatique située dans le tampon sera effacée par la couleur de vidage spécifiée par cette commande glClearColor(0.0, 0.0, 0.0, 0.0);.

- Ensuite le tampon de profondeur attribue une profondeur, ou une distance la plus éloignée pour chaque pixel de la zone d'affichage. En d'autres termes, chaque fois que vous videz le tampon de profondeur (GL_DEPTH_BUFFER_BIT) alors tous les pixels de cette zone deviennent complètement éteints ou sinon la couleur de vidage choisie.

Bon à présent nous sommes prêts à utiliser les lumières.

IV-E - Les modèles de lumières

OpenGL nous offre la possibilité d'utiliser huit lumières individuelles qui seront représentées par GL_LIGHT0, GL_LIGHT1, GL_LIGHT2…GL_LIGHT7. Il est possible de changer la couleur de chaque lumière, les déplacer, changer leur intensité, leurs propriétés réflectives et j'en passe. Voici les différents types d'éclairages :

- Ambiante : l'éclairage ambiant correspond à une lumière dispersée par l'environnement. Sa source est impossible à déterminer. Quand celle-ci frappe une surface, elle est dispersée de manière égale dans toutes les directions ;

- Diffuse : cette lumière provient d'une source particulière et est aussi dispersée de manière égale dans toutes les directions ;

- Spéculaire : comparable à la brillance d'une surface comme le métal, un miroir ou autres surfaces ayant des propriétés très réfléchissantes.

Si vous êtes quelque peu mélangé dans tous ces termes, voici une petite explication :
Imaginez : vous avancez dans la forêt avec une lampe de poche, si vous éclairez le chemin sur lequel vous marchez, vous verrez un spot blanc-jaune, celui-ci est le reflet direct de la lumière venant de votre lampe. Ce spot se nomme SPÉCULAIRE.
Le chemin sera éclairé autour de ce spot, cette lumière sera appelée DIFFUSE.
Et la lumière contenue autour de vous et plus loin dans le chemin sera l'AMBIANTE.

IV-F - Démarche à suivre pour la création d'une lumière

IV-F-1 - Les normales de surfaces

Le plus complexe à comprendre dans l'utilisation des lumières en 3D est de déterminer la normale des facettes d'un objet. Comme je n'ai fait que de l'algèbre linéaire et non vectorielle…Alors il sera impossible pour moi de vous expliquer en détail comment on procède dans les situations complexes pour calculer les normales de surfaces.

Qu'est-ce qu'une NORMALE de surface ?

Une normale est en fait un vecteur déterminant la position de l'objet par rapport aux sources de lumière lui étant soumises. Exemple graphique tiré du Red Book (cliquez sur l'image pour agrandir) :

Les petites flèches noires sont toutes des normales de surfaces donc elles sont perpendiculaires à leur facette. Donc, pour que la lumière soit bien reflétée sur chaque facette de vos primitives, vous ne devez jamais oublier de spécifier chaque normale de ces facettes. Si vous ne les spécifiez pas alors vous obtiendrez un éclairage non plausible et je dirais même qu'elle n'aura aucun sens.

De plus, n'oubliez jamais que votre Normale doit être donnée sous forme unitaire donc sa norme doit être égale à 1. Si vous ne voulez pas les normaliser vous-même alors OpenGL peut le faire pour vous, mais aux dépens de la rapidité de votre application : glEnable( GL_NORMALIZE ) ou glEnable( GL_RESCALE_NORMAL ).

Une chose que j'ai failli oublier pour ce tutoriel ! Nous nous servirons des objets créés par l'utilitaire GLUT, car les normales sont toutes déjà précalculées. Mais pour la cause, j'ai quand même réalisé, dans le programme relié à ce tutoriel, un cube donc les normales de surfaces sont spécifiées manuellement. Le principe est très simple sur un cube, comme il ne comporte pas vraiment beaucoup de surface alors on peut donner les normales une à une, et ce, avec la commande glNormal3f. Vous comprendrez vite le principe de toujours donner un vecteur perpendiculaire à la surface en question. Sinon allez voir un excellent travail réalisé par Jason Hallen, concepteur du site DelphiGL.

IV-F-2 - Créer et positionner la ou les sources de lumière

La commande glLightfv() permet en outre de spécifier l'emplacement de la lumière. Comme c'est la première lumière, elle sera représentée par la constante GL_LIGHT0. Une fois que les caractéristiques de la lumière sont définies, il faut l'activer avec la commande glEnable( GL_LIGHT0 ). N'oubliez pas que plus vous ajoutez de lumière (GL_LIGHT0, GL_LIGHT1, GL_LIGHT2…) à votre scène et plus les performances de votre application seront diminuées.

IV-F-3 - Définir les propriétés des matières contenues par les objets de la scène

La lumière ce n'est pas tout, car nous devons spécifier quelle couleur est avalée par l'objet recevant la source de lumière. De plus, nous savons que la couleur n'est qu'un principe d'absorption par une matière quelconque. Bref, c'est la manière dont la lumière réfléchit sur l'objet.

Chaque objet réfléchit une couleur dont l'ambiante, la diffuse et la spéculaire. Il est possible de définir la brillance de cette réflectivité. Pour la spécification de réflectivité de la matière, la commande est la suivante : glMaterialf( GLenum face, GLenum pname, GLfloat param );

Le paramètre face spécifie la ou lesquelles facettes d'un objet seront touchées par la lumière, il peut prendre une des trois valeurs suivantes : GL_FRONT, GL_BACK, ou GL_FRONT_AND_BACK.
Le paramètre pname sera expliqué dans le tableau ci-bas.
Le paramètre param est un pointeur sur un tableau contenant les informations sur les modifications.

glMaterialf Paramètre pname

Paramètre

Valeurs par défaut

Description

GL_AMBIENT

( 0.2, 0.2, 0.2, 1.0 )

Couleur ambiante de la matière.

GL_DIFFUSE

( 1.0, 1.0, 1.0, 1.0 )

Couleur diffuse de la matière.

GL_AMBIENT_AND_GL_DIFFUSE

( 0.0, 0.0, 0.0, 1.0 )

Couleur ambiante et diffuse de la matière.

GL_SPECULAR

( 0.0, 0.0, 1.0, 0.0 )

Couleur spéculaire de la matière.

GL_SHINNINESS

0.0

Exposant spéculaire [0..128].

GL_EMISSION

0.0

Couleur émissive de la matière.

GL_COLOR_INDEXES

180.0

Index des couleurs ambiante, diffuse et spéculaire.

IV-F-4 - Création des sources de lumière

Une lumière comporte plusieurs propriétés comme : sa couleur, sa position, sa direction…
Pour modifier les propriétés d'une lumière quelconque, utilisez cette commande :

glLightfv( GLenum light, GLenum pname, const GLint *params ) avec les paramètres ci-dessous.
D'où le paramètre light spécifie la lumière à modifier (GL_LIGHT0…GL_LIGHT7).
Le deuxième paramètre pname est expliqué dans le tableau ci-bas.
Le dernier paramètre params est un pointeur vers un tableau contenant les informations à modifier.

glLightfv Paramètre pname

Paramètre

Valeurs par défaut

Description

GL_AMBIENT

( 0.0, 0.0, 0.0, 1.0 )

Intensité ambiante de la lumière.

GL_DIFFUSE

( 1.0, 1.0, 1.0, 1.0 )

Intensité diffuse de la lumière.

GL_SPECULAR

( 0.0, 0.0, 0.0, 1.0 )

Intensité spéculaire de la lumière.

GL_POSITION

( 0.0, 0.0, 1.0, 0.0 )

( X, Y, Z, W) position de la lumière dans l'espace.

GL_SPOT_DIRECTION

( 0.0, 0.0, -1.0 )

( X, Y, Z ) Direction du faisceau lumineux de la lumière. La direction est spécifiée par un vecteur.

GL_SPOT_EXPONENT

0.0

Exposant du faisceau lumineux.

GL_SPOT_CUTOFF

180.0

Angle de coupure du faisceau lumineux.

GL_CONSTANT_ATTENUATION

0.0

Facteur d'atténuation constant.

GL_LINEAR_ATTENUATION

0.0

Facteur d'atténuation linéaire.

GL_QUADRATIC_ATTENUATION

0.0

Facteur d'atténuation quadratique.

IV-G - Un petit peu de code ?

Nous allons déclarer quelques variables globales qui contiendront les informations par rapport aux propriétés réflectives de l'objet touché ainsi que les propriétés de base de la lumière active.

{Propriétés réflectives de l'objet touché par la lumière}
Matiere_Ambiante   : Array[0..3] Of TGLfloat = (0.0, 0.0, 0.3, 1.0 );
Matiere_Diffuse    : Array[0..3] Of TGLfloat = (0.0, 0.0, 0.5, 1.0 );
Matiere_Speculaire : Array[0..3] Of TGLfloat = (1.0, 1.0, 1.0, 1.0 );

{Propriété de la lumière}
Lumiere_Ambiante   : Array[0..3] Of TGLfloat = (0.0, 0.0, 0.3, 1.0 );
Lumiere_Diffuse    : Array[0..3] Of TGLfloat = (0.5, 0.0, 0.5, 1.0 );
Lumiere_Speculaire : Array[0..3] Of TGLfloat = (0.0, 0.0, 1.0, 1.0 );
Lumiere_Position   : Array[0..3] Of TGLfloat = (-2.0, 2.0, -2.0, 1.0 );

Ces variables globales sont de type Tableau et peuvent contenir quatre valeurs de type glFloat. Parfait, maintenant passons à la procédure d'affichage qui elle affecte ces valeurs avec la lumière ou plutôt l'objet touché par celle-ci. Pour l'instant nous ferons ces affectations de valeur dans la procédure OnCreate de votre contrôle fenêtré.

{Définir les propriétés de l'objet touché par la lumière
ainsi que la lumière}
glMaterialfv( GL_FRONT, GL_AMBIENT, @Matiere_Ambiante);
glMaterialfv( GL_FRONT, GL_DIFFUSE, @Matiere_Diffuse);
glMaterialfv( GL_FRONT, GL_SPECULAR, @Matiere_Speculaire);
glMaterialf( GL_FRONT, GL_SHININESS, 20);

glLightfv( GL_LIGHT0, GL_AMBIENT, @Lumiere_Ambiante );
glLightfv( GL_LIGHT0, GL_DIFFUSE, @Lumiere_Diffuse );
glLightfv( GL_LIGHT0, GL_SPECULAR, @Lumiere_Speculaire );
glLightfv( GL_LIGHT0, GL_POSITION, @Lumiere_Position );

{Activation de l'éclairage}
glEnable( GL_LIGHTING );

{Activation de la première lumière}
glEnable( GL_LIGHT0 );

{Activation du test de profondeur}
glEnable( GL_DEPTH_TEST );

J'espère que vous avez tout pigé !

Captures d'écran :

IV-H - Programme relié

Ce petit exemple offre la possibilité de voir l'effet de la lumière sur cinq primitives. Pour changer de primitives, servez-vous des touches 1 à 5 de votre clavier.

Ensuite, modifiez le programme à votre guise, amusez-vous à changer :

- les couleurs de réflexion de l'objet ou de la lumière ;

- la direction de l'objet ;

- la direction de la lumière ;

- ajoutez un angle de coupe *Indice : glLightfv( GL_LIGHT0, GL_CUTOFF… ).

Source

Pour ceux que cela intéresse, voici l'autre manière. Source (à la C++).

V - Lumière en mouvement

V-A - Spécification de réflectivité de matière (petit truc)

Vous vous rappelez que dans le quatrième tutoriel nous avons abordé la manière de procéder pour spécifier les propriétés réflectives de la matière d'un objet ? Si vous vous en souvenez alors je suis sûr que vous avez remarqué qu'il est long et gourmand en mémoire de déclarer trois variables distinctes pour chaque matière située dans une scène. Ce que je vous propose c'est de spécifier la réflectivité de votre matière non pas en raccourcissant votre code de programmation, mais en réduisant les variables utilisées pour le faire. Par contre cette commande ne s'avèrera intéressante (optimisation) que si vous ne devez spécifier qu'un seul paramètre de matière pour la majorité des sommets de la scène. Dans mon exemple, à télécharger en bas, vous constaterez que j'ai utilisé cette commande pour tous les paramètres, mais ici ce n'est pas important, car ce n'est que pour comprendre le principe. Une fois que vous avez terminé l'utilisation de cette commande, il est conseillé de la désactiver pour augmenter les performances de votre application.

Activation et désactivation de la commande en question :

glEnable( GL_COLOR_MATERIAL );
...
Spécification des paramètres de réflectivité des ou de la matière
...
glDisable( GL_COLOR_MATERIAL );

V-B - Créer une lumière éclairant un objet en mouvement

Comme la lumière doit suivre l'objet en question alors la position de celle-ci doit toujours être invoquée juste après la transformation de l'objet à suivre. N'oubliez pas que si vous dessinez d'autres primitives après avoir invoqué la position de la lumière alors la lumière affectera aussi ces primitives. Si vous désirez dessiner plusieurs primitives dans votre scène alors, n'oubliez pas de vous servir gracieusement des deux commandes magiques : glPushMatrix et glPopMatrix. Je parle encore des deux commandes magiques, car il ne faut pas oublier que la lumière affecte autant la matrice de modélisation que la lumière peut affecter la couleur des primitives dans la scène.

Si vous désirez afficher une primitive représentant la lumière elle-même alors elle ne devrait pas être affectée par sa propre source de lumière donc il serait mieux d'éteindre la lumière, d'afficher la primitive représentant la lumière et d'ensuite rallumer. Comment procède-t-on ?
glDisable( GL_LIGHTING ) et de la réactiver avec glEnable( GL_LIGHTING ).

Maintenant je vous propose une partie du code représentant deux projecteurs lumineux qui reflètent sur une plateforme :

glPushMatrix();
glTranslated(-4 * Cos(DegreRot1) *-5 * Cos(DegreRot2),
5 * Sin(DegreRot1),
-6 * Cos(DegreRot1) * 4 * Sin(DegreRot2));

glLightf( GL_LIGHT0, GL_SPOT_EXPONENT, 30 + (10 * Sin(DegreRot2)) );
glLightfv( GL_LIGHT0, GL_POSITION, @Lumiere0_Position );

glDisable( GL_LIGHTING );
glPointSize( 3.0 );
glColor3f( 1.0, 1.0, 1.0);
glBegin( GL_POINTS );
glVertex3d( Lumiere0_Position[0], Lumiere0_Position[1], Lumiere0_Position[2] );
glEnd();
glEnable( GL_LIGHTING );

glPopMatrix();

- glTranslated… : en premier lieu, une translation est effectuée dans tous les sens pour faire pivoter la première lumière. Le but est seulement d'en faire un projecteur actif alors il n'y a rien de spécial à comprendre ici, à part le fait d'effectuer plein de rotations dans la matrice de modélisation.

- glLightf( GL_LIGHT0, GL_SPOT_EXPONENT… : « Lumiere0_Position » est une variable tableau de trois valeurs GLfloat qui représentent la position de la lumière. Quand la scène bouge ou plutôt dire quand la matrice de modélisation est affectée par des déplacements quelconques alors la lumière se déplace en fonction du mouvement de la scène. Donc la position de la lumière n'est plus spécifiée par la variable « Lumiere0_Position », mais plutôt récupérée par celle-ci.

- glPointSize( 3.0 ) : Ah Ha! Nouvelle commande inédite… Elle n'a vraiment rien de spécial à part de spécifier la grosseur des pixels dessinés sur la scène. Cette commande est surtout utilisée dans le procédé d'Antialiasing, expliqué dans un prochain tutoriel.

- glVertex3d( Lumiere0_Position[0]… ); : vous connaissez déjà cette commande j'espère . Comme la variable « Lumiere0_Position » a récupéré la nouvelle position de la lumière dans la scène alors nous nous en servons pour placer un gros pixel qui représente le projecteur lui-même.

V-C - Éclairage se déplaçant avec le point de vue

Si l'on veut déplacer la lumière avec le point de vue ou plutôt, déplacer le point de vue et que la lumière suive, il faut lui donner sa position dans la procédure Redimension(), juste avant la transformation de visualisation (PROJECTION), ou si vous aimez mieux juste avant que la matrice de projection soit activée.
Comme ceci :

glLightfv( GL_LIGHT0, GL_POSITION, @LumiereX_Position );

glViewport( 0, 0, ClientWidth, ClientHeight );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();

gluPerspective( 45, Width / Height, 0.1, 200.0 );

glMatrixMode( GL_MODELVIEW );

Capture d'écran :

Hum!!! Ma vielle Voodoo Banshee…
Excusez-moi les captures laissent souvent à désirer.
Une chance je suis à quelques jours de changer de système .

V-D - Programme relié

Cet exemple n'est pas des plus originaux, mais il représente des projecteurs situés en haut d'une plateforme qui reflète la lumière de ceux-ci. Ce qui est à surveiller, c'est l'optimisation possible que l'on peut apporter à cette petite application, par exemple les fonctions Cos et Sin qui sont extrêmement utilisées. Pour remédier à ce problème, il faudrait créer deux tableaux précalculés de ces valeurs… De plus, quelques commandes OpenGL sont répétées en double à maintes reprises et cela n'est fait que dans un but pédagogique, donc ils ne servent à rien.

En pratique, vous pourriez :

- optimiser le programme (si vous comprenez bien le fonctionnement) ;

- créer des murs à la plateforme et s'organiser pour que les lumières reflètent sur le mur (servez-vous de la direction de la lumière).

- Amusez-vous…

Source

Pour ceux que cela intéresse, voici l'autre manière. Source (à la C++).

VI - Transparence

VI-A - Survol du tutoriel

Le « Blending » est en quelque sorte des mélanges de couleurs qui mènent à des effets de transparence sur des primitives. Quand nous dessinons une scène, vous vous rappelez qu'il faut vider le tampon de valeurs chromatiques de cette manière : glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT);, mais maintenant nous allons voir qu'il est possible de garder ces valeurs et de les mélanger aux nouvelles qui seront dessinées par-dessus. Nous pouvons les mélanger de plusieurs manières dont : ne garder qu'une partie de la couleur résidente, les mélanger également, etc. Bref il existe plusieurs façons de réaliser le Blending…

P.-S. Petit rappel de ce qu'est la zone de valeurs chromatiques : pensez à une grille qui contient plusieurs pixels dont ceux-ci possèdent chacun quatre propriétés, la couleur Rouge, Vert, Bleu et sa valeur ALPHA. La valeur Alpha est introduite dans ce chapitre et vous comprendrez son utilité.

VI-B - Fusionnement des couleurs

Vous vous êtes sûrement demandé à quoi sert le quatrième paramètre de la commande servant à spécifier la couleur d'un objet :

glColor4f( 1.0, 0.5, 0.5, 1.0 );
glColor4d( 0.5, 0.5, 0.5, 0.5 );
glColor4ub( 128, 0, 255, 90 );
glColor4b( 128, 0, 30, 50 );

Cette valeur nommée « ALPHA » est utilisée entre autres pour spécifier le taux de transparence de l'objet. Prenons par exemple la première donnée 1.0 donnée sous type GLfloat, ici elle spécifie que l'objet est complètement opaque et dans ce cas sa surface ne laisse rien passer. En revanche, la deuxième commande attestera que l'objet sera d'un ton gris, mais laissant passer 50 %, donc « ½ » de transparence.

- Valeur Alpha élevée : la couleur sera de plus en plus opaque. La valeur 1.0 ne laisse passer aucune transparence.

- Valeur Alpha basse : la couleur sera de plus en plus transparente. La valeur 0.0 est complètement transparente.

Prenez par exemple un cube ayant la valeur Alpha = 1 et une sphère dessinée juste devant avec une valeur Alpha de 0.15. Le cube quant à lui ne laisse rien passer, mais ce n'est pas grave, car il est situé à l'arrière de la sphère. Mais la sphère laisse passé 15 % de transparence, ce qui donnerait donc la possibilité d'apercevoir le cube à travers elle.

Le principe est simple, si nous ne spécifions pas de fonctions de blending, alors quand nous dessinerons deux objets un par-dessus l'autre, OpenGL les dessinera tous deux comme étant deux objets opaques, car le premier objet est dessiné et quand l'autre vient pour l'être alors OpenGL écrase chaque valeur chromatique du cube pour les remplacer par celles de la Sphere. Par conséquent, si le blending est activé alors il est possible de conserver les valeurs chromatiques du cube et de les accoupler à celles de la sphère. D'où vient le nom Blending (Transparence). Un peu plus bas vous verrez un tableau donnant liberté aux diverses fonctions du blending, ses fonctions agissent toujours sur une Source et une Destination. Voici les rôles de l'exemple que je viens de citer avec cube et la sphère :

- Sphère : elle joue le rôle de la SOURCE, car ses valeurs chromatiques seront affectées à celles du cube ;

- Cube : il joue le rôle de la DESTINATION, car il est le résultat de l'accouplement de ses valeurs chromatiques et celles de la sphère.

Maintenant voyons comment activer le Blending sous OpenGL : glEnable( GL_BLENDING ) ou pour désactiver : glEnable( GL_BLENDING ).

Voici les différents types d'accouplements entre la source et la destination en utilisant la commande suivante : glBlendFunc( GLenum sfactor, GLenum dfactor );

Facteur de blenging (Source et Destination )

Constante

Facteur concerné

Facteur de blending calculé

GL_ZERO

Source ou Destination

(0, 0, 0, 0)

GL_ONE

Source ou Destination

(1, 1, 1, 1)

GL_DST_COLOR

Source

(Rd, Gd, Bd, Ad)

GL_SRC_COLOR

Destination

(Rs, Gs, Bs, As)

GL_ONE_MINUS_DST_COLOR

Source

(1, 1, 1, 1) - (Rd, Gd, Bd, Ad)

GL_ONE_MINUS_SRC_COLOR

Destination

(1, 1, 1, 1) - (Rs, Gs, Bs, As)

GL_SRC_ALPHA

Source ou Destination

(As, As, As, As)

GL_ONE_MINUS_SRC_ALPHA

Source ou Destination

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

GL_DST_ALPHA

Source ou Destination

(Ad, Ad, Ad, Ad)

GL_ONE_MINUS_DST_ALPHA

Source ou Destination

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

GL_SRC_ALPHA_SATURATE

Source

(f, f, f, 1);f = min( As, 1-Ad)

GL_CONSTANT_COLOR

Source ou Destination

(Rc, Gc, Bc, Ac)

GL_ONE_MINUS_CONSTANT_COLOR

Source ou Destination

(1, 1, 1, 1) - (Rc, Gc, Bc, Ac)

GL_CONST_ALPHA

Source ou Destination

(Ac, Ac, Ac, Ac)

GL_ONE_MINUS_CONSTANT_ALPHA

Source ou Destination

(1, 1, 1, 1) - (Ac, Ac, Ac, Ac)

Comme dirait HOMER : Maudit Verra ! Oui ça l'air compliqué, mais ne vous en faites pas, ce n'est que du charabia qui en fait se résume à quelques explications simplistes pour comprendre

glBlendFunc( GL_ONE, GL_ZERO );
glEnable( GL_BLEND );

Si l'on dessine une ou des primitives en utilisant le blending de cette façon, cela n'aura aucun effet de transparence et donc nous aurons les mêmes résultats que si le Blending était désactivé. Pourquoi ? Parce que si l'on se fie au tableau précédent, nous spécifions que les valeurs chromatiques de la source sont multipliées par (1, 1, 1, 1), ce qui ne change absolument rien aux valeurs chromatiques sources. Par contre les valeurs chromatiques de la destination sont écrasées, car elles sont multipliées par (0, 0, 0, 0).

Je vais vous laisser vous amuser avec tous ces facteurs, ils vous offrent beaucoup de possibilités, mais parmi celles-ci, une est souvent utilisée et la voici :

glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glEnable( GL_BLEND );

Pour ce qui est d'utiliser le Blending avec des primitives en mouvement, il faut tenir compte que OpenGL calcule toujours la distance entre les yeux de l'utilisateur et la primitive à afficher. Donc, si vous restituez une primitive Opaque devant une primitive Transparente alors OpenGL ne dessinera que la primitive Opaque du fait qu'elle cache la transparente. Mais si la primitive transparente est située devant celle opaque alors il ne faut pas que le tampon de profondeur supprime la primitive opaque, car elle est visible à travers la transparente. Donc il faudra désactiver le tampon de profondeur pendant la restitution de celle-ci. La démarche à suivre est la suivante, commencez à dessiner toutes les surfaces opaques et quand vous serez prêt à dessiner les transparentes, activez le tampon de profondeur en lecture seule. Pour activer le tampon de profondeur en lecture seule, voici la commande :

glDepthMask( GL_FALSE );  //Tampon en lecture seule
// Primitives transparentes...
glDepthMask( GL_TRUE );   //Retour du tampon en mode lecture / écriture

Dépendamment de votre position par rapport à la primitive, vous pouvez exiger que les surfaces arrière ou avant ne soient pas affichées. Cela peut être pratique pour récupérer du temps machine et surtout éliminer des effets non désirés. N'oubliez pas quand vous créez un polygone de toujours positionner ses sommets de manière à ce qu'ils soient dans le sens contraire des aiguilles d'une montre, cela vous évitera des tonnes de problèmes. Si par contre vous devez les dessiner dans le sens des aiguilles d'une montre alors, utilisez cette commande :

glFrontFace( GL_CCW );    // Sens contraire des aiguilles d'une montre
glFrontFace( GL_CW );     // Sens des aiguilles d'une montre

Bon et maintenant voici la méthode pour éliminer les surfaces arrière des primitives :

glCullFace( GL_BACK );
glEnable( GL_CULL_FACE );

//Restitution des primitives...

glDisable( GL_CULL_FACE );

Voici un programme qui vous démontrera les bienfaits du Blending.

VI-C - Programme relié

Ce petit programme montre un cube en mouvement à l'intérieur d'une pyramide. Chaque face de la pyramide a une valeur de transparence différente.

Je vous laisse vous amuser avec les différentes possibilités de facteurs de blending.

Source

Pour ceux que cela intéresse, voici l'autre manière. Source (à la C++)

VII - Listes d'affichage

VII-A - Qu'est-ce qu'une liste d'affichage

Une liste d'affichage permet d'améliorer les performances de votre application dans la mesure où il est possible d'y stocker une ou plusieurs commandes d'OpenGL et de les rappeler par UNE simple commande. Le principe est assez simple, on demande à OpenGL de nous donner un numéro d'autorisation pour la nouvelle liste et une fois ce numéro récupéré, nous stockons les commandes à l'intérieur de ces deux clauses que voici : glNewList(…); / glEndList();

Les commandes pouvant être stockées peuvent être par exemple :

- des formes géométriques (Primitives) ;

- rotations, translations et redimensionnements… ;

- ou encore une partie d'une scène ou une scène entière.

VII-B - À quoi servent plus précisément les listes d'affichage

Quand on construit un cube sommet par sommet avec les deux commandes glBegin( GL_QUADS); / glEnd();, ce code prend considérablement de la place et par ailleurs il doit être recalculé à chaque rendu dans notre scène. Pour contrer ce problème, la liste d'affichage permet de créer en permanence l'objet ou l'action de votre choix et de simplement le ou la rappeler avec la commande suivante : glCallList( Numéro_de_la_liste );.

Voici un problème donné

Vous voulez dessiner un bonhomme de neige, mais vous constatez que vous devez créer trois sphères de différentes grosseurs (à supposer aussi que GLUT n'existe pas). Évidemment les trois sphères ont toutes la même forme, mais elles sont de dimensions différentes et par la même occasion vous vous dites :
mais je n'ai pas besoin de créer trois sphères, car je n'ai qu'à en fabriquer une et la redimensionner avant de l'afficher !
Et comme dit précédemment, à supposer que GLUT n'existe pas, alors il vous faudra créer la sphère avec les mathématiques et cela implique le besoin de se servir des fonctions Cos et Sin qui sont très gourmandes en temps machine. Cela n'aurait pas de sens de fabriquer trois sphères à chaque fois que votre scène se redessine, ce serait trop long. La solution…

Donc, il faut fabriquer une fonction capable de créer une sphère avec un rayon et le nombre de méridiens et parallèles donnés. Une fois celle-ci fabriquée alors nous pourrons nous en servir pour construire le bonhomme de neige en redimensionnant la sphère en fonction de la partie du corps à dessiner.

En conclusion, il suffit de stocker la ou les bases géométriques dans une liste d'affichage et y faire appel autant de fois que nécessaire. Quand vous n'aurez plus besoin de cette liste, il sera aussi possible de la supprimer, ce qui libère de la mémoire, bref… tout pour donner une chance aux performances de votre application. Mais notez qu'une fois supprimés, elle et son contenu ne seront plus en mémoire, mais par contre l'index pris par celle-ci sera maintenant réutilisable pour la création d'une nouvelle liste au besoin.

Voyons comment créer une liste d'affichage :

1. Demandez à OpenGL d'attribuer (Générer) un index à notre liste d'affichage avec la commande suivante : glGenList(…); ;

2. Créez la liste en vous servant de ces deux commandes : glNewList( GLuint list, GLenum mode ); / glEndList(); ;

3. Une fois l'utilisation terminée (souvent à la fermeture du programme) alors supprimez la liste avec la commande : glDeleteList(…);.

Voici un exemple simple :

procedure TFrmOpenGL.FormCreate(Sender: TObject);
Begin
...
glClearColor( 0, 0, 0, 1 );
glEnable( GL_DEPTH_TEST );

Sphere := glGenLists( 1 );
glNewList( Sphere, GL_COMPILE );
CreerSphere( 30, 30, 5.0 );
glEndList();
...
End;

Procedure TFrmOpenGL.CreerSphere( Const NbParalleles :GLint;
Const NbMeridiens :GLint;
Const Rayon :GLdouble );
Const
DOUBLE_PI = 2 * PI;
Var
I, J          :Integer;
Theta,Theta2  :GLfloat;
Phi           :GLfloat;
Vx, Vy, Vz    :GLdouble;
begin
glPushMatrix();
For I := 0 To NbParalleles  Do
Begin
glBegin( GL_TRIANGLE_STRIP );

Theta  := I       * DOUBLE_PI / NbParalleles;
Theta2 := (I + 1) * DOUBLE_PI / NbParalleles;
For J := 0 To NbMeridiens Do
Begin
Phi  := J * DOUBLE_PI / NbMeridiens;

Vx := Cos(Theta) * Cos(Phi);
Vy := Cos(Theta) * Sin(Phi);
Vz := Sin(Theta);
glNormal3f( Vx, Vy, Vz );
glVertex3d( Rayon * Vx, Rayon * Vy, Rayon * Vz );

Vx := Cos(Theta2) * Cos(Phi);
Vy := Cos(Theta2) * Sin(Phi);
Vz := Sin(Theta2);
glNormal3f( Vx, Vy, Vz );
glVertex3d( Rayon * Vx, Rayon * Vy, Rayon * Vz );

End;
glEnd();
End;
glPopMatrix;
End;

Procedure TFrmOpenGL.DessinerScene;
Begin
glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT);
glClearColor( 0.0, 0.0, 0.0, 1.0 );

glMatrixMode(GL_MODELVIEW);
glLoadIdentity;

glTranslated( 0.0, 0.0, -20.0 );
glCallList( Sphere );

SwapBuffers( Canvas.Handle );
End;

VII-C - Explication de l'exemple

C'est loin d'être un exemple complet, mais j'ai juste inclus les principaux points importants pour me servir d'une liste d'affichage, vous y verrez plus clair dans l'exemple livré avec ce tutoriel dans le bas de la page.

glGenLists(1);
On demande à OpenGL de générer un nombre de listes en mémoire, ici on lui à passé 1 en paramètre donc une liste sera générée. Cette commande retourne un indice qui sera attribué à notre liste. Si glGenLists retourne 0 alors c'est qu'OpenGL n'a pu créer la liste, soit par manque de mémoire ou parce que tous les indices sont déjà pris.

glNewList( Boule, GL_COMPILE ); / glEndList();
Le premier paramètre spécifie l'index de la liste. Donc une variable de type entier non signé qui contient l'indice de la liste précédemment récupéré avec la commande glGenList(…). Le deuxième paramètre spécifie le mode de compilation. GL_COMPILE ou GL_COMPILE_AND_EXECUTE. La différence entre ces deux paramètres est la suivante : si vous utilisez GL_COMPILE alors la liste d'affichage est compilée et prête à être utilisée, en revanche si vous utilisez le mode GL_COMPILE_AND_EXECUTE alors la liste peut être utilisée lors de sa compilation. Tous les commandes, appels de fonctions ou procédures… se trouvant à l'intérieur de ces deux commandes feront partie de la conception de la liste. Donc quand vous ferez appel à cette liste, tout ce qui était à l'intérieur aura été compilé et prêt à être utilisé.

glCallList( Sphere );
Le résultat de cette commande est de faire appel à tout ce qui a été compilé à l'intérieur de la liste d'affichage, par conséquent dans ce cas-ci, se sera notre sphère qui sera dessinée.
Son seul paramètre est l'index de la liste d'affichage, alors nous lui passons la variable Sphere. Cette variable contient l'index, car elle lui a été attribuée à l'aide de la commande glGenList(…).

Je ne l'ai pas mentionné, mais il est aussi possible pendant la conception d'une liste d'affichage de faire appel à d'autres listes d'affichage selon le besoin.

Et maintenant voici l'exemple concernant les listes d'affichage :

VII-D - Programme relié

Hmmm ! Pas facile, mais cette horloge est entièrement faite avec la même sphère. Tout un défi ! L'exemple n'est pas des plus faciles, mais je vous conseille fortement de l'étudier et ensuite vous devriez très bien maîtriser les listes d'affichage.

Source

Pour ceux que cela intéresse, voici l'autre manière. Source (à la C++)

VIII - Lissage - (Antialiasing)

Ah ! ce qu'OpenGL ne ferait pas pour nous simplifier la vie et nous donner de bons résultats. Ce tutoriel sera consacré au bienfait du lissage ou plus communément surnommé L'Antialiasing. Nous verrons pourquoi il est important, mais aussi pourquoi il ne faut pas en abuser.

VIII-A - Lissage ou antialiasing

Le lissage ou (antialiasing en anglais) est un technique qui renferme des calculs mathématiques complexes permettant d'éliminer l'effet de créneaux quand on trace des lignes ou des points. Je m'explique par une image pour ceux à qui le mot « créneau » ne veut rien dire.

Un ensemble de créneaux ressemble de très près à un escalier de points formant une ligne ou l'extrémité de la surface d'un polygone. Sans Antialiasing sur une scène dessinée à l'écran d'un ordinateur, il est assez souvent très facile d'apercevoir l'effet de points en escalier et parfois cela gâche la beauté de la scène. Mais OpenGL est encore là pour nous et il nous offre la possibilité de les estomper via le lissage. Bon je vous dis tout de suite que pour l'activer c'est un jeu d'enfant, mais il faut l'utiliser modérément, car il bouffe énormément de temps machine. Donc il ne faut pas exagérer quand une scène n'en à pas besoin. Le lissage sera surtout utilisé quand on sera très près (Zoom) d'une scène ou d'un objet quelconque.

Tout d'abord avant d'activer le lissage, il faut utiliser le blending et j'espère que vous vous souvenez comment on procède ?

glEnable( GL_POLYGON_SMOOTH );

glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glEnable( GL_BLEND );

Les valeurs ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) passées en paramètres à la commande glBlendFunc(…) sont les plus susceptibles d'être utilisées. Le lissage peut s'actionner de trois manières et les voici :

- glEnable(GL_POINT_SMOOTH );

- glEnable(GL_LINE_SMOOTH );

- glEnable(GL_POLYGON_SMOOTH );

Alors il est possible de lisser des points, des lignes et pour terminer des polygones. Le mode de lissage que vous aurez à activer sera choisi en conséquence selon la manière dont vous avez dessiné vos primitives. Si par exemple vous avez dessiné un triangle à l'aide de l'argument glBegin( GL_LINES ); alors il vous faudra activer le Lissage de cette manière glEnable( GL_LINE_SMOOTH );.

Autres commandes intéressantes lors du lissage sont ceux-ci :

- glPointSize( Size );

- glLineWidth( Width );

Ces deux commandes affecteront la grosseur des pixels ou des lignes lors du lissage, parfois cela peut-être utile et par conséquent je vous laisse trouver pourquoi…

Une fois que les valeurs nécessaires sont affectées aux paramètres de lissage, il ne reste plus qu'à activer le blending comme vu au tutoriel précédent. Et ce n'est pas tout, il est aussi possible de le désactiver de cette manière : supposons que vous avez activé le lissage des points, et bien pour le désactiver, il vous faudra procéder de cette manière : glDisable( GL_POINT_SMOOTH );

Voilà un petit programme qui vous prouvera que le lissage est très utile pour donner un meilleur aspect et définition graphique à vos scènes ou primitives.

VIII-B - Programme relié

Pour activer l'Antialiasing appuyez sur la touche « a » de votre clavier. Pour le désactiver, vous n'avez qu'à réappuyer sur la même touche.

Si vous ne voyez pas de différence alors il y a plusieurs raisons :

- votre carte vidéo (j’ai essayé un 3D Prophet 4500 Kyro II et je ne voyais pas vraiment de différence) ;

- changer de lunettes  ;

- changer de moniteur  ;

- allez vous coucher .

P.-S. Petit truc, réduisez la valeur Alpha (inférieure à 1.0 ) de la couleur d'une primitive et activez le Lissage. Ensuite, activez le Blending, mais avec le paramètre GL_ONE comme destination :

glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );

Vous devriez apercevoir toutes les extrémités (lignes) des surfaces de la primitive affectée par ce Blending. Mais pour ça vous devez travailler avec un polygone, donc l'exemple ci-dessus ne vous le permettra pas…

Source

Pour ceux que cela intéresse, voici l'autre manière. Source (à la C++)

IX - Texture

IX-A - Pourquoi utiliser des textures ?

Depuis le début de ces tutoriels, nous avons créé des primitives dotées soit d'une couleur solide ou dégradée. Mais imaginez-vous dessiner une tour de briques par exemple, la seule solution possible serait de dessiner plusieurs carrés collés un par-dessus l'autre avec une couleur et une bordure adaptée. Néanmoins, cette solution n'est pas mauvaise, mais elle ne donnerait pas vraiment un mur très réaliste du fait qu'il serait trop parfait. Donc pour résoudre ce petit problème, il serait plutôt intéressant de dessiner un grand rectangle et de le recouvrir d'une texture de brique. Il existe des tonnes de problèmes comme celui-ci qui nécessitent une texture telle que :

- un sol : un terrain de tourbe, un plancher de tuiles…

- un arbuste : un arbre, une plante, une fleur… ;

- des objets : une automobile, un avion, un vaisseau… ;

- un menu : des boutons, des titres…

Pour appuyer mes dires, voici une capture d'écran du jeu Unreal :

Remarquez le plancher de cette scène qui est complètement recouvert par une texture qui lui donne un air très plausible. Même les contours de planchers portent des bordures pour les rendre encore plus convaincants.
De plus l'éclairage qui ne manque pas de venir ajouter son grain de réalisme. Constatez aussi que le menu est couvert de multiples textures en guise de fond et contour.

Les textures offrent beaucoup de possibilités quant à leurs manipulations, propriétés et applications, mais il faut préciser que c'est aussi un domaine immense et c'est pour cette raison que je ne pourrais le couvrir en entier.

Pour débuter, j'aimerais vous préciser que la dimension d'une texture doit absolument être une puissance de 2. Si la largeur ou hauteur de votre texture n'est pas une puissance de deux alors elle sera indéfinie et par conséquent non affichée par OpenGL. Voyons maintenant les trois types de textures qu’OpenGL met à notre disposition :

- texture à une dimension ;

- texture à deux dimensions ;

- texture à trois dimensions.

Vous vous demandez sûrement quelle est la différence.

Une Dimension
Ce type de texture possède 1 pixel en hauteur et 2x pixels en largeur. Il est surtout utilisé pour recouvrir des bandes dans n'importe quelle direction. Cette texture sera créée à l'aide de la commande suivante : glTexImage1D(..).

Deux Dimensions
Ce type de texture est sûrement le plus utilisé dans les scènes conventionnelles. Il propose une image ayant 2x de largeur et 2y de hauteur. On peut facilement comparer cette texture à une image bitmap ayant deux coordonnées dont le X et Y. Cette texture sera créée à l'aide de la commande suivante : glTexImage2D(..) que nous verrons plus loin.

Trois Dimensions
Souvent utilisé dans le domaine scientifique, voire médical. On pourrait définir ce type de texture comme plusieurs couches de textures deux dimensions en une seule texture. Cette texture sera créée à l'aide de la commande suivante : glTexImage3D(..).

En fait, les textures sont simplement des rectangles de données contenant des valeurs chromatiques (Couleurs). Les valeurs contenues dans celles-ci sont appelées « Texel ». Ce qui est intéressant c'est qu'OpenGL nous offre la possibilité de répéter, resserrer, élargir nos textures et plus encore… Mais le plus important c'est que nous pouvons faire appel à lui pour appliquer de la texture automatiquement sans spécifier de coordonnées d'application de texture. Cela peut être utile quand on commence à recouvrir une primitive non rectangulaire et complexe. En revanche, il ne faut pas ambitionner sur cette option, car elle coûte cher en temps machine et parfois on n’obtient pas les résultats attendus.

Voici la manière dont fonctionne les coordonnées de texturage :

Si vous désirez texturer un simple carré alors vous allez préciser les coordonnées de texturage de la même manière que sur la photo à gauche. Les coordonnées d'une texture fonctionnent sous le même principe qu'un plan cartésien standard sauf qu'elles sont dépourvues de valeurs négatives. Alors le X peut prendre de 0 à 1 et même chose pour Y.
Si vous désirez accéder au milieu de la texture il faudrait préciser les coordonnées comme ceci : X = 0.5 et Y = 0.5
Pour resserrer la texture, on n'a qu'à dépasser les bornes alors :
X := 3 et Y := 1 resserrerait la texture dans l'axe des X.

Notez maintenant que j'utilise les deux coordonnées X et Y, mais il faut faire attention, car sous OpenGL elles sont appelées S et T. En fait, les coordonnées d'une texture sont localisées en se servant de ces quatre coordonnées que voici : S, T, R et Q.

D'où R et Q sont utilisés pour des textures ayant plus de deux dimensions. Si la texture ne comporte qu'une dimension alors T, R et Q prennent la valeur 0. De même que si une texture possède deux dimensions alors les coordonnées R et Q prennent la valeur 0.

IX-B - Étapes pour l'application de texture

Voici les principales étapes pour l'application d'une texture.

1. Créer une scène (Primitives).

2. Charger une texture en mémoire.

3. Préciser à OpenGL que nous désirons utiliser le texturage.

4. Préciser le mode d'application de la texture pour chaque pixel.

5. Activer l'application…

Ah ! Il est enfin temps d'aller découvrir en détail, comment réaliser toutes ces étapes.

Créer une scène (Primitives)

Ce sujet a été exploité dans les tutoriels précédents alors je ne crois pas avoir à me prononcer une seconde fois :-)

Charger une texture en mémoire

Tout d'abord il faut déterminer quel format adoptera notre image qui sera utilisée en guise de texture. Ce que je veux dire par format est bien tout simplement qu'il faudra déterminer le nombre de composants nécessaires pour réaliser une couleur. Ici les plus souvent utilisés sont RGB et RGBA.

- RGB signifie qu'il faut trois (octets) intensités de chaque couleur (Rouge, Vert, Bleu) pour réaliser une couleur.

- RGBA demande la même chose que RGB, mais plus une valeur souvent appelée Alpha qui peut être utilisée pour gérer la transparence des texels.

- Luminance trois ingrédients (YUV) et qui par la suite permet de créer de la luminosité selon la valeur Y. Ce type ne peut être avoir une taille supérieure à 16 bits.

- Il existe d'autres modèles, mais pour plus d'information :Manuel OpenGL.

Selon le type de format, il est aussi possible d'appliquer la texture selon deux modes :

- le premier consiste à remplacer la valeur chromatique d'un pixel dans le tampon par celle d'un pixel de la texture. Autrement dit la couleur du pixel d'un polygone est recouverte par la couleur du pixel de la texture ;

- la deuxième possibilité consiste à combiner la valeur chromatique d'un pixel avec celle de la texture. Ainsi il est possible de combiner les valeurs chromatiques d'un polygone avec l'effet de lumière appliqué à une texture.

Une fois la texture créée et son mode d'application spécifiée alors il est maintenant temps d'activer la texturation à l'aide de la commande glEnable( GL_TEXTURE1D ) ou glEnable( GL_TEXTURE2D ) ou glEnable( GL_TEXTURE3D ). N'oubliez pas que ce qui est activé par la commande glEnable est aussi désactivable par la commande glDisable().

Créer la texture

Et maintenant voyons les quelques commandes permettant de créer et spécifier le mode d'application de la texture sur les pixels. De la même manière qu'une liste d'affichage, la texture doit être représentée par un numéro d'identification et dont celui-ci sera nommé avec la commande glGenTextures().

Autre chose très importante : vous devez absolument préciser le nombre de textures qui seront générées dès l'appel de cette commande, car ensuite vous ne pourrez revenir sur vos pas à moins de supprimer ceux déjà existants. En termes plus clairs, cela signifie que si vous avez trois textures à créer alors il vous faut appeler cette commande avec trois comme nombre de texture et ensuite créer vos textures. En passant, il n'est pas possible d'utiliser cette commande à l'intérieur d'une liste d'affichage.

glGenTextures( 1, @IDTexture );

Ici nous avons demandé à OpenGL de nous créer une texture sous l'index 1. Le nom de la texture est ainsi retourné dans la variable IDTexture qui est de type Integer. Le premier paramètre spécifie l'index d'une texture et l'autre est un paramètre de type pointeur par lequel cette commande renvoie le nom (ID) de la texture créée. Si vous n'avez plus besoin de la texture alors supprimez-la à l'aide le la commande glDeleteTextures(1, @IDTexture);

Ensuite la commande glBindTexture() rend cette texture active. Donc si le texturage est activé alors toutes les primitives dessinées seront recouvertes par la texture numéro « 1 » ou sinon celle étant spécifiée par glBindTexture.

glBindTexture( GL_TEXTURE_2D, IDTexture );

Le premier paramètre consiste à préciser quel type de texture nous désirons utiliser. Ici nous avons choisi une texture deux dimensions, mais cela aurait pu être GL_TEXTURE_1D ou GL_TEXTURE_3D. Le deuxième paramètre spécifie le nom (ID) de la texture. Si nous faisons appel à un nom de texture déjà existant alors la texture ne sera pas recréée, mais plutôt sélectionnée en mémoire comme étant la texture active. Par conséquent, si vous désirez savoir si un ID possède déjà une texture alors servez-vous de cette fonction glIsTexture( ID );, elle retournera un Vrai ou Faux déterminant si ce ID est déjà occupé par une texture ou pas.

Précisez le mode d'application de la texture pour chaque pixel

Pour spécifier la manière dont la texture sera répétée et combinée aux pixels déjà présents dans le tampon chromatique, nous ferons appel à cette commande :

glTexParameter*( Target :TGLenum;
Pname  :TGLenum;
Param  :PGLfloat );

Le premier paramètre de cette fonction demande une cible, cela peut être une des valeurs suivantes :GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D. Et pour les deux autres paramètres, voici un tableau :

Paramètres de la commande glTexParameter*(…)

Pname

Param

GL_TEXTURE_WRAP_S

GL_CLAMP
GL_CLAMP_TO_EDGE
GL_REPEAT

GL_TEXTURE_WRAP_T

GL_CLAMP
GL_CLAMP_TO_EDGE
GL_REPEAT

GL_TEXTURE_WRAP_R

GL_CLAMP
GL_CLAMP_TO_EDGE
GL_REPEAT

GL_TEXTURE_MAG_FILTER

GL_NEAREST
GL_LINEAR

GL_TEXTURE_MIN_FILTER

GL_NEAREST
GL_LINEAR
GL_NEAREST_MIPMAP_NEAREST
GL_NEAREST_MIPMAP_LINEAR
GL_LINEAR_MIPMAP_NEAREST
GL_LINEAR_MIPMAP_LINEAR

GL_TEXTURE_BORDER_COLOR

Une des quatre valeurs situées dans l'intervalle des coordonnées de la texture donc [0.0 à 1.1]

GL_TEXTURE_PRIORITY

Une des quatre valeurs situées dans l'intervalle des coordonnées de la texture donc [0.0 à 1.1]

GL_TEXTURE_MIN_LOD

Une valeur quelconque donnée sous format virgule flottante.

GL_TEXTURE_MAX_LOD

Une valeur quelconque donnée sous format virgule flottante.

GL_TEXTURE_BASE_LEVEL

Un entier non négatif quelconque.

GL_TEXTURE_MAX_LEVEL

Un entier non négatif quelconque.

Il serait trop long de vous expliquer toutes les valeurs de ce tableau, mais je vous conseille de suivre les tutoriels et j'en exploiterai quelques-unes. Lors de leur utilisation dans les programmes reliés, j'écrirai des commentaires pour les expliquer.

Et pour ajouter la cerise sur le « sunday », il reste cette avant-dernière commande qui spécifie le mode de combinaison que nous désirons effectuer entre le tampon chromatique et les couleurs de la texture.

glTexEnv*( target :TGLenum;
Pname  :TGLenum;
param  :PGLfloat );

Le paramètre Target doit être GL_TEXTURE_ENV. Pour les deux autres paramètres, il y a le tableau suivant :

Pname

Param

GL_TEXTURE_ENV_MODE

GL_DECAL GL_REPLACE GL_MODULATE
GL_BLEND

GL_TEXTURE_ENV_COLOR

Un tableau de quatre valeurs en virgule flottante représentant les composants R, G, B et A. Ces valeurs ne sont utilisées que si la fonction de texture GL_BLEND a été spécifiée.

Bon maintenant voici la dernière commande qui est la PLUS importante :

glTexImage*( Target         :TGLenum;
Level          :TGLint;
InternalFormat :TGLint;
Width          :TGLsizei;
Height         :TGLsizei;
Border         :TGLint;
Format         :TGLenum;
Textel         :Pointer );

Cette commande sert à définir une texture soit à 1, 2 ou 3 dimensions. Voici le résumé des paramètres de celle-ci :

- Target : ce paramètre peut prendre une des trois valeurs suivantes : GL_TEXTURE_1D, GL_TEXTURE_2D OU GL_TEXTURE_3D ;

- Level : spécifie le taux de résolution. Ici il restera à 0 pour nos besoins ;

- InternalFormat : type de format de données dans l'image. Les plus utilisés sont : GL_ALPHA, GL_RGB, GL_RGBA… Pour les autres vous les retrouverez soit dans le fichier d'aide OpenGL ou sur le manuel directement sur le site web que j'ai mentionné un peu plus haut ;

- Width : largeur de l'image servant en guise de texture. N'oubliez pas que les largeur et hauteur doivent être une puissance de 2 ;

- Height : hauteur de l'image servant en guise de texture. N'oubliez pas que la largeur et hauteur doivent être une puissance de 2 ;

- Border : est-ce que la texture nécessite une bordure, si oui alors cela agira aussi sur la largeur et hauteur qui devront alors être sous cette forme : 2x + 2b et 2y + 2b. Où 2b représente les bordures de la texture ;

- Format : type de format de données contenu dans l'image représentant la texture. La valeur de ce paramètre est souvent similaire à celui de InternalFormat ;

- Texel : pointeur sur les données de l'image qui formeront la texture.

Activer l'application…

J'espère ne pas avoir à vous montrer comment si vous êtes rendu à lire ce tutoriel ?

Ce tutoriel ne comporte pas de source, mais il n'est là que pour comprendre la base du fonctionnement des textures. Dans le prochain tutoriel sur les textures, je vous présenterai quelques programmes permettant de mieux comprendre. Et surtout je vous proposerai quelques bibliothèques pour charger la texture en mémoire. J'espère que cela vous éclairera un peu, sinon n'hésitez pas à me contacter et je ferai ce qu'il m'est possible pour vous aider.

IX-C - Utiliser une texture gérée par programmation

Créer une texture par programmation signifie que nous créerons une texture à l'aide d'une variable tableau qui contiendra trois valeurs chromatiques (RGB) + A qui sera une valeur Alpha (RGBA), donc une fois ces quatre composants réunis et assemblés un à un ils formeront une texture avec une dimension donnée. En fait nous travaillerons sur une couleur codée sur 32 bits, parce que chaque émission de couleur possède 4 octets pour sa description (R+G+B+A).

Pour débuter, voici la procédure qui nous offrira de créer quatre textures avec des motifs différents.

Var
Texture   :Array[1..64, 1..64, 1..4] Of TGLubyte;
LaQuel    :TGLubyte;

IDTexture      :TGLint;
...

Texture, LaQuel, IDTexture
La variable tableau Texture consiste en un tableau de 64 pixels de largeur par 64 pixels de hauteur. Chaque couleur des pixels est donnée sous format RGB alors c'est pour ça qu'il est doté de trois valeurs en profondeur. Quand je parle du format RGB, cela signifie toujours Rouge, Vert et Bleu qui sont trois intensités d'émission de lumière qui consistent à former une couleur une fois unies. Il ne faut tout de même pas oublier notre chère valeur Alpha (RGBA) qui sera utilisée un peu plus loin pour la combinaison de couleurs.

Ensuite la variable LaQuel spécifie la texture qui sera créée selon le choix de l'utilisateur. Enfin la variable IDTexture représente un index ou plutôt un nom en mémoire qui représente une texture.

Procedure CreerTexture;
Var
X, Y, I :Integer;
Begin
For X := 1 To 64 Do
For Y := 1 To 64 Do
For I := 1 To 3 Do
Begin
Case LaQuel Of
1 : Texture[X, Y, I] :=
((((X * 32) * (Y * 32)) Mod 10) * 100) Xor 1;

2 : Texture[X, Y, I] :=
((((X Xor 32) * (Y Xor 32)) Mod 200) * 100) Xor 255;

3 : Texture[X, Y, I] :=
(((((X * Y) * (Y * X)) Mod 64 )* 100) Div 100) Xor 128;

4 : Texture[X, Y, I] :=
(((((X * Y) Mod 128) * (Y * X)) * 128 )) Xor 32;
End;

Texture[X, Y, 4] := 255;  {Valeur Alpha}
End;

End;

For X := 1 To 64 Do / End;
Ces trois boucles servent à remplir notre variable tableau qui constituera notre texture. Le X et Y servent au déplacement de largeur et hauteur tandis que le I se déplace de composant en composant soit R, G et B.
Ne vous en faites pas avec le paquet d'opérateurs binaires et la complication de ces lignes, il n'y a rien à comprendre à part le fait qu'elles sont présentes que pour donner des effets de motif aux trois composants chromatiques.

glDeleteTextures(1, @IDTexture);

glGenTextures( 1, @IDTexture );

glBindTexture( GL_TEXTURE_2D, IDTexture );

glEnable( GL_DEPTH_TEST );

glEnable( GL_TEXTURE_2D );

glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
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_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
GL_REPEAT );

glTexImage2d(GL_TEXTURE_2D,    {Type de texture = 2D}
0,               {Niveau de détail}
GL_RGBA,         {Nombre de composants pour la
couleur}
64,              {Largeur de la texture}
64,              {Hauteur de la texture}
0,               {Bordure [0 ou 1]}
GL_RGBA,         {Format de couleur. Nombre de
de composants pour décrire une
couleur}
GL_UNSIGNED_BYTE,{Type de données dans le
tableau de pixels}
@Texture);       {Pointeur vers un tableau de
pixels}
End;

glDeleteTextures(1, @IDTexture);
S'assurer que l'index où sera créée notre texture est bel et bien libre. Le fait d'utiliser cette commande libère l'index donné en paramètre. Si l'index donné n'est pas déjà utilisé par une texture alors cette commande ne provoque pas d'erreur alors utilisez-la…

glGenTextures( 1, @IDTexture );
Générer un nouveau nom pour une nouvelle texture. Le nom généré pour la nouvelle texture sera retourné dans la variable IDTexture.

glBindTexture( GL_TEXTURE_2D, IDTexture );
Créer la texture portant un nom préalablement généré par la commande glGenTextures. Ici le nom de notre nouvelle texture a été stocké dans la variable IDTexture.

glEnable( GL_TEXTURE_2D );
Activer la texturation. N'oubliez pas qu'il est aussi possible de la désactiver à l'aide de la commande glDisable( GL_TEXTURE_2D );

glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
Spécifier à OpenGL le format de stockage des pixels dans notre tableau qui contient la Texture. Ici chaque pixel de notre tableau est stocké un à un alors il faut spécifier à OpenGL qu'il doit les lire un par un.

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
Spécifier le mode d'application de la texture sur chaque pixel de notre primitive. Pour les paramètres possibles, il serait utile que vous alliez jeter un coup d'œil sur le Manuel d'OpenGL ou dans le fichier d'aide que je vous ai donné. Mais bref les deux premières lignes spécifient que la texture doit être calculée en mode linéaire pour les filtres de réduction et d'agrandissement. Linéaire est plus souvent utiliser que GL_NEAREST, car celui-ci ne donne une texture plus que souvent éméchée. Ensuite les deux dernières lignes spécifient que la texture doit être répétée dans l'axe des X et Y si elle n'est pas assez large.

glTexImage2d
Définir la texture 2D. Je ne crois pas avoir d'autres choses à ajouter sur les commentaires inclus dans le code donc si vous avez des questions sur celui-ci n'hésitez pas à me contacter.

Bon maintenant parlons des changements importants à effectuer pour les coordonnées de la primitive à texturer. Premièrement, n'ayez pas peur de lui donner une couleur, car en absence de texture elle ne sera plus ce qu'elle devrait être . Ces couleurs sont aussi nécessaires et intéressantes quant à la possibilité de jumeler les valeurs chromatiques de la primitive avec celles de la texture. En quelque sorte, effectuer une combinaison des couleurs.
Pour spécifier le mode de combinaison des couleurs, il faut utiliser la commande suivante (vue dans le tutoriel précédent) glTexEnvi(…).

Voici un bout de code qui illustre une bonne manière de spécifier les coordonnées de notre primitive :

glBegin(GL_QUADS);
glColor3f(0.9, 0.1, 0.5); glTexCoord2f(0, 0); glVertex3i( 3, 3, 3 );
glColor3f(0.5, 0.3, 0.2); glTexCoord2f(1, 0); glVertex3i( 3,-3, 3 );
glColor3f(0.1, 0.1, 0.1); glTexCoord2f(1, 1); glVertex3i(-3,-3, 3 );
glColor3f(0.0, 0.5, 0.9); glTexCoord2f(0, 1); glVertex3i(-3, 3, 3 );

glColor3f(0.6, 0.1, 0.7); glTexCoord2f(0, 0); glVertex3i( 3, 3,-3 );
...

Remarquez que chaque sommet de la primitive possède sa propre couleur. La commande glTextCoord2f spécifie les coordonnées de la texture qui seront appliquées à la primitive. Si vous avez bien compris la manière dont fonctionnent les coordonnées d'une texture au tutoriel précédant alors je ne crois pas avoir à en dire davantage sur celle-ci.

Et voilà, c'est tout…

IX-D - Quelques petits trucs utiles

Amélirer la perspective
Pour améliorer la perspective ou plutôt la corriger, nous pouvons nous servir de la commande suivante :
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );.
Celle-ci devient utile si vous éprouvez quelques petites déformations d'application de texture.

Interroger OpenGL
Quelques fois il est nécessaire de questionner OpenGL. Eh Oui ! C'est possible de lui passer des requêtes en rapport avec des paramètres, positions, des commandes…
Le tout se fait avec la commande suivante glGet*(…). Pour avoir la liste des possibilités, référez-vous au Manuel OpenGL ou sinon au fichier d'aide que je vous ai livré dans le premier tutoriel.
Mais pour être gentil, je vais vous montrer un exemple de son utilisation en demandant à OpenGL combien de lumière notre matériel est capable de supporter :

Var
NbLumiere :TGLint;
Begin
glGetIntegerv( GL_MAX_LIGHTS, @NbLumiere );
MessageBox( 0, PChar( 'Nombre de lumières supportées : ' +
IntToStr( NbLumiere ) ),
'Lumière maximum', MB_OK );
End;

Mais avant d'utiliser cette commande, assurez-vous que le lien entre OpenGL et votre fenêtre est créé. Bref il existe quatre versions de cette commande dépendamment du type de la variable réponse qui sera renvoyé : glGetBooleanv(…), glGetDoublev(…), glGetFloatv(…), glGetIntegerv(…). Amusez-vous bien…

IX-E - Source

Amusez-vous avec les divers paramètres d'application de textures et aussi je ne l'ai pas mentionné, mais essayer de changer la valeur Alpha de la texture vous constaterez que cela affecte le mode de combinaison des Texels.

Source

IX-F - Qu'est-ce qui se traque derrière le chargement d'une texture ?

Qu'est-ce que signifie utiliser un bitmap en guise de texture ? Bien tout simplement que l'on désire se servir de la couleur des pixels de l'image comme étant une texture propre. Il existe énormément de formats de fichiers pouvant servir de texture, mais parmi tous ceux-ci, voilà ceux qui seront les plus utilisés :

- les Bitmaps ;

- les fichiers TGA ;

- les fichiers JPG ;

- les fichiers RGB .

Celui que je préfère est le Bitmap, car il est tout simplement facile à gérer et offre beaucoup de compatibilité avec plusieurs interfaces. Certains possèdent une palette et d'autres non et cela n'a pas d'importance ici, car nous n'en avons pas besoin du fait que nous travaillerons sur des Bitmaps dont la couleur est codée sur 24 bits (RGB) ou 32 bits (RGBA). Il est plus intéressant de travailler avec des Bitmaps 32 bpp, car ils nous laissent exploiter leur valeur Alpha (Blending). Autre chose intéressante est que leurs traitements est beaucoup plus compatibles que les 24 bpp du fait que la plupart des ordinateurs disposent de processeurs avec des bus pouvant gérer 32bits à la fois, donc une Couleur, un Pixel ou encore ces trois valeurs RGBA à la fois. Il faut noter que ces trois valeurs ont une définition différente, mais ils reviennent tous à signifier la même chose : un pixel doté d'une couleur codé sur un nombre de bits précis.

Si vous avez déjà travaillé avec les Bitmaps alors vous n'aurez pas vraiment de problème à charger une texture en mémoire, pour les autres je vous propose mon tutoriel de trois pages sur les Bitmaps.

IX-G - Différentes bibliothèques pour charger des textures

Voici quelques excellentes bibliothèques permettant de charger des textures en mémoire.

GLAUX
Cette bibliothèque est l'une des plus anciennes, elle vous permet de charger plusieurs formats d'image en mémoire et est très facile d'utilisation. Le désavantage avec GLAUX, c'est que vous devez préalablement redimensionner vos images à une dimension (puissance de 2), car sinon il ne vous avertit pas et vous ne verrez jamais l'image. La voici Glaux.

La mienne
Je ne pourrais dire que c'est une merveille, mais ce qui est pratique c'est que le code vous est complètement dévoilé et elle est BOURRÉE de commentaires. La voici : CLbmp. Et voici en outre ce qu'elle vous permet de faire :

- chargement d'un bitmap ;

- afficher la palette du bitmap ;

- renverser l'image (horizontal ou vertical) ;

- ajustement des couleurs Rouge, Vert et bleu selon un pourcentage ;

- griser une image (GrayScale) ;

- Inverser toutes les couleurs d'un bitmap ;

- récupérer les informations en rapport avec le Bitmap ;

- possibilité de charger ou enregistrer la palette du Bitmap ;

- conversions :

- 1bpp à 24bpp,

- 4bpp à 24bpp,

- 8bpp à 24bpp,

- 16bpp à 24bpp,

- 32pp à 24bpp,

- 1bpp à 32bpp avec valeur Alpha,

- 4bpp à 32bpp avec valeur Alpha,

- 8bpp à 32bpp avec valeur Alpha,

- 16bpp à 32bpp avec valeur Alpha,

- 24pp à 32bpp avec valeur Alpha ;

- Enregistrement sous le format courant.

glBMP
Cette bibliothèque d'images a été complètement écrite en Delphi. Intéressant ici, car on peut charger différents formats d'images en guise de texture :

- Bitmap ;

- Jpg ;

- TGA ;

- Png.

Et beaucoup d'autres fonctions intéressantes comme la sauvegarde et création d'images. Application d'un stencil d'image… Bref vous la trouverez à cette adresse : glBMP.

GraphicEx
La meilleure des bibliothèques disponibles et ça avec le code source. Je ne peux vous énumérer tout ce qu'elle peut charger comme types de fichiers, car elle en dispose de beaucoup… Aller la télécharger à cette adresse : GraphicEx

GLAUX

CLbmp (Avec un rouge élevé à 25 %)

IX-H - Programme relié

Pas vraiment d'animation, mais seulement l'intéressant : Le fonctionnement des textures

Source (Avec GLAUX)
Source (Avec CLbmp)

X - Brume

X-A - Avantages de la brume

La brume, bien qu'elle soit fatigante quand on conduit elle s'avère très utile pour ce qui est d'animation 3D.

Nous verrons pourquoi… Le brouillard nous offre beaucoup d'avantages dont les suivants :

- apporter du réalisme aux scènes et à l’effet 3D (par rapport à leurs distances entre la vue du spectateur et l'objet lui-même. Par exemple regardez des montagnes de très loin et elles vous apparaîtront s'affadirent, mais comparé à un objet vu de près, il est très clair et précis) ;

- dans un simulateur de vol, la brume peut s'avérer utile pour donner l'impression d'une visibilité réduite ;

- sur des GROS programmes de simulation 3D, le fog peut améliorer les performances en éliminant les objets dont leur distance est trop éloignée pour être perçus ;

- donner une meilleure impression d'être sous l'eau ;

- il y a des tonnes d'autres avantages…

Voici le jeu Quake, on voit bien ici le rôle de la brume en arrière-plan. Celle-ci aide à donner un meilleur réalisme à la scène, de plus on réalise bien que le couloir est profond et loin, mais sans brume ce couloir aurait l'air irréel, car il serait d'une perfection totale et ce n'est pas le cas dans la réalité.

Bon maintenant je dois vous aviser qu'activer le FOG sous OpenGL est une vraie farce, une affaire de deux secondes tout au plus . Voilà la manière de procéder :

FogCouleur :Array[0..3] Of TGLfloat = (0.5, 0.5, 0.5, 1.0 );
glFogf( GL_FOG_MODE, GL_LINEAR );
glFogf( GL_FOG_DENSITY, 0.35 );
glFogi( GL_FOG_START, 3.0);
glFogi( GL_FOG_END, 30.0);
glFogfv( GL_FOG_COLOR, @FogCouleur );

glClearColor( 0.5, 0.5, 0.5, 1.0 );
glEnable( GL_DEPTH_TEST );
glEnable( GL_FOG );

FogCouleur :Array[0..3] Of TGLfloat = (0.5, 0.5, 0.5, 1.0 );
Variable tableau contenant la couleur de la brume, donnée sous format (R, G, B, A).
Ordinairement grisâtre…

glFogf( GL_FOG_MODE, GL_LINEAR );
Détermine le facteur de brume, GL_LINEAR, GL_EXP ou GL_EXP2. Celui le plus couramment utilisé est le mode linéaire. Pour plus d'informations, consultez le fichier OpenGL.hlp que je vous ai donné au premier tutoriel.

glFogf( GL_FOG_DENSITY, 0.35 );
Détermine la densité de la brume d'où la plus petite valeur est 0.0 et la valeur la plus élevée est 1.0.

glFogi( GL_FOG_START, 3.0);
Détermine la position où la brume commence à faire son effet.

glFogi( GL_FOG_END, 30.0);
Détermine la position où la brume perd son effet et par conséquent où OpenGL arrête de dessiner les primitives, car elles sont « PERDUES DANS LA BRUME ».

glFogfv( GL_FOG_COLOR, @FogCouleur );
Affecte une couleur à la brume, ici la couleur est contenue dans notre variable tableau FogCouleur. Comme cette commande demande un pointeur (adresse) sur un tableau ayant les paramètres requis pour la couleur alors c'est pour cela que nous plaçons un « @ » devant le nom de la variable tableau.

glClearColor( 0.5, 0.5, 0.5, 1.0 );
Bien, vous allez me dire qu'on a déjà vu cette commande et qu'elle sert à vider le tampon chromatique avec une couleur passée en paramètre ou encore en d'autres mots elle sert à effacer l'écran. Et bien ici, elle a l'utilité de donner un effet normal à la brume, normalement la couleur de vidage est la même que celle de la brume. Comme la brume est plus que souvent grisâtre alors…

glEnable( GL_DEPTH_TEST );
Activation du tampon de profondeur.

glEnable( GL_FOG );
Bien entendu, si l'on veut utiliser la brume nous devons l'activer.

Le tout assisté de quelques lumières pour donner des couleurs, cela donne une très belle scène… Alors ce n'est pas sorcier ? Si vous avez des questions, n'hésitez pas à me contacter.

X-B - Objets quadriques de l'utilitaire GLU

Qu'est-ce que c'est que ça encore ?

Surprise je vous explique comment se servir des objets quadriques de l'utilitaire GLU. Celui-ci nous offre la possibilité de construire des primitives telle que :

- gluCylinder : afficher un cylindre ;

- gluDisk : afficher un disque ou plutôt un cercle plein ;

- gluPartialDisk : même chose que le précédent, mais possibilité de le construire partiellement donc par exemple une pointe de tarte ;

- gluSphere : à la sphère… Indispensable.

Je parle de ces quadriques, car ils sont plus pratiques que ceux générés par l'utilitaire GLUT. On peut les texturer et non ceux de GLUT, car les coordonnées de textures ne sont pas spécifiées. On pourrait tout de même utiliser la texturation automatique pour texturer les primitives de GLUT, mais on verra ça dans un autre tutoriel. J'espère que vous êtes impatient de savoir comment ils fonctionnent ?

Pour créer un quadrique, on procède de la manière suivante :

Var
{Object quadratique (GLU)}
Objet :GLUquadricObj;
...
Begin
{Créer l'object quadratics}
Objet := gluNewQuadric;

{Créer une sphère avec ses coordonnées de texture}
gluQuadricTexture( Objet, GL_TRUE );
gluSphere( Objet, 1.0, 20, 20 );

{Créer une sphère SANS coordonnée de texture}
gluQuadricTexture( Objet, GL_FALSE );
gluSphere( Objet, 1.0, 20, 20 );

...

{Ne pas oublier de supprimer l'objet}
gluDeleteQuadric( Objet );
End;

C'est une petite intro de ce que l'on peut faire de ces objets, mais je vous propose de jeter un coup d'œil dans le fichier d'aide d'OpenGL pour accéder aux autres possibilités de ceux-ci.

X-C - Programme relié

Bon OK… Ce n'est pas Quake, mais au moins les lumières s'éteignent !
Quand vous pressez les touches de 1 à 3 vous pouvez éteindre les lumières dans l'ordre avant vers l'arrière. Ce n'est pas un petit exemple, car j'ai conclu que la brume était trop facile à gérer alors j'ai mis un peu de « challenge » pour la fin. Bon apprentissage…

Source

Pour ceux que cela intéresse, voici l'autre manière. Source (à la C++)

XI - Courbes de Bézier

Nous verrons ce qu'OpenGL met à notre disposition pour dessiner des courbes et des surfaces de base. Il sera aussi question de comprendre en quoi les courbes de Bézier peuvent nous être utiles. Comme je l'ai déjà mentionné, je ne suis pas un professeur de mathématiques alors je n'en connais pas énormément sur celles-ci, mais je crois quand même en connaître assez pour vous montrer la base.

Autre chose importante aussi, je vais vous montrer comment faire pour récupérer le Frame rate de votre scène, donc le nombre de fois où votre scène est redessinée en une seconde ou sous un autre terme : (FPS).

XI-A - Courbe de Bézier (définition)

En quoi les courbes de Bézier peuvent-elles nous être utiles ? Tout d'abord avant de se poser cette question il faut comprendre comment elles fonctionnent et quel en est leur résultat.

- Génération d'un terrain, montagnes.

- Simulation d'éclairs ou d'électricité.

- Diminuer le nombre de sommets utilisés pour réaliser une figure 3D.

- Dessiner des formes géométriques à l'aide de la souris (points de contrôles).

Voici à quoi peut ressembler une courbe de Bézier. Cette courbe-ci est de degré 3 donc 4 points de contrôles. Le degré de la courbe est défini par l'équation suivante n-1, n représentant le nombre de points de contrôles. Ce qui est pratique c'est que l'on positionne nos points de contrôles dans l'espace et ensuite chacun d'eux est relié par une simple ligne, ce qui est appelé l'interpolation. La ligne touche toujours au premier et dernier point de contrôle, mais jamais à ceux entre les deux. Elle ne fait qu'être attirée vers eux. L'inconvénient dans tout cela, c'est que si l'on déplace un point de contrôle alors la ligne risque sévèrement d'être affectée, en raison de l'attirance qu'elle possède envers chacun de ces points. Il existe une autre forme de Courbe nommée B-Spline qui elle permet de déplacer les points de contrôle un à un sans toutefois affecter l'intégrité de la ligne. Mais je ne crois pas en faire un tutoriel.

Points rouges
Ces points sont simplement les points de contrôles de la ligne. Cette courbe en possède quatre. Remarquez que la courbe de Bézier ne touche jamais à ces points de contrôles, à l'exception du premier et du dernier.

Ligne blanche
La ligne blanche représente une droite partant d'un point vers un autre donc du premier vers le deuxième, le deuxième vers le troisième… Celle-ci est seulement visible pour démontrer l'interpolation effectuée par une courbe de Bézier comparativement à une ligne tracée de point en point. On appelle « linéaire » ce type d'interpolation.

Pointillé jaune
Les petits points jaunes sont justement la courbe de Bézier, celle-ci étant attirée vers chacun de ses points de contrôle.

Comme je ne suis pas un génie en mathématiques, je vais me contenter de vous spécifier la mince base des courbes de Bézier, mais ne vous en faites pas je travaille à la réalisation de courbes de Bézier sans l'usage d'OpenGL. Surveillez bien la section Graphique de Delphi.

Avant d'utiliser les courbes de Bézier avec OpenGL, il faut comprendre que la courbe a une vie égale à T = 0..1. Qu'est-ce que tout cela signifie ? Cela veut dire que la courbe débute à son premier point de contrôle, donc T = 0 et termine sont trajet à T = 1 Donc tous les nombres réels inclus à l'intérieur de cet intervalle constituent la précision de la courbe. La courbe située sur la photo en haut, a été réalisée avec une précision de 100, ce qui signifie que sa création a suscité 100 itérations (incluses entre 0 et 1). Si vous ne saisissez pas, ne vous en faites pas vous comprendrez avec l'exemple à la fin du tutoriel.

XI-B - Courbe de Bézier (OpenGL)

Comment réaliser une courbe de Bézier à l'aide d'OpenGL. Premièrement nous avons besoin de cette commande-ci pour définir un évaluateur :glMapX. Le « x » à la fin signifie que nous pouvons traiter des courbes 1D, 2D et 3D. Cette fonction requiert un pointeur vers un tableau de points de contrôle et une valeur indiquant le nombre de points de contrôle situé dans le tableau. Mais avant d'utiliser cette commande, nous devons l'actionner à l'aide de glEnable( GL_MAP1_VERTEX_3 ). Voici un petit bout de code :

Type
TPtrCtrl = Array[0..4, 0..2] Of TGLfloat;

Var
PointsDeCtrl : TPtrCtrl =(
( -10.0,  0.0, 0.0 ),
(  -5.0, -5.0, 0.0 ),
(   0.0,  7.0, 0.0 ),
(   5.0,  5.0, 0.0 ),
(  10.0,  0.0, 0.0 ) );

TPtrCtrl
Voici le tableau de points de contrôle. Il renferme quatre points de contrôle spécifiés sous le format XYZ.

Pour que l'évaluateur évalue la courbe, nous devons en faire la demande à l'aide de la commande suivante : glEvalCoord. Cette commande requiert un degré de précision entre 0..1.

Chaque fois que les coordonnées des points de contrôle changent alors il suffit d'appeler la commande glEvalCoord pour que la courbe soit réévaluée. Alors si les points se déplacent constamment, il serait mieux d'appeler cette commande dans notre procédure d'affichage.

glMap1f( GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 5, @PointsDeCtrl );

glColor4f( 1.0, 1.0, 1.0, 1.0 );
glBegin( GL_LINE_STRIP );
For I := 0 To 20 Do
Begin
glColor4f( 1.0, 1.0, 1.0, 1.0 );
glEvalCoord1f( I / 20.0 );
End;
glEnd();

glPointSize( 5.0 );
glColor4f( 1.0, 1.0, 0.0, 1.0 );
glBegin( GL_POINTS );
For I := 0 To 4 Do
glVertex3f( PointsDeCtrl[I, 0],
PointsDeCtrl[I, 1],
PointsDeCtrl[I, 2] );
glEnd();

glBegin( GL_LINE_STRIP )..glEnd();
Dans cette boucle, nous évaluons la courbe de Bézier avec une précision de 20.

glBegin( GL_POINTS )..glEnd();
Cette boucle-ci sert à tracer les points de contrôles, elle est optionnelle.

XI-C - Compteur de cadre par seconde (FPS)

La création d'un compteur de cadre est assez simple si l'on part du fait que l'on doit calculer combien de fois une scène est affichée en une seconde. Tout d'abord il faut mettre sur pied un compteur, mais comment ? Simple en servant de la fonction SetTimer et de notre fenêtre OpenGL comme cible du Timer. Ensuite pour intercepter le timer, il nous suffit de capter le message envoyé à notre fenêtre WM_TIMER. Le timer doit être démarré avec un intervalle de 1000 millisecondes donc 1 seconde.

XI-D - Programme relié

Source

Remarquer l'attirance de la courbe vers chacun de ses points de contrôles. Voici deux petites choses à essayer :

- augmenter le nombre de points de contrôle ;

- augmenter la précision de la courbe de Bézier.

XII - Fontaine de particules

XII-A - Détail du projet

Le tout est réalisé sous Delphi en utilisant les capacités d'OpenGL. Le but premier est de réaliser une fontaine de particules. Une particule est représentée par une surface carrée munie d'une texture. Pour l'affichage des particules, cette fontaine-ci utilise une technique appelée « BillBoarding ». Qu'est-ce que le BillBoarding ? Très bonne question, et la meilleure définition possible dans ce cas-ci serait de dire que toute particule affichée fait toujours face à la caméra. Donc peu importe la position de la caméra et la direction où elle regarde, la surface de la particule dessinée sera toujours face à celle-ci.

Voici deux liens très utiles sur l'application du BillBoarding :

1. Mr-GameMaker ;

2. Nate Miller.

Ce projet se compose d'un Unit incluant une classe permettant la gestion de cette fontaine. Voici les propriétés de cette classe.

Active
Détermine si la fontaine est active ou pas. Si la fontaine n'est pas active alors les particules sont quand même dessinées, mais elles ne bougent pas. La valeur par défaut est True.

NbParticules
Retourne le nombre de particules présentement hébergées par la fontaine. Cette propriété est en lecture seulement.

MaxParticules
Détermine le nombre maximum de particules pouvant être hébergées par la fontaine.

VelociteMax / VelociteMin
Détermine la vélocité minimale et maximale des particules. La vélocité doit prendre une valeur plus élevée que 0. La valeur par défaut de ces deux propriétés est 14 et 15.

AngleLancement
Détermine l'angle de lancement des particules. Donc 360 / AngleLancement. Exemple, si nous spécifions un angle de 1° alors la fontaine n'aura qu'un jet de lancement. La valeur par défaut est 360°.

Plancher
Détermine où se situe le plancher de la fontaine. Normalement le plancher se situe à ( 0.0, 0.0, 0.0 ).

LargeurParticulesMax/LargeurParticulesMin
Détermine la largeur de la surface représentant une particule. Les valeurs par défaut sont 20 et 40.

FacteurRebondissement
Détermine le pourcentage de perte de vitesse lors d'un rebond. La valeur par défaut est 55 %.

ParticuleMasse
Détermine la masse des particules. La valeur par défaut est 5.0.

FacteurTemps
Détermine le temps de mouvement pour les particules. La valeur par défaut est 0.005.

FacteurVie
Détermine la valeur à soustraire pour diminuer la vie des particules. Souvent, cette valeur suivra la propriété FacteurTemps. La valeur par défaut est 0.005.

Rebondis
Détermine si les particules doivent rebondir ou pas. La valeur par défaut est False.

CouleurDebut/CouleurFin Détermine la couleur de début des particules et la couleur de fin. Les valeurs par défaut sont bleu et bleu.

XII-B - Captures d'écran

XII-C - Programme relié

Pour utiliser cette classe vous aurez besoin des Units suivants : Vecteur et ListSimplePTR
Ces units seront distribués dans le fichier incluant la fontaine.

Bon… Voici le paquet : Source