web-dev-qa-db-fra.com

GlVertexAttribPointer clarification

Je veux juste m'assurer que je comprends bien (je demanderais sur SO Chat, mais c'est mort dedans!):

Nous avons un tableau de sommets que nous rendons "courant" en le liant
alors nous avons un tampon, que nous lions à une cible
puis nous remplissons cette cible via glBufferData qui renseigne essentiellement tout ce qui était lié à cette cible, c'est-à-dire notre tampon
et ensuite nous appelons glVertexAttribPointer qui décrit comment les données sont présentées - les données étant ce qui est lié à GL_ARRAY_BUFFER et ce descripteur est sauvegardé dans notre Vertex Array original

(1) Est-ce que ma compréhension est correcte?
Le documentation est un peu clair sur la façon dont tout est en corrélation.

(2) Existe-t-il une sorte de matrice de sommets par défaut? Parce que j'ai oublié/omis glGenVertexArrays et glBindVertexArray et mon programme a bien fonctionné sans cela.


Edit: J'ai raté une étape ... glEnableVertexAttribArray.

(3) Est-ce que l'attribut de sommet est lié au tableau de sommets au moment où glVertexAttribPointer est appelé, puis nous pouvons activer/désactiver cet attribut via glEnableVertexAttribArray à tout moment, quel que soit le sommet de sommets actuellement lié?

Ou bien (3b) Est-ce que l'attribut de sommet est lié au tableau de sommets au moment où glEnableVertexAttribArray est appelé, et nous pouvons donc ajouter le même attribut de sommet à plusieurs tableaux de sommets en appelant glEnableVertexAttribArray à des moments différents, quand différents réseaux de sommets sont liés?

92
mpen

Une partie de la terminologie est un peu décalée:

  • Un Vertex Array Est juste un tableau (généralement un float[]) Qui contient des données de sommet. Il n'a pas besoin d'être lié à quoi que ce soit. Ne pas confondre avec un Vertex Array Object Ou un VAO, que je reviendrai plus tard
  • Un Buffer Object, Communément appelé Vertex Buffer Object Lors du stockage de sommets, ou VBO, est ce que vous appelez simplement un Buffer.
  • Rien n'est enregistré dans le tableau de sommets, glVertexAttribPointer fonctionne exactement comme glVertexPointer ou glTexCoordPointer, mais au lieu d'attributs nommés, vous devez fournir un nombre spécifiant votre propre attribut. . Vous transmettez cette valeur sous la forme index. Tous vos appels glVertexAttribPointer sont mis en file d'attente pour votre prochain appel à glDrawArrays ou glDrawElements. Si vous avez une liaison VAO, le VAO stockera les paramètres de tous vos attributs.

Le problème principal ici est que vous confondez les attributs de sommet avec les VAO. Les attributs de sommet ne sont que la nouvelle façon de définir les sommets, les texcoords, les normales, etc. pour le dessin. Etat des magasins VAO. Je vais d'abord expliquer le fonctionnement du dessin avec les attributs de sommet, puis expliquer comment vous pouvez réduire le nombre d'appels de méthode avec des VAO:

  1. Vous devez activer un attribut avant de pouvoir l'utiliser dans un shader. Par exemple, si vous souhaitez envoyer des sommets à un shader, vous allez probablement l'envoyer en tant que premier attribut, 0. Donc, avant de générer le rendu, vous devez l'activer avec glEnableVertexAttribArray(0);.
  2. Maintenant qu'un attribut est activé, vous devez définir les données qu'il va utiliser. Pour ce faire, vous devez lier votre VBO - glBindBuffer(GL_ARRAY_BUFFER, myBuffer);.
  3. Et maintenant, nous pouvons définir l'attribut - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);. Dans l'ordre du paramètre: 0 est l'attribut que vous définissez, 3 est la taille de chaque sommet, GL_FLOAT Est le type, GL_FALSE Signifie ne pas normaliser chaque sommet, la moyenne des deux derniers zéros qu'il n'y a pas de foulée ou d'offset sur les sommets.
  4. Dessine quelque chose avec elle - glDrawArrays(GL_TRIANGLES, 0, 6);
  5. La prochaine chose que vous dessinez peut ne pas utiliser l'attribut 0 (ce sera réaliste, mais ceci est un exemple), nous pouvons donc le désactiver - glDisableVertexAttribArray(0);

Enveloppez cela dans les appels glUseProgram() et vous obtenez un système de rendu qui fonctionne correctement avec les shaders. Mais disons que vous avez 5 attributs différents, sommets, texcoords, normales, couleurs et coordonnées lightmap. Tout d'abord, vous ne feriez qu'un seul appel glVertexAttribPointer pour chacun de ces attributs et vous devrez préalablement activer tous les attributs. Supposons que vous définissiez les attributs 0 à 4 tels qu'ils sont énumérés. Vous les activeriez tous comme ceci:

for (int i = 0; i < 5; i++)
    glEnableVertexAttribArray(i);

Et vous devrez alors lier différents VBO pour chaque attribut (à moins que vous ne les stockiez tous dans un seul VBO et utilisiez des décalages/stride), vous devez alors effectuer 5 appels différents glVertexAttribPointer, à partir de glVertexAttribPointer(0,...); to glVertexAttribPointer(4,...); pour les sommets aux coordonnées lightmap, respectivement.

Espérons que ce système seul a du sens. Je vais maintenant passer aux VAO pour leur expliquer comment les utiliser pour réduire le nombre d'appels de méthode lors de ce type de rendu. Notez que l'utilisation d'un ordinateur VAO n'est pas nécessaire.

Un Vertex Array Object Ou un VAO est utilisé pour stocker l'état de tous les appels glVertexAttribPointer et des VBO ciblés lors de chacun des appels glVertexAttribPointer.

Vous en générez un avec un appel à glGenVertexArrays. Pour stocker tout ce dont vous avez besoin dans un VAO, associez-le avec glBindVertexArraypuis faites un appel de tirage complet. Tous les dessiner les appels de liaison sont interceptés et stockés par le VAO. Vous pouvez dissocier le VAO avec glBindVertexArray(0);

Maintenant, lorsque vous voulez dessiner l'objet, vous n'avez pas besoin de rappeler toutes les liaisons VBO ou les appels glVertexAttribPointer, il vous suffit de lier le VAO avec glBindVertexArray, puis d'appeler glDrawArrays ou glDrawElements et vous dessinerez exactement la même chose que si vous utilisiez tous ces appels de méthode. Vous voudrez probablement également dissocier le VAO par la suite.

Une fois que vous avez dissocié le VAO, tous les états retrouvent leur état antérieur à la liaison du VAO. Je ne sais pas si les modifications que vous apportez pendant que le VAO est lié sont conservées, mais cela peut facilement être compris avec un programme de test. Je suppose que vous pouvez imaginer que glBindVertexArray(0); soit lié au VAO "par défaut" ...


Mise à jour: Quelqu'un m'a signalé le besoin d'un appel de tirage au sort. En fin de compte, vous n'avez pas besoin de faire un appel de tirage COMPLET lors de la configuration du VAO, mais seulement de tout ce qui vous lie. Je ne sais pas pourquoi je pensais que c'était nécessaire plus tôt, mais c'est corrigé maintenant.

207
Robert Rouhani

La terminologie et la séquence des API à appeler sont assez déroutantes. Ce qui est encore plus déroutant, c'est comment les différents aspects - tampon, attribut de sommet générique et variable d'attribut shader sont associés. Voir Terminologie OpenGL pour une très bonne explication.

De plus, le lien OpenGL-VBO, shader, VAO montre un exemple simple avec les appels d'API nécessaires. C'est particulièrement intéressant pour ceux qui passent du mode immédiat au pipeline programmable.

J'espère que ça aide.

Edit: Comme vous pouvez le constater à partir des commentaires ci-dessous, les utilisateurs peuvent émettre des hypothèses et avancer rapidement aux conclusions. La réalité est que c'est assez déroutant pour les débutants.

2
ap-osd