¿Qué son los objetos Vertex Array?

114

Estoy empezando a aprender OpenGL hoy con este tutorial: http://openglbook.com/the-book/
Llegué al capítulo 2, donde dibujo un triángulo, y entiendo todo excepto los VAO (¿está bien este acrónimo?). El tutorial tiene este código:

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

Si bien entiendo que el código es necesario, no tengo ni idea de lo que hace. Aunque nunca uso VaoId más allá de este punto (excepto para destruirlo), el código no funciona sin él. Supongo que esto se debe a que debe estar vinculado, pero no sé por qué. ¿Este código exacto solo necesita ser parte de todos los programas OpenGL? El tutorial explica los VAO como:

Un Vertex Array Object (o VAO) es un objeto que describe cómo se almacenan los atributos de vértice en un Vertex Buffer Object (o VBO). Esto significa que VAO no es el objeto real que almacena los datos del vértice, sino el descriptor de los datos del vértice. Los atributos de vértice se pueden describir mediante la función glVertexAttribPointer y sus dos funciones hermanas glVertexAttribIPointer y glVertexAttribLPointer, la primera de las cuales exploraremos a continuación.

No entiendo cómo la VAO describe los atributos de vértice. No los he descrito de ninguna manera. ¿Obtiene la información del glVertexAttribPointer? Supongo que debe ser esto. ¿Es la VAO simplemente un destino para la información de glVertexAttribPointer?

En una nota al margen, ¿es aceptable el tutorial que estoy siguiendo? ¿Hay algo que deba tener en cuenta o un tutorial mejor a seguir?

Patricio
fuente

Respuestas:

100

"Vertex Array Object" es presentado por el Subcomité OpenGL ARB para Nombres tontos.

Piense en ello como un objeto de geometría. (Como un antiguo programador de SGI Performer, los llamo conjuntos geográficos). Las variables de instancia / miembros del objeto son su puntero de vértice, puntero normal, puntero de color, puntero attrib N, ...

Cuando un VAO se vincula por primera vez, asigna estos miembros llamando

glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer...;
glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer...;

y así. Los atributos que están habilitados y los indicadores que proporciona se almacenan en el VAO.

Después de eso, cuando vuelva a vincular el VAO, todos esos atributos y punteros también se actualizan. Entonces, una glBindVertexArrayllamada es equivalente a todo el código que se necesitaba anteriormente para configurar todos los atributos. Es útil para pasar geometría entre funciones o métodos sin tener que crear sus propias estructuras u objetos.

(La configuración única, el uso múltiple es la forma más fácil de usar VAO, pero también puede cambiar los atributos simplemente vinculándolos y haciendo más llamadas de habilitación / puntero. Los VAO no son constantes).

Más información en respuesta a las preguntas de Patrick:

El valor predeterminado para un VAO recién creado es que está vacío (AFAIK). Sin geometría en absoluto, ni siquiera vértices, por lo que si intenta dibujarlo, obtendrá un error de OpenGL. Esto es razonablemente sensato, como en "inicializar todo en False / NULL / zero".

Solo lo necesita glEnableClientStatecuando configura las cosas. El VAO recuerda el estado de activación / desactivación de cada puntero.

Sí, VAO almacenará glEnableVertexAttribArrayy glVertexAttrib. Las matrices viejas de vértice, normal, color, ... son las mismas que las matrices de atributos, vértice == # 0 y así sucesivamente.

Hugh
fuente
62
'El Subcomité de OpenGL ARB para nombres tontos le ofrece "Vertex Array Object".' Sí, un nombre tan tonto para un objeto que almacena enlaces de matriz de vértices .
Nicol Bolas
2
Además, son VAOS en absoluto relacionados conglVertexAttribPointer
Patrick
2
Agregue información sobre el uso de atributos de vértice genéricos para las personas que están usando el perfil principal.
Oskar
8
@NicolBolas Un mejor nombre sería VertexArrayMacroo algo parecido.
bobobobo
7
@NicolBolas "Vertex Array Object" es un nombre horrible. Se trata de vincular datos a atributos . No se trata de una matriz de vértices, como su nombre lo indica. No hay ninguna referencia a enlaces o atributos en el nombre, y dado que "matriz de vértices" es un concepto separado en sí mismo, hace que la comprensión sea aún más difícil. En mi humilde opinión, "Objeto vinculante de atributos (vértice)" es más fácil de entender. Incluso Geometry Object es mejor: no me gusta, pero al menos no está sobrecargado.
AkiRoss
8

Los objetos Vertex Array son como macros en los programas de procesamiento de texto y similares. Aquí se encuentra una buena descripción .

Las macros solo recuerdan las acciones que realizó, como activar este atributo, vincular ese búfer, etc. Cuando llama glBindVertexArray( yourVAOId ), simplemente reproduce esos enlaces de puntero de atributo y enlaces de búfer.

Entonces, su próxima llamada a dibujar usa lo que esté sujeto a VAO.

Los VAO no almacenan datos de vértices . No. Los datos de vértice se almacenan en un búfer de vértice o en una matriz de memoria del cliente.

bobobobo
fuente
19
-1: No son como macros. Si lo fueran, entonces vincular un nuevo VAO no deshabilitaría los arreglos de vértices habilitados por un VAO anterior, a menos que el nuevo VAO haya "registrado" que usted deshabilitó explícitamente esos arreglos. Los VAO, como todos los objetos OpenGL, mantienen el estado , no los comandos. Los comandos simplemente cambian de estado, pero los objetos vienen con un estado predeterminado. Es por eso que vincular un VAO recién creado siempre desactivará todos los atributos.
Nicol Bolas
6

Siempre pienso en VAO como una matriz de búferes de datos utilizados por OpenGL. Usando OpenGL moderno, creará un VAO y Vertex Buffer Objects.

ingrese la descripción de la imagen aquí

//vaoB is a buffer
glGenVertexArrays(1, vaoB); //creates one VAO
glBindVertexArray(vao.get(0));
glGenBuffers(vbo.length, vbo, 0); //vbo is a buffer
glBindVertexArray(vao.get(1));
glGenBuffers(vbo1.length, vbo1, 0); //vbo1 is a buffer
glBindVertexArray(vao.get(2));
glGenBuffers(vbo2.length, vbo2, 0); //vbo2 is a buffer

El siguiente paso es vincular datos a un búfer:

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER,vertBuf.limit()*4, vertBuf, GL_STATIC_DRAW); //vertf buf is a floatbuffer of vertices

En este punto, OpenGL ve:

ingrese la descripción de la imagen aquí

Ahora podemos usar glVertexAttribPointer para decirle a OpenGL qué representan los datos en el búfer:

glBindBuffer(GL_ARRAY_BUFFER, 0); //bind VBO at 0
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); //each vertex has 3 components of size GL_FLOAT with 0 stride (space) between them and the first component starts at 0 (start of data)

ingrese la descripción de la imagen aquí

OpenGL ahora tiene los datos en el búfer y sabe cómo se organizan los datos en vértices. El mismo proceso se puede aplicar a las coordenadas de textura, etc., pero para las coordenadas de textura habría dos valores.

glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER,coordBuf.limit()*4, coordBuf, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);

A continuación, puede unir texturas y dibujar matrices, querrá crear un sombreador Vert y Frag, compilarlo y adjuntarlo a un programa (no incluido aquí).

glActiveTexture(textureID); //bind our texture
glBindTexture(GL_TEXTURE_2D, textureID);
glDrawArrays(GL_TRIANGLES,0,6); //in this case 6 indices are used for two triangles forming a square
vasmos
fuente
5

VAO es un objeto que representa la etapa de búsqueda de vértices de la canalización de OpenGL y se utiliza para proporcionar entrada al sombreador de vértices.

Puede crear un objeto de matriz de vértices como este

GLuint vao;
glCreateVertexArrays(1, &vao);
glBindVertexArray(vao);

Primero hagamos un ejemplo simple. Considere un parámetro de entrada de este tipo en un código de sombreado

layout (location = 0) in vec4 offset; // input vertex attribute

Para completar este atributo podemos usar

glVertexAttrib4fv(0, attrib); // updates the value of input attribute 0

Aunque el objeto de matriz de vértices almacena estos valores de atributos estáticos por usted, puede hacer mucho más.

Después de crear el objeto de matriz de vértices, podemos comenzar a completar su estado. Le pediremos a OpenGL que lo llene automáticamente usando los datos almacenados en un objeto de búfer que proporcionamos. Cada atributo de vértice obtiene datos de un búfer vinculado a uno de varios enlaces de búfer de vértice. Para este fin utilizamos glVertexArrayAttribBinding(GLuint vao, GLuint attribindex, GLuint bindingindex). También usamos la glVertexArrayVertexBuffer()función para vincular un búfer a uno de los enlaces de búfer de vértice. Usamos la glVertexArrayAttribFormat()función para describir el diseño y formato de los datos, y finalmente habilitamos el llenado automático del atributo llamando glEnableVertexAttribArray().

Cuando se habilita un atributo de vértice, OpenGL enviará datos al sombreador de vértices según el formato y la información de ubicación que haya proporcionado glVertexArrayVertexBuffer()y glVertexArrayAttribFormat(). Cuando el atributo está deshabilitado, el sombreador de vértices recibirá la información estática que proporcione con una llamada glVertexAttrib*().

// First, bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(vao, 0, buffer, 0, sizeof(vmath::vec4));

// Now, describe the data to OpenGL, tell it where it is, and turn on automatic
// vertex fetching for the specified attribute
glVertexArrayAttribFormat(vao, 0, 4, GL_FLOAT, GL_FALSE, 0);

glEnableVertexArrayAttrib(vao, 0);

Y codifica en un sombreador

layout (location = 0) in vec4 position;

Después de todo, debes llamar a glDeleteVertexArrays(1, &vao).


Puede leer OpenGL SuperBible para comprenderlo mejor.

Yola
fuente
3
Es bueno ver a la gente promocionando el uso de OpenGL estilo DSA.
Nicol Bolas