0. Introduction

Souvenez-vous... Il fut un temps où, pour optimiser son application OpenGL, certains recommandaient l'utilisation des display lists. Les display lists permettaient de « sauvegarder » plusieurs commandes dans un seul et même objet, ce qui nous économisait à la fois des lignes de code et surtout permettait à OpenGL d'optimiser certaines opérations puisqu'il savait déjà à l'avance ce qu'il devait faire. Et bien, les vertex array objects, c'est un peu la même chose, sauf que c'est une fonctionnalité toute nouvelle venue avec OpenGL 3.0 (en fait, cette extension, GL_ARB_vertex_array_object, est basée sur l'extension GL_APPLE_vertex_array_object). Vous allez voir, elle est extrêmement facile à utiliser !

I. Quand utiliser cette extension ?

C'est simple : chaque fois que vous utilisez des VBO, je vous recommande d'utiliser cette extension, puisque les arguments qui s'appliquaient aux display lists s'appliquent également aux VAO. D'après les premiers retours des forums, le gain de performance est relativement faible (mais réel), mais il est possible que les drivers utilisent bien mieux cette fonctionnalité dans le futur afin d'augmenter les performances. Dans tous les cas, elle a au moins le mérite de simplifier l'utilisation des VBO.

Reprenons un bout de code source du tutoriel précédent :

 
Sélectionnez
// On commence par activer le shader
glUseProgram (shaderProgram);

// On envoit la variable uniform
glUniformMatrix4fv (uniformID, 1, GL_FALSE, worldViewProjectionMatrix.GetMatrixForOpenGL());

// On active les deux tableaux génériques
glEnableVertexAttribArray (vertexPositionAttrib);
glEnableVertexAttribArray (vertexColorAttrib);

// On active le buffer des vertices
glBindBuffer(GL_ARRAY_BUFFER, cubeBuffers[0]);

// On envoie les données
glVertexAttribPointer (vertexColorAttrib, 3, GL_FLOAT, GL_FALSE, sizeof (float) * 6, (float *) NULL + (0));
glVertexAttribPointer (vertexPositionAttrib, 3, GL_FLOAT, GL_FALSE, sizeof (float) * 6, (float *) NULL + (3));

// On active le buffer des indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cubeBuffers[1]);

// Rendu de notre géométrie
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

// On désactive les tableaux génériques
glDisableVertexAttribArray (vertexPositionAttrib);
glDisableVertexAttribArray (vertexColorAttrib);

glDrawElements (GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

// On désactive le shader
glUseProgram (0);

Ce code est utilisé à chaque boucle et avouez qu'il est plutôt redondant. Une fois les buffers et les shaders créés, on active toujours les mêmes tableaux génériques, on active toujours les mêmes buffers, et on envoie toujours les mêmes données. Bref, très long ! Heureusement, les VAO viennent à notre rescousse !

II. Utilisation des VAO

La section qui suit va détailler la création et l'utilisation des vertex array objects.

II-A. Création d'un VAO

Comme tout le temps avec OpenGL, on doit utiliser une fonction glGen* pour créer le VAO. Ici, rien de plus simple, et voici le prototype de cette fonction :

 
Sélectionnez
void glGenVertexArrays( sizei n, uint *arrays );

Le premier paramètre est le nombre de VAO que vous voulez générer, et le second un tableau d'entiers non signés.

II-B. Destruction d'un VAO

Pour détruire un VAO, il suffit d'appeler la fonction glDeleteVertexArrays dont le prototype est le suivant :

 
Sélectionnez
void glDeleteVertexArrays( sizei n, const uint *arrays );

II-C. Activation et désactivation d'un VAO

Encore une fois, rien de bien compliqué pour activer et désactiver un VAO. Tout se fait grâce à cette fonction :

 
Sélectionnez
void glBindVertexArray (uint array) ;

Pour activer un VAO, il suffit de donner à cette fonction l'identifiant du VAO que vous avez construit avec la fonction glGenVertexArrays, et pour le désactiver il suffit de lui passer le paramètre 0.

II-D. Et après, on en fait quoi ?

Encore une fois, leur utilisation est d'une simplicité enfantine. En gros, il suffit d'activer votre VAO, appeler toutes les commandes que vous voulez « stocker », et le désactiver. Puis, lorsque vous réactiverez votre VAO, toutes les commandes qui y ont été stockées seront automatiquement appelées. Reste à connaître maintenant les fonctions valides que vous pouvez appeler dedans.

Pour rester concret, vous pouvez y mettre tout ce qui concerne les états des tableaux de sommets. Ceci comprend donc l'activation des tableaux génériques (via la fonction glEnableVertexAttribArray), l'activation des différents buffers (avec la fonction glBindBuffer) et enfin l'envoi des données grâce à la fonction glVertexAttribPointer ou glVertexAttribIPointer. Par contre, sachez que vous ne pouvez pas y ajouter les fonctions de dessin (comme glDrawElements) car cela ne concerne pas directement l'état des sommets.

Ainsi, et en reprenant notre exemple de code mis plus haut, voici une fonction initialisant un VAO :

 
Sélectionnez
void CreateVAO ()
{
// Création du VAO
glGenVertexArrays (1, &vertexArrayObject);

// On bind le VAO
glBindVertexArray (vertexArrayObject);

// Et on appelle toutes les fonctions qui modifient l'état des tableaux de sommets

glEnableVertexAttribArray (vertexPositionAttrib);
glEnableVertexAttribArray (vertexColorAttrib);

// On active le buffer des vertices
glBindBuffer(GL_ARRAY_BUFFER, cubeBuffers[0]);

// On envoie les données
glVertexAttribPointer (vertexColorAttrib, 3, GL_FLOAT, GL_FALSE, sizeof (float) * 6, (float *) NULL + (0));
glVertexAttribPointer (vertexPositionAttrib, 3, GL_FLOAT, GL_FALSE, sizeof (float) * 6, (float *) NULL + (3));

// On active le buffer des indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cubeBuffers[1]);

// On unbind le VAO, tout ceci est enregistré
glBindVertexArray (0);
}

Cette fonction n'est à appeler qu'une seule fois, et notre fonction de dessin, que l'on appelle à chaque boucle, est donc réduite à ça :

 
Sélectionnez
// On commence par activer le shader
glUseProgram (shaderProgram);

// On envoit la variable uniform
glUniformMatrix4fv (uniformID, 1, GL_FALSE, worldViewProjectionMatrix.GetMatrixForOpenGL());

glBindVertexArray (vertexArrayObject);

glDrawElements (GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

// On peut éventuellement désactiver les tableaux génériques car on a pas enregistré ces commandes dans le VAO, mais ceci n'est
// pas nécessaire pour une application 100% shader qui respecte OGL 3.x
glBindVertexArray (0);

// On désactive le shader
glUseProgram (0);

Jolie économie n'est-ce pas ?

III. Conclusion

Ce tutoriel vous a donc présenté l'utilisation des vertex array objects. Comme vous l'avez remarqué, leur utilisation est extrêmement aisée et permet de simplifier énormément le code. Alors, pourquoi s'en priver ?

IV. Sources du programme

Les sources sont disponibles à cette adresse : ftp://ftp-developpez.com/bakura/tutoriels/jeux/VAO.zip

V. Remerciements

Merci à IrmatDen pour sa relecture.