clarification de glVertexAttribPointer

94

Je veux juste m'assurer de bien comprendre cela (je demanderais sur SO Chat, mais c'est mort là-dedans!):

Nous avons un Vertex Array, que nous rendons "courant" en le liant
puis nous avons un Buffer, que nous lions à une cible
puis nous remplissons cette cible via glBufferData qui remplit essentiellement tout ce qui était lié à cette cible, c'est-à-dire notre Buffer
puis nous appelons glVertexAttribPointerqui décrit comment les données sont disposées - les données étant ce à quoi elles sont liées GL_ARRAY_BUFFER et ce descripteur est enregistré dans notre Vertex Array d'origine

(1) Ma compréhension est-elle correcte?
La documentation est un peu clairsemée sur la façon dont tout est corrélé.

(2) Existe-t-il une sorte de Vertex Array par défaut? Parce que j'ai oublié / omis glGenVertexArrayset glBindVertexArrayque mon programme fonctionnait bien sans cela.


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

(3) L'attribut Vertex est-il lié au Vertex Array au moment où il glVertexAttribPointerest appelé, puis nous pouvons activer / désactiver cet attrib via glEnableVertexAttribArrayà tout moment, quel que soit le Vertex Array actuellement lié?

Ou (3b) L'attribut Vertex est-il lié au tableau Vertex au moment où glEnableVertexAttribArrayest appelé, et ainsi nous pouvons ajouter le même attribut Vertex à plusieurs tableaux Vertex en appelant glEnableVertexAttribArrayà des moments différents, lorsque différents tableaux Vertex sont liés?

mpen
la source

Réponses:

210

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

  • A Vertex Arrayest juste un tableau (généralement a float[]) qui contient des données de sommet. Il n'a besoin d'être lié à rien. A ne pas confondre avec a Vertex Array Objectou VAO, que je reviendrai plus tard
  • A Buffer Object, communément appelé a Vertex Buffer Objectlors du stockage de sommets, ou VBO en abrégé, est ce que vous appelez simplement un Buffer.
  • Rien n'est enregistré dans le tableau de sommets, glVertexAttribPointerfonctionne exactement comme glVertexPointerou glTexCoordPointerfonctionne, juste au lieu d'attributs nommés, vous obtenez un nombre qui spécifie votre propre attribut. Vous passez cette valeur comme index. Tous vos glVertexAttribPointerappels sont mis en file d'attente pour la prochaine fois que vous appelez glDrawArraysou glDrawElements. Si vous avez une liaison VAO, le VAO stockera les paramètres pour tous vos attributs.

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

  1. Vous devez activer un attribut avant de pouvoir l'utiliser dans un shader. Par exemple, si vous voulez envoyer des sommets à un shader, vous allez probablement l'envoyer comme premier attribut, 0. Donc, avant de rendre, 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_FLOATest le type, GL_FALSEsignifie ne pas normaliser chaque sommet, les 2 derniers zéros signifient qu'il n'y a pas de foulée ou de décalage sur les sommets.
  4. Dessinez quelque chose avec - glDrawArrays(GL_TRIANGLES, 0, 6);
  5. La prochaine chose que vous dessinez peut ne pas utiliser l'attribut 0 (de manière réaliste, mais c'est un exemple), nous pouvons donc le désactiver - glDisableVertexAttribArray(0);

Enveloppez cela dans les glUseProgram()appels et vous avez un système de rendu qui fonctionne correctement avec les shaders. Mais disons que vous avez 5 attributs, sommets, texcoords, normales, couleurs et coordonnées lightmap différents. Tout d'abord, vous feriez un seul glVertexAttribPointerappel pour chacun de ces attributs et vous devrez activer tous les attributs au préalable. Supposons que vous définissiez les attributs 0-4 tels que je les ai répertoriés. Vous les activeriez tous comme ceci:

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

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

Espérons que ce système à lui seul a du sens. Je vais maintenant passer aux VAO pour 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 VAO n'est pas nécessaire.

A Vertex Array Objectou VAO est utilisé pour stocker l'état de tous les glVertexAttribPointerappels et des VBO ciblés lors de chacun des glVertexAttribPointerappels.

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

Maintenant, lorsque vous voulez dessiner l'objet, vous n'avez pas besoin de rappeler toutes les liaisons VBO ou les glVertexAttribPointerappels, il vous suffit de lier le VAO avec glBindVertexArraypuis d'appeler glDrawArraysou glDrawElementset vous dessinerez exactement la même chose que si vous faisaient tous ces appels de méthode. Vous voudrez probablement aussi dissocier le VAO par la suite.

Une fois que vous avez dissocié le VAO, tout l'état revient à ce qu'il était avant 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 penser glBindVertexArray(0);que cela est lié au VAO "par défaut" ...


Mise à jour: Quelqu'un a attiré mon attention sur la nécessité de l'appel de tirage au sort. En fait, vous n'avez pas vraiment besoin de faire un appel de dessin COMPLET lors de la configuration du VAO, juste toutes les choses de liaison. Je ne sais pas pourquoi je pensais que c'était nécessaire plus tôt, mais c'est corrigé maintenant.

Robert Rouhani
la source
10
"Un objet tampon Vertex, ou VBO (parfois appelé juste un objet tampon)" C'est "parfois" appelé comme ça parce que c'est en fait ce qu'on appelle. Il s'agit simplement d'un objet tampon, pas différent de tout autre objet tampon que vous pourriez utiliser pour des blocs uniformes, un transfert de pixels, un retour de transformation ou toute autre utilisation. La spécification OpenGL ne fait jamais référence à quoi que ce soit comme un "objet tampon de sommet"; même la spécification d'extension d'origine ne l'appelle jamais ainsi.
Nicol Bolas
3
Excellente réponse. Merci d'avoir pris le temps d'écrire ceci! Quelques questions de suivi cependant: (1) Vous avez dit "avant de rendre, et avant de définir l'attribut, vous devez l'activer avec glEnableVertexAttribArray (0)" - êtes-vous sûr qu'il doit être activé avant l'appel à glVertexAttribPointer? Dans mes tests, l'ordre ne semble pas avoir d'importance. (2) Si je vous comprends bien, les attributs Vertex sont globaux et seul leur état activé / désactivé est enregistré dans le VAO actuellement lié?
mpen
1
(1) Je ne pense pas que l'ordre compte, tant que vous l'avez activé avant glDrawArraysou glDrawElements. Je mettrai à jour l'article pour refléter cela (2) Oui, mais ce n'est pas seulement l'état d'activation / désactivation qui est stocké, c'est tout ce qui concerne ces appels - ce qui était lié à GL_ARRAY_BUFFER à l'époque, le type, la foulée et le décalage. Essentiellement, il stocke suffisamment pour modifier tous les attributs de sommet de la manière dont vous les avez configurés avec le VAO.
Robert Rouhani
2
oui, les VAO sont conçus pour vous permettre de remplacer la plupart des méthodes de dessin par la liaison d'un VAO. Chaque entité peut avoir un VAO distinct et cela fonctionnera toujours correctement. Vous devrez toujours mettre à jour les uniformes et lier vos propres textures. Et vous devez utiliser les mêmes index d'attributs que vous avez pour lier les attributs de votre shader avec des indices d'attribut, que ce soit via layout(location = x)le shader ou avec glBindAttributeLocationlors de la compilation du shader. Exemple
Robert Rouhani
8
Notez que dans OpenGL moderne, il n'y a plus d'objet de tableau de vertex par défaut, vous devez en créer un vous-même ou votre application ne fonctionnera pas dans un contexte compatible avec les avancées.
Overv
3

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

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

J'espère que ça aide.

Edit: Comme vous pouvez le voir dans les commentaires ci-dessous, les gens peuvent faire des hypothèses et tirer des conclusions. La réalité est que c'est assez déroutant pour les débutants.

ap-osd
la source
" Voir OpenGL-Terminology pour une assez bonne explication. " En moins d'une minute, j'ai déjà trouvé un morceau de désinformation: "Ceux-ci sont remplacés par des attributs de sommet génériques avec un identifiant (appelé index) qui s'associe à une variable de shader (pour coordonnées, couleur, etc.) qui traitent l'attribut. " Ils ne sont pas appelés «indices»; ce sont des «emplacements». C'est une distinction très importante car les attributs de sommet ont également des "indices" , qui sont très différents des emplacements. C'est un site Web très terrible.
Nicol Bolas
2
C'est un commentaire juste mais pas tout à fait exact. Si vous regardez l'API OpenGL pour définir un attribut générique glVertexAttribPointer , void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)l'identifiant est appelé index. Le même identifiant dans le contexte du programme est appelé locationdans l'API glGetAttribLocation .
ap-osd