glVertexAttribPointer chiarimento


94

Voglio solo assicurarmi di averlo capito correttamente (chiederei su SO Chat, ma è morto lì dentro!):

Abbiamo un Vertex Array, che rendiamo "corrente" legandolo,
quindi abbiamo un Buffer, che leghiamo a un Target,
quindi riempiamo quel Target attraverso il glBufferData quale essenzialmente popola tutto ciò che era legato a quel target, cioè il nostro Buffer
e quindi chiamiamo glVertexAttribPointerche descrive come sono disposti i dati: i dati sono ciò a cui è associato GL_ARRAY_BUFFER e questo descrittore viene salvato nel nostro array Vertex originale

(1) La mia comprensione è corretta?
La documentazione è un po 'scarsa su come tutto sia correlato.

(2) Esiste una sorta di Vertex Array predefinito? Perché ho dimenticato / omesso glGenVertexArrayse glBindVertexArrayil mio programma ha funzionato bene anche senza.


Edit: ho perso un passo ... glEnableVertexAttribArray.

(3) L'attributo Vertex è collegato al Vertex Array al momento in cui glVertexAttribPointerviene chiamato, quindi possiamo abilitare / disabilitare quell'attrib tramite glEnableVertexAttribArrayin qualsiasi momento, indipendentemente da quale Vertex Array è attualmente associato?

Oppure (3b) L'attributo Vertex è legato al Vertex Array al momento in cui glEnableVertexAttribArrayviene chiamato, e quindi possiamo aggiungere lo stesso Vertex Attrib a più Vertex Arrays chiamando glEnableVertexAttribArrayin momenti diversi, quando diversi Vertex Arrays sono legati?

Risposte:


210

Parte della terminologia è un po 'fuori luogo:

  • A Vertex Arrayè solo un array (tipicamente a float[]) che contiene i dati dei vertici. Non ha bisogno di essere legato a nulla. Da non confondere con a Vertex Array Objecto VAO, di cui parlerò più avanti
  • A Buffer Object, comunemente indicato come a Vertex Buffer Objectquando si memorizzano i vertici, o VBO in breve, è ciò che stai chiamando solo a Buffer.
  • Niente viene salvato nell'array dei vertici, glVertexAttribPointerfunziona esattamente come glVertexPointero glTexCoordPointerfunziona, solo che al posto degli attributi denominati, puoi fornire un numero che specifica il tuo attributo. Si passa questo valore come index. Tutte le tue glVertexAttribPointerchiamate vengono messe in coda per la prossima volta che chiami glDrawArrayso glDrawElements. Se si dispone di un limite VAO, il VAO memorizzerà le impostazioni per tutti gli attributi.

Il problema principale qui è che stai confondendo gli attributi dei vertici con i VAO. Gli attributi dei vertici sono solo il nuovo modo di definire vertici, texcoords, normali, ecc. Per il disegno. Stato del negozio VAO. Per prima cosa spiegherò come funziona il disegno con gli attributi dei vertici, quindi spiegherò come ridurre il numero di chiamate al metodo con i VAO:

  1. È necessario abilitare un attributo prima di poterlo utilizzare in uno shader. Ad esempio, se vuoi inviare vertici a uno shader, molto probabilmente lo invierai come primo attributo, 0. Quindi, prima di renderizzare, devi abilitarlo con glEnableVertexAttribArray(0);.
  2. Ora che un attributo è abilitato, è necessario definire i dati che utilizzerà. Per fare ciò devi associare il tuo VBO - glBindBuffer(GL_ARRAY_BUFFER, myBuffer);.
  3. E ora possiamo definire l'attributo - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);. In ordine di parametro: 0 è l'attributo che stai definendo, 3 è la dimensione di ogni vertice, GL_FLOATè il tipo, GL_FALSEsignifica non normalizzare ogni vertice, gli ultimi 2 zeri significano che non c'è passo o offset sui vertici.
  4. Disegna qualcosa con esso - glDrawArrays(GL_TRIANGLES, 0, 6);
  5. La prossima cosa che disegni potrebbe non usare l'attributo 0 (realisticamente lo farà, ma questo è un esempio), quindi possiamo disabilitarlo - glDisableVertexAttribArray(0);

Avvolgilo nelle glUseProgram()chiamate e avrai un sistema di rendering che funziona correttamente con gli shader. Ma diciamo che hai 5 diversi attributi, vertici, texcoords, normali, colore e coordinate lightmap. Prima di tutto, faresti una singola glVertexAttribPointerchiamata per ciascuno di questi attributi e dovresti abilitare tutti gli attributi in anticipo. Supponiamo che tu definisca gli attributi 0-4 come li ho elencati. Abilitali tutti in questo modo:

for (int i = 0; i < 5; i++)
    glEnableVertexAttribArray(i);

E poi dovresti associare diversi VBO per ogni attributo (a meno che non li memorizzi tutti in un VBO e usi offset / falcata), quindi devi effettuare 5 glVertexAttribPointerchiamate diverse , rispettivamente da glVertexAttribPointer(0,...);a glVertexAttribPointer(4,...);per vertici a coordinate lightmap.

Si spera che quel sistema da solo abbia un senso. Ora passerò ai VAO per spiegare come usarli per ridurre il numero di chiamate al metodo quando si esegue questo tipo di rendering. Notare che l'utilizzo di un VAO non è necessario.

A Vertex Array Objecto VAO viene utilizzato per memorizzare lo stato di tutte le glVertexAttribPointerchiamate e dei VBO targetizzati quando ciascuna delle glVertexAttribPointerchiamate è stata effettuata.

Ne generi uno con una chiamata a glGenVertexArrays. Per memorizzare tutto ciò di cui hai bisogno in un VAO, collegalo con glBindVertexArray, quindi fai un full draw call . Tutte le chiamate di pareggio vengono intercettate e archiviate dal VAO. Puoi svincolare il VAO conglBindVertexArray(0);

Ora, quando vuoi disegnare l'oggetto, non hai bisogno di richiamare tutti i binding VBO o le glVertexAttribPointerchiamate, devi solo associare il VAO con glBindVertexArraypoi chiama glDrawArrayso glDrawElementse disegnerai esattamente la stessa cosa come se tu stavano facendo tutte quelle chiamate di metodo. Probabilmente in seguito vorrai anche svincolare il VAO.

Una volta svincolato il VAO, tutto lo stato torna a com'era prima di vincolare il VAO. Non sono sicuro che le modifiche apportate mentre il VAO è associato vengano mantenute, ma ciò può essere facilmente capito con un programma di prova. Immagino che tu possa pensare a glBindVertexArray(0);come vincolante al VAO "predefinito" ...


Aggiornamento: qualcuno ha portato alla mia attenzione la necessità dell'effettiva chiamata al sorteggio. A quanto pare, in realtà non è necessario eseguire una chiamata FULL draw quando si imposta il VAO, ma solo tutte le cose vincolanti. Non so perché prima pensavo fosse necessario, ma ora è stato risolto.


10
"Un Vertex Buffer Object, o VBO (a volte indicato semplicemente come un Buffer Object)" È "a volte" chiamato così perché in realtà è così che viene chiamato. È solo un oggetto buffer, non diverso da qualsiasi altro oggetto buffer che potresti usare per blocchi uniformi, trasferimento di pixel, feedback di trasformazione o qualsiasi altro uso. La specifica OpenGL non fa mai riferimento a qualcosa come un "oggetto buffer dei vertici"; anche la specifica dell'estensione originale non lo chiama mai così.
Nicol Bolas

3
Ottima risposta. Grazie per aver dedicato del tempo a scriverlo! Tuttavia, un paio di domande successive: (1) Hai detto "prima di eseguire il rendering e prima di definire l'attributo, devi abilitarlo con glEnableVertexAttribArray (0)" - sei sicuro che debba essere abilitato prima della chiamata a glVertexAttribPointer? Nei miei test, l'ordine non sembra avere importanza. (2) Se ho capito bene, gli attributi Vertex sono globali e solo il loro stato abilitato / disabilitato viene salvato nel VAO attualmente associato?
mpen

1
(1) Non penso che l'ordine sia importante, a patto che tu lo abbia abilitato prima glDrawArrayso glDrawElements. Aggiornerò il post per riflettere questo (2) Sì, ma non è solo lo stato di abilitazione / disabilitazione che viene memorizzato, è tutto ciò che riguarda quelle chiamate - ciò che era associato a GL_ARRAY_BUFFER in quel momento, il tipo, il passo e l'offset. Essenzialmente sta memorizzando abbastanza per cambiare tutti gli attributi dei vertici nel modo in cui li hai impostati con il VAO.
Robert Rouhani

2
sì, i VAO sono progettati per consentire di sostituire la maggior parte di un metodo di disegno con l'associazione di un VAO. Ogni entità può avere un VAO separato e funzionerà comunque bene. Dovrai comunque aggiornare le uniformi e legare le tue trame. E devi usare gli stessi indici degli attributi di cui devi legare gli attributi del tuo shader con gli indici degli attributi, sia che layout(location = x)avvenga nello shader che glBindAttributeLocationdurante la compilazione dello shader. Esempio
Robert Rouhani

8
Nota che nella moderna OpenGL non esiste più un oggetto array di vertici predefinito, devi crearne uno tu stesso o la tua applicazione non funzionerà in un contesto compatibile con le versioni successive.
Overv

3

La terminologia e la sequenza delle API da chiamare è davvero piuttosto confusa. Ciò che è ancora più confuso è il modo in cui vengono associati i vari aspetti: buffer, attributo vertice generico e variabile attributo shader. Vedi OpenGL-Terminology per una spiegazione abbastanza buona.

Inoltre, il collegamento OpenGL-VBO, shader, VAO mostra un semplice esempio con le chiamate API necessarie. È particolarmente utile per coloro che passano dalla modalità immediata alla pipeline programmabile.

Spero che sia d'aiuto.

Modifica: come puoi vedere dai commenti qui sotto, le persone possono fare supposizioni e saltare a conclusioni. La realtà è che è abbastanza confuso per i principianti.


" Vedi OpenGL-Terminology per una spiegazione abbastanza buona. " In meno di 1 minuto, ho già trovato un pezzo di disinformazione: "Questi sono sostituiti con attributi di vertice generici con un identificatore (chiamato indice) che si associa a una variabile shader (per coordinate, colore ecc.) che elaborano l'attributo. " Non si chiamano "indici"; sono "luoghi". Questa è una distinzione molto importante perché gli attributi dei vertici hanno anche "indici" , che sono molto diversi dalle posizioni. Questo è un sito terribile.
Nicol Bolas

2
Questo è un commento equo ma non del tutto accurato. Se guardate l'API OpenGL per definire un attributo generico glVertexAttribPointer , void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer), l'identificatore viene indicato come index. Lo stesso identificatore nel contesto del programma viene chiamato locationnell'API glGetAttribLocation .
ap-osd
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.