Strumenti Utente



tutorial_qt:opengl_02_vbo_qopenglbuffer

Qt e OpenGL - Vertex Buffer Object con la classe QOpenGLBuffer

Abbiamo visto nei precedenti articoli come accedere ala funzionalità di OpenGL derivando le classi QWindow e 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 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()

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

Il risultato finale del tutorial con il tringolo che ruota

Nel codice disponibile all'indirizzo 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...

Comments


tutorial_qt/opengl_02_vbo_qopenglbuffer.txt · Ultima modifica: 2015/05/05 14:42 da mickele

Facebook Twitter Google+ Digg Reddit LinkedIn StumbleUpon Email