====== Qt e OpenGL - Vertex Buffer Object con la classe QOpenGLBuffer ====== Abbiamo visto nei precedenti articoli come accedere ala funzionalità di OpenGL derivando le classi [[tutorial_qt:opengl_01|QWindow]] e [[tutorial_qt:opengl_01bis|QOpenGLWidget]]. Vedremo ora come creare un Vertex Buffer Object con le librerie Qt. Un Vertex Buffer Object (d'ora in poi VBO) è un caratteristica delle OpenGL che permette di caricare sulla memoria della GPU informazioni specifiche per ciascun vertice, rendendole disponibili per le successive fasi di disegno. In questo modo si evitano continui passaggi di dati dalla CPU alla GPU ad ogni ridisegno della scena, ottimizzando l'impiego delle risorse. Le informazioni che possono essere passate spaziano da singoli valori numerici a vettori di qualsiasi dimensione, purché prefissata. ===== I VBO in generale ===== Analizziamo quali sono le API OpenGL necessarie per creare i VBO. Per farlo partiremo dal codice del [[tutorial_qt:opengl_01bis|precedente articolo]]. Un metodo molto semplice per accedere direttamente alle API OpenGL è derivare dalla classe QOpenGLFunctions. Pertanto la nostra classe GLWidget deriverà sia da QOpenGLWidget che da QOpenGLFunctions class GLWidget : public QOpenGLWidget, public QOpenGLFunctions Non è però sufficiente: è necessario lanciare initializeOpenGLFunctions() nel metodo in initializeGL() ~~READMORE~~ void GLWidget::initializeGL() { // inizializziamo le funzioni OpenGL initializeOpenGLFunctions(); Ciasun VBO viene individuato con da un indice di tipo GLuint. Nella classe GLWidget aggiungiamo due attributi privati per individuare i VBO delle coordinate e dei colori dei vertici del triangolo GLuint m_vertexVBO; GLuint m_colorVBO; Per creare il VBO delle coordinate e caricarne in memoria i relativi valori useremo il codice // vettore contenete le coordinate dei vertici GLfloat vertexData[] = { GLfloat(0.0), GLfloat(0.707), GLfloat(0.0), GLfloat(1.0), GLfloat(-0.5), GLfloat(-0.50), GLfloat(0.0), GLfloat(1.0), GLfloat(0.5), GLfloat(-0.50), GLfloat(0.0), 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); Analogamente per i colori // vettore contenete le coordinate dei colori 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) }; // 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 colorData glBufferData(GL_ARRAY_BUFFER, sizeof(colorData), colorData, GL_STATIC_DRAW); // disattiva il VBO glBindBuffer(GL_ARRAY_BUFFER, 0); Per attivare i VBO al momento del disegno usiamo invece // Attiviamo il VBO dei vertici glBindBuffer(GL_ARRAY_BUFFER, m_vertexVBO); // attiva il VBO glEnableVertexAttribArray( m_posAttr ); glVertexAttribPointer( m_posAttr, 4, // numero di elementi per vertice nel nostro caso 4 (x,y,z,w) GL_FLOAT, // tipo di elemento 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, // numero di elementi per vertice nel nostro caso 4 (x,y,z,w) GL_FLOAT, // tipo di elemento GL_FALSE, 0, 0 ); // Disegniamo il triangolo glDrawArrays(GL_TRIANGLES, 0, 3); Infine per eliminare i VBO, nel distruttore, dopo avere disattivato gli shader, daremo i comandi glDeleteBuffers(1, &m_vertexVBO); glDeleteBuffers(1, &m_colorVBO); Al link [[http://ingegnerialibera.altervista.org/blog-file/opengltut02-01.zip|ingegnerialibera.altervista.org/blog-file/opengltut02-01.zip]] troviato il codice riepilogativo. Qualora siate interessati ad approfondire i comandi appena visti vi rimando al tutorial di Wikibooks disponibile all'indirizzo [[http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_02|en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_02]]. ===== I VBO secondo la Qt-way: la classe QOpenGLBuffer ===== Adesso analizziamo l'utilizzo dei VBO con le librerie Qt. Ci serviremo della classe [[http://doc.qt.io/qt-5/qopenglbuffer.html|QOpenGLBuffer]]. Il costruttore della classe richiede che si definisca il tipo di QOpenGLBuffer che si intende creare. Per il disegno interattivo sono due le tipologie di QOpenGLBuffer che ci interessano: * QOpenGLBuffer::VertexBuffer, per creare i VBO; è l'opzione di default ed è quella che utilizzeremo tra breve; * QOpenGLBuffer::IndexBuffer, per creare gli Indexed Buffer Object (ne vedremo l'impiego in un prossimo articolo). Anche in questo caso riprendiamo il codice del [[tutorial_qt:opengl_01bis|precedente articolo]] introducendo come attributi privati due puntatori alla classe QOpenGLBuffer QOpenGLBuffer * m_vertexVBO; QOpenGLBuffer * m_colorVBO; Useremo il primo per le coordinate dei vertici, il secondo per i relativi colori. Nel metodo initializeGL() creiamo l'istanza della classe OpenGLBuffer m_vertexVBO = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); A seguire rendiamo operativo l'oggetto col metodo create() m_vertexVBO->create(); Col metodo setUsagePattern definiamo quante volte i dati in questione verranno letti e reimpostati nel coso del prgramma, di modo da ottimizzare l'impiego delle risorse di sistema. Nel nostro caso abbiamo a che fare con valori che verranno impostati una volta ma letti molte volte; useremo perciò m_vertexVBO->setUsagePattern( QOpenGLBuffer::StaticDraw ); A questo punto attiviamo il VBO e carichiamo i valori numerici con i comandi GLfloat vertexData[] = { GLfloat(0.0), GLfloat(0.707), GLfloat(0.0), GLfloat(1.0), GLfloat(-0.5), GLfloat(-0.50), GLfloat(0.0), GLfloat(1.0), GLfloat(0.5), GLfloat(-0.50), GLfloat(0.0), GLfloat(1.0) }; m_vertexVBO->bind(); m_vertexVBO->allocate( vertexData, 3 * 4 * sizeof(GLfloat)); m_vertexVBO->release(); Si procede analogamente con m_colorVBO 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) }; m_colorVBO = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); m_colorVBO->create(); m_colorVBO->setUsagePattern( QOpenGLBuffer::StaticDraw ); m_colorVBO->bind(); m_colorVBO->allocate( colorData, 3 * 4 * sizeof(GLfloat)); m_colorVBO->release(); Passiamo ora al disegno vero e proprio definito nel metodo paintGL(). Attiviamo il VBO usando i comando m_vertexVBO->bind(); m_program->enableAttributeArray( m_posAttr ); m_program->setAttributeBuffer( m_posAttr, GL_FLOAT, 0, 4); m_vertexVBO->release(); Facciamo lo stesso per i colori m_colorVBO->bind(); m_program->enableAttributeArray( m_colAttr ); m_program->setAttributeBuffer( m_colAttr, GL_FLOAT, 0, 4); m_colorVBO->release(); Possiamo quindi disegnare il triangolo con il comando glDrawArrays(GL_TRIANGLES, 0, 3); Al link [[http://ingegnerialibera.altervista.org/blog-file/opengltut02-02.zip|ingegnerialibera.altervista.org/blog-file/opengltut02-02.zip]] troverete il codice finale con tutte le modifiche fin qui viste. Se confrontiamo i due diversi approcci (quello generale con l'impiego delle API OpenGL e quello specifico delle librerie Qt) notiamo che il principale vantaggio della classe QOpenGLBuffer non sia tanto nella riduzione del codice, quanto nella sua maggior leggibilità. ===== Facciamo girare il triangolo! ===== Tornando sul tema dell'interazione tra funzioni OpenGL e progammazione Qt classica, movimentiamo la nostra scena introducendo un ulteriore elemento di complessità: facciamo ruotare il nostro triangolo! Per farlo definiamo l'attributo privato QMatrix4x4 m_matrix; Impostiamo un timer aggiungendo nel costruttore il comando startTimer( 10 ); Ogni 10 ms verrà attivato il metodo timerEvent(QTimerEvent *event) nel quale faremo ruotare la matrice modello/vista e forzeremo il ridisegno della finestra con il comando update() void GLWidget::timerEvent(QTimerEvent *event) { // Ruotiamo di 1 grado attorno all'asse y nel sistema di riferimento dell'osservatore m_matrix.rotate(1.0, 0.0, 1.0, 0.0 ); // Calcoliamo l'angolo di rotazione che compare sullo schermo m_rotation += 1.0; m_rotation = m_rotation >= 360.0? m_rotation - 360.0: m_rotation; // Aggiorniamo il disegno update(); } Ed ecco il risultato finale! {{ :tutorial_qt:opengltut02.png |Il risultato finale del tutorial con il tringolo che ruota}} Nel codice disponibile all'indirizzo [[http://ingegnerialibera.altervista.org/blog-file/opengltut02-03.zip|ingegnerialibera.altervista.org/blog-file/opengltut02-03.zip]] troverete che la rotazione si interrompe o riprende al click del mouse sulla finestra. Lascio a voi l'analisi di questa piccola variante sul tema. ===== Potrebbero interessarti anche... ===== Un elenco degli altri articoli disponibili sull'argomento: