Difference between revisions of "OpenGL Tutorial/fr"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting; removed categories included in template)
 
(33 intermediate revisions by 3 users not shown)
Line 17: Line 17:
 
Afin d'utiliser GLUT, vous devez d'abord l'initialiser. C'est fait en utilisant la fonction <b>glutInit</b>. Cette fonction peut analyser la ligne de commande et fixer les paramètres de la fenêtre principale, mais il s'attend à une entrée dans le style C/C++ . Vous devrez écrire votre propre fonction pour faire la conversion à partir de ParamCount et  ParamStr vers des paramètres en ligne de commande similaire à C/C++.
 
Afin d'utiliser GLUT, vous devez d'abord l'initialiser. C'est fait en utilisant la fonction <b>glutInit</b>. Cette fonction peut analyser la ligne de commande et fixer les paramètres de la fenêtre principale, mais il s'attend à une entrée dans le style C/C++ . Vous devrez écrire votre propre fonction pour faire la conversion à partir de ParamCount et  ParamStr vers des paramètres en ligne de commande similaire à C/C++.
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
  procedure glutInitPascal(ParseCmdLine: Boolean);  
 
  procedure glutInitPascal(ParseCmdLine: Boolean);  
 
  var
 
  var
Line 32: Line 32:
 
   glutInit(@CmdCount, @Cmd);
 
   glutInit(@CmdCount, @Cmd);
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
 
Essentiellement , vous créez un tableau et le remplissez avec les chaînes de caractères de ParamStr. Cette fonction prend également un paramètre qui peut contrôler ce qui est passé à glutInit -- ou bien la ligne de commande en entier ou juste le nom du fichier exécutable .
 
Essentiellement , vous créez un tableau et le remplissez avec les chaînes de caractères de ParamStr. Cette fonction prend également un paramètre qui peut contrôler ce qui est passé à glutInit -- ou bien la ligne de commande en entier ou juste le nom du fichier exécutable .
  
Line 56: Line 56:
  
 
Votre fonction de dessin pourrait ressembler à ceci :
 
Votre fonction de dessin pourrait ressembler à ceci :
<delphi>
+
<syntaxhighlight lang=pascal>
 
  procedure DrawGLScene; cdecl;
 
  procedure DrawGLScene; cdecl;
 
  begin
 
  begin
Line 62: Line 62:
 
   glutSwapBuffers;
 
   glutSwapBuffers;
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
 
Cela va seulement effacer la fenêtre à la couleur du fond et remettre à zéro le zbuffer (ne pas s'inquiéter pour  zbuffer... vous en saurez plus à ce propos plus tard).
 
Cela va seulement effacer la fenêtre à la couleur du fond et remettre à zéro le zbuffer (ne pas s'inquiéter pour  zbuffer... vous en saurez plus à ce propos plus tard).
  
 
Votre fonction de redimension pourrait ressembler à ceci :  
 
Votre fonction de redimension pourrait ressembler à ceci :  
<delphi>
+
<syntaxhighlight lang=pascal>
 
  procedure ReSizeGLScene(Width, Height: Integer); cdecl;
 
  procedure ReSizeGLScene(Width, Height: Integer); cdecl;
 
  begin
 
  begin
Line 80: Line 80:
 
   glLoadIdentity;
 
   glLoadIdentity;
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
 
Avec ce code, vous dites à OpenGL où dans la fenêtre il devrait dessiner et fixer les matrices aux valeurs désirées (les fonctions de matrice seront expliquées plus tard ).
 
Avec ce code, vous dites à OpenGL où dans la fenêtre il devrait dessiner et fixer les matrices aux valeurs désirées (les fonctions de matrice seront expliquées plus tard ).
  
 
L'entrée au clavier est évaluée avec la procedure de rappel suivante:
 
L'entrée au clavier est évaluée avec la procedure de rappel suivante:
<delphi>
+
<syntaxhighlight lang=pascal>
 
  procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 
  procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 
  begin
 
  begin
Line 90: Line 90:
 
     Halt(0);
 
     Halt(0);
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
 
Cette fonction demandera à votre programme de quitter si vous appuyez sur la touche ESC. GLUT est conduit par des évènement et la seule manière de terminer votre programme  est d'appeler  <b>Halt</b> à l'intérieur d'une de vos fonctions de rappel. Si vous fermez la fenêtre d'une autre manière, elle disparaîtra , mais le programme continuera à faire une boucle au travers de la routine principale indéfiniment .
 
Cette fonction demandera à votre programme de quitter si vous appuyez sur la touche ESC. GLUT est conduit par des évènement et la seule manière de terminer votre programme  est d'appeler  <b>Halt</b> à l'intérieur d'une de vos fonctions de rappel. Si vous fermez la fenêtre d'une autre manière, elle disparaîtra , mais le programme continuera à faire une boucle au travers de la routine principale indéfiniment .
  
Line 96: Line 96:
  
 
La partie principale de votre programme pourrait ressembler à ceci :
 
La partie principale de votre programme pourrait ressembler à ceci :
<delphi>
+
<syntaxhighlight lang=pascal>
 
  const  
 
  const  
 
   AppWidth = 640;  
 
   AppWidth = 640;  
Line 126: Line 126:
 
   glutMainLoop;  
 
   glutMainLoop;  
 
  end.
 
  end.
</delphi>
+
</syntaxhighlight>
 
Le tutoriel suivant ajoutera un certain code qui dessinera une forme simple .  
 
Le tutoriel suivant ajoutera un certain code qui dessinera une forme simple .  
  
Line 136: Line 136:
  
 
Expliquons le code que vous avez déjà.
 
Expliquons le code que vous avez déjà.
<delphi>  
+
<syntaxhighlight lang=pascal>  
 
   .
 
   .
 
   .
 
   .
Line 147: Line 147:
 
   glLoadIdentity;
 
   glLoadIdentity;
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
 
En employant la fonction <b>glMatrixMode</b> vous choisissez quelle matrice vous voulez changer . OpenGL travaille avec 3 matrices :
 
En employant la fonction <b>glMatrixMode</b> vous choisissez quelle matrice vous voulez changer . OpenGL travaille avec 3 matrices :
 
<b>GL_MODELVIEW</b>: celle-ci est employée pour déplacer les sommets vers l'espace modèle .
 
<b>GL_MODELVIEW</b>: celle-ci est employée pour déplacer les sommets vers l'espace modèle .
Line 160: Line 160:
  
 
OK ... et maintenant , le code pour dessiner la première forme :
 
OK ... et maintenant , le code pour dessiner la première forme :
<delphi>
+
<syntaxhighlight lang=pascal>
 
  procedure DrawGLScene; cdecl;
 
  procedure DrawGLScene; cdecl;
 
  begin
 
  begin
Line 181: Line 181:
 
   glutSwapBuffers;
 
   glutSwapBuffers;
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
 
Nous avons déjà employé la fonction glClear. Elle remettra juste à zéro les mémoires tampons(buffers). Nous passerons les deux fonctions suivantes et nous nous dirigerons vers celles de dessin.
 
Nous avons déjà employé la fonction glClear. Elle remettra juste à zéro les mémoires tampons(buffers). Nous passerons les deux fonctions suivantes et nous nous dirigerons vers celles de dessin.
  
Line 219: Line 219:
 
Parfois vous devrez dessiner quelques objets de multiples fois sur la scène. OpenGL a la capacité de construire  <b>des listes d'affichage</b> ce qui rend le dessin un peu plus rapide.Créer une liste d'affichage est très facile ... dessinez juste les sommets comme vous l'avez fait dans le précédent tutoriel et enfermez-les avec les appels à <b>glNewList</b> et  <b>glEndList</b>.
 
Parfois vous devrez dessiner quelques objets de multiples fois sur la scène. OpenGL a la capacité de construire  <b>des listes d'affichage</b> ce qui rend le dessin un peu plus rapide.Créer une liste d'affichage est très facile ... dessinez juste les sommets comme vous l'avez fait dans le précédent tutoriel et enfermez-les avec les appels à <b>glNewList</b> et  <b>glEndList</b>.
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
  const
 
  const
 
   LIST_OBJECT = 1;
 
   LIST_OBJECT = 1;
Line 264: Line 264:
 
   glEndList;
 
   glEndList;
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
  
 
<b>glNewList</b> crée une nouvelle liste d'affichage et toutes les fonctions de dessin seront enregistrées jusqu'à ce que <b>glEndList</b> soit appelé. Le premier paramètre pour la fonction glNewList est l'identification de la liste(ID).Chaque liste est définie par son ID. Si une liste avec une identification donnée est déjà créée son contenu est effacé avant l'enregistrement. Si le deuxième paramètre est  GL_COMPILE alors toutes les fonctions de dessin sont juste enregistrées , mais si c'est  GL_COMPILE_AND_EXECUTE alors elles sont enregistrées et exécutées automatiquement.
 
<b>glNewList</b> crée une nouvelle liste d'affichage et toutes les fonctions de dessin seront enregistrées jusqu'à ce que <b>glEndList</b> soit appelé. Le premier paramètre pour la fonction glNewList est l'identification de la liste(ID).Chaque liste est définie par son ID. Si une liste avec une identification donnée est déjà créée son contenu est effacé avant l'enregistrement. Si le deuxième paramètre est  GL_COMPILE alors toutes les fonctions de dessin sont juste enregistrées , mais si c'est  GL_COMPILE_AND_EXECUTE alors elles sont enregistrées et exécutées automatiquement.
Line 273: Line 273:
 
Toutes les listes créées devraient être supprimées. Vous ferez cela avant de quitter le programme :
 
Toutes les listes créées devraient être supprimées. Vous ferez cela avant de quitter le programme :
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
  procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 
  procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 
  begin
 
  begin
Line 282: Line 282:
 
   end;
 
   end;
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
  
 
<b>glDeleteLists</b> prend 2 paramètres, l'identification de la liste d'affichage et le nombre de listes à supprimer. Si l'identification est r, et le nombre de listes à supprimer est  n, les listes supprimées sont : r, r+1, r+2,..., r+n-1
 
<b>glDeleteLists</b> prend 2 paramètres, l'identification de la liste d'affichage et le nombre de listes à supprimer. Si l'identification est r, et le nombre de listes à supprimer est  n, les listes supprimées sont : r, r+1, r+2,..., r+n-1
Line 288: Line 288:
 
Maintenant vous savez créer et supprimer des listes d'affichage ... voyons comment les dessiner :
 
Maintenant vous savez créer et supprimer des listes d'affichage ... voyons comment les dessiner :
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
  procedure DrawGLScene; cdecl;
 
  procedure DrawGLScene; cdecl;
 
  begin
 
  begin
Line 310: Line 310:
 
   glutSwapBuffers;
 
   glutSwapBuffers;
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
  
 
[[Image:DisplayListsPic1.jpg|thumb]] En utilisant <b>glCallList</b> vous pouvez dessiner seulement une liste d'affichage . Dans ce tutoriel, avant de dessiner une liste d'affichage, vous changez la matrice modèle et dessinez l'objet en différents endroits .
 
[[Image:DisplayListsPic1.jpg|thumb]] En utilisant <b>glCallList</b> vous pouvez dessiner seulement une liste d'affichage . Dans ce tutoriel, avant de dessiner une liste d'affichage, vous changez la matrice modèle et dessinez l'objet en différents endroits .
Line 343: Line 343:
 
Entrer dans le mode plein écran est facile avec GLUT. Changeons la partie principale du programme :
 
Entrer dans le mode plein écran est facile avec GLUT. Changeons la partie principale du programme :
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
  const
 
  const
 
   FSMode = '800x600:32@75';
 
   FSMode = '800x600:32@75';
Line 363: Line 363:
 
   glutMainLoop;
 
   glutMainLoop;
 
  end.
 
  end.
</delphi>
+
</syntaxhighlight>
 
Puisque nous ne voulons pas que GLUT analyse la ligne de commande cette fois nous appelons glutInitPascal avec le paramètre false. Comme vous pouvez le voir, il n'y a aucun code pour la création de fenêtre. GLUT a <b>glutEnterGameMode</b> qui crée une fenêtre en mode plein écran. Pour indiquer quel genre de mode plein écran vous voulez, vous appelez la fonction <b>glutGameModeString</b> qui prend un chaine de caractères qui définie le mode que vous aimez .
 
Puisque nous ne voulons pas que GLUT analyse la ligne de commande cette fois nous appelons glutInitPascal avec le paramètre false. Comme vous pouvez le voir, il n'y a aucun code pour la création de fenêtre. GLUT a <b>glutEnterGameMode</b> qui crée une fenêtre en mode plein écran. Pour indiquer quel genre de mode plein écran vous voulez, vous appelez la fonction <b>glutGameModeString</b> qui prend un chaine de caractères qui définie le mode que vous aimez .
 
Le format de cette chaine de caractères est :
 
Le format de cette chaine de caractères est :
Line 401: Line 401:
 
Maintenant , regardons l'arrêt de programme là où vous devez quitter le mode plein écran :
 
Maintenant , regardons l'arrêt de programme là où vous devez quitter le mode plein écran :
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
  procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 
  procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 
  begin
 
  begin
Line 410: Line 410:
 
   end;
 
   end;
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
 
Comme vous pouvez le voir , tout que vous devez faire est d'appeler  <b>glutLeaveGameMode</b>.
 
Comme vous pouvez le voir , tout que vous devez faire est d'appeler  <b>glutLeaveGameMode</b>.
  
 
Maintenant , nous allons présenter quelques nouvelles fonctions de matrice. D'abord, changeons la fonction ReSizeGLScene :
 
Maintenant , nous allons présenter quelques nouvelles fonctions de matrice. D'abord, changeons la fonction ReSizeGLScene :
<delphi>
+
<syntaxhighlight lang=pascal>
 
  procedure ReSizeGLScene(Width, Height: Integer); cdecl;
 
  procedure ReSizeGLScene(Width, Height: Integer); cdecl;
 
  begin
 
  begin
Line 425: Line 425:
 
   gluLookAt(0, 20, 25, 0, 0, 0, 0, 1, 0);
 
   gluLookAt(0, 20, 25, 0, 0, 0, 0, 1, 0);
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
 
<b>gluLookAt</b> créer la matrice qui définira d'où êtes vous pour regarder les objets. Les 3 premiers paramètres sont les coordonnées X, Y et Z de la position de la caméra. les 3 paramètres suivants sont les coordonnées X, Y et Z du point vers où la caméra regarde, et les 3 derniers paramètres définissent un vecteur « vers le haut »  (C'est « vers le haut » pour la caméra). Habituellement , « vers le haut »  est l'axe y positif.
 
<b>gluLookAt</b> créer la matrice qui définira d'où êtes vous pour regarder les objets. Les 3 premiers paramètres sont les coordonnées X, Y et Z de la position de la caméra. les 3 paramètres suivants sont les coordonnées X, Y et Z du point vers où la caméra regarde, et les 3 derniers paramètres définissent un vecteur « vers le haut »  (C'est « vers le haut » pour la caméra). Habituellement , « vers le haut »  est l'axe y positif.
  
 
OK, dessinons maintenant. Puisque vous avez défini la matrice avec gluLookAt laquelle devrait être employé avec tous les objets, vous ne pouvez pas simplement employer glLoadIdentity pour remettre à zéro la matrice pour le prochain objet ... vous sauverez l'état précédent de la matrice et le restaurerez après que l'objet soit dessiné :
 
OK, dessinons maintenant. Puisque vous avez défini la matrice avec gluLookAt laquelle devrait être employé avec tous les objets, vous ne pouvez pas simplement employer glLoadIdentity pour remettre à zéro la matrice pour le prochain objet ... vous sauverez l'état précédent de la matrice et le restaurerez après que l'objet soit dessiné :
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
  procedure DrawGLScene; cdecl;
 
  procedure DrawGLScene; cdecl;
 
  var
 
  var
Line 471: Line 471:
 
   glutSwapBuffers;
 
   glutSwapBuffers;
 
  end;
 
  end;
</delphi>
+
</syntaxhighlight>
 
[[Image:FullScreenAnimationPic1.jpg|thumb]] <b>glPushMatrix</b> et <b>glPopMatrix</b> sont employés pour enregistrer et restaurer l'état de la matrice. Comme vous pouvez le voir, nous enregistrons l'état de la matrice, ensuite changeons la matrice afin de dessiner l'objet à la bonne place, et restaurons ensuite l'ancien état de la matrice .
 
[[Image:FullScreenAnimationPic1.jpg|thumb]] <b>glPushMatrix</b> et <b>glPopMatrix</b> sont employés pour enregistrer et restaurer l'état de la matrice. Comme vous pouvez le voir, nous enregistrons l'état de la matrice, ensuite changeons la matrice afin de dessiner l'objet à la bonne place, et restaurons ensuite l'ancien état de la matrice .
  
Line 487: Line 487:
  
 
Dans l'immédiat, il y aura seulement des fonctions fondamentales pour nous aider à obtenir le temps courant et la variation de temps(temps qui s'est écoulé depuis un appel à un rendu à l'autre) afin de calculer le nombre d'images par secondes.
 
Dans l'immédiat, il y aura seulement des fonctions fondamentales pour nous aider à obtenir le temps courant et la variation de temps(temps qui s'est écoulé depuis un appel à un rendu à l'autre) afin de calculer le nombre d'images par secondes.
<delphi>
+
<syntaxhighlight lang=pascal>
 
  unit utils;
 
  unit utils;
 
   
 
   
Line 541: Line 541:
 
   
 
   
 
  end.
 
  end.
</delphi>
+
</syntaxhighlight>
  
Comme vous pouvez le voir, il n'y a rien de compliqué dans cette unité. Le temps est simplement enregistré entre chaque appel et la différence est retournée. FrameRendered devrait être appelé chaque fois que vous dessinez la scène ainsi la fonction peut calculer les FPS .
+
Comme vous pouvez le voir, il n'y a rien de compliqué dans cette unité. Le temps est simplement enregistré entre chaque appel et la différence est retournée. FrameRendered devrait être appelé chaque fois que vous dessinez la scène ainsi la fonction peut calculer les FPS.
  
Maintenant , amusons nous avec les lumières .
+
Maintenant, amusons nous avec les lumières.
  
OpenGL avoir plusieurs types de lumières ... ambiante , diffuse, ponctuelle, étendue(spot), lumière spéculaire et émissive .
+
OpenGL a plusieurs types de lumières ... ambiante, diffuse, ponctuelle, spot, lumière spéculaire et émissive.
  
La lumière ambiante est quelque chose comme le Soleil. Quand les rayons du soleil traversent la fenêtre d'une salle ils frappent les murs et sont réfléchis et dispersés dans toutes les différentes directions ce qui illumine en moyenne toute la salle. Tous les sommets sont éclairés avec la lumière ambiante .
+
La lumière ambiante est quelque chose comme le Soleil. Quand les rayons du soleil traversent la fenêtre d'une salle, ils frappent les murs et sont réfléchis et dispersés dans toutes les différentes directions ce qui illumine en moyenne toute la salle. Tous les sommets sont éclairés avec la lumière ambiante .
  
La lumière diffuse peut être représentée en tant que rayons de lumière parallèles venant de loin. ils vont seulement éclairer les sommets qui sont orientés vers la source lumineuse .
+
La lumière diffuse peut être représentée par des rayons de lumière parallèles venant de loin. Ils vont seulement éclairer les sommets qui sont orientés vers la source lumineuse.
  
La lumière ponctuelle éclaire tout autour d'elle. C'est comme une boule de feu, elle envoit des rayons lumineux tout autour d'elle et éclaire les sommets qui sont orientés vers la source lumineuse et qui sont assez proche .
+
La lumière ponctuelle éclaire tout autour d'elle. C'est comme une boule de feu, elle envoie des rayons lumineux tout autour d'elle et éclaire les sommets qui sont orientés vers la source lumineuse et qui sont assez proche.
  
Lumière étendue est comme la lumière d'une lampe-torche. C'est simplement une source ponctuelle lumineuse avec un petit rayon de lumière en forme de cône . Tous les sommets qui tombent à l'intérieur du cône et sont assez proche sont éclairés.
+
La lumière spot est comme la lumière d'une lampe-torche. C'est simplement une source ponctuelle lumineuse avec un petit faisceau de lumière en forme de cône. Tous les sommets qui tombent à l'intérieur du cône et sont assez proche sont éclairés.
  
Just like Diffuse light, Specular light is a directional type of light. It comes from one particular direction. The difference between the two is that specular light reflects off the surface in a sharp and uniform way. The rendering of specular light relies on the angle between the viewer and the light source. From the viewer’s standpoint specular light creates a highlighted area on the surface of the viewed object known as specular highlight or specular reflection.
+
Juste comme la lumière diffuse, la lumière spéculaire est un type directionnel de lumière. Elle vient d'une direction particulière. La différence entre les deux est que la lumière spéculaire se reflète sur la surface d'une manière directionnelle et uniforme. Le rendu de la lumière spéculaire repose sur l'observateur et la source lumineuse. Du point de vue de l'observateur, la lumière spéculaire crée un secteur accentué sur la surface de l'objet vu, connu en tant que  "specular highlight" ou réflexion spéculaire.
  
Emissive light is a little different than any other previously explained light components. This light comes out of object you draw but don't lit other objects in nearby.
+
La lumière émissive est peu une différente que tout autre composants lumineux précédemment expliqué. Cette lumière sort de l'objet que vous dessinez but pas n'éclaire pas d'autres objets dans son environnement .
  
For simplicity we'll use only diffuse light in this tutorial. Later on, some other lights may appear in tutorials :)
+
Pour plus de simplicité, nous emploierons seulement la lumière diffuse dans ce tutoriel. Plus tard, d'autres lumières peuvent apparaître dans les tutoriels :)
 
 
Let's see how to enable light in scene:
 
  
 +
Voyons comment autoriser la lumière dans la scène :
 +
<syntaxhighlight lang=pascal>
 
  const
 
  const
 
   DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);
 
   DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);
Line 571: Line 571:
 
   glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
 
   glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
 
   glEnable(GL_LIGHT0);
 
   glEnable(GL_LIGHT0);
 +
</syntaxhighlight>
 +
Comme vous le voyez, nous permettons l'éclairage dans OpenGL ainsi les lumières affectent la scène dont vous faites le rendu. Des paramètres lumineux sont fixés avec la fonction <b>glLightfv</b>. Elle prend 3 paramètres... un pour le numéro de la lumière que vous voulez changer (OpenGL supporte jusqu'à 8 lumières), ensuite dit à OpenGL quel paramètre lumineux changer, et le dernier est un nouveau paramètre pour la lumière .
 +
Vous fixerez juste la couleur diffuse pour la lumière dans ce tutoriel. Après cela, vous pouvez permettre la lumière et il y aura la lumière dans la scène ... mais ... ce n'est pas tout .
  
As you see, we enable lighting in OpenGL so lights affect scene you are rendering. Light parameters are set with <b>glLightfv</b> function. It takes 3 parameters... one for light number you want to change (OpenGL suports up to 8 lights), next tells OpenGL what light parameter to change, and the last one is new parameter for light.
+
Plus à propos de [http://www.opengl.org//documentation/specs/man_pages/hardcopy/GL/html/gl/light.html glLightfv]
You'll set just diffuse color for light in this tutorial. After that, you can enable light and there will be light in the scene... but... that is not all.
 
  
More about glLightfv: http://www.opengl.org//documentation/specs/man_pages/hardcopy/GL/html/gl/light.html
+
Si vous voulez employer des lumières, vous ne pouvez pas simplement fixer la couleur des sommets ... vous devez choisir le matériau pour les sommets . Installons le matériau pour le dessin :
 
 
If you want to use lights you can't just set color for vertex... you must set material for vertices. Let's setup material for drawing:
 
  
 
  glEnable(GL_COLOR_MATERIAL);
 
  glEnable(GL_COLOR_MATERIAL);
 
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
 
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
  
[[Image:LightPic1.jpg|thumb]] You expected something more complicated, do you? :) Well, this code allows us to use glColor function to set material to vertices. By using glEnable function and GL_COLOR_MATERIAL flag, you can define what material properties will glColor change. <b>glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)</b> tells OpenGL that glColor changes ambient and diffuse material. We'll discus materials more in later tutorials.
+
[[Image:LightPic1.jpg|thumb]] Vous vous êtes attendus à quelque chose plus compliquée, n'est ce pas? :) Bien, ce code nous permet d'employer la fonction glColor pour fixer le matériau aux sommets. En employant la fonction glEnable et le drapeau GL_COLOR_MATERIAL, vous pouvez définir quelles propriétés matérielles vont changer glColor. <b>glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)</b> dit à OpenGL que glColor change le matériau ambiant et diffus. Nous discuterons des matériaux davantage dans des tutoriels postérieurs .
  
One more thing that is important when using lights... every vertex must have normal associated with it. Normal is used to find the direction of vertex so light can be calculated properly. You'll use GLUT function to draw cube and it provides normals for us, so this time we'll just walk by normals.
+
Une chose de plus qui est importante en utilisant des lumières... chaque sommet doit avoir une normale qui lui est associée. La normale est employée pour trouver la direction du sommet ainsi la lumière peut être calculée correctement . Vous emploierez un fonction GLUT pour dessiner le cube et elle fournit les normales pour nous, ainsi cette fois nous marcherons juste par les normales (???).
  
After all those setting ups, light will shine up your cube :)
+
Après tout ces réglages, la lumière fera briller votre cube :)
  
Part of the text is copied from [http://www.falloutsoftware.com/tutorials/gl/gl8.htm The OpenGL Light Bible]
+
Une partie du texte est copiée depuis [http://www.falloutsoftware.com/tutorials/gl/gl8.htm La bible de la lumière avec OpenGL]
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 le dépôt Lazarus CCR de SourceForge].
  
=Bitmap fonts=
+
=Polices de caractères d'image =
  
Games and programs usually need to write some text on screen. GLUT provides several functions for drawing chars that are platform independent.
+
Les jeux et les programmes doivent habituellement écrire un certain texte sur l'écran. GLUT fournit plusieurs fonctions  pour dessiner des caractères qui sont indépendantes de la plate-forme .
  
First, we'll show how to use default bitmap fonts. Almost all code additions will be made to utils.pas unit.
+
D'abord, nous montrerons comment employer les polices de caractères d'image par défaut. Presque toutes les additions de code seront faites à l'unité utils.pas .
  
Since text will be drawn in 2D, we'll need to know width and height of viewport... so, we'll write two functions for that:
+
Puisque le texte sera dessiné en 2D, nous devrons savoir la largeur et la hauteur de la clôture(viewport) ... ainsi, nous écrirons deux fonctions pour cela :
  
 +
<syntaxhighlight lang=pascal>
 
  function glGetViewportWidth: Integer;
 
  function glGetViewportWidth: Integer;
 
  var
 
  var
Line 615: Line 616:
 
   Result := Rect[3] - Rect[1];
 
   Result := Rect[3] - Rect[1];
 
  end;
 
  end;
 +
</syntaxhighlight>
  
We just get left/right, top/bottom and calculate width/height by subtracting them.
+
Nous récupérons juste la gauche /la droite, le haut/le bas et nous calculons la largeur/la hauteur en les soustrayant.
 
 
There must be functions for entering and leaving 2D mode:
 
  
 +
Il doit y avoir des fonctions pour entrer et quitter le mode 2D :
 +
<syntaxhighlight lang=pascal>
 
  procedure glEnter2D;
 
  procedure glEnter2D;
 
  begin
 
  begin
Line 643: Line 645:
 
   glEnable(GL_DEPTH_TEST);
 
   glEnable(GL_DEPTH_TEST);
 
  end;
 
  end;
 +
</syntaxhighlight>
  
When entering 2D mode, we save current matrices and set 2D matrix using <b>gluOrtho2D</b> function. This way if we draw some thing on 100, 100 it will be drawn on exactly 100 pixels from left edge of window, and 100 pixels form bottom edge (positive Y is up). Also, we disable ZBuffer. This way text won't alter ZBuffer.
+
Quand on entre dans le mode 2D, nous enregistrons les matrices en cours et paramétrons les matrices 2D en utilisant la fonction <b>gluOrtho2D</b>. De cette façon si nous dessinons quelque chose sur (un viewport de) 100, 100 cette chose sera dessinée sur exactement 100 Pixels à partir du bord gauche de la fenêtre, et 100 Pixels depuis le bord inférieur (le y positif est vers le haut). En outre , nous interdisons ZBuffer. De cette façon, le texte n'altèrera pas ZBuffer.
  
Leaving 2D mode just returns old matrices and enable ZBuffer.
+
Le fait de quitter le mode 2D renvoie juste les anciennes matrices et autorise ZBuffer.
 
 
Now, we can create function for text drawing:
 
  
 +
Maintenant, nous pouvons créer la fonction pour dessiner du texte:
 +
<syntaxhighlight lang=pascal>
 
  procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);
 
  procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);
 
  var
 
  var
Line 658: Line 661:
 
     glutBitmapCharacter(Font, Integer(Text[I]));
 
     glutBitmapCharacter(Font, Integer(Text[I]));
 
  end;
 
  end;
 +
</syntaxhighlight>
  
<b>glutBitmapCharacter</b> can draw only one character of selected font. First parameter is desired font (GLUT_BITMAP_9_BY_15, GLUT_BITMAP_8_BY_13, GLUT_BITMAP_TIMES_ROMAN_10, GLUT_BITMAP_TIMES_ROMAN_24, GLUT_BITMAP_HELVETICA_10, GLUT_BITMAP_HELVETICA_12 or GLUT_BITMAP_HELVETICA_18) and other one is character.
+
<b>glutBitmapCharacter</b> peut dessiner seulement un caractère de la police de caractères choisie. Le premier paramètre est la police de caractères désirée (GLUT_BITMAP_9_BY_15, GLUT_BITMAP_8_BY_13, GLUT_BITMAP_TIMES_ROMAN_10, GLUT_BITMAP_TIMES_ROMAN_24, GLUT_BITMAP_HELVETICA_10, GLUT_BITMAP_HELVETICA_12 ou GLUT_BITMAP_HELVETICA_18) et l'autre paramètre est le caractère.
  
Character will be drawn at current raster position. To set desired raster position we call <b>glRasterPos</b> function. glRasterPos can handle different number and types of parameters just like glVertex function. Coordinate specified is transformed by model and projection matrix to get 2D coordinate where new raster position will be. Since we entered 2D mode, X and Y coordinates are actual 2D coordinates where drawing will occur.
+
Le caractère sera dessiné à la position de trame en cours. Pour placer la position désirée de trame nous appelons la fonction <b>glRasterPos</b>. glRasterPos peut traiter des nombres et des types de paramètres différents juste comme la fonction glVertex. La coordonnée indiquée est transformée par les matrices modèle et de projection pour obtenir la coordonnée 2D là où sera la nouvelle position de trame. Depuis que nous sommes entrés dans le mode 2D , Les coordonnées X et Y sont les coordonnées 2D réelles où le dessin se produira .
 
 
This new functions will make text drawing very easy:
 
  
 +
Ces nouvelles fonctions vont rendre le dessin du texte très facile:
 +
<syntaxhighlight lang=pascal>
 
  procedure DrawGLScene; cdecl;
 
  procedure DrawGLScene; cdecl;
 
  begin
 
  begin
Line 702: Line 706:
 
   FrameRendered;
 
   FrameRendered;
 
  end;
 
  end;
 +
</syntaxhighlight>
 
[[Image:BitmapFontsPic1.jpg|thumb]]
 
[[Image:BitmapFontsPic1.jpg|thumb]]
We draw red cube and rotate it, and some text to show how various bitmap fonts look like.
+
Nous dessinons un cube rouge et le faisons tourner, et un peu de texte pour montrer à quel point les diverses polices de caractères d'image ressemblent.
<b>glutBitmapLength</b> function is used to find width of string so it could be aligned to right. Code can easily be altered to center text.
+
La fonction <b>glutBitmapLength</b> est employée pour trouver la largeur des chaînes de caractères ainsi elles pourraient être alignées vers la droite. Le code peut facilement être changé pour centrer le texte .
  
Note: See how cube looks without light.
+
Note: Voir comment les cubes apparaissent sans lumière.
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 le dépôt Lazarus CCR de SourceForge].
  
 
=Textures=
 
=Textures=
  
It's time to use textures :)
+
Il est temps d'employer des textures :)
 
 
This tutorial will show how to draw textured polygons and how to blend textures using multipass technic.
 
Since OpenGL has no builtin mechanism for loading textures, we'll use external library: [http://imaginglib.sourceforge.net/ Vampyre Imaging Library].
 
We'll use just OpenGL helper functions, but you may find this lib handy for some other things to.
 
  
Let's get started... we'll create display list for drawing textured rectangle:
+
Ce tutoriel montrera comment dessiner les polygones texturisés et comment mélanger des textures en utilisant une technique à plusieurs passages.
 +
Puisque OpenGL n'a aucun mécanisme interne pour télécharger des textures, nous emploierons une bibliothèque externe : [http://imaginglib.sourceforge.net/ la bibliothèque Imaging de Vampyre].
 +
Nous emploierons juste des fonctions d'aide d'OpenGL, mais vous pouvez trouver cette bibliothèque maniable pour aussi d'autres choses.
  
 +
Soyons prêt à démarrer... nous créerons une liste d'affichage pour dessiner un rectangle texturisé :
 +
<syntaxhighlight lang=pascal>
 
  procedure CreateList;
 
  procedure CreateList;
 
  begin
 
  begin
Line 735: Line 740:
 
   glEndList;
 
   glEndList;
 
  end;
 
  end;
 +
</syntaxhighlight>
 +
Notez les fonctions <b>glTexCoord</b>. Elles sont utilisées pour indiquer quelle partie de la texture est assignée au sommet. Les coordonnées définies dans ces fonctions vont de 0 à 1 (Les valeurs supérieures à 1 sont permises mais peuvent produire des résultats différents). 0 est le premier Pixel  et 1 est le dernier Pixel . Ainsi, 0.5 sera exactement au milieu de la texture.
  
Notice <b>glTexCoord</b> functions. They are used to specify which part of texture is assigned to vertex. Coordinates defined in this functions are from 0 to 1 (values greater than 1 are allowed but can generate different results). 0 is first pixel and 1 is last pixel. So, 0.5 will be right in the middle of texture.
+
Le téléchargement de texture est extrêmement aisé avec la bibliothèque Imaging de Vampyre:
 
+
<syntaxhighlight lang=pascal>
Texture loading is extremely easy with Vampyre Imaging Library:
 
 
 
 
  var
 
  var
 
   Tex1, Tex2: GLuint;
 
   Tex1, Tex2: GLuint;
Line 750: Line 755:
 
   glEnable(GL_TEXTURE_2D);
 
   glEnable(GL_TEXTURE_2D);
 
  end;
 
  end;
 +
</syntaxhighlight>
  
<b>LoadGLTextureFromFile</b> loads texture from file and returns it's ID. When texture is loaded it is allready setup for rendering.
+
<b>LoadGLTextureFromFile</b> charge une texture depuis un fichier et renvoie son ID. Quand la texture est chargée elle est déjà paramétrée pour le rendu.
Last line just enables 2D textures.
+
La dernière ligne autorise juste les textures 2D.
  
To draw textured polygon you have to bind texture and setup texture coordinations (texture coordinations are set in display list in this tutorial):
+
Pour dessiner un polygone texturisé vous devez lier la texture et paramétrer les coordonnées de texture(les coordonnées de texture sont fixées dans la liste d'affichage dans ce tutoriel):
  
 +
<syntaxhighlight lang=pascal>
 
   ...
 
   ...
 
   glLoadIdentity;
 
   glLoadIdentity;
Line 762: Line 769:
 
   glCallList(LIST_OBJECT);
 
   glCallList(LIST_OBJECT);
 
   ...
 
   ...
 +
</syntaxhighlight>
 +
La fonction <b>glBindTexture</b> est employée pour choisir la texture. Quand vous dessinez des polygones ils auront la texture sélectionnée sur eux. C'est aussi simple :)
  
<b>glBindTexture</b> function is used to select texture. When you draw polygins they will have selected texture on them. It's that easy :)
+
Ainsi , employer une texture est facile ... mais comment mélanger deux textures . Fondamentalement vous dessinez le polygone une fois avec une texture, paramétrez les paramètres du mélange, et dessinez le polygone une fois de plus avec une autre texture. Vous pouvez mélanger des centaines de texture de cette façon. Voyons à quoi le code pour faire ceci ressemble :
 
+
<syntaxhighlight lang=pascal>
So, using one texture is easy... but how to blend two textures. Basicly you draw polygon once with one texture, setup blending parameters, and draw polygon once more time with other texture. You can blend houndreds of textures this way. Let's see how code for this looks:
 
 
 
 
   ...
 
   ...
 
   glLoadIdentity;
 
   glLoadIdentity;
Line 781: Line 788:
 
   glDisable(GL_BLEND);
 
   glDisable(GL_BLEND);
 
  ...
 
  ...
 
+
</syntaxhighlight>
As you can see, polygon is drawn first time like we allready know. Before second drawing we enable blending by calling <b>glEnable(GL_BLEND)</b>. Blending means that finall pixel color is calculated like this:
+
Comme vous pouvez le voir , le polygone est dessiné la première fois comme nous le savons déjà. Avant le deuxième dessin  nous permettons le mélange de textures en appelant la procédure <b>glEnable(GL_BLEND)</b>. Mélanger signifie que la couleur finale du Pixel est calculée comme ceci :
  
 
  DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND
 
  DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND
  
SRCBLEND and DESTBLEND are defined using <b>glBlendFunc</b> function. In this tutorial we set SRCBLEND to GL_ZERO (zero) and DESTBLENT to GL_SRC_COLOR (DrawingColor) and finall color is then:
+
SRCBLEND et  DESTBLEND sont définis en utilisant la fonction <b>glBlendFunc</b>. Dans ce tutoriel nous paramétrons SRCBLEND à GL_ZERO (zero) et DESTBLENT à GL_SRC_COLOR (DrawingColor) et la couleur finale est alors:
  
 
  DrawingColor * 0 + BackgroundColor * DrawingColor
 
  DrawingColor * 0 + BackgroundColor * DrawingColor
 
[[Image:TexturesPic1.jpg|thumb]]
 
[[Image:TexturesPic1.jpg|thumb]]
  
This means that background will get darker when you draw with dark colors... when you draw with white color, background color will not be changed. The result will look like this
+
Ceci signifie que l'arrière plan deviendra plus foncé quand vous dessinez avec des couleurs foncées ... quand vous dessinez avec de la couleur blanche , la couleur de l'arrière plan ne sera pas changée. Le résultat ressemblera à ceci
  
Next time, we'll use extensions to show how to use singlepass multitexturing.
+
La fois prochaine , nous utiliserons des extensions pour montrer comment utiliser le multitexturing en une passe.
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 le dépôt Lazarus CCR de SourceForge].
  
 
=Multitexturing (extensions)=
 
=Multitexturing (extensions)=
  
When youknow multipass multi texturing, singlepass is very easy. Texturing is separated in stages. First stage setup and draw first texture, second stage draws another one and so on. All you have to do is to setup texture stages and to render object.
+
Quand vous connaissez le multitexturing en plusieurs passes, celui en une passe est très simple. Appliquer une texture est séparé en étapes. La première étape permet de paramétrer et de dessiner la première texture, la seconde étape en dessine une autre et ainsi de suite. Tous que vous devez faire est de paramétrer les étapes avec les textures et de faire le rendu de l'objet.
 
 
Let's see how code looks like:
 
  
 +
Voyons à quoi le code ressemble :
 +
<syntaxhighlight lang=pascal>
 
  procedure InitializeGL;
 
  procedure InitializeGL;
 
  begin
 
  begin
Line 816: Line 823:
 
   glBindTexture(GL_TEXTURE_2D, Tex2);
 
   glBindTexture(GL_TEXTURE_2D, Tex2);
 
  end;
 
  end;
 +
</syntaxhighlight>
 +
D'abord nous avons besoin de l'extension OpenGL qui nous permettrons d'utiliser les fonctions de multitexture. <b>Load_GL_ARB_multitexture</b> essayera de charger ces extensions et renverra TRUE si l'opération est réussie.
  
First we need load OpenGL extension that will allow us to use multitexture functions. <b>Load_GL_ARB_multitexture</b> will try to load those extensions and will return TRUE if operation was successful.
+
Pour choisir l'étape de texture sur laquelle vous voulez travailler, utilisez la fonction <b>glActiveTextureARB</b>. Elle demande seulement un paramètre qui défini l'étape que vous voulez. Après ça toutes les fonctions de texture (enabling, disabling, binding, creating...) affecteront cette étape .
 
 
To select texture stage you want to work on, use <b>glActiveTextureARB</b> function. It takes only one parameter that define which stage you want. After that all texture functions (enabling, disabling, binding, creating...) will affect that stage.
 
 
 
Since we setup every thing in initialization function, all we have to do is to draw object:
 
  
 +
Puisque nous avons paramétrés tout dans la fonction d'initialisation, tout que nous avons à faire est de dessiner l'objet :
 +
<syntaxhighlight lang=pascal>
 
  procedure DrawGLScene; cdecl;
 
  procedure DrawGLScene; cdecl;
 
  begin
 
  begin
Line 847: Line 854:
 
   glutSwapBuffers;
 
   glutSwapBuffers;
 
  end;
 
  end;
 +
</syntaxhighlight>
 
[[Image:MultitexturePic1.jpg|thumb]]
 
[[Image:MultitexturePic1.jpg|thumb]]
As you can see, difference is only in defining texture coordinations. We now use <b>glMultiTexCoord2fARB</b> function that takes texture stage and texture coordinations. Every thing else is unchanged.
+
Comme vous pouvez le voir, la différence est seulement dans la définitions des coordonnées de texture. Nous utilisons maintenant la fonction <b>glMultiTexCoord2fARB</b> qui prend l'étape de texture et les coordonnées de texture en paramètre. Tout le reste est inchangé.
  
Today almost all graphic cards supports at least 2 texture stages. Using singlepass multitexturing is faster than multipass version since you draw objects only once. If hardware supports singlepass multitexturing (Load_GL_ARB_multitexture returns TRUE) use it.
+
Aujourd'hui presque toutes les cartes graphiques supportent au moins 2 étapes de texture . L'utilisation du multitexturing en une passe est plus rapide que la version multipasse puisque vous dessinez les objets une seule fois. Si le matériel supporte le multitexturing en une passe (Load_GL_ARB_multitexture renvoie TRUE) utilisez le.
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 le dépôt Lazarus CCR de SourceForge].
 
 
=Render to texture=
 
  
This one will be short. OpenGL can capture current scene to texture so you can use it for texturing other objects (TV screen, mirror or some thing else). Well just render scene to texture and apply it to rotating plane.
+
=Faire du rendu pour obtenir une texture=
  
First, we must create empty texture which well use to capture scene:
+
Ce paragraphe sera court. OpenGL peut capturer la scène courante pour en faire une texture ainsi vous pouvez l'employer pour appliquer une texture sur d'autres objets (écran TV , miroir ou quelque chose d'autre). Bien faisons immédiatement un rendu de scène vers une texture et appliquons la à un plan rotatif.
  
 +
D'abord, nous devons créer une texture vide qui est bien utilisée pour capturer la scène:
 +
<syntaxhighlight lang=pascal>
 
  procedure SetupRenderTexture;
 
  procedure SetupRenderTexture;
 
  var
 
  var
Line 872: Line 880:
 
   FreeMem(Data);
 
   FreeMem(Data);
 
  end;
 
  end;
 +
</syntaxhighlight>
 +
La mémoire tampon de l'image RGB  256*256 pixels est crée et elle est utilisée pour paramétrer la texture 2D.
  
Buffer for 256*256 RGB image is created and it is used to setup 2D texture.
+
La partie principale est dans la fonction de dessin:
 
+
<syntaxhighlight lang=pascal>
Main part is in drawing function:
 
 
 
 
  procedure DrawGLScene; cdecl;
 
  procedure DrawGLScene; cdecl;
 
  var
 
  var
Line 924: Line 932:
 
   glutSwapBuffers;
 
   glutSwapBuffers;
 
  end;
 
  end;
 +
</syntaxhighlight>
 
[[Image:RenderToTexturePic1.jpg|thumb]]
 
[[Image:RenderToTexturePic1.jpg|thumb]]
First, everything is setup for scene that will be captured. Viewport is reduced to 256*256 so it will fit into texture and scene is drawn. <b>glCopyTexImage2D</b> is used to capture scene to currently selected texture.
+
D'abord, tout est paramétré pour la scène qui sera capturée. La fenêtre de clôture(Viewport)est réduite à  256*256 de telle façon qu'elle s'adapte à la texture et la scène est dessinée . <b>glCopyTexImage2D</b> est employé pour capturer la scène à la texture actuellement choisie .
  
When we have scene captured to texture, everything can be cleared again, viewport can be returned to original size and final scene is drawn using previous scene as texture.
+
Quand nous avons capturé la scène vers la texture, tout peut être effacé à nouveau, La fenêtre de clôture(viewport) peut se remettre à sa taille d'origine et la scène finale est dessinée en utilisant la scène précédente comme texture.
  
P.S. Captured texture can be saved using <b>SaveGLTextureToFile</b> function from [http://imaginglib.sourceforge.net/ Vampyre Imaging Library].
+
P.S. une texture capturée peut être enregistrée en utilisant la fonction <b>SaveGLTextureToFile</b> de la bibliothèque [http://imaginglib.sourceforge.net/ Imaging de Vampyre].
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 le dépôt Lazarus CCR de SourceForge].
  
=Vertex array=
+
=Tableau de Sommets=
  
OpenGL is capable of rendering primitives using data that is stored in buffers insted of calling glVertex. Buffers can be used to define vertex and texture coordinates, and colors (index and RGBA), normals and edge flags.
+
OpenGL est capable de faire le rendu de primitives en utilisant les données qui sont stockées dans les mémoires tampons  au lieu d'appeler glVertex. Les mémoires tampons peuvent être appelées pour définir les coordonnées des sommets et celles des textures, et les couleurs (index et RGBA), les drapeaux normaux et de bord.
  
In this tutorial well use only vertex and color buffers, and we'll show non-indexed and indexed drawing. Non-indexed mode draws buffers as streams. Indexed mode will draw buffer elements in order that is defined in index buffer. But enough talking... let's start coding.
+
Dans ce tutoriel nous utiliserons seulement les mémoires tampon de sommet et de couleur, et nous montrerons du dessin non-indexé et indexé. le mode non-indexé dessine des mémoires tampon en tant que flux. Le mode indexé dessinera des éléments de la mémoire tampon dans l'ordre dans lequel cela a été défini dans l'index de la mémoire tampon. Mais assez parlé... Commençons à coder.
 
 
First, let's define some types and constants:
 
  
 +
D'abord, définissons quelques types et constantes :
 +
<syntaxhighlight lang=pascal>
 
  type
 
  type
 
   TVertex3f = record
 
   TVertex3f = record
Line 966: Line 975:
 
     (R : 1; G : 1; B : 0)
 
     (R : 1; G : 1; B : 0)
 
   );
 
   );
 +
</syntaxhighlight>
 +
Nous avons deux mémoires tampon. Une pour les coordonnées des sommets et une pour les couleurs des sommets. Ces 6 sommets définissent 2 triangles qui forment un rectangle.
  
We have two buffers. One for vertex coordinates and one for vertex colors. This 6 vertices defines 2 triangles that forms rectangle.
+
Dessiner des primitives en utilisant les mémoires tampons est facile:
 
 
Drawing primitives using buffers is easy:
 
  
 +
<syntaxhighlight lang=pascal>
 
   glEnableClientState(GL_VERTEX_ARRAY);
 
   glEnableClientState(GL_VERTEX_ARRAY);
 
   glEnableClientState(GL_COLOR_ARRAY);
 
   glEnableClientState(GL_COLOR_ARRAY);
Line 980: Line 990:
 
   glDisableClientState(GL_VERTEX_ARRAY);
 
   glDisableClientState(GL_VERTEX_ARRAY);
 
   glDisableClientState(GL_COLOR_ARRAY);
 
   glDisableClientState(GL_COLOR_ARRAY);
 +
</syntaxhighlight>
  
First we enable buffers we want to use using <b>glEnableClientState</b> function. Than we can select buffers we want to use. Every buffer type has own function for selecting (<b>glColorPointer</b>, <b>glEdgeFlagPointer</b>, <b>glIndexPointer</b>, <b>glNormalPointer</b>, <b>glTexCoordPointer</b>, <b>glVertexPointer</b>).
+
D'abord nous autorisons les mémoires tampon que nous voulons employer en utilisant la fonction <b>glEnableClientState</b>. Ensuite nous pouvons sélectionner les mémoires tampons que nous voulons utiliser. Chaque type de mémoire tampon a une fonction propre pour cette sélection (<b>glColorPointer</b>, <b>glEdgeFlagPointer</b>, <b>glIndexPointer</b>, <b>glNormalPointer</b>, <b>glTexCoordPointer</b>, <b>glVertexPointer</b>).
First parameter in those functions defines how many numbers every element contains. For example, let's take vertex buffer. If this parameter is 2 than OpenGL expects that every element in buffer contains x and y coordinate. If this parameter is, for example, 4, than every element should contains x, y, z and w coordinate.
+
Le premier paramètre de ces fonctions défini combien de nombres chaque élément contient. Par exemple , prenons la mémoire tampon des sommets. Si ce paramètre est 2 alors OpenGL compte que chaque élément de la mémoire tampon contienne les coordonnées x et y. Si ce paramètre est , par exemple , 4, alors chaque élément devrait contenir les coordonnées x, y, z et w.
Next parameter defines what type of data element contains (GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT or GL_DOUBLE).
+
Le paramètre suivant défini quel type d'élément de donnée la mémoire tampon contient (GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT ou GL_DOUBLE).
Next one defines how many bytes are between each element. This way you can have buffer that contains vertex coordinates and some custom data. For arbitrary data type, this parameter can be calculated like this:
+
Le suivant défini combien il y a d'octets entre chaque élément. De cette façon, vous pouvez avoir une mémoire tampon qui contient des coordonnées de sommets et quelques données faites sur commande. Pour des types de données arbitraire, ce paramètre peut être calculé ainsi:
 
+
<syntaxhighlight lang=pascal>
 
  type
 
  type
 
   TBufferData = record
 
   TBufferData = record
Line 994: Line 1,005:
 
   
 
   
 
  Bytes between elements = SizeOf(TDataBefore) + SizeOf(TDataAfter)
 
  Bytes between elements = SizeOf(TDataBefore) + SizeOf(TDataAfter)
 +
</syntaxhighlight>
 +
Le dernier paramètre est un pointeur vers le début de la mémoire tampon.
  
Last parameter if pointer to the begginig of buffer.
+
Quand les mémoires tampons sont sélectionnées, nous pouvons les dessiner employant les fonctions <b>glDrawArrays</b>. Toutes les mémoires tampons autorisées sont utilisées pour dessiner les primitives. Le genre de polygones qui sont générés  est défini dans le premier paramètre (pareil que avec la fonction glBegin). Les deux paramètres suivant définissent le sous-ensemble de mémoire tampon qui est utilisé pour dessiner (début et compte).
 
 
When buffers are selected we can draw them using <b>glDrawArrays</b> functions. All enabled buffers are used to draw primitives. What kind of polygons are being generated is defined in first parameter (same as in glBegin function). Next two defines subset of buffer which is used for drawing (start and count).
 
 
 
When buffers are not needed you can disable them.
 
  
To demonstrate indexed mode, I made some simple mesh class that can load vertex, color and index data from external files:
+
Quand les mémoires tampons ne sont pas nécessaires vous pouvez les neutraliser .
  
 +
Pour démontrer le mode indexé, j'ai réalisé un classe de maillage simple qui peut charger des sommet, des couleurs et indexer les données depuis des fichiers externes:
 +
<syntaxhighlight lang=pascal>
 
  type
 
  type
 
   TMesh = class
 
   TMesh = class
Line 1,016: Line 1,027:
 
     procedure DrawMesh;
 
     procedure DrawMesh;
 
   end;
 
   end;
 +
</syntaxhighlight>
 +
FVertices contiendra les données sur les sommets , FColors les données sur les couleurs et FIndices les données sur les  index  quand le fichier externe est chargé.
  
FVertices will contain data about vertices, FColors data about color and FIndices data about indices when external file is loaded.
+
D'abord, nous écrirons du code qui traite la création et la destruction de classe:
 
+
<syntaxhighlight lang=pascal>
First we'll write some code that deals with creation and destruction of class:
 
 
 
 
  procedure TMesh.FreeBuffers;
 
  procedure TMesh.FreeBuffers;
 
  begin
 
  begin
Line 1,038: Line 1,049:
 
   inherited Destroy;
 
   inherited Destroy;
 
  end;
 
  end;
 +
</syntaxhighlight>
  
File that will contain mesh data is simple text file. First row will contain number of vertices and indices separated by space character. After that row will come rows for every vertex and color. X, Y, Z, R, G and B all separated by space character. In the end, there will be rows for indices... every index number is written in its own row... so, for one triangle, data file will look like this:
+
Le fichier qui contiendra les données de maillage est un simple fichier texte. La première ligne contiendra le nombre de sommets et les index séparés par un espace. Après cette ligne viendra les lignes pour chaque sommet et couleur. X, Y, Z, R, G et B toutes séparées par un espace. A la fin, il y aura les lignes des index... chaque nombre représentant un index est écrit dans sa propre ligne... ainsi , pour un triangle, le fichier de données ressemblera à ceci:
  
 
  3 3
 
  3 3
Line 1,049: Line 1,061:
 
  2
 
  2
  
This means that there is 3 vertices and 3 indices defined in file. First vrtex is at -1, -1, 0 and has color 1, 1, 1 and so on. Indices defines that order in which vertices are drawn (in this case vertices are drawn in the same order as they are defined).
+
Cela signifie qu'il y a 3 sommets et 3 index défini dans le fichier. Le premier sommer est à la position -1, -1, 0 et a comme couleur RGB 1, 1, 1 et ainsi de suite . Les index définissent cet ordre dans lequel les sommets sont dessinés (dans ce cas-ci les sommets sont dessinés dans le même ordre qu'ils sont défini ).
 
 
Code for loading this data will loke like this:
 
  
 +
Le code pour charger ces données ressemblera à celui-ci:
 +
<syntaxhighlight lang=pascal>
 
  procedure TMesh.LoadMesh(FileName: String);
 
  procedure TMesh.LoadMesh(FileName: String);
 
  var
 
  var
Line 1,080: Line 1,092:
 
   CloseFile(MeshFile);
 
   CloseFile(MeshFile);
 
  end;
 
  end;
 
+
</syntaxhighlight>
After loading data, we have everything for drawing:
+
Après le chargement des données, nous avons tout pour faire le dessin :
 
+
<syntaxhighlight lang=pascal>
 
  procedure TMesh.DrawMesh;
 
  procedure TMesh.DrawMesh;
 
  begin
 
  begin
Line 1,095: Line 1,107:
 
   glDisableClientState(GL_COLOR_ARRAY);
 
   glDisableClientState(GL_COLOR_ARRAY);
 
  end;
 
  end;
 +
</syntaxhighlight>
 +
[[Image:VertexArrayPic1.jpg|thumb]]Comme vous pouvez le voir, presque tout est identique que faire un dessin non indexé, excepté la fonction qui dessine actuellement les polygones. Dans ce cas nous employons la fonction <b>glDrawElements</b>. Pour celle-ci, nous indiquons quel genre de polygones nous voulons, combien d'index sont dans la mémoire tampon, le type de données dans l'index de la mémoire tampon et un pointeur vers le début de l'index de la mémoire tampon.
  
As you can see, allmost everything is the same as for non-indexed drawing, except function that actually draw polygons. In this case we use <b>glDrawElements</b> function. For this one we specify what kind of polygons we want, how many indices are in index buffer, type of data in index buffer and pointer to the beginning of index buffer.
+
Le code source complet vient avec le fichier de données de maillage que cette classe peut employer pour générer un rectangle qui est identique à celui qui est dessiné en utilisant le mode non-indexé. Le fichier de données de maillage ressemble à ceci:
 
 
[[Image:VertexArrayPic1.jpg|thumb]]Full source code comes with mesh data file that this class can use to generates rectangle that is identical with one that is drawn using non-indexed mode. Mesh data file looks like this:
 
  
 
  4 6
 
  4 6
Line 1,112: Line 1,124:
 
  3
 
  3
  
As you can see, there is data for only 4 vertices and 6 indices. So, first triangle is defined by vertices 0, 1 and 2, and the seccond one by vertices 0, 2 and 3. By using indexed mode we don't have to duplicate vertices.
+
Comme vous pouvez le voir, il y a des données pour seulement 4 sommets et 6 index. Ainsi, le premier triangle est défini par les sommets 0, 1 et 2, et le second par les sommets  0, 2 et 3. En utilisant le mode indexé, nous n'avons pas à dupliquer les sommets.
  
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].
+
Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 le dépôt Lazarus CCR de SourceForge].
 
 
 
 
[[category:Example programs]]
 

Latest revision as of 06:18, 23 February 2020

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) 한국어 (ko) русский (ru) 中文(中国大陆)‎ (zh_CN)

OpenGL est l'environnement idéal pour développer des applications graphiques portables , interactives en 2D et 3D. Depuis son introduction en 1992 , OpenGL est devenu l'interface de programmation d'applications (API) soutenant les graphiques 2D et 3D la plus largement utilisée de l'industrie, amenant plusieurs milliers d'applications à une grande variété de plateformes d'ordinateur. OpenGL stimule l'innovation et accélère le développement d'applications en incorporant un large ensemble de rendus, mappage de texture, effets spéciaux , et d'autres fonctions puissantes de visualisation . Les développeurs peuvent accroître la puissance de OpenGL à travers toutes les plate-formes de bureau communes et les plate-formes de poste de travail, assurant un large déploiement d'applications.

Vous pouvez trouver plus d'information à propos d'OpenGL ici.

GLUT (prononcé comme the glut in gluttony) est une trousse à outils pour OpenGL, une trousse à outil indépendante du système window pour écrire des programmes OpenGL. Il implémente une simple interface de programmation d'application avec fenêtrage (API) pour OpenGL. GLUT rend considérablement plus facile l'apprentissage et l'exploration de la programmation OpenGL. GLUT fournit une API portable ainsi vous pouvez écrire un programme simple avec OpenGL qui travaille sur les plate-formes avec système d'exploitation à base de PC et de station de travail.

Vous pouvez trouver plus d'information à propos de GLUT ici.

Beaucoup d'OS viennent avec GLUT préinstallé, mais si le votre n'en a pas un, vous pouvez facilement le trouver en utilisant Google.

Les fichiers binaires Windows peut être téléchargé depuis www.xmission.com.

Créer votre premier programme GLUT

Afin d'utiliser GLUT, vous devez d'abord l'initialiser. C'est fait en utilisant la fonction glutInit. Cette fonction peut analyser la ligne de commande et fixer les paramètres de la fenêtre principale, mais il s'attend à une entrée dans le style C/C++ . Vous devrez écrire votre propre fonction pour faire la conversion à partir de ParamCount et ParamStr vers des paramètres en ligne de commande similaire à C/C++.

 procedure glutInitPascal(ParseCmdLine: Boolean); 
 var
   Cmd: array of PChar;
   CmdCount, I: Integer;
 begin
   if ParseCmdLine then
     CmdCount := ParamCount + 1
   else
     CmdCount := 1;
   SetLength(Cmd, CmdCount);
   for I := 0 to CmdCount - 1 do
     Cmd[I] := PChar(ParamStr(I));
   glutInit(@CmdCount, @Cmd);
 end;

Essentiellement , vous créez un tableau et le remplissez avec les chaînes de caractères de ParamStr. Cette fonction prend également un paramètre qui peut contrôler ce qui est passé à glutInit -- ou bien la ligne de commande en entier ou juste le nom du fichier exécutable .

Plus à propos de glutInit: ici

Après , vous devez créer une fenêtre principale . Fixer le mode d'affichage de la fenêtre principale en utilisant glutInitDisplayMode. Cela nécessite seulement un paramètre qui est une combinaison des drapeaux. Habituellement GLUT_DOUBLE ou GLUT_RGB ou GLUT_DEPTH est tout ce dont vous aurez besoin.

Plus à propos de glutInitDisplayMode: ici

La position et la taille de la fenêtre est contrôlée en utilisant glutInitWindowPosition et glutInitWindowSize. Elles ont 2 paramètres . Les coordonnées X et Y dans le premier, et la largeur et la hauteur dans le dernier. Vous pouvez employer glutGet pour trouver la taille de l'écran et centrer la fenêtre.

Plus à propos de glutInitWindowPosition, glutInitWindowSize et glutGet: ici et

Enfin, la fenêtre devrait être créée en utilisant la fonction glutCreateWindow. Il créera la fenêtre et fixera son caption au moyen d'un paramètre. Comme résultat il renverra l'handle de la fenêtre . Ceci peut être employé avec d'autres fonctions qui l'exigent .

Plus à propos de glutCreateWindow: ici

Avant que votre programme puisse entrer dans la boucle principale, vous devez paramétrer quelques procedures de rappel(callbacks). Vous aurez besoin de procédures de rappel pour dessiner la fenêtre , pour redimensionner et pour obtenir les entrées au clavier. Ces procédures de rappel sont paramétrées en utilisant glutDisplayFunc, glutReshapeFunc et glutKeyboardFunc.

Plus à propos du paramétrage des fonctions de rappel: ici

Votre fonction de dessin pourrait ressembler à ceci :

 procedure DrawGLScene; cdecl;
 begin
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
   glutSwapBuffers;
 end;

Cela va seulement effacer la fenêtre à la couleur du fond et remettre à zéro le zbuffer (ne pas s'inquiéter pour zbuffer... vous en saurez plus à ce propos plus tard).

Votre fonction de redimension pourrait ressembler à ceci :

 procedure ReSizeGLScene(Width, Height: Integer); cdecl;
 begin
   if Height = 0 then
     Height := 1;
 
   glViewport(0, 0, Width, Height);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity;
   gluPerspective(45, Width / Height, 0.1, 1000);
 
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity;
 end;

Avec ce code, vous dites à OpenGL où dans la fenêtre il devrait dessiner et fixer les matrices aux valeurs désirées (les fonctions de matrice seront expliquées plus tard ).

L'entrée au clavier est évaluée avec la procedure de rappel suivante:

 procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 begin
   if Key = 27 then
     Halt(0);
 end;

Cette fonction demandera à votre programme de quitter si vous appuyez sur la touche ESC. GLUT est conduit par des évènement et la seule manière de terminer votre programme est d'appeler Halt à l'intérieur d'une de vos fonctions de rappel. Si vous fermez la fenêtre d'une autre manière, elle disparaîtra , mais le programme continuera à faire une boucle au travers de la routine principale indéfiniment .

Pour commencer la boucle principale , appellez glutMainLoop. Il écrira une boucle qui ne finit jamais , qui appelle toutes vos procedures de rappel.

La partie principale de votre programme pourrait ressembler à ceci :

 const 
   AppWidth = 640; 
   AppHeight = 480; 
 
 procedure InitializeGL; 
 begin 
   glClearColor(0.18, 0.20, 0.66, 0); 
 end; 
 
 var 
   ScreenWidth, ScreenHeight: Integer; 
 begin 
   glutInitPascal(True); 
   glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH); 
   glutInitWindowSize(AppWidth, AppHeight); 
   ScreenWidth := glutGet(GLUT_SCREEN_WIDTH); 
   ScreenHeight := glutGet(GLUT_SCREEN_HEIGHT); 
   glutInitWindowPosition((ScreenWidth - AppWidth) div 2,
     (ScreenHeight - AppHeight) div 2); 
   glutCreateWindow('OpenGL Tutorial 1'); 
 
   InitializeGL; 
 
   glutDisplayFunc(@DrawGLScene); 
   glutReshapeFunc(@ReSizeGLScene); 
   glutKeyboardFunc(@GLKeyboard); 
 
   glutMainLoop; 
 end.

Le tutoriel suivant ajoutera un certain code qui dessinera une forme simple .

Télécharger le code source ou un executable linux/windows depuis le site SourceForge CCR de Lazarus.

Dessiner une forme simple

Cette fois nous ajouterons juste quelques lignes de code et nous nous focaliserons sur l'explication de certaines des fonctions OpenGL .

Expliquons le code que vous avez déjà.

 
   .
   .
   .
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity;
   gluPerspective(45, Width / Height, 0.1, 1000);
 
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity;
 end;

En employant la fonction glMatrixMode vous choisissez quelle matrice vous voulez changer . OpenGL travaille avec 3 matrices : GL_MODELVIEW: celle-ci est employée pour déplacer les sommets vers l'espace modèle . GL_PROJECTION: celle-ci est employée pour convertir les coordonnées 3d en coordonnées 2d pour la position finale des pixels. GL_TEXTURE: celle-ci est employée pour changer des coordonnées de texture .

Une fois que vous avez choisi la matrice vous voulez changer , vous pouvez appeler les fonctions qui affectent les valeurs de la matrice. glLoadIdentity remettra à zéro la matrice ainsi il n'affecte pas la position des sommets. Puisque la plupart des fonctions de matrice multiplient la matrice courante par celle générée , vous devez parfois effacer la matrice avec cette fonction .

Afin de paramétrer la matrice de perspective, vous pouvez employer la fonction gluPerspective. Quatre paramètres montrent le champ visuel , l'allongement , la surface plane proche et lointaine. C'est aussi simple.

Maintenant , vous changerez la matrice modèle ... pendant ce temps , vous l'avez juste paramétré à la matrice identité .

OK ... et maintenant , le code pour dessiner la première forme :

 procedure DrawGLScene; cdecl;
 begin
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glLoadIdentity;
   glTranslatef(0, 0, -5);
 
   glBegin(GL_TRIANGLES);
     glColor3f(1, 0, 0);
     glVertex3f(-1, -1, 0);
 
     glColor3f(0, 1, 0);
     glVertex3f(1, -1, 0);
 
     glColor3f(0, 0, 1);
     glVertex3f(0, 1, 0);
   glEnd;
 
   glutSwapBuffers;
 end;

Nous avons déjà employé la fonction glClear. Elle remettra juste à zéro les mémoires tampons(buffers). Nous passerons les deux fonctions suivantes et nous nous dirigerons vers celles de dessin.

glBegin marque le commencement du bloc de dessin . Après cette fonction vous pouvez commencer à entrer les sommets. Le paramètre(entre parenthèse) décrit comment sont utilisés les sommets en dessinant : GL_POINTS: Traite chaque sommet comme simple point. Le sommet n définit le point n . N points sont dessinés.

GL_LINES: Traite chaque paire de sommets comme segment de ligne indépendant. Les sommets 2n-1 et 2n définissent la ligne n . n/2 lignes sont tracées .

GL_LINE_STRIP: Dessine un groupe relié de segments de ligne du premier sommet au dernier. n-1 lignes sont tracées.

GL_LINE_LOOP: Dessine un groupe relié de segments de ligne du premier sommet au dernier, en revenant vers le premier. Les sommets n et n+1 définissent la ligne n . La dernière ligne , cependant , est défini par les sommets n et 1.n lignes sont tracées .

GL_TRIANGLES: Traite chaque triplet des sommets comme un triangle indépendant . Les sommets 3n-2, 3n-1 et 3n définissent le triangle n. n/3 triangles sont tracées .

GL_TRIANGLE_STRIP: Dessine un groupe relié de triangles . Un triangle est défini pour chaque sommet présenté après les deux premiers sommets. Pour n impair, les sommets n, n+1 et n+2 définissent le triangle n. Pour n pair, les sommets n+1, n et n+2 définissent le triangle n . n-2 triangles sont tracées .

GL_TRIANGLE_FAN: Dessine un groupe relié de triangles . Une triangle est définie pour chaque sommet présenté après les deux premiers sommets . Les sommets 1, n+1 et n+2 définissent le triangle n. n-2 triangles sont dessinés .

GL_QUADS: Traite chaque groupe de quatre sommets comme un quadrilatère indépendant. Les sommets 4n-3, 4n-2, 4n-1 et 4n définissent le quadrilatère n. n/4 quadrilatères sont dessinés.

GL_QUAD_STRIP: Dessine un groupe relié de quadrilatères. Un quadrilatère est défini pour chaque paire de sommets présentés après la première paire.Les sommets 2n-1, 2n, 2n+2 et 2n+1 définissent le quadrilatère n. n/2-1 quadrilatères sont dessinés. Notez que l'ordre dans lequel des sommets sont employés pour construire un quadrilatère à partir des données reliées est différent de celui utilisé avec des données indépendantes.

GL_POLYGON: Dessine un polygone simple et convexe . Les sommets 1 jusqu'à n définissent ce polygone .

SimpleShapePic1.jpg

Vous tracerez un triangle simple et pour cela le drapeau GL_TRIANGLES accomplira cette astuce. La fonction glVertex3f définit la position d'un sommet que vous voulez dessiner. Il y a plus de fonctions glVertex* . La seule différence est le nombre et le type de paramètres que ces fonctions prennent. Par exemple ... glVertex2i prend deux paramètres (x et y) du type nombre entier. glVertex3f sera presque toujours juste ce que vous avez besoin.

Avant glVertex vous pouvez fixer la couleur, le matériel , les textures... Pour la simplicité vous indiquerez juste la couleur pour chaque sommet dans tutoriel . La couleur est paramétrée en utilisant la fonction glColor3f. glColor peut aussi prendre différents ensemble de paramètres comme glVertex.

Si on jette un coup d'oeil sur le code, nous pouvons voir que Z est paramétré à 0 pour tous les sommets. Puisque vous avez fixé la plan proche à 0.1, le triangle ne sera pas visible. C'est là que nous avons passé les deux fonctions au commencement. Nous savons déjà que glLoadIdentity remet à zéro la matrice. glTranslatef déplace les triangles selon les valeurs X, Y et Z que vous fournissez. Puisque vous avez fixé Z à -5 (z négatif est plus loin de la caméra) tous les sommets seront dessinés avec un éloignement de 5 unités du point de vue et seront visibles.

À la fin vous appelez les fonctions glEnd qui finissent le dessin. Vous pourriez maintenant commencer d'autres bloc de dessin avec une nouvelle fonction glBegin si vous le souhaitez .

Téléchargez le code source , l'exécutable linux ou l'exécutable windows depuis le dépôt Lazarus CCR de SourceForge.

Employer des listes d'affichage

Parfois vous devrez dessiner quelques objets de multiples fois sur la scène. OpenGL a la capacité de construire des listes d'affichage ce qui rend le dessin un peu plus rapide.Créer une liste d'affichage est très facile ... dessinez juste les sommets comme vous l'avez fait dans le précédent tutoriel et enfermez-les avec les appels à glNewList et glEndList.

 const
   LIST_OBJECT = 1;
 
 procedure CreateList;
 begin
   glNewList(LIST_OBJECT, GL_COMPILE);
     glBegin(GL_TRIANGLE_FAN);
       glColor3f(1, 0, 0);
       glVertex3f(0, 0.5, 0);
 
       glColor3f(1, 1, 0);
       glVertex3f(-0.5, -0.5, 0.5);
 
       glColor3f(1, 1, 1);
       glVertex3f(0.5, -0.5, 0.5);
 
       glColor3f(0, 1, 1);
       glVertex3f(0.5, -0.5, -0.5);
 
       glColor3f(0, 0, 1);
       glVertex3f(-0.5, -0.5, -0.5);
 
       glColor3f(0, 1, 0);
       glVertex3f(-0.5, -0.5, 0.5);
     glEnd;
 
     glBegin(GL_QUADS);
       glColor3f(1, 1, 0);
       glVertex3f(-0.5, -0.5, 0.5);
 
       glColor3f(1, 1, 1);
       glVertex3f(0.5, -0.5, 0.5);
 
       glColor3f(0, 1, 1);
       glVertex3f(0.5, -0.5, -0.5);
 
       glColor3f(0, 0, 1);
       glVertex3f(-0.5, -0.5, -0.5);
 
       glColor3f(0, 1, 0);
       glVertex3f(-0.5, -0.5, 0.5);
     glEnd;
   glEndList;
 end;

glNewList crée une nouvelle liste d'affichage et toutes les fonctions de dessin seront enregistrées jusqu'à ce que glEndList soit appelé. Le premier paramètre pour la fonction glNewList est l'identification de la liste(ID).Chaque liste est définie par son ID. Si une liste avec une identification donnée est déjà créée son contenu est effacé avant l'enregistrement. Si le deuxième paramètre est GL_COMPILE alors toutes les fonctions de dessin sont juste enregistrées , mais si c'est GL_COMPILE_AND_EXECUTE alors elles sont enregistrées et exécutées automatiquement.

La fonction glIsList peut vous aider avec des listes d'affichage. Il peut indiquer si une certaine identification de liste est déjà remplie de données. Une autre fonction utile est glGenLists. Il créera de multiple listes d'affichage vides. Vous passez le nombre de listes d'affichage dont vous avez besoin et vous obtenez l'identification de la première. Si vous exigez n listes, et obtenez r identifications,les listes d'affichage produites sont : r, r+1, r+2,..., r+n-1

Toutes les listes créées devraient être supprimées. Vous ferez cela avant de quitter le programme :

 procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 begin
   if Key = 27 then
   begin
     glDeleteLists(LIST_OBJECT, 1);
     Halt(0);
   end;
 end;

glDeleteLists prend 2 paramètres, l'identification de la liste d'affichage et le nombre de listes à supprimer. Si l'identification est r, et le nombre de listes à supprimer est n, les listes supprimées sont : r, r+1, r+2,..., r+n-1

Maintenant vous savez créer et supprimer des listes d'affichage ... voyons comment les dessiner :

 procedure DrawGLScene; cdecl;
 begin
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glLoadIdentity;
   glTranslatef(-2, 0, -5);
   glRotatef(40, 1, 0, 1);
   glCallList(LIST_OBJECT);
 
   glLoadIdentity;
   glTranslatef(1, -2, -10);
   glRotatef(62, 0, 1, 0);
   glCallList(LIST_OBJECT);
 
   glLoadIdentity;
   glTranslatef(-4, 0.5, -15);
   glRotatef(200, 1, 0, 0);
   glCallList(LIST_OBJECT);
 
   glutSwapBuffers;
 end;
DisplayListsPic1.jpg

En utilisant glCallList vous pouvez dessiner seulement une liste d'affichage . Dans ce tutoriel, avant de dessiner une liste d'affichage, vous changez la matrice modèle et dessinez l'objet en différents endroits .

Quelques fois vous voudriez dessiner des listes multiples d'un coup. C'est possible en utilisant la fonction glCallLists. Il prend le nombre de listes que vous voulez dessiner, le type de tableau qui contient les identifications de liste d'affichage et le tableau avec les identifications de liste d'affichage. Le type de liste peut être l'un des suivant :

GL_BYTE: la liste est traitée comme un tableau d'octets signés, chacun dans l'intervalle de -128 à 127 .

GL_UNSIGNED_BYTE: la liste est traitée comme un tableau d'octets non signés, chacun dans l'intervalle de 0 à 255.

GL_SHORT: la liste est traitée comme un tableau d'entiers sur 2 octets signés, chacun dans l'intervalle de -32768 à 32767.

GL_UNSIGNED_SHORT: la liste est traitée comme un tableau d'entiers sur 2 octets non signés, chacun dans l'intervalle de 0 à 65535.

GL_INT: la liste est traitée comme un tableau d'entiers sur 4 octets signés.

GL_UNSIGNED_INT: la liste est traitée comme un tableau d'entiers sur 4 octets non signés.

GL_FLOAT: la liste est traitée comme un tableau de valeurs à virgule flottante sur 4 octets.

GL_2_BYTES: la liste est traitée comme un tableau d'octets non signés. Chaque paire d'octet spécifie une identification simple de la liste d'affichage. La valeur de la paire est calculée comme étant 256 fois la valeur du premier octet non signée plus la valeur du deuxième octet non signée .

GL_3_BYTES: la liste est traitée comme un tableau d'octets non signés. Chaque triplet d'octet spécifie une identification simple de la liste d'affichage. La valeur du triplet est calculée comme étant 65536 fois la valeur du premier octet non signée, plus 256 fois la valeur du deuxième octet non signée, plus la valeur du troisième octet non signée.

GL_4_BYTES: la liste est traitée comme un tableau d'octets non signés. Chaque quadruplet d'octet spécifie une identification simple de la liste d'affichage. La valeur du quadruplet est calculée comme étant 16777216 fois la valeur du premier octet non signée, plus 65536 fois la valeur du deuxième octet non signée, plus 256 la valeur du troisième octet non signée, plus la valeur du quatrième octet non signée.

C'est tout pour le moment. Le prochain tutoriel montrera comment créer petit système planétaire. Nous parlerons des matrices et comment faire une scène animée qui ne dépend pas du nombre d'images par seconde .

Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis le dépôt Lazarus CCR de SourceForge.

Animation en plein écran

Entrer dans le mode plein écran est facile avec GLUT. Changeons la partie principale du programme :

 const
   FSMode = '800x600:32@75';
 
 begin
   glutInitPascal(False);
   glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);
   glutGameModeString(FSMode);
   glutEnterGameMode;
   glutSetCursor(GLUT_CURSOR_NONE);
 
   InitializeGL;
 
   glutDisplayFunc(@DrawGLScene);
   glutReshapeFunc(@ReSizeGLScene);
   glutKeyboardFunc(@GLKeyboard);
   glutIdleFunc(@DrawGLScene);
 
   glutMainLoop;
 end.

Puisque nous ne voulons pas que GLUT analyse la ligne de commande cette fois nous appelons glutInitPascal avec le paramètre false. Comme vous pouvez le voir, il n'y a aucun code pour la création de fenêtre. GLUT a glutEnterGameMode qui crée une fenêtre en mode plein écran. Pour indiquer quel genre de mode plein écran vous voulez, vous appelez la fonction glutGameModeString qui prend un chaine de caractères qui définie le mode que vous aimez . Le format de cette chaine de caractères est :

[largeur  "x" hauteur][":" bpp]["@" hertz]

Dans la chaine de caractères FSMode nous avons déclaré que le mode plein écran devrait être 800x600, avec une pallete de 32bit et un rafraîchissements de 75Hz. Il est possible de sauter un des groupes . Si vous omettez la taille , GLUT essayera d'employer la taille courante ou la première des plus petites qui peut fonctionner. Cette politique est employée et pour d'autres paramètres .

Habituellement dans le mode plein écran le curseur n'est pas visible. Pour cacher le curseur vous utilisez la fonction glutSetCursor. Elle utilise seulement un paramètre qui décrit le curseur que vous voudriez voir :

GLUT_CURSOR_RIGHT_ARROW
GLUT_CURSOR_LEFT_ARROW
GLUT_CURSOR_INFO
GLUT_CURSOR_DESTROY
GLUT_CURSOR_HELP
GLUT_CURSOR_CYCLE
GLUT_CURSOR_SPRAY
GLUT_CURSOR_WAIT
GLUT_CURSOR_TEXT
GLUT_CURSOR_CROSSHAIR
GLUT_CURSOR_UP_DOWN
GLUT_CURSOR_LEFT_RIGHT
GLUT_CURSOR_TOP_SIDE
GLUT_CURSOR_BOTTOM_SIDE
GLUT_CURSOR_LEFT_SIDE
GLUT_CURSOR_RIGHT_SIDE
GLUT_CURSOR_TOP_LEFT_CORNER
GLUT_CURSOR_TOP_RIGHT_CORNER
GLUT_CURSOR_BOTTOM_RIGHT_CORNER
GLUT_CURSOR_BOTTOM_LEFT_CORNER
GLUT_CURSOR_FULL_CROSSHAIR
GLUT_CURSOR_NONE
GLUT_CURSOR_INHERIT

glutIdleFunc définit une fonction de rappel que vous voulez appeler chaque fois que votre programme n'a aucun message à traiter. Puisque nous voulons juste faire le rendu de la nouvelle image s'il n'y a rien à faire, juste faire tourner en idle DrawGLScene. Certains autres tutoriels montrent que la fonction idle devrait envoyer un message de refresh plutôt que de dessin, mais ainsi j'ai 50-100 images de moins qu'en utilisant la méthode que j'ai décrit .

Maintenant , regardons l'arrêt de programme là où vous devez quitter le mode plein écran :

 procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
 begin
   if Key = 27 then
   begin
     glutLeaveGameMode;
     Halt(0);
   end;
 end;

Comme vous pouvez le voir , tout que vous devez faire est d'appeler glutLeaveGameMode.

Maintenant , nous allons présenter quelques nouvelles fonctions de matrice. D'abord, changeons la fonction ReSizeGLScene :

 procedure ReSizeGLScene(Width, Height: Integer); cdecl;
 begin
   .
   .
   .

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity;
   gluLookAt(0, 20, 25, 0, 0, 0, 0, 1, 0);
 end;

gluLookAt créer la matrice qui définira d'où êtes vous pour regarder les objets. Les 3 premiers paramètres sont les coordonnées X, Y et Z de la position de la caméra. les 3 paramètres suivants sont les coordonnées X, Y et Z du point vers où la caméra regarde, et les 3 derniers paramètres définissent un vecteur « vers le haut » (C'est « vers le haut » pour la caméra). Habituellement , « vers le haut » est l'axe y positif.

OK, dessinons maintenant. Puisque vous avez défini la matrice avec gluLookAt laquelle devrait être employé avec tous les objets, vous ne pouvez pas simplement employer glLoadIdentity pour remettre à zéro la matrice pour le prochain objet ... vous sauverez l'état précédent de la matrice et le restaurerez après que l'objet soit dessiné :

 procedure DrawGLScene; cdecl;
 var
   T: Single;
 begin
   T := glutGet(GLUT_ELAPSED_TIME) / 1000;
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glPushMatrix;
     glRotatef(5 * T, 0, 1, 0);
     glColor3f(1, 1, 0);
     glutWireSphere(2, 20, 20);
   glPopMatrix;
 
   glPushMatrix;
     glRotatef(90 * T, 0, 1, 0);
     glTranslatef(5, 0, 0);
     glRotatef(40 * T, 0, 1, 0);
     glColor3f(1, 0, 0);
     glutWireSphere(0.6, 10, 10);
   glPopMatrix;
 
   glPushMatrix;
     glRotatef(60 * T, 0, 1, 0);
     glTranslatef(-3, 0, 9);
     glRotatef(50 * T, 0, 1, 0);
     glColor3f(0, 1, 0);
     glutWireSphere(1, 16, 16);
 
     glPushMatrix;
       glRotatef(360 * T, 0, 1, 0);
       glTranslatef(-1.7, 0, 0);
       glRotatef(50 * T, 0, 1, 0);
       glColor3f(0, 0, 1);
       glutWireSphere(0.4, 10, 10);
     glPopMatrix;
 
   glPopMatrix;
 
   glutSwapBuffers;
 end;
FullScreenAnimationPic1.jpg

glPushMatrix et glPopMatrix sont employés pour enregistrer et restaurer l'état de la matrice. Comme vous pouvez le voir, nous enregistrons l'état de la matrice, ensuite changeons la matrice afin de dessiner l'objet à la bonne place, et restaurons ensuite l'ancien état de la matrice .

Vous pouvez vous demander quel est le rôle de la variable T . Bien, elle est employée pour déterminer la vitesse d'animation. Chaque changement qui dépend du temps est multiplié par T. De cette manière, la vitesse d'animation est constante pour chaque rapidité d'affichage . La fonction glutGet avec le paramètre GLUT_ELAPSED_TIME renvoie le temps en milliseconds depuis que glutInit a été appelé. En divisant cette valeur par 1000, nous avons le temps en secondes .

La fonction glRotatef créer une matrice de rotation. Le premier paramètre est l'angle en degrés,et les 3 derniers paramètres définissent l'axe autour duquel la rotation sera faite . Puisque vous avez multiplié l'angle par T, l'objet sera tourné de cet angle en exactement 1 seconde.

Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis le dépôt Lazarus CCR de SourceForge.

Lumière

Ce tutoriel présentera une certaine lumière à la scène. Vous ferez tourner un cube et une lumière ajoutera du réalisme à la scène, mais d'abord faisons une unité d'outils .

Dans l'immédiat, il y aura seulement des fonctions fondamentales pour nous aider à obtenir le temps courant et la variation de temps(temps qui s'est écoulé depuis un appel à un rendu à l'autre) afin de calculer le nombre d'images par secondes.

 unit utils;
 
 {$mode objfpc}{$H+}
 
 interface
 
 uses
   glut;
 
 function GetTotalTime: Single;
 function GetDeltaTime: Single;
 procedure FrameRendered(Count: Integer = 1);
 function GetFPS: Single;
 
 implementation
 
 var
   OldTime: Integer = 0;
   FPSTime: Integer = 0;
   FPSCount: Integer = 0;
 
 function GetTotalTime: Single;
 begin
   Result := glutGet(GLUT_ELAPSED_TIME) / 1000;
 end;
 
 function GetDeltaTime: Single;
 var
   NewTime: Integer;
 begin
   NewTime := glutGet(GLUT_ELAPSED_TIME);
   Result := (NewTime - OldTime) / 1000;
   OldTime := NewTime;
 end;
 
 procedure FrameRendered(Count: Integer);
 begin
   Inc(FPSCount, Count);
 end;
 
 function GetFPS: Single;
 var
   NewTime: Integer;
 begin
   NewTime := glutGet(GLUT_ELAPSED_TIME);
 
   Result := FPSCount / ((NewTime - FPSTime) / 1000);
 
   FPSTime := NewTime;
   FPSCount := 0;
 end;
 
 end.

Comme vous pouvez le voir, il n'y a rien de compliqué dans cette unité. Le temps est simplement enregistré entre chaque appel et la différence est retournée. FrameRendered devrait être appelé chaque fois que vous dessinez la scène ainsi la fonction peut calculer les FPS.

Maintenant, amusons nous avec les lumières.

OpenGL a plusieurs types de lumières ... ambiante, diffuse, ponctuelle, spot, lumière spéculaire et émissive.

La lumière ambiante est quelque chose comme le Soleil. Quand les rayons du soleil traversent la fenêtre d'une salle, ils frappent les murs et sont réfléchis et dispersés dans toutes les différentes directions ce qui illumine en moyenne toute la salle. Tous les sommets sont éclairés avec la lumière ambiante .

La lumière diffuse peut être représentée par des rayons de lumière parallèles venant de loin. Ils vont seulement éclairer les sommets qui sont orientés vers la source lumineuse.

La lumière ponctuelle éclaire tout autour d'elle. C'est comme une boule de feu, elle envoie des rayons lumineux tout autour d'elle et éclaire les sommets qui sont orientés vers la source lumineuse et qui sont assez proche.

La lumière spot est comme la lumière d'une lampe-torche. C'est simplement une source ponctuelle lumineuse avec un petit faisceau de lumière en forme de cône. Tous les sommets qui tombent à l'intérieur du cône et sont assez proche sont éclairés.

Juste comme la lumière diffuse, la lumière spéculaire est un type directionnel de lumière. Elle vient d'une direction particulière. La différence entre les deux est que la lumière spéculaire se reflète sur la surface d'une manière directionnelle et uniforme. Le rendu de la lumière spéculaire repose sur l'observateur et la source lumineuse. Du point de vue de l'observateur, la lumière spéculaire crée un secteur accentué sur la surface de l'objet vu, connu en tant que "specular highlight" ou réflexion spéculaire.

La lumière émissive est peu une différente que tout autre composants lumineux précédemment expliqué. Cette lumière sort de l'objet que vous dessinez but pas n'éclaire pas d'autres objets dans son environnement .

Pour plus de simplicité, nous emploierons seulement la lumière diffuse dans ce tutoriel. Plus tard, d'autres lumières peuvent apparaître dans les tutoriels :)

Voyons comment autoriser la lumière dans la scène :

 const
   DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);
 
   glEnable(GL_LIGHTING);
   glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
   glEnable(GL_LIGHT0);

Comme vous le voyez, nous permettons l'éclairage dans OpenGL ainsi les lumières affectent la scène dont vous faites le rendu. Des paramètres lumineux sont fixés avec la fonction glLightfv. Elle prend 3 paramètres... un pour le numéro de la lumière que vous voulez changer (OpenGL supporte jusqu'à 8 lumières), ensuite dit à OpenGL quel paramètre lumineux changer, et le dernier est un nouveau paramètre pour la lumière . Vous fixerez juste la couleur diffuse pour la lumière dans ce tutoriel. Après cela, vous pouvez permettre la lumière et il y aura la lumière dans la scène ... mais ... ce n'est pas tout .

Plus à propos de glLightfv

Si vous voulez employer des lumières, vous ne pouvez pas simplement fixer la couleur des sommets ... vous devez choisir le matériau pour les sommets . Installons le matériau pour le dessin :

glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
LightPic1.jpg

Vous vous êtes attendus à quelque chose plus compliquée, n'est ce pas? :) Bien, ce code nous permet d'employer la fonction glColor pour fixer le matériau aux sommets. En employant la fonction glEnable et le drapeau GL_COLOR_MATERIAL, vous pouvez définir quelles propriétés matérielles vont changer glColor. glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE) dit à OpenGL que glColor change le matériau ambiant et diffus. Nous discuterons des matériaux davantage dans des tutoriels postérieurs .

Une chose de plus qui est importante en utilisant des lumières... chaque sommet doit avoir une normale qui lui est associée. La normale est employée pour trouver la direction du sommet ainsi la lumière peut être calculée correctement . Vous emploierez un fonction GLUT pour dessiner le cube et elle fournit les normales pour nous, ainsi cette fois nous marcherons juste par les normales (???).

Après tout ces réglages, la lumière fera briller votre cube :)

Une partie du texte est copiée depuis La bible de la lumière avec OpenGL

Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis le dépôt Lazarus CCR de SourceForge.

Polices de caractères d'image

Les jeux et les programmes doivent habituellement écrire un certain texte sur l'écran. GLUT fournit plusieurs fonctions pour dessiner des caractères qui sont indépendantes de la plate-forme .

D'abord, nous montrerons comment employer les polices de caractères d'image par défaut. Presque toutes les additions de code seront faites à l'unité utils.pas .

Puisque le texte sera dessiné en 2D, nous devrons savoir la largeur et la hauteur de la clôture(viewport) ... ainsi, nous écrirons deux fonctions pour cela :

 function glGetViewportWidth: Integer;
 var
   Rect: array[0..3] of Integer;
 begin
   glGetIntegerv(GL_VIEWPORT, @Rect);
   Result := Rect[2] - Rect[0];
 end;
 
 function glGetViewportHeight: Integer;
 var
   Rect: array[0..3] of Integer;
 begin
   glGetIntegerv(GL_VIEWPORT, @Rect);
   Result := Rect[3] - Rect[1];
 end;

Nous récupérons juste la gauche /la droite, le haut/le bas et nous calculons la largeur/la hauteur en les soustrayant.

Il doit y avoir des fonctions pour entrer et quitter le mode 2D :

 procedure glEnter2D;
 begin
   glMatrixMode(GL_PROJECTION);
   glPushMatrix;
   glLoadIdentity;
   gluOrtho2D(0, glGetViewportWidth, 0, glGetViewportHeight);
 
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix;
   glLoadIdentity;
 
   glDisable(GL_DEPTH_TEST);
 end;
 
 procedure glLeave2D;
 begin
   glMatrixMode(GL_PROJECTION);
   glPopMatrix;
   glMatrixMode(GL_MODELVIEW);
   glPopMatrix;
 
   glEnable(GL_DEPTH_TEST);
 end;

Quand on entre dans le mode 2D, nous enregistrons les matrices en cours et paramétrons les matrices 2D en utilisant la fonction gluOrtho2D. De cette façon si nous dessinons quelque chose sur (un viewport de) 100, 100 cette chose sera dessinée sur exactement 100 Pixels à partir du bord gauche de la fenêtre, et 100 Pixels depuis le bord inférieur (le y positif est vers le haut). En outre , nous interdisons ZBuffer. De cette façon, le texte n'altèrera pas ZBuffer.

Le fait de quitter le mode 2D renvoie juste les anciennes matrices et autorise ZBuffer.

Maintenant, nous pouvons créer la fonction pour dessiner du texte:

 procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);
 var
   I: Integer;
 begin
   glRasterPos2f(X, Y);
   for I := 1 to Length(Text) do
     glutBitmapCharacter(Font, Integer(Text[I]));
 end;

glutBitmapCharacter peut dessiner seulement un caractère de la police de caractères choisie. Le premier paramètre est la police de caractères désirée (GLUT_BITMAP_9_BY_15, GLUT_BITMAP_8_BY_13, GLUT_BITMAP_TIMES_ROMAN_10, GLUT_BITMAP_TIMES_ROMAN_24, GLUT_BITMAP_HELVETICA_10, GLUT_BITMAP_HELVETICA_12 ou GLUT_BITMAP_HELVETICA_18) et l'autre paramètre est le caractère.

Le caractère sera dessiné à la position de trame en cours. Pour placer la position désirée de trame nous appelons la fonction glRasterPos. glRasterPos peut traiter des nombres et des types de paramètres différents juste comme la fonction glVertex. La coordonnée indiquée est transformée par les matrices modèle et de projection pour obtenir la coordonnée 2D là où sera la nouvelle position de trame. Depuis que nous sommes entrés dans le mode 2D , Les coordonnées X et Y sont les coordonnées 2D réelles où le dessin se produira .

Ces nouvelles fonctions vont rendre le dessin du texte très facile:

 procedure DrawGLScene; cdecl;
 begin
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glLoadIdentity;
   glTranslatef(0, 0, -5);
   glRotatef(GetTotalTime * 10, 0, 0.5, 0.5);
 
   glColor3f(1, 0, 0);
   glutSolidCube(2);
 
   glEnter2D;
 
   glColor3f(0.2, 0.8 + 0.2 * Sin(GetTotalTime * 5), 0);
   glWrite(20, glGetViewportHeight - 20, GLUT_BITMAP_8_BY_13,
     Format('OpenGL Tutorial :: Bitmap Fonts :: FPS - %.2f FPS', [FPS]));
 
   glColor3f(1, 1, 1);
   glWrite(50, glGetViewportHeight - 60, GLUT_BITMAP_9_BY_15, 'GLUT_BITMAP_9_BY_15');
   glWrite(50, glGetViewportHeight - 90, GLUT_BITMAP_8_BY_13, 'GLUT_BITMAP_8_BY_13');
   glWrite(50, glGetViewportHeight - 120, GLUT_BITMAP_TIMES_ROMAN_10, 'GLUT_BITMAP_TIMES_ROMAN_10');
   glWrite(50, glGetViewportHeight - 150, GLUT_BITMAP_TIMES_ROMAN_24, 'GLUT_BITMAP_TIMES_ROMAN_24');
   glWrite(50, glGetViewportHeight - 180, GLUT_BITMAP_HELVETICA_10, 'GLUT_BITMAP_HELVETICA_10');
   glWrite(50, glGetViewportHeight - 210, GLUT_BITMAP_HELVETICA_12, 'GLUT_BITMAP_HELVETICA_12');
   glWrite(50, glGetViewportHeight - 240, GLUT_BITMAP_HELVETICA_18, 'GLUT_BITMAP_HELVETICA_18');
 
   glColor3f(0.5, 0.5, 1);
   glWrite(
     glGetViewportWidth - glutBitmapLength(GLUT_BITMAP_9_BY_15, LazText) - 5,
     10, GLUT_BITMAP_9_BY_15, LazText);
 
   glLeave2D;
 
   glutSwapBuffers;
 
   FrameRendered;
 end;
BitmapFontsPic1.jpg

Nous dessinons un cube rouge et le faisons tourner, et un peu de texte pour montrer à quel point les diverses polices de caractères d'image ressemblent. La fonction glutBitmapLength est employée pour trouver la largeur des chaînes de caractères ainsi elles pourraient être alignées vers la droite. Le code peut facilement être changé pour centrer le texte .

Note: Voir comment les cubes apparaissent sans lumière.

Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis le dépôt Lazarus CCR de SourceForge.

Textures

Il est temps d'employer des textures :)

Ce tutoriel montrera comment dessiner les polygones texturisés et comment mélanger des textures en utilisant une technique à plusieurs passages. Puisque OpenGL n'a aucun mécanisme interne pour télécharger des textures, nous emploierons une bibliothèque externe : la bibliothèque Imaging de Vampyre. Nous emploierons juste des fonctions d'aide d'OpenGL, mais vous pouvez trouver cette bibliothèque maniable pour aussi d'autres choses.

Soyons prêt à démarrer... nous créerons une liste d'affichage pour dessiner un rectangle texturisé :

 procedure CreateList;
 begin
   glNewList(LIST_OBJECT, GL_COMPILE);
     glBegin(GL_QUADS);
       glTexCoord2f(1, 0);
       glVertex3f( 2, 2, 0);
       glTexCoord2f(0, 0);
       glVertex3f(-2, 2, 0);
       glTexCoord2f(0, 1);
       glVertex3f(-2,-2, 0);
       glTexCoord2f(1, 1);
       glVertex3f( 2,-2, 0);
     glEnd;
   glEndList;
 end;

Notez les fonctions glTexCoord. Elles sont utilisées pour indiquer quelle partie de la texture est assignée au sommet. Les coordonnées définies dans ces fonctions vont de 0 à 1 (Les valeurs supérieures à 1 sont permises mais peuvent produire des résultats différents). 0 est le premier Pixel et 1 est le dernier Pixel . Ainsi, 0.5 sera exactement au milieu de la texture.

Le téléchargement de texture est extrêmement aisé avec la bibliothèque Imaging de Vampyre:

 var
   Tex1, Tex2: GLuint;
 
 procedure InitializeGL;
 begin
   glClearColor(0, 0, 0, 0);
   Tex1 := LoadGLTextureFromFile('ashwood.bmp');
   Tex2 := LoadGLTextureFromFile('Flare.bmp');
   glEnable(GL_TEXTURE_2D);
 end;

LoadGLTextureFromFile charge une texture depuis un fichier et renvoie son ID. Quand la texture est chargée elle est déjà paramétrée pour le rendu. La dernière ligne autorise juste les textures 2D.

Pour dessiner un polygone texturisé vous devez lier la texture et paramétrer les coordonnées de texture(les coordonnées de texture sont fixées dans la liste d'affichage dans ce tutoriel):

   ...
   glLoadIdentity;
   glTranslatef(-5, 0, -15);
   glBindTexture(GL_TEXTURE_2D, Tex1);
   glCallList(LIST_OBJECT);
   ...

La fonction glBindTexture est employée pour choisir la texture. Quand vous dessinez des polygones ils auront la texture sélectionnée sur eux. C'est aussi simple :)

Ainsi , employer une texture est facile ... mais comment mélanger deux textures . Fondamentalement vous dessinez le polygone une fois avec une texture, paramétrez les paramètres du mélange, et dessinez le polygone une fois de plus avec une autre texture. Vous pouvez mélanger des centaines de texture de cette façon. Voyons à quoi le code pour faire ceci ressemble :

   ...
   glLoadIdentity;
   glTranslatef(5, 0, -15);
   glBindTexture(GL_TEXTURE_2D, Tex1);
   glCallList(LIST_OBJECT);
 
   glEnable(GL_BLEND);
   glBlendFunc(GL_ZERO, GL_SRC_COLOR);
   glLoadIdentity;
   glTranslatef(5, 0, -15);
   glBindTexture(GL_TEXTURE_2D, Tex2);
   glCallList(LIST_OBJECT);
   glDisable(GL_BLEND);
 ...

Comme vous pouvez le voir , le polygone est dessiné la première fois comme nous le savons déjà. Avant le deuxième dessin nous permettons le mélange de textures en appelant la procédure glEnable(GL_BLEND). Mélanger signifie que la couleur finale du Pixel est calculée comme ceci :

DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND

SRCBLEND et DESTBLEND sont définis en utilisant la fonction glBlendFunc. Dans ce tutoriel nous paramétrons SRCBLEND à GL_ZERO (zero) et DESTBLENT à GL_SRC_COLOR (DrawingColor) et la couleur finale est alors:

DrawingColor * 0 + BackgroundColor * DrawingColor
TexturesPic1.jpg

Ceci signifie que l'arrière plan deviendra plus foncé quand vous dessinez avec des couleurs foncées ... quand vous dessinez avec de la couleur blanche , la couleur de l'arrière plan ne sera pas changée. Le résultat ressemblera à ceci

La fois prochaine , nous utiliserons des extensions pour montrer comment utiliser le multitexturing en une passe.

Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis le dépôt Lazarus CCR de SourceForge.

Multitexturing (extensions)

Quand vous connaissez le multitexturing en plusieurs passes, celui en une passe est très simple. Appliquer une texture est séparé en étapes. La première étape permet de paramétrer et de dessiner la première texture, la seconde étape en dessine une autre et ainsi de suite. Tous que vous devez faire est de paramétrer les étapes avec les textures et de faire le rendu de l'objet.

Voyons à quoi le code ressemble :

 procedure InitializeGL;
 begin
   Load_GL_ARB_multitexture;
   glClearColor(0, 0, 0, 0);
   Tex1 := LoadGLTextureFromFile('Lazarus.bmp');
   Tex2 := LoadGLTextureFromFile('Mask.bmp');
   glActiveTextureARB(GL_TEXTURE0_ARB);
   glEnable(GL_TEXTURE_2D);
   glBindTexture(GL_TEXTURE_2D, Tex1);
   glActiveTextureARB(GL_TEXTURE1_ARB);
   glEnable(GL_TEXTURE_2D);
   glBindTexture(GL_TEXTURE_2D, Tex2);
 end;

D'abord nous avons besoin de l'extension OpenGL qui nous permettrons d'utiliser les fonctions de multitexture. Load_GL_ARB_multitexture essayera de charger ces extensions et renverra TRUE si l'opération est réussie.

Pour choisir l'étape de texture sur laquelle vous voulez travailler, utilisez la fonction glActiveTextureARB. Elle demande seulement un paramètre qui défini l'étape que vous voulez. Après ça toutes les fonctions de texture (enabling, disabling, binding, creating...) affecteront cette étape .

Puisque nous avons paramétrés tout dans la fonction d'initialisation, tout que nous avons à faire est de dessiner l'objet :

 procedure DrawGLScene; cdecl;
 begin
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glLoadIdentity;
   glTranslatef(0, 0, -5);
 
   glBegin(GL_QUADS);
     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
     glVertex3f(2.516, 2, 0);
     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
     glVertex3f(-2.516, 2, 0);
     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
     glVertex3f(-2.516,-2, 0);
     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
     glVertex3f(2.516,-2, 0);
   glEnd;
 
   glutSwapBuffers;
 end;
MultitexturePic1.jpg

Comme vous pouvez le voir, la différence est seulement dans la définitions des coordonnées de texture. Nous utilisons maintenant la fonction glMultiTexCoord2fARB qui prend l'étape de texture et les coordonnées de texture en paramètre. Tout le reste est inchangé.

Aujourd'hui presque toutes les cartes graphiques supportent au moins 2 étapes de texture . L'utilisation du multitexturing en une passe est plus rapide que la version multipasse puisque vous dessinez les objets une seule fois. Si le matériel supporte le multitexturing en une passe (Load_GL_ARB_multitexture renvoie TRUE) utilisez le.

Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis le dépôt Lazarus CCR de SourceForge.

Faire du rendu pour obtenir une texture

Ce paragraphe sera court. OpenGL peut capturer la scène courante pour en faire une texture ainsi vous pouvez l'employer pour appliquer une texture sur d'autres objets (écran TV , miroir ou quelque chose d'autre). Bien faisons immédiatement un rendu de scène vers une texture et appliquons la à un plan rotatif.

D'abord, nous devons créer une texture vide qui est bien utilisée pour capturer la scène:

 procedure SetupRenderTexture;
 var
   Data: Pointer;
 begin
   GetMem(Data, 256*256*3);
   glGenTextures(1, @RenderTexture);
   glBindTexture(GL_TEXTURE_2D, RenderTexture);
   glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, Data);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   FreeMem(Data);
 end;

La mémoire tampon de l'image RGB 256*256 pixels est crée et elle est utilisée pour paramétrer la texture 2D.

La partie principale est dans la fonction de dessin:

 procedure DrawGLScene; cdecl;
 var
   TotalTime: Single;
 begin
   glClearColor(0, 0, 0, 0);
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
   glEnable(GL_LIGHTING);
   glDisable(GL_TEXTURE_2D);
   glViewport(0, 0, 256, 256);
 
   TotalTime := GetTotalTime;
 
   glLoadIdentity;
   glTranslatef(0, 0, -5);
   glRotatef(50 * TotalTime, 1, 0, 0);
   glRotatef(100 * TotalTime, 0, 1, 0);
   glRotatef(50 * TotalTime, 0, 0, 1);
 
   glColor3f(1, 1, 1);
   glutSolidCube(2);
 
   glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 256, 256, 0);
 
   glClearColor(0.18, 0.20, 0.66, 0);
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
   glDisable(GL_LIGHTING);
   glEnable(GL_TEXTURE_2D);
   glViewport(0, 0, AppWidth, AppHeight);
 
   glLoadIdentity;
   glTranslatef(0, 0, -7);
   glRotatef(20 * TotalTime, 1, 0, 0);
   glRotatef(50 * TotalTime, 0, 1, 0);
 
   glBegin(GL_QUADS);
     glTexCoord2f(1, 0);
     glVertex3f(2, 2, 0);
     glTexCoord2f(0, 0);
     glVertex3f(-2, 2, 0);
     glTexCoord2f(0, 1);
     glVertex3f(-2,-2, 0);
     glTexCoord2f(1, 1);
     glVertex3f(2,-2, 0);
   glEnd;
 
   glutSwapBuffers;
 end;
RenderToTexturePic1.jpg

D'abord, tout est paramétré pour la scène qui sera capturée. La fenêtre de clôture(Viewport)est réduite à 256*256 de telle façon qu'elle s'adapte à la texture et la scène est dessinée . glCopyTexImage2D est employé pour capturer la scène à la texture actuellement choisie .

Quand nous avons capturé la scène vers la texture, tout peut être effacé à nouveau, La fenêtre de clôture(viewport) peut se remettre à sa taille d'origine et la scène finale est dessinée en utilisant la scène précédente comme texture.

P.S. une texture capturée peut être enregistrée en utilisant la fonction SaveGLTextureToFile de la bibliothèque Imaging de Vampyre.

Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis le dépôt Lazarus CCR de SourceForge.

Tableau de Sommets

OpenGL est capable de faire le rendu de primitives en utilisant les données qui sont stockées dans les mémoires tampons au lieu d'appeler glVertex. Les mémoires tampons peuvent être appelées pour définir les coordonnées des sommets et celles des textures, et les couleurs (index et RGBA), les drapeaux normaux et de bord.

Dans ce tutoriel nous utiliserons seulement les mémoires tampon de sommet et de couleur, et nous montrerons du dessin non-indexé et indexé. le mode non-indexé dessine des mémoires tampon en tant que flux. Le mode indexé dessinera des éléments de la mémoire tampon dans l'ordre dans lequel cela a été défini dans l'index de la mémoire tampon. Mais assez parlé... Commençons à coder.

D'abord, définissons quelques types et constantes :

 type
   TVertex3f = record
     X, Y, Z: Single;
   end;
 
   TColor3f = record
    R, G, B: Single;
   end;
 
   VertexBuffer: array [0..5] of TVertex3f = (
     (X : 1; Y : 1; Z : 0),
     (X : -1; Y : 1; Z : 0),
     (X : -1; Y : -1; Z : 0),
     (X : 1; Y : 1; Z : 0),
     (X : -1; Y : -1; Z : 0),
     (X : 1; Y : -1; Z : 0)
   );
   ColorBuffer: array [0..5] of TColor3f = (
     (R : 1; G : 0; B : 1),
     (R : 0; G : 0; B : 1),
     (R : 0; G : 1; B : 0),
     (R : 1; G : 0; B : 1),
     (R : 0; G : 1; B : 0),
     (R : 1; G : 1; B : 0)
   );

Nous avons deux mémoires tampon. Une pour les coordonnées des sommets et une pour les couleurs des sommets. Ces 6 sommets définissent 2 triangles qui forment un rectangle.

Dessiner des primitives en utilisant les mémoires tampons est facile:

   glEnableClientState(GL_VERTEX_ARRAY);
   glEnableClientState(GL_COLOR_ARRAY);
   glVertexPointer(3, GL_FLOAT, 0, @VertexBuffer[0]);
   glColorPointer(3, GL_FLOAT, 0, @ColorBuffer[0]);
 
   glDrawArrays(GL_TRIANGLES, 0, Length(VertexBuffer));
 
   glDisableClientState(GL_VERTEX_ARRAY);
   glDisableClientState(GL_COLOR_ARRAY);

D'abord nous autorisons les mémoires tampon que nous voulons employer en utilisant la fonction glEnableClientState. Ensuite nous pouvons sélectionner les mémoires tampons que nous voulons utiliser. Chaque type de mémoire tampon a une fonction propre pour cette sélection (glColorPointer, glEdgeFlagPointer, glIndexPointer, glNormalPointer, glTexCoordPointer, glVertexPointer). Le premier paramètre de ces fonctions défini combien de nombres chaque élément contient. Par exemple , prenons la mémoire tampon des sommets. Si ce paramètre est 2 alors OpenGL compte que chaque élément de la mémoire tampon contienne les coordonnées x et y. Si ce paramètre est , par exemple , 4, alors chaque élément devrait contenir les coordonnées x, y, z et w. Le paramètre suivant défini quel type d'élément de donnée la mémoire tampon contient (GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT ou GL_DOUBLE). Le suivant défini combien il y a d'octets entre chaque élément. De cette façon, vous pouvez avoir une mémoire tampon qui contient des coordonnées de sommets et quelques données faites sur commande. Pour des types de données arbitraire, ce paramètre peut être calculé ainsi:

 type
   TBufferData = record
     DataBefore: TDataBefore;
     Vertex: TVertex;
     DataAfter: TDataAfter;
   end;
 
 Bytes between elements = SizeOf(TDataBefore) + SizeOf(TDataAfter)

Le dernier paramètre est un pointeur vers le début de la mémoire tampon.

Quand les mémoires tampons sont sélectionnées, nous pouvons les dessiner employant les fonctions glDrawArrays. Toutes les mémoires tampons autorisées sont utilisées pour dessiner les primitives. Le genre de polygones qui sont générés est défini dans le premier paramètre (pareil que avec la fonction glBegin). Les deux paramètres suivant définissent le sous-ensemble de mémoire tampon qui est utilisé pour dessiner (début et compte).

Quand les mémoires tampons ne sont pas nécessaires vous pouvez les neutraliser .

Pour démontrer le mode indexé, j'ai réalisé un classe de maillage simple qui peut charger des sommet, des couleurs et indexer les données depuis des fichiers externes:

 type
   TMesh = class
   private
     FVertices: array of TVertex3f;
     FColors: array of TColor3f;
     FIndices: array of Integer;
     procedure FreeBuffers;
   public
     constructor Create;
     destructor Destroy; override;
     procedure LoadMesh(FileName: String);
     procedure DrawMesh;
   end;

FVertices contiendra les données sur les sommets , FColors les données sur les couleurs et FIndices les données sur les index quand le fichier externe est chargé.

D'abord, nous écrirons du code qui traite la création et la destruction de classe:

 procedure TMesh.FreeBuffers;
 begin
   FVertices := nil;
   FColors := nil;
   FIndices := nil;
 end;
 
 constructor TMesh.Create;
 begin
   FreeBuffers;
 end;
 
 destructor TMesh.Destroy;
 begin
   FreeBuffers;
   inherited Destroy;
 end;

Le fichier qui contiendra les données de maillage est un simple fichier texte. La première ligne contiendra le nombre de sommets et les index séparés par un espace. Après cette ligne viendra les lignes pour chaque sommet et couleur. X, Y, Z, R, G et B toutes séparées par un espace. A la fin, il y aura les lignes des index... chaque nombre représentant un index est écrit dans sa propre ligne... ainsi , pour un triangle, le fichier de données ressemblera à ceci:

3 3
-1 -1 0 1 1 1
1 -1 0 1 1 1
0 1 0 1 1 1
0
1
2

Cela signifie qu'il y a 3 sommets et 3 index défini dans le fichier. Le premier sommer est à la position -1, -1, 0 et a comme couleur RGB 1, 1, 1 et ainsi de suite . Les index définissent cet ordre dans lequel les sommets sont dessinés (dans ce cas-ci les sommets sont dessinés dans le même ordre qu'ils sont défini ).

Le code pour charger ces données ressemblera à celui-ci:

 procedure TMesh.LoadMesh(FileName: String);
 var
   MeshFile: TextFile;
   VertexCount, IndexCount: Integer;
   iV, iI: Integer;
 begin
   FreeBuffers;
 
   AssignFile(MeshFile, FileName);
   Reset(MeshFile);
 
   ReadLn(MeshFile, VertexCount, IndexCount);
 
   SetLength(FVertices, VertexCount);
   SetLength(FColors, VertexCount);
   SetLength(FIndices, IndexCount);
 
   for iV := 0 to VertexCount - 1 do
     ReadLn(MeshFile,
       FVertices[iV].X, FVertices[iV].Y, FVertices[iV].Z,
       FColors[iV].R, FColors[iV].G, FColors[iV].B);
 
   for iI := 0 to IndexCount - 1 do
     ReadLn(MeshFile, FIndices[iI]);
 
   CloseFile(MeshFile);
 end;

Après le chargement des données, nous avons tout pour faire le dessin :

 procedure TMesh.DrawMesh;
 begin
   glEnableClientState(GL_VERTEX_ARRAY);
   glEnableClientState(GL_COLOR_ARRAY);
   glVertexPointer(3, GL_FLOAT, 0, @FVertices[0]);
   glColorPointer(3, GL_FLOAT, 0, @FColors[0]);
 
   glDrawElements(GL_TRIANGLES, Length(FIndices), GL_UNSIGNED_INT, @FIndices[0]);
 
   glDisableClientState(GL_VERTEX_ARRAY);
   glDisableClientState(GL_COLOR_ARRAY);
 end;
VertexArrayPic1.jpg

Comme vous pouvez le voir, presque tout est identique que faire un dessin non indexé, excepté la fonction qui dessine actuellement les polygones. Dans ce cas nous employons la fonction glDrawElements. Pour celle-ci, nous indiquons quel genre de polygones nous voulons, combien d'index sont dans la mémoire tampon, le type de données dans l'index de la mémoire tampon et un pointeur vers le début de l'index de la mémoire tampon.

Le code source complet vient avec le fichier de données de maillage que cette classe peut employer pour générer un rectangle qui est identique à celui qui est dessiné en utilisant le mode non-indexé. Le fichier de données de maillage ressemble à ceci:

4 6
1 1 0 1 0 1
-1 1 0 0 0 1
-1 -1 0 0 1 0
1 -1 0 1 1 0
0
1
2
0
2
3

Comme vous pouvez le voir, il y a des données pour seulement 4 sommets et 6 index. Ainsi, le premier triangle est défini par les sommets 0, 1 et 2, et le second par les sommets 0, 2 et 3. En utilisant le mode indexé, nous n'avons pas à dupliquer les sommets.

Téléchargez le code source, l'exécutable linux ou l'exécutable windows depuis le dépôt Lazarus CCR de SourceForge.