Parte de la terminología está un poco fuera de lugar:
- A
Vertex Array
es solo una matriz (normalmente a float[]
) que contiene datos de vértice. No necesita estar vinculado a nada. No confundir con a Vertex Array Object
o VAO, que repasaré más adelante
- A
Buffer Object
, comúnmente conocido como Vertex Buffer Object
cuando se almacenan vértices, o VBO para abreviar, es lo que llama simplemente a Buffer
.
- Nada se guarda en la matriz de vértices,
glVertexAttribPointer
funciona exactamente como glVertexPointer
o glTexCoordPointer
funciona, solo que en lugar de atributos con nombre, puede proporcionar un número que especifica su propio atributo. Pasa este valor como index
. Todas sus glVertexAttribPointer
llamadas se ponen en cola para la próxima vez que llame glDrawArrays
o glDrawElements
. Si tiene un VAO vinculado, el VAO almacenará la configuración de todos sus atributos.
El problema principal aquí es que está confundiendo los atributos de vértice con los VAO. Los atributos de vértice son solo la nueva forma de definir vértices, texcoords, normales, etc. para dibujar. Estado de la tienda VAO. Primero voy a explicar cómo funciona el dibujo con atributos de vértice, luego explicaré cómo puede reducir la cantidad de llamadas a métodos con VAO:
- Debe habilitar un atributo antes de poder usarlo en un sombreador. Por ejemplo, si desea enviar vértices a un sombreador, lo más probable es que lo envíe como el primer atributo, 0. Por lo tanto, antes de renderizar, debe habilitarlo con
glEnableVertexAttribArray(0);
.
- Ahora que un atributo está habilitado, debe definir los datos que utilizará. Para hacerlo, debe vincular su VBO -
glBindBuffer(GL_ARRAY_BUFFER, myBuffer);
.
- Y ahora podemos definir el atributo -
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
. En orden de parámetro: 0 es el atributo que está definiendo, 3 es el tamaño de cada vértice, GL_FLOAT
es el tipo, GL_FALSE
significa no normalizar cada vértice, los últimos 2 ceros significan que no hay zancada o desplazamiento en los vértices.
- Dibuja algo con él -
glDrawArrays(GL_TRIANGLES, 0, 6);
- Es posible que lo siguiente que dibuje no use el atributo 0 (de manera realista lo hará, pero este es un ejemplo), por lo que podemos deshabilitarlo:
glDisableVertexAttribArray(0);
Envuelva eso en glUseProgram()
llamadas y tendrá un sistema de renderizado que funciona con sombreadores correctamente. Pero digamos que tiene 5 atributos diferentes, vértices, coordenadas de texto, normales, colores y coordenadas de mapa de luz. En primer lugar, realizaría una única glVertexAttribPointer
llamada para cada uno de estos atributos y tendría que habilitar todos los atributos de antemano. Digamos que define los atributos 0-4 como los tengo enumerados. Los habilitarías a todos así:
for (int i = 0; i < 5; i++)
glEnableVertexAttribArray(i);
Y luego tendría que vincular diferentes VBO para cada atributo (a menos que los almacene todos en un VBO y use compensaciones / zancadas), luego debe realizar 5 glVertexAttribPointer
llamadas diferentes , desde glVertexAttribPointer(0,...);
hasta glVertexAttribPointer(4,...);
para vértices hasta coordenadas de mapa de luz, respectivamente.
Con suerte, ese sistema solo tiene sentido. Ahora voy a pasar a los VAO para explicar cómo usarlos para reducir la cantidad de llamadas a métodos al realizar este tipo de renderizado. Tenga en cuenta que no es necesario utilizar un VAO.
A Vertex Array Object
o VAO se utiliza para almacenar el estado de todas las glVertexAttribPointer
llamadas y los VBO a los que se dirigió cuando se realizó cada una de las glVertexAttribPointer
llamadas.
Generas uno con una llamada a glGenVertexArrays
. Para almacenar todo lo que necesita en un VAO, vincúlelo con glBindVertexArray
y luego haga una llamada de extracción completa . Todas las llamadas de vinculación de dibujo son interceptadas y almacenadas por VAO. Puede desvincular el VAO conglBindVertexArray(0);
Ahora, cuando desee dibujar el objeto, no necesita volver a llamar todos los enlaces VBO o las glVertexAttribPointer
llamadas, solo necesita vincular el VAO con glBindVertexArray
luego llamar glDrawArrays
o glDrawElements
y estará dibujando exactamente lo mismo que si estaban haciendo todas esas llamadas a métodos. Probablemente también desee desvincular el VAO después.
Una vez que desvincula la VAO, todo el estado vuelve a ser como era antes de vincular la VAO. No estoy seguro de si se mantienen los cambios que realiza mientras la VAO está vinculada, pero eso se puede resolver fácilmente con un programa de prueba. Supongo que se puede considerar glBindVertexArray(0);
vinculante para el VAO "predeterminado" ...
Actualización: Alguien me llamó la atención sobre la necesidad de la llamada de sorteo real. Resulta que, en realidad, no necesita hacer una llamada de extracción COMPLETA al configurar el VAO, solo todas las cosas vinculantes. No sé por qué pensé que era necesario antes, pero ahora está arreglado.
glVertexAttribPointer
? En mis pruebas, el orden no parece importar. (2) Si lo entiendo correctamente, los atributos de vértice son globales y solo su estado habilitado / deshabilitado se guarda en el VAO vinculado actualmente.glDrawArrays
oglDrawElements
. Actualizaré la publicación para reflejar eso (2) Sí, pero no es solo el estado de habilitación / deshabilitación lo que se almacena, es todo lo relacionado con esas llamadas: lo que estaba vinculado a GL_ARRAY_BUFFER en ese momento, el tipo, la zancada y el desplazamiento. Básicamente, está almacenando lo suficiente para cambiar todos los Atributos de vértice de nuevo a la forma en que los configuró con VAO.layout(location = x)
del sombreador oglBindAttributeLocation
al compilar el sombreador. EjemploLa terminología y la secuencia de las API a llamar es bastante confusa. Lo que es aún más confuso es cómo se asocian los diversos aspectos: búfer, atributo de vértice genérico y variable de atributo de sombreado. Consulte Terminología de OpenGL para obtener una explicación bastante buena.
Además, el enlace OpenGL-VBO, shader, VAO muestra un ejemplo sencillo con las llamadas API necesarias. Es particularmente bueno para aquellos que están en transición del modo inmediato al canal programable.
Espero eso ayude.
Editar: como puede ver en los comentarios a continuación, las personas pueden hacer suposiciones y sacar conclusiones precipitadas. La realidad es que es bastante confuso para los principiantes.
fuente
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)
el identificador se denominaindex
. El mismo identificador en el contexto del programa se llamalocation
en la API glGetAttribLocation .