O que são objetos Vertex Array?

114

Estou começando a aprender OpenGL hoje com este tutorial: http://openglbook.com/the-book/
Cheguei ao capítulo 2, onde desenho um triângulo, e entendo tudo, exceto VAOs (esta sigla está OK?). O tutorial tem este código:

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

Embora eu entenda que o código é necessário, não tenho ideia do que ele faz. Embora eu nunca use o VaoId além desse ponto (exceto para destruí-lo), o código não funciona sem ele. Presumo que seja porque é obrigatório, mas não sei por quê. Esse código exato precisa apenas fazer parte de todos os programas OpenGL? O tutorial explica VAOs como:

Um Vertex Array Object (ou VAO) é um objeto que descreve como os atributos de vértice são armazenados em um Vertex Buffer Object (ou VBO). Isso significa que o VAO não é o objeto real que armazena os dados do vértice, mas o descritor dos dados do vértice. Os atributos do vértice podem ser descritos pela função glVertexAttribPointer e suas duas funções irmãs glVertexAttribIPointer e glVertexAttribLPointer, a primeira das quais exploraremos a seguir.

Não entendo como o VAO descreve os atributos do vértice. Eu não os descrevi de forma alguma. Ele obtém as informações do glVertexAttribPointer? Eu acho que deve ser isso. O VAO é simplesmente um destino para as informações do glVertexAttribPointer?

Por outro lado, o tutorial que estou seguindo é aceitável? Há algo que devo observar ou um tutorial melhor a seguir?

Patrick
fonte

Respostas:

100

"Vertex Array Object" é oferecido a você pelo Subcomitê ARB do OpenGL para nomes bobos.

Pense nisso como um objeto geométrico. (Como um antigo programador SGI Performer, eu os chamo de geoets.) As variáveis ​​de instância / membros do objeto são o seu ponteiro de vértice, ponteiro normal, ponteiro de cor, ponteiro de atributo N, ...

Quando um VAO é vinculado pela primeira vez, você atribui esses membros chamando

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

e assim por diante. Quais atributos estão ativados e os ponteiros que você fornece são armazenados no VAO.

Depois disso, quando você vincula o VAO novamente, todos os atributos e ponteiros também se tornam atuais. Portanto, uma glBindVertexArraychamada é equivalente a todo o código anteriormente necessário para configurar todos os atributos. É útil para passar geometria entre funções ou métodos sem ter que criar suas próprias estruturas ou objetos.

(Configuração única, uso múltiplo é a maneira mais fácil de usar VAOs, mas você também pode alterar atributos apenas vinculando-os e fazendo mais chamadas de ativação / ponteiro. VAOs não são constantes.)

Mais informações em resposta às perguntas de Patrick:

O padrão para um VAO recém-criado é que ele esteja vazio (AFAIK). Nenhuma geometria, nem mesmo vértices, então se você tentar desenhá-la, obterá um erro de OpenGL. Isso é razoavelmente lógico, como em "inicializar tudo para False / NULL / zero".

Você só precisa fazer isso glEnableClientStateao configurar as coisas. O VAO lembra o estado de ativação / desativação de cada ponteiro.

Sim, o VAO irá armazenar glEnableVertexAttribArraye glVertexAttrib. Os arrays de vértice antigo, normal, cor, ... são iguais aos arrays de atributo, vértice == # 0 e assim por diante.

Hugh
fonte
62
'' Vertex Array Object 'é trazido a você pelo Subcomitê ARB do OpenGL para nomes bobos.' Sim, um nome tão bobo para um objeto que armazena ligações de array de vértices .
Nicol Bolas
2
Além disso, todos os VAOs estão relacionados comglVertexAttribPointer
Patrick
2
Adicione algumas informações sobre o uso de atributos de vértice genéricos para as pessoas que estão usando o perfil principal.
Oskar
8
@NicolBolas Um nome melhor seria VertexArrayMacroou algo semelhante.
bobobobo
7
@NicolBolas "Vertex Array Object" é um nome horrível. Trata-se de vincular dados a atributos . Não se trata de array de vértices, como o nome indica. Não há referência a vínculos ou atributos no nome e, como "array de vértices" é um conceito separado, torna o entendimento ainda mais difícil. IMHO, "(Vertex) Attributes Binding Object" é mais fácil de entender. Mesmo Geometry Object é melhor: eu não gosto, mas pelo menos não está sobrecarregado.
AkiRoss
8

Os Vertex Array Objects são como macros em programas de processamento de texto e similares. Uma boa descrição pode ser encontrada aqui .

As macros apenas lembram as ações que você executou, como ativar este atributo, vincular aquele buffer, etc. Quando você chama glBindVertexArray( yourVAOId ), ele simplesmente reproduz aquelas vinculações de ponteiro de atributo e vinculações de buffer.

Portanto, sua próxima chamada para desenhar usa o que foi vinculado pelo VAO.

VAO's não armazenam dados de vértice . Não. Os dados do vértice são armazenados em um buffer de vértice ou em uma matriz de memória do cliente.

bobobobo
fonte
19
-1: Eles não são como macros. Se fossem, vincular um novo VAO não desabilitaria os arrays de vértices habilitados por um VAO anterior, a menos que o novo VAO tenha "registrado" você explicitamente desativando esses arrays. VAOs, como todos os objetos OpenGL, mantêm o estado , não os comandos. Os comandos simplesmente mudam de estado, mas os objetos vêm com o conjunto de estados padrão. É por isso que vincular um VAO recém-criado sempre desabilitará todos os atributos.
Nicol Bolas
6

Sempre penso no VAO como um conjunto de buffers de dados usados ​​pelo OpenGL. Usando OpenGL moderno, você criará um VAO e Objetos de buffer de vértice.

insira a descrição da imagem aqui

//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

A próxima etapa é vincular os dados a um buffer:

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

Neste ponto, o OpenGL vê:

insira a descrição da imagem aqui

Agora podemos usar glVertexAttribPointer para dizer ao OpenGL o que os dados no buffer representam:

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)

insira a descrição da imagem aqui

OpenGL agora tem os dados no buffer e sabe como os dados são organizados em vértices. O mesmo processo pode ser aplicado a coordenadas de textura, etc., mas para coordenadas de textura haveria dois 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);

Em seguida, você pode vincular textura e desenhar matrizes, você vai querer criar um sombreador Vert e Frag, compilá-lo e anexá-lo a um programa (não incluído aqui).

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
fonte
5

VAO é um objeto que representa o estágio de busca de vértice do pipeline OpenGL e é usado para fornecer entrada para o sombreador de vértice.

Você pode criar um objeto de matriz de vértices como este

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

Primeiro vamos fazer um exemplo simples. Considere esse parâmetro de entrada em um código de sombreador

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

Para preencher este atributo, podemos usar

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

Embora o objeto de matriz de vértice armazene esses valores de atributos estáticos para você, ele pode fazer muito mais.

Depois de criar o objeto de array de vértices, podemos começar a preencher seu estado. Pediremos ao OpenGL para preenchê-lo automaticamente usando os dados armazenados em um objeto buffer que fornecemos. Cada atributo de vértice obtém dados de um buffer vinculado a uma das várias ligações de buffer de vértice. Para este fim usamos glVertexArrayAttribBinding(GLuint vao, GLuint attribindex, GLuint bindingindex). Também usamos a glVertexArrayVertexBuffer()função para vincular um buffer a uma das vinculações de buffer de vértice. Usamos a glVertexArrayAttribFormat()função para descrever o layout e formato dos dados e, finalmente, habilitamos o preenchimento automático do atributo por meio de chamada glEnableVertexAttribArray().

Quando um atributo de vértice é ativado, o OpenGL alimenta dados para o sombreador de vértice com base no formato e nas informações de localização que você forneceu com glVertexArrayVertexBuffer()e glVertexArrayAttribFormat(). Quando o atributo é desabilitado, o sombreador de vértice receberá as informações estáticas que você fornece com uma chamada para 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);

E codificar em um shader

layout (location = 0) in vec4 position;

Afinal, você precisa ligar para glDeleteVertexArrays(1, &vao).


Você pode ler o OpenGL SuperBible para entendê-lo melhor.

Yola
fonte
3
É bom ver pessoas promovendo o uso de OpenGL estilo DSA.
Nicol Bolas