Qt e OpenGL - Index Buffer Object con la classe QOpenGLBuffer
In questo articolo parleremo degli Index Buffer Object. Come già accaduto nel precedente articolo, prima vedremo come usare questo caratteristica utilizzando direttamente le API OpenGL, e, a seguire, con gli strumenti presenti nelle librerie Qt.
A cosa servono gli Index Buffer Object
Per capire a cosa servano gli index Buffer Object (d'ora in poi IBO) riferiamoci al caso concreto del disegno di un cubo. Utilizzando gli strumenti visti nel precedente articolo, per disegnare le facce che delimitano un cubo con triangoli, dovremmo creare un VBO contenente le coordinate di 36 punti (6 facce * 2 triangoli * 3 punti), ripetendo più volte le coordinate degli otto vertici che geometricamente definiscono il cubo.
L'idea dietro gli IBO è molto semplice:
- carico un VBO contenente, nel nostro caso, i soli 8 vertici effettivamente necessari
- disegno le facce del cubo comunicando volta per volta alla GPU i soli indici dei vertici che intendo disegnare.
L'IBO è il buffer nel quale individuo gli indici dei vertici. Trattandosi di indici, tipicamente l'IBO conterrà interi senza segno (GLushort piuttosto che GLuint).
Gli IBO in generale
Vediamo ora all'opera gli IBO utilizzando direttamente le API OpenGL. Prenderò come base i sorgenti di uno dei precedenti articoli.
~~READMORE~~
Prima di tutto creiamo i VBO per le coordinate dei vertici e per i colori
// vettore contenete le coordinate dei vertici double l = 1.5/2.0; GLfloat vertexData[] = { GLfloat(-l), GLfloat(-l), GLfloat(-l), GLfloat(1.0), GLfloat(l), GLfloat(-l), GLfloat(-l), GLfloat(1.0), GLfloat(l), GLfloat(l), GLfloat(-l), GLfloat(1.0), GLfloat(-l), GLfloat(l), GLfloat(-l), GLfloat(1.0), GLfloat(-l), GLfloat(-l), GLfloat(l), GLfloat(1.0), GLfloat(l), GLfloat(-l), GLfloat(l), GLfloat(1.0), GLfloat(l), GLfloat(l), GLfloat(l), GLfloat(1.0), GLfloat(-l), GLfloat(l), GLfloat(l), GLfloat(1.0) }; // genera il VBO glGenBuffers(1, &m_vertexVBO); // attiva il VBO glBindBuffer(GL_ARRAY_BUFFER, m_vertexVBO); // carica nella memoria della GPU i valori contenuti in vertexData glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW); // disattiva il VBO glBindBuffer(GL_ARRAY_BUFFER, 0); // vettore contenete i colori dei vertici GLfloat colorData[] = { GLfloat(1.0), GLfloat(0.0), GLfloat(0.0), GLfloat(1.0), GLfloat(0.0), GLfloat(1.0), GLfloat(0.0), GLfloat(1.0), GLfloat(0.0), GLfloat(0.0), GLfloat(1.0), GLfloat(1.0), GLfloat(1.0), GLfloat(1.0), GLfloat(0.0), GLfloat(1.0), GLfloat(1.0), GLfloat(0.0), GLfloat(1.0), GLfloat(1.0), GLfloat(0.0), GLfloat(1.0), GLfloat(1.0), GLfloat(1.0), GLfloat(1.0), GLfloat(1.0), GLfloat(1.0), GLfloat(1.0), GLfloat(0.1), GLfloat(0.1), GLfloat(0.1), GLfloat(1.0) }; // genera il VBO glGenBuffers(1, &m_colorVBO); // attiva il VBO glBindBuffer(GL_ARRAY_BUFFER, m_colorVBO); // carica nella memoria della GPU i valori contenuti in vertexData glBufferData(GL_ARRAY_BUFFER, sizeof(colorData), colorData, GL_STATIC_DRAW); // disattiva il VBO glBindBuffer(GL_ARRAY_BUFFER, 0);
Passiamo ora all'IBO. Come già per il VBO, individuiamo il buffer con un'attributo intero senza segno dichiarato privato nell'header della classe
class GLWidget: ... { ... private: ... GLuint m_IBO ...
Nel metodo intializeGL() creiamo l'IBO
GLushort indices[] = { 0, 1, 2, 0, 2, 3, 0, 1, 4, 1, 5, 4, 1, 2, 6, 1, 6, 5, 2, 6, 3, 3, 6, 7, 0, 3, 4, 3, 7, 4, 4, 5, 7, 5, 6, 7 }; glGenBuffers(1, &m_IBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), &indices[0], GL_STATIC_DRAW);
Per i VBO il primo argomento dei comandi glBindBuffer e glBufferData era pari al parametro ARRAY_BUFFER; per creare un IBO sostituiamo ARRAY_BUFFER con GL_ELEMENT_ARRAY_BUFFER.
L'altra differenza sostanziale è presente nella fase di disegno. Infatti nel metodo paintGL(), dopo aver attivato i VBO e l'IBO, disegneremo i triangoli con glDrawElements invece che con glDrawArray
// Attiviamo il VBO dei vertici glBindBuffer(GL_ARRAY_BUFFER, m_vertexVBO); // attiva il VBO glEnableVertexAttribArray( m_posAttr ); glVertexAttribPointer( m_posAttr, 4, GL_FLOAT, GL_FALSE, 0, 0 ); // Attiviamo il VBO dei colori glBindBuffer(GL_ARRAY_BUFFER, m_colorVBO); // attiva il VBO glEnableVertexAttribArray( m_colAttr ); glVertexAttribPointer( m_colAttr, 4, GL_FLOAT, GL_FALSE, 0, 0 ); // attiviamo l'IBO glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO); // Disegna i triangoli glDrawElements( GL_TRIANGLES, // modalita' disegno 36, // numero indici GL_UNSIGNED_SHORT, // tipologia degli indici 0 // offset tra gli indici );
Al link ingegnerialibera.altervista.org/blog-file/opengltut03-01.zip trovate il codice sorgente riepilogativo di quanto visto, di modo da permettervi di studiarlo con calma. Compilando tale codice vedrete un cubo multicolore che ruota
Gli IBO con la classe QOpenGLBuffer
Vediamo ora come implementare l'IBO con la classe QOpenGLBuffer.
Nella classe GLWidget dichiareremo l'attributo privato m_IBO, puntatore alla classe QOpenGLBuffer
class GLWidget: ... { ... private: ... QOpenGLBuffer * m_IBO; ...
Per la creazione del VBO abbiamo invocato il costruttore della classe QOpenGLBuffer con il parametro QOpenGLBuffer::VertexBuffer, per creare un IBO usiamo invece il parametro QOpenGLBuffer::IndexBuffer. Nel metodo initializeGL() aggiungiamo pertanto
GLushort indices[] = { 0, 1, 2, 0, 2, 3, 0, 1, 4, 1, 5, 4, 1, 2, 6, 1, 6, 5, 2, 6, 3, 3, 6, 7, 0, 3, 4, 3, 7, 4, 4, 5, 7, 5, 6, 7 }; m_IBO = new QOpenGLBuffer( QOpenGLBuffer::IndexBuffer ); m_IBO->create(); m_IBO->setUsagePattern( QOpenGLBuffer::StaticDraw ); m_IBO->bind(); m_IBO->allocate( indices, sizeof(indices)); m_IBO->release();
Il vettore indices contiene l'elenco degli indici dei triangoli che delimitano il cubo che stiamo disegnando.
In paintGL(), per il disegno del cubo, dopo avere attivato i VBO delle coordinate e dei colori, attiviamo l'IBO con il metodo bind()
// Attiviamo il VBO dei vertici m_vertexVBO->bind(); m_program->enableAttributeArray( m_posAttr ); m_program->setAttributeBuffer( m_posAttr, GL_FLOAT, 0, 4); m_vertexVBO->release(); // Attiviamo il VBO dei colori m_colorVBO->bind(); m_program->enableAttributeArray( m_colAttr ); m_program->setAttributeBuffer( m_colAttr, GL_FLOAT, 0, 4); m_colorVBO->release(); // Attiviamo l'IBO m_IBO->bind(); // Disegniamo i triangoli glDrawElements( GL_TRIANGLES, // modalita' 36, // numero indici GL_UNSIGNED_SHORT, // tipologia degli indici 0 // offset tra gli indici );
Il codice finale è disponibile al link ingegnerialibera.altervista.org/blog-file/opengltut03-02.zip. Il risultato è lo stesso ottenuto al paragrafo precedente usando direttamente le API OpenGL.
Potrebbero interessarti anche...
Un elenco degli altri articoli disponibili sull'argomento: <blog related> </blog>