Les extensions OpenGL en VBA et VB6

Image non disponible


précédentsommairesuivant

VI. Les Vertex Buffer Objects

Pour plus d'informations sur les Vertex Buffer Object (VBO), consultez ces articles :

Vous pouvez utiliser les VBO pour accélérer le rendu de scènes complexes.
Ceux-ci étant apparus avec la version 1.5, ils sont supportés par de nombreuses cartes graphiques (même sur portable ou ordinateur de bureautique).

Commençons tout de même par vérifier que la carte graphique supporte les VBO, en plaçant ce code avant l'appel à InitScene :

Teste le support des VBO
Sélectionnez
' Teste si les VBO sont supportés, soit par la version, soit l'extension ARB
If val(LongToString(glGetString(GL_VERSION))) < 1.5 And InStr(LongToString(glGetString(GL_EXTENSIONS)), "GL_ARB_vertex_buffer_object") = 0 Then
    MsgBox "VBO non supportés", vbOKOnly
    Exit Function 
End If

Bien entendu, Exit Function est à remplacer par Exit Sub si le test est codé dans la procédure Main d'une application VB6.
Les VBO sont supportés à partir de la version 1.5, ou dans l'extension GL_ARB_vertex_buffer_object.
Si on n'est pas dans ce cas, on affiche un message et on quitte la fonction.

Importez maintenant le module ModOpenGL_1_5 qui contient les fonctions de la version 1.5.
N'oubliez pas de « mapper » ensuite les fonctions en exécutant la procédure RemapVBToGL de ce module.

Initialisation des fonctions
Sélectionnez
' Initialise les fonctions
ModOpenGL_1_5.RemapVBToGL

Nous voici prêts à programmer les VBO !

VI-A. Préparation des tableaux de données

Pour utiliser les VBO, il faut mettre les données du cube en tableaux.

Pour remplir facilement des tableaux, ajoutez la fonction BuildArray vue dans le premier tutoriel. :

Fonction pour remplir un tableau
Sélectionnez
' Fonction pour remplir un tableau
Public Function BuildArray(parray, pValues As Variant)
Dim lcpt As Long
Dim lIndice As Long
lIndice = LBound(parray)
For lcpt = LBound(pValues) To UBound(pValues)
    parray(lIndice) = pValues(lcpt)
    lIndice = lIndice + 1
Next
End Function

Les tableaux de données sont initialisés à la fin de la procédure InitScene :

Déclaration des tableaux
Sélectionnez
' Déclaration des tableaux
Dim CubeArray(1 To 144) As Single
Dim IndiceArray(1 To 36) As Long

CubeArray est un tableau contenant les coordonnées des vertex (les sommets) et leurs couleurs.
Il y a pour chaque sommet trois coordonnées et trois composantes de couleurs.
On définit quatre vertex par face, car chaque sommet est utilisé par plusieurs faces avec une couleur différente.
Donc le tableau CubeArray a une taille de (3 coordonnées + 3 couleurs) * 4 sommets * 6 faces = 144 vertex.

IndiceArray est un tableau d'indices qui va désigner chaque vertex du tableau CubeArray qu'il faut utiliser pour dessiner le cube.
Nous allons dessiner ce cube à base de triangles : le triangle étant l'élément de base géré par les cartes graphiques, il est le plus rapide à afficher.
Chaque face du cube est composée de deux triangles de trois sommets.
Donc le tableau IndiceArray a une taille de 6 faces * 2 triangles * 3 sommets = 36 indices.

Voici l'initialisation de ces deux tableaux :

Initialisation des tableaux
Sélectionnez
' Initialisation des tableaux
BuildArray CubeArray, Array( _
    -1, -1, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, -1, 1, 1, 1, 1, 0, _
    1, 1, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, -1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, _
    -1, -1, -1, 1, 0.5, 0, 1, -1, -1, 1, 0.5, 0, 1, -1, 1, 1, 0.5, 0, -1, -1, 1, 1, 0.5, 0, _
    1, -1, -1, 1, 0, 0, -1, -1, -1, 1, 0, 0, -1, 1, -1, 1, 0, 0, 1, 1, -1, 1, 0, 0, _
    -1, 1, 1, 1, 0, 1, -1, 1, -1, 1, 0, 1, -1, -1, -1, 1, 0, 1, -1, -1, 1, 1, 0, 1, _
    1, 1, -1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, -1, 1, 0, 0, 1, 1, -1, -1, 0, 0, 1)
            
BuildArray IndiceArray, Array(0, 1, 2, 0, 2, 3, _
                            4, 5, 6, 4, 6, 7, _
                            8, 9, 10, 8, 10, 11, _
                            12, 13, 14, 12, 14, 15, _
                            16, 17, 18, 16, 18, 19, _
                            20, 21, 22, 20, 22, 23)

Petite explication de ces tableaux, avec en exemple les premières lignes qui correspondent à la face avant :

Image non disponible

En haut de l'image le tableau CubeArray contient les coordonnées des sommets (en rouge) et leur couleur (en bleu).

Notez qu'on utilise un tableau entrelacé : c'est-à-dire qu'on alterne coordonnées et couleur.
La face avant est positionnée en z = 1, donc la troisième coordonnée est toujours 1.
La face est jaune, donc la couleur est composée de rouge et de vert => (1, 1, 0).
On définit ainsi quatre sommets, dont la couleur est jaune.

En bas à droite le tableau IndiceArray contient les indices des sommets qui composent les triangles.
Attention, le premier sommet ne commence pas à 1 mais à 0.

Le premier triangle est composé des points d'indice 0, 1 et 2 ; c'est le triangle en bas à droite.
Vous remarquez la flèche verte qui montre que les points sont définis dans le sens inverse au sens horaire.
La face avant du triangle est donc celle que l'on voit.
Il est important de faire attention à l'orientation des faces, notamment pour le culling (masquage des faces arrière par exemple pour accélérer le rendu).

Le deuxième triangle est composé des points d'indice 0, 2 et 3.
Nous utilisons donc plusieurs fois les mêmes vertex.

VI-B. Création des buffers

Chacun des tableaux va être envoyé dans un buffer.
Les identifiants de ces buffers sont stockés dans un tableau, que nous déclarons en en-tête de module :

Décalaration des buffers
Sélectionnez
' Buffers pour le cube
Dim CubeBuffers(1 To 2) As Long

Retournons ensuite à la fin de la procédure InitScene.
À la suite du code d'initialisation des tableaux, générez deux buffers :

Génération des buffers
Sélectionnez
' Crée les buffers pour le cube
glGenBuffers 2, CubeBuffers(1)

On demande la création de deux buffers, et on donne le premier buffer en paramètre :

  • CubeBuffers(1) contient l'identifiant du premier buffer ;
  • CubeBuffers(2) contient l'identifiant du deuxième buffer.

Ces buffers sont vides, il faut ensuite les remplir avec la fonction glBufferData.

Avant d'utiliser la fonction glBufferData, nous devons « activer » le buffer souhaité avec la fonction glBindBuffer.

Cela nous donne pour le premier buffer qui contient les données de vertex et de couleurs :

Données de vertex et couleurs
Sélectionnez
' Données de vertex / couleur
glBindBuffer GL_ARRAY_BUFFER, CubeBuffers(1)
glBufferData GL_ARRAY_BUFFER, UBound(CubeArray) * Len(CubeArray(1)), VarPtr(CubeArray(1)), GL_STATIC_DRAW
glBindBuffer GL_ARRAY_BUFFER, 0&

GL_ARRAY_BUFFER désigne un tableau de données : vertex, couleurs, normales, coordonnées de textures…

Le deuxième paramètre de la fonction glBufferData est la taille des données, en bytes.
Cette taille est calculée en multipliant le nombre de données dans le tableau par la taille d'un élément du tableau.

Le troisième paramètre de la fonction glBufferData est un pointeur vers le premier élément du tableau.
Ce pointeur est obtenu grâce à la fonction VarPtr.

Le dernier paramètre de la fonction glBufferData est le type d'utilisation des données du buffer.
GL_STATIC_DRAW désigne une utilisation du buffer en lecture seule.
Nous verrons ensuite une utilisation dynamique d'un buffer.

On procède de la même manière pour remplir le buffer d'indices, mais avec un type de tableau GL_ELEMENT_ARRAY_BUFFER (car c'est un tableau d'indices) :

Données d'indices
Sélectionnez
' Données d'indices
glBindBuffer GL_ELEMENT_ARRAY_BUFFER, CubeBuffers(2)
glBufferData GL_ELEMENT_ARRAY_BUFFER, UBound(IndiceArray) * Len(IndiceArray(1)), VarPtr(IndiceArray(1)), GL_STATIC_DRAW
glBindBuffer GL_ELEMENT_ARRAY_BUFFER, 0&

Une fois les buffers remplis, les tableaux IndiceArray et CubeArray ne sont plus utilisés.

Il ne faut pas oublier de supprimer les buffers lorsqu'ils ne sont plus utiles.

Supprime les buffers
Sélectionnez
' Supprime les buffers
glDeleteBuffers 2, CubeBuffers(1)

Pour notre application il n'est pas utile de supprimer les buffers.
Lorsqu'on ferme la fenêtre et que la fonction glutMainLoop nous rend la main, les buffers sont déjà supprimés.

Pour s'en assurer il suffit de tester après glutMainLoop si les identifiants dans le tableau CubeBuffers sont des buffers valides.

Teste les buffers
Sélectionnez
' Teste les buffers
MsgBox "Buffer 1 : " & glIsBuffer(CubeBuffers(1))
MsgBox "Buffer 2 : " & glIsBuffer(CubeBuffers(2))
Résultat
Sélectionnez
Buffer 1 : 0
Buffer 2 : 0

VI-C. Utilisation des buffers

L'utilisation des buffers est très similaire à l'utilisation des tableaux vue dans le premier tutoriel.

La différence est que les données ne sont plus stockées dans un tableau, mais dans un buffer.
Lorsqu'on appelle les fonctions, on ne donne donc plus en paramètre un tableau, mais une position dans le buffer actif.

Avant d'écrire le nouveau code de dessin du cube, supprimez les anciennes lignes situées dans la procédure Render (de glBegin à glEnd inclus).

Nous allons écrire à la place le code avec VBO.

Il faut d'abord activer les tableaux utilisés, pour notre cas on active les tableaux de vertex et de couleurs :

Activation des tableaux
Sélectionnez
' Début de dessin du cube
' Activation des tableaux
glEnableClientState GL_VERTEX_ARRAY
glEnableClientState GL_COLOR_ARRAY

Ensuite on positionne les pointeurs pour indiquer où sont les données de vertex et de couleurs.
Ces données étant dans le premier buffer, on l'active au préalable :

Position des données à utiliser
Sélectionnez
' Position des données à utiliser
glBindBuffer GL_ARRAY_BUFFER, CubeBuffers(1)
glVertexPointer 3, GL_FLOAT, (3 + 3) * 4, ByVal 0&
glColorPointer 3, GL_FLOAT, (3 + 3) * 4, ByVal 12&

Les fonctions glVertexPointer et glColorPointer méritent une explication.

1er paramètre = 3 : c'est le nombre d'informations par sommet : trois coordonnées ou trois composantes de couleur.

2e paramètre = GL_FLOAT : c'est le type des données contenues dans le buffer (GL_FLOAT = Single).

3e paramètre = (3 + 3) * 4 : c'est la taille de données entre deux sommets.
Il y a 3 coordonnées + 3 composantes de couleur, que l'on multiplie par la taille d'un élément du tableau.
Ce 3e paramètre, nommé stride est très important dans ce cas où le tableau est entrelacé.
Si on avait utilisé un tableau par type de données, ce paramètre serait égal à zéro.

4e paramètre = 0& ou 12& : c'est le décalage des informations, en bytes.
Les vertex commencent au début du tableau donc avec un décalage de 0, alors que les coordonnées commencent après les trois premiers éléments, donc un décalage de 3*4=12 (car chaque élément occupe quatre bytes).

Notez l'utilisation de ByVal pour le dernier paramètre afin de bien passer la valeur en paramètre.

À ce stade on a défini un buffer de données avec GL_ARRAY_BUFFER.
Ce buffer serait suffisant si on n'utilisait pas de buffer d'indices : on utiliserait alors la fonction glDrawArrays pour dessiner.

Mais ici nous utilisons un buffer d'indices, c'est le deuxième buffer que nous activons :

Utilisation des indices
Sélectionnez
' Utilisation des indices
glBindBuffer GL_ELEMENT_ARRAY_BUFFER, CubeBuffers(2)

Et enfin on peut exécuter la commande de dessin :

Dessine le cube
Sélectionnez
' Dessine le cube
glDrawElements GL_TRIANGLES, 36, GL_UNSIGNED_INT, ByVal 0&

Cette fonction demande l'affichage :

  • de triangles (GL_TRIANGLES) ;
  • au nombre de 12 (composés de 36 indices) ;
  • à partir de données d'indices de type Long (GL_UNSIGNED_INT) ;
  • et sans décalage dans le buffer d'indices (ByVal 0&).

Enfin, il faut désactiver l'utilisation des tableaux et buffers :

Désactive les tableaux et buffers
Sélectionnez
' Désactivation des tableaux
glDisableClientState GL_VERTEX_ARRAY
glDisableClientState GL_COLOR_ARRAY
' Désactive les buffers
glBindBuffer GL_ARRAY_BUFFER, 0&
glBindBuffer GL_ELEMENT_ARRAY_BUFFER, 0&

Voici le cube dessiné avec les VBO :

Image non disponible

VI-D. Modification des buffers

Il est également possible de modifier dynamiquement le contenu des buffers.

Pour cela on récupèrera la position des données en mémoire avec la fonction glMapBuffer.

Avant toute chose, il faut définir le type de buffer afin qu'il soit modifiable.

GL_STATIC_DRAW est utilisé pour un buffer statique, donc jamais modifié.
GL_STREAM_DRAW est utilisé pour un buffer que l'on modifie une fois par affichage.
GL_DYNAMIC_DRAW est utilisé pour un buffer dynamique que l'on modifie souvent, plusieurs fois par affichage.

Remplacez GL_STATIC_DRAW par GL_STREAM_DRAW pour le tableau CubeArray :

Données de vertex et de couleurs modifiables
Sélectionnez
glBufferData GL_ARRAY_BUFFER, UBound(CubeArray) * Len(CubeArray(1)), VarPtr(CubeArray(1)), GL_STREAM_DRAW

Juste avant l'utilisation du buffer d'indices, on va boucler sur les données pour modifier dynamiquement leur couleur.

Il nous faut donc un pointeur (lPtr), un compteur (lCpt) et un tableau de composantes de couleur (lColor) :

Déclaration
Sélectionnez
#If VBA7 Then
Dim lPtr As LongPtr
Dim lCpt As LongPtr
#Else
Dim lPtr As Long
Dim lCpt As Long
#End If
Dim lColor(1 To 3) As Single

Les variables lPtr et lCpt sont des pointeurs et doivent donc être déclarés en fonction de l’environnement d’exécution (LongPtr ou Long).

On lit ensuite la position des données du buffer :

Position des données
Sélectionnez
lPtr = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE)

Puis on boucle sur les données, on les lit et on les modifie :

Boucle de traitement des données
Sélectionnez
' Boucle sur les données
For lCpt = 1 To 24
    ' Lecture des données de couleur
    RtlMoveMemory lColor(1), ByVal lPtr + (lCpt - 1) * (3 + 3) * 4 + 3 * 4, 3 * 4
    ' Atténuation de la couleur
    lColor(1) = lColor(1) - 0.005
    lColor(2) = lColor(2) - 0.005
    lColor(3) = lColor(3) - 0.005
    ' Écriture des nouvelles données de couleur
    RtlMoveMemory ByVal lPtr + (lCpt - 1) * (3 + 3) * 4 + 3 * 4, lColor(1), 3 * 4
Next

On boucle de 1 à 24, car on a 24 sommets.
Le tableau lColor reçoit les composantes de couleurs.
Pour lire le buffer, on utilise la fonction RtlMoveMemory (déclarée dans le module ModOpenGLTools).
Cette fonction permet de copier des zones de la mémoire.
Notez l'utilisation du ByVal lorsqu'on utilise un pointeur.

Enfin on libère le buffer avec glUnmapBuffer :

Libère le buffer
Sélectionnez
' Libère le buffer
glUnmapBuffer GL_ARRAY_BUFFER

L'exécution de ce projet affiche un cube dont les couleurs sont progressivement atténuées :

Image non disponible

Il est bien sûr possible de modifier également les coordonnées des sommets.
Il suffit de modifier les données de vertex (décalage 0 au lieu de 3*4) à la place des données de couleurs.

On obtient ainsi des objets dont la géométrie peut varier, tout en conservant une grande rapidité d'exécution.


précédentsommairesuivant

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

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2009 Thierry GASPERMENT. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.