web-dev-qa-db-fra.com

Quel est le rôle de glBindVertexArrays vs glBindBuffer et quelle est leur relation?

Je suis nouveau sur OpenGL et la programmation graphique. J'ai lu un manuel qui a été vraiment approfondi et bien écrit jusqu'à présent, mais j'ai touché un point dans le code que je ne comprends pas très bien et je voudrais donner un sens à ces lignes avant de passez.

GLuint abuffer;

glGenVertexArrays(1, &abuffer);
glBindVertexArray(abuffer);

GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

Le livre explique que les trois premières lignes créent un objet vertex-array, qui est utilisé pour regrouper les données associées avec un vertex array. La deuxième ligne trouve un nom inutilisé (je suppose un identifiant d'entier non signé stocké dans abuffer) et la troisième ligne crée l'objet/le rend actif.

Le livre explique que les 4e-7e lignes créant un objet tampon pour stocker nos données, la 5e ligne nous donnant un identifiant inutilisé (similaire à la ligne 2 pour l'objet de tableau de sommets?), La 6e ligne créer le tampon, et la 7ème ligne allouer suffisamment de mémoire sur le CPU et créer un pointeur vers nos données (points) pour GL_STATIC_DRAW.

Que signifie que l'objet soit actif? Quand utiliseriez-vous par la suite abuffer? Qu'est-ce que cela signifie pour un tableau de sommets de regrouper les données associées, et quand les données étaient-elles associées à cet objet de tableau de sommets?

Je suis confus quant à la relation entre abuffer et buffer. Je suis confus quant à la relation entre le tableau de sommets et l'objet tampon et à quel point cette relation est formée. Je ne sais pas s'ils sont en fait liés, mais ils sont présentés l'un après l'autre dans le manuel.

Toute aide serait appréciée. Merci.

67
DashAnimal

Du point de vue de bas niveau, vous pouvez considérer un tableau comme ayant deux parties:

  • Informations sur la taille, la forme et le type du tableau (par exemple, nombres à virgule flottante 32 bits, contenant des rangées de vecteurs avec quatre éléments chacune).

  • Les données du tableau, qui ne sont guère plus qu'une grosse masse d'octets.

Même si le concept de bas niveau est resté essentiellement le même, la façon dont vous spécifiez les tableaux a changé plusieurs fois au fil des ans.

OpenGL 3.0/ARB_vertex_array_object

C'est ainsi que vous probablement devriez faire les choses aujourd'hui. Il est très rare de trouver des personnes qui ne peuvent pas exécuter OpenGL 3.x et qui ont encore de l'argent à dépenser pour votre logiciel.

Un objet tampon dans OpenGL est un gros blob de bits. Considérez le tampon "actif" comme une simple variable globale, et il y a un tas de fonctions qui utilisent le tampon actif au lieu d'utiliser un paramètre. Ces variables d'état globales sont le côté laid d'OpenGL (avant l'accès direct à l'état, qui est couvert ci-dessous).

GLuint buffer;

// Generate a name for a new buffer.
// e.g. buffer = 2
glGenBuffers(1, &buffer);

// Make the new buffer active, creating it if necessary.
// Kind of like:
// if (opengl->buffers[buffer] == null)
//     opengl->buffers[buffer] = new Buffer()
// opengl->current_array_buffer = opengl->buffers[buffer]
glBindBuffer(GL_ARRAY_BUFFER, buffer);

// Upload a bunch of data into the active array buffer
// Kind of like:
// opengl->current_array_buffer->data = new byte[sizeof(points)]
// memcpy(opengl->current_array_buffer->data, points, sizeof(points))
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

Maintenant, votre vertex shader typique prend vertexes en entrée, pas un gros blob de bits. Vous devez donc spécifier comment le blob de bits (le tampon) est décodé en sommets. C'est le travail du tableau. De même, il existe un tableau "actif" que vous pouvez considérer comme une simple variable globale:

GLuint array;
// Generate a name for a new array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);

// Make the buffer the active array buffer.
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Attach the active buffer to the active array,
// as an array of vectors with 4 floats each.
// Kind of like:
// opengl->current_vertex_array->attributes[attr] = {
//     type = GL_FLOAT,
//     size = 4,
//     data = opengl->current_array_buffer
// }
glVertexAttribPointer(attr, 4, GL_FLOAT, GL_FALSE, 0, 0);
// Enable the vertex attribute
glEnableVertexAttribArray(attr);

OpenGL 2.0 (à l'ancienne)

Dans OpenGL 2.x, il n'y avait pas de tableaux de vertex et les données étaient juste globales. Vous deviez encore appeler glVertexAttribPointer() et glEnableVertexAttribArray(), mais vous deviez les appeler à chaque fois que vous utilisiez un tampon. Dans OpenGL 3.x, vous venez de configurer le tableau une seule fois.

Pour en revenir à OpenGL 1.5, vous pouvez réellement utiliser des tampons, mais vous avez utilisé une fonction distincte pour lier chaque type de données. Par exemple, glVertexPointer() était pour les données de sommet et glNormalPointer() était pour les données normales. Avant OpenGL 1.5, il n'y avait pas de tampons, mais vous pouviez utiliser des pointeurs dans la mémoire de votre application.

OpenGL 4.3/ARB_vertex_attrib_binding

Dans 4.3, ou si vous avez l'extension ARB_vertex_attrib_binding, vous pouvez spécifier le format d'attribut et les données d'attribut séparément. C'est bien car cela vous permet de basculer facilement un tableau de sommets entre différents tampons.

GLuint array;
// Generate a name for a new array array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);

// Enable my attributes
glEnableVertexAttribArray(loc_attrib);
glEnableVertexAttribArray(normal_attrib);
glEnableVertexAttribArray(texcoord_attrib);
// Set up the formats for my attributes
glVertexAttribFormat(loc_attrib,      3, GL_FLOAT, GL_FALSE, 0);
glVertexAttribFormat(normal_attrib,   3, GL_FLOAT, GL_FALSE, 12);
glVertexAttribFormat(texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexAttribBinding(loc_attrib,      0);
glVertexAttribBinding(normal_attrib,   0);
glVertexAttribBinding(texcoord_attrib, 0);

// Quickly bind all attributes to use "buffer"
// This replaces several calls to glVertexAttribPointer()
// Note: you don't need to bind the buffer first!  Nice!
glBindVertexBuffer(0, buffer, 0, 32);

// Quickly bind all attributes to use "buffer2"
glBindVertexBuffer(0, buffer2, 0, 32);

OpenGL 4.5/ARB_direct_state_access

Dans OpenGL 4.5, ou si vous avez l'extension ARB_direct_state_access, vous n'avez plus besoin d'appeler glBindBuffer() ou glBindVertexArray() juste pour configurer les choses ... vous spécifiez directement les tableaux et les tampons. Il vous suffit de lier le tableau à la fin pour le dessiner.

GLuint array;
// Generate a name for the array and create it.
// Note that glGenVertexArrays() won't work here.
glCreateVertexArrays(1, &array);
// Instead of binding it, we pass it to the functions below.

// Enable my attributes
glEnableVertexArrayAttrib(array, loc_attrib);
glEnableVertexArrayAttrib(array, normal_attrib);
glEnableVertexArrayAttrib(array, texcoord_attrib);
// Set up the formats for my attributes
glVertexArrayAttribFormat(array, loc_attrib,      3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(array, normal_attrib,   3, GL_FLOAT, GL_FALSE, 12);
glVertexArrayAttribFormat(array, texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexArrayAttribBinding(array, loc_attrib,      0);
glVertexArrayAttribBinding(array, normal_attrib,   0);
glVertexArrayAttribBinding(array, texcoord_attrib, 0);

// Quickly bind all attributes to use "buffer"
glVertexArrayVertexBuffer(array, 0, buffer, 0, 32);

// Quickly bind all attributes to use "buffer2"
glVertexArrayVertexBuffer(array, 0, buffer2, 0, 32);

// You still have to bind the array to draw.
glBindVertexArray(array);
glDrawArrays(...);

ARB_direct_state_access est sympa pour plusieurs raisons. Vous pouvez oublier de lier des tableaux et des tampons (sauf lorsque vous dessinez) afin de ne pas avoir à penser aux variables globales cachées qu'OpenGL suit pour vous. Vous pouvez oublier la différence entre "générer un nom pour un objet" et "créer un objet" car glCreateBuffer() et glCreateArray() font les deux en même temps.

Vulkan

Vulkan va encore plus loin et vous fait écrire du code comme le pseudocode que j'ai écrit ci-dessus. Vous verrez donc quelque chose comme:

// This defines part of a "vertex array", sort of
VkVertexInputAttributeDescription attrib[3];
attrib[0].location = 0; // Feed data into shader input #0
attrib[0].binding = 0;  // Get data from buffer bound to slot #0
attrib[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attrib[0].offset = 0;
// repeat for attrib[1], attrib[2]
130
Dietrich Epp

Votre interprétation du livre n'est pas complètement correcte. Les objets Vertex Array ne stockent aucune donnée. Il s'agit d'une classe d'objets appelés conteneurs, comme les objets Framebuffer. Vous pouvez y attacher/associer d'autres objets, mais ils ne stockent jamais eux-mêmes les données. En tant que tels, ils ne sont pas une ressource partageable en contexte.

Fondamentalement, les objets de tableau de sommets encapsulent l'état de tableau de sommets dans OpenGL 3.0. À partir d'OpenGL 3.1 (au lieu de GL_ARB_compatibility) Et des profils OpenGL 3.2+ Core, vous devez avoir un VAO non nul à tout moment pour des commandes comme glVertexAttribPointer (...) ou glDrawArrays (...) Pour fonctionner. Le VAO lié forme le contexte nécessaire pour ces commandes et stocke l'état de manière persistante.

Dans les anciennes versions de GL (et compatibilité), l'état stocké par les VAO faisait partie de la machine d'état globale.

Il convient également de mentionner que la liaison "actuelle" pour GL_ARRAY_BUFFER Est pas l'un des indique que les VAO suivent. Bien que cette liaison soit utilisée par des commandes telles que glVertexAttribPointer (...), les VAO ne stockent pas la liaison, ils stockent uniquement des pointeurs (l'extension GL_ARB_vertex_attrib_binding introduite à côté de GL 4.3 complique un peu cela, alors ignorons-le pour plus de simplicité).

VAOs do rappelez-vous ce qui est lié à GL_ELEMENT_ARRAY_BUFFER, Cependant, de sorte que les commandes de dessin indexées telles que glDrawElements (...) fonctionne comme vous vous en doutez ( par exemple Les VAO réutilisent la dernière borne de tampon du tableau d'éléments).

15
Andon M. Coleman

La relation est créée lors de l'appel de glVertexAttribPointer .

Overview of VertexArrays

GL_VERTEX_ARRAY_BINDING et GL_ARRAY_BUFFER_BINDING sont des constantes mais elles peuvent pointer vers l'état global de la liaison. Je fais référence à l'état et non à la constante (orange) de l'image. Utilisez glGet pour rechercher différents états globaux.

VertexArray regroupe des informations (y compris un tampon de tableau) sur un sommet ou plusieurs sommets parallèles.

Utilisation GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING avec glGetVertexAttrib pour trouver quel tampon de tableau d'attributs est défini.

glBindBuffer (GL_ARRAY_BUFFER définit l'état global GL_ARRAY_BUFFER_BINDING

glBindVertexArray définit l'état global GL_VERTEX_ARRAY_BINDING

6
Jossi

OpenGL est une interface dynamique. C'est mauvais, désuet et laid mais c'est un héritage pour toi.

Un objet tableau de sommets est une collection de liaisons de tampon que le pilote peut utiliser pour obtenir les données pour les appels de dessin, la plupart des didacticiels n'utilisent que l'un et n'expliquent jamais comment utiliser plusieurs VAO.

Une liaison de tampon indique à opengl d'utiliser ce tampon pour les méthodes connexes en particulier pour les méthodes glVertexAttribPointer.

5
ratchet freak