OpenGL avec Delphi


précédentsommairesuivant

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);

Image non disponible

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

Image non disponible

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

Image non disponible

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

Image non disponible

glutWireDodecahedron(void);
glutSolidDodecahedron(void);

Image non disponible

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

Image non disponible

glutWireOctahedron(void);
glutSolidOctahedron(void);

Image non disponible

glutWireTetrahedron(void);
glutSolidTetrahedron(void);

Image non disponible

glutWireIcosahedron(void);
glutSolidIcosahedron(void);

Image non disponible

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 :

 
Sélectionnez
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 :

 
Sélectionnez
{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.

Image non disponible

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) :

Image non disponible

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.

 
Sélectionnez
{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é.

 
Sélectionnez
  {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 :

Image non disponible Image non disponible

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++).


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2002 Martin Beaudet. Aucune reproduction, même partielle, ne peut être faite de ce site ni 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.