Algumas das terminologias estão um pouco erradas:
- A
Vertex Array
é apenas uma matriz (normalmente a float[]
) que contém dados de vértice. Não precisa estar vinculado a nada. Não deve ser confundido com um Vertex Array Object
ou VAO, sobre o qual falarei mais tarde
- A
Buffer Object
, comumente referido como a Vertex Buffer Object
ao armazenar vértices, ou VBO, é o que você está chamando de apenas a Buffer
.
- Nada é salvo de volta no array de vértices,
glVertexAttribPointer
funciona exatamente como glVertexPointer
ou glTexCoordPointer
funciona, apenas em vez de atributos nomeados, você fornece um número que especifica seu próprio atributo. Você passa esse valor como index
. Todas as suas glVertexAttribPointer
chamadas são colocadas em fila para a próxima vez que você ligar glDrawArrays
ou glDrawElements
. Se você tiver um limite VAO, o VAO armazenará as configurações de todos os seus atributos.
O principal problema aqui é que você está confundindo atributos de vértice com VAOs. Os atributos de vértice são apenas a nova maneira de definir vértices, texcoords, normais, etc. para desenho. Estado de armazenamento VAOs. Vou primeiro explicar como o desenho funciona com atributos de vértice e, em seguida, explicar como você pode reduzir o número de chamadas de método com VAOs:
- Você deve habilitar um atributo antes de usá-lo em um sombreador. Por exemplo, se você deseja enviar vértices para um sombreador, provavelmente o enviará como o primeiro atributo, 0. Portanto, antes de renderizar, é necessário habilitá-lo com
glEnableVertexAttribArray(0);
.
- Agora que um atributo está habilitado, você precisa definir os dados que ele usará. Para fazer isso, você precisa vincular seu VBO -
glBindBuffer(GL_ARRAY_BUFFER, myBuffer);
.
- E agora podemos definir o atributo -
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
. Na ordem do parâmetro: 0 é o atributo que você está definindo, 3 é o tamanho de cada vértice, GL_FLOAT
é o tipo, GL_FALSE
significa não normalizar cada vértice, os últimos 2 zeros significam que não há passo ou deslocamento nos vértices.
- Desenhe algo com ele -
glDrawArrays(GL_TRIANGLES, 0, 6);
- A próxima coisa que você desenhar pode não usar o atributo 0 (realisticamente, ele usará, mas este é um exemplo), então podemos desativá-lo -
glDisableVertexAttribArray(0);
Envolva isso em glUseProgram()
chamadas e você terá um sistema de renderização que funciona com shaders corretamente. Mas digamos que você tenha 5 atributos diferentes, vértices, texcoords, normais, cores e coordenadas de mapa de luz. Em primeiro lugar, você faria uma única glVertexAttribPointer
chamada para cada um desses atributos e teria que habilitar todos os atributos com antecedência. Digamos que você defina os atributos 0-4 conforme eu os listo. Você habilitaria todos eles assim:
for (int i = 0; i < 5; i++)
glEnableVertexAttribArray(i);
E então você teria que vincular VBOs diferentes para cada atributo (a menos que você os armazene todos em um VBO e use offsets / stride), então você precisa fazer 5 glVertexAttribPointer
chamadas diferentes , de glVertexAttribPointer(0,...);
a glVertexAttribPointer(4,...);
para vértices e coordenadas do mapa de luz, respectivamente.
Esperançosamente, esse sistema sozinho faz sentido. Agora, vou passar para os VAOs para explicar como usá-los para reduzir o número de chamadas de método ao fazer esse tipo de renderização. Observe que o uso de um VAO não é necessário.
A Vertex Array Object
ou VAO é usado para armazenar o estado de todas as glVertexAttribPointer
chamadas e os VBOs que foram direcionados quando cada uma das glVertexAttribPointer
chamadas foi feita.
Você gera um com uma chamada para glGenVertexArrays
. Para armazenar tudo o que você precisa em um VAO, vincule-o a glBindVertexArray
e faça uma chamada completa . Todas as chamadas draw bind são interceptadas e armazenadas pelo VAO. Você pode desvincular o VAO comglBindVertexArray(0);
Agora quando você deseja desenhar o objeto, você não precisa chamar novamente todos os vínculos VBO ou as glVertexAttribPointer
chamadas, você só precisa vincular o VAO com a glBindVertexArray
chamada glDrawArrays
ou glDrawElements
e você estará desenhando exatamente a mesma coisa como se você estavam fazendo todas essas chamadas de método. Você provavelmente deseja desvincular o VAO posteriormente também.
Depois de desvincular o VAO, todo o estado retorna ao que era antes de você vincular o VAO. Não tenho certeza se as alterações feitas enquanto o VAO está vinculado são mantidas, mas isso pode ser facilmente descoberto com um programa de teste. Eu acho que você pode pensar glBindVertexArray(0);
em uma vinculação ao VAO "padrão" ...
Atualização: Alguém chamou minha atenção para a necessidade do sorteio real. Acontece que você não precisa fazer uma chamada FULL draw ao configurar o VAO, apenas todas as coisas de ligação. Não sei por que eu pensei que era necessário antes, mas está corrigido agora.
glVertexAttribPointer
? Em meus testes, a ordem não parece importar. (2) Se bem entendi, os atributos do vértice são globais e apenas seu estado ativado / desativado é salvo no VAO atualmente vinculado?glDrawArrays
ouglDrawElements
. Vou atualizar a postagem para refletir isso (2) Sim, mas não é apenas o estado de ativação / desativação que está armazenado, é tudo relacionado a essas chamadas - o que foi vinculado a GL_ARRAY_BUFFER no momento, o tipo, passo e deslocamento. Essencialmente, ele está armazenando o suficiente para alterar todos os atributos de vértice de volta para a forma como você os configurou com o VAO.layout(location = x)
do shader ou comglBindAttributeLocation
ao compilar o shader. ExemploA terminologia e a sequência das APIs a serem chamadas são bastante confusas. O que é ainda mais confuso é como os vários aspectos - buffer, atributo genérico de vértice e variável de atributo shader são associados. Veja Terminologia OpenGL para uma boa explicação.
Além disso, o link OpenGL-VBO, shader, VAO mostra um exemplo simples com as chamadas API necessárias. É particularmente bom para quem está fazendo a transição do modo imediato para o pipeline programável.
Espero que ajude.
Edit: Como você pode ver nos comentários abaixo, as pessoas podem fazer suposições e tirar conclusões precipitadas. A realidade é que é bastante confuso para iniciantes.
fonte
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)
, o identificador é referido comoindex
. O mesmo identificador no contexto do programa é chamadolocation
na API glGetAttribLocation .