Tutoriel 10 - Dessins indexés

Par OGLdev, traduit par DragonJoker



Introduction

Après les dessins ordonnés, les dessins indexés.

Contexte

OpenGL fournit diverses fonctions de dessins. glDrawArrays, que nous avons utilisé jusqu'à présent, est classée dans la catégorie des « dessins ordonnés ». Cela signifie que le tampon de sommets est parcouru à partir de l'indice spécifié, et pour chaque X (1 pour les points, 2 pour les lignes…) sommets, une primitive est générée. C'est très simple d'utilisation, cependant le revers de la pièce est que si un sommet est utilisé dans plusieurs primitives, alors il doit être présent plusieurs fois dans le tampon de sommets. C'est à dire qu'il n'y a pas de concept de partage de sommets. Le partage de sommets est fourni par les fonctions de dessins appartenant à la catégorie des « dessins indexés ». Là, en plus du tampon de sommets, il y a aussi un tampon d'indices qui contient les indices dans le tampon de sommets. Le parcours du tampon d'indices est similaire au parcours du tampon de sommets : tous les X indices, une primitive est générée. Pour vous exercer au partage de sommets, vous pouvez simplement répéter l'indice du sommet partagé plusieurs fois. Le partage de sommets est très important en termes d'occupation mémoire car la plupart des objets sont représentés par des maillages fermés de triangles, et la plupart des sommets prend part dans la composition de plus d'un triangle.

Voici un exemple de dessin ordonné :

Si nous effectuons un rendu de triangles, le GPU va générer la liste suivante : V0/1/2, V3/4/5 V6/7/8, etc…

Voici un exemple de dessin indexé :

Dans ce cas le GPU va générer les triangles suivants : V4/0/1, V5/2/1, V6/1/7, etc…

L'utilisation de dessins indexés en OpenGL requiert la génération et le remplissage d'un tampon d'indices. Ce tampon doit être lié en plus du tampon de sommets avant la fonction de dessin et une API différente doit être utilisée.

Explication du code

GLuint IBO;

Nous avons ajouté un nouvel identifiant pour le tampon d'indices.

Vertices[0] = Vector3f(-1.0f, -1.0f, 0.0f);
Vertices[1] = Vector3f(0.0f, -1.0f, 1.0f);
Vertices[2] = Vector3f(1.0f, -1.0f, 0.0f);
Vertices[3] = Vector3f(0.0f, 1.0f, 0.0f);

Pour montrer le partage de sommets, nous avons besoin d'un maillage un peu plus complexe. De nombreux tutoriels utilisent le fameux cube tournant pour cela. Il requiert 8 sommets et 12 triangles. Comme je suis fainéant, j'utilise la pyramide tournante à la place. Elle requiert uniquement 4 sommets et 4 triangles, ce qui est beaucoup plus rapide à générer manuellement.

Lorsqu'on regarde ces sommets de dessus (le long de l'axe Y), nous voyons le schéma suivant :


unsigned int Indices[] = { 0, 3, 1,
                           1, 3, 2,
                           2, 3, 0,
                           0, 1, 2 };

Le tampon d'indices est rempli en utilisant un tableau d'indices. Les indices correspondent à la position des sommets dans le tampon d'indices. Lorsqu'on regarde le tableau et le schéma au-dessus, nous pouvons voir que le dernier triangle est la base de la pyramide tandis que les trois autres constituent ses faces. La pyramide n'est pas symétrique mais est très facile à spécifier.

glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);

Nous créons et remplissons le tampon d'indices en utilisant notre tableau. Vous pouvez voir que la seule différence entre la création d'un tampon de sommets et la création d'un tampon d'indices et que le tampon de sommets prend GL_ARRAY_BUFFER en tant que type de tampon alors que le tampon d'indices prend GL_ELEMENT_ARRAY_BUFFER.

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);

En plus de lier le tampon de sommets, nous devons aussi lier le tampon d'indices avant de dessiner. Là encore, nous utilisons GL_ELEMENT_ARRAY_BUFFER comme type de tampon.

glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);

Nous utilisons glDrawElements au lieu de glDrawArrays. Le premier paramètre est le type de primitives à rendre (comme pour glDrawArrays). Le second paramètre est le nombre d'indices parmi ceux contenus dans le tampon d'indices à utiliser pour la génération des primitives. Le paramètre suivant est le type de chaque indice. Le GPU doit être informé de la taille de chaque indice sinon il ne saura pas comment parcourir le tampon. Les valeurs possibles ici sont GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT. Si l'intervalle d'indices est petit vous voudrez le plus petit type de données possible qui prend moins de place en mémoire, et si l'intervalle d'indices est grand, vous voudrez les types les plus larges. Le dernier paramètre dit au GPU le point de départ en octets à partir duquel il commence le parcourt du tampon. C'est utile quand le même tampon d'indices contient les indices de plusieurs objets. En spécifiant un point de départ et un nombre d'indices, vous dites au GPU quel objet dessiner. Dans notre cas nous voulons commencer au début, donc nous spécifions zéro. Notez que le type du dernier paramètre est Glvoid*, ainsi, si vous voulez spécifier autre chose que zéro, vous devrez le convertir dans ce type.

Remerciements

Merci à Etay Meiri de me permettre de traduire ses tutoriels.

Résultat :
resultat

Article d'origine