OpenGL – Modellare con gli Array

Abbiamo visto come si può disegnare qualsivoglia figura inserendo i vari vertici della figura manualmente. Abbiamo visto anche che prima di inserire un vertice dobbiamo prepararlo impostando, ad esempio, il colore, il vettore normale (vedremo a cosa serve quando parleremo della luce), le texture e via dicendo. Questo può sembrarvi estremamente macchinoso… e lo è: dobbiamo usare circa 3-4 istruzioni per inserire ogni vertice! Un metodo decisamente inadatto per inserire oggetti formati da centinaia di vertici!

Anche volendo disegnare un semplice cubo (quindi con la miseria di 8 vertici) ci rendiamo conto che ogni vertice andrebbe specificato ben 3 volte (uno per ogni faccia a cui appartiene) con inutile spreco di prestazioni!

Ovviamente esiste la soluzione: usare gli array. Tramite gli array possiamo inserire con una manciata di comandi intere liste di vertici. Ma andiamo per passi.

1 – ABILITARE GLI ARRAY

Come gran parte degli stati di OpenGL gli array sono disabilitati di default. Esistono 8 diversi array da abilitare:

  • GL_VERTEX_ARRAY
  • GL_COLOR_ARRAY
  • GL_SECOND_COLOR_ARRAY
  • GL_INDEX_ARRAY
  • GL_NORMAL_ARRAY
  • GL_FOG_COORDINATE_ARRAY
  • GL_TEXTURE_COORDINATE_ARRAY
  • GL_EDGE_FLAG_ARRAY

Ovviamente non ci serviranno tutte adesso e possiamo limitarci, ad esempio, solamente a vertici e colori. Possiamo quindi abilitare gli array che ci interessano con glEnableClientState() come abbiamo visto in precedenza.

2 – INSERIRE DATI NEGLI ARRAY

Una volta che gli array sono abilitati bisogna inserire dei dati al loro interno. Esiste una funzione per ogni tipo di array. Noi però per il momento vedremo solamente le funzioni che ci interessano:

  • glVertexPointer(size, type, stride, pointer) è la funzione che permette di inserire un vettore all’interno dell’array dei vertici. size è semplicemente il numero di coordinate per vertice (può valere solo 2,3 o 4). type indica il tipo con cui sono specificati i vertici (GL_INT_, GL_FLOAT e così via). stride è un valore che indica l’offeset fra vertici consecutivi all’interno del vettore. Questo parametro è solitamente 0 per array che contengono solamente vertici. pointer è invece il puntatore al vettore che contiene i vertici da inserire nell’array.
  • glColorPointer(size, type, stride, pointer) è analoga a sopra ma serve per impostare il vettore dei colori.

3 – UTILIZZARE GLI ARRAY

Esistono diverse funzioni per utilizzare i dati presenti negli array. La prima funzione base è glArrayElement(k). Questa funzione non fa altro che inserire il k-esimo elemento dell’array. Facciamo un esempio. Supponiamo di avere due array vertex e color correttamente inizializzati e inseriti negli array di OpenGL.

A questo punto i due codici sono equivalenti:

glBegin(GL_TRIANGLES)
glArrayElement(2)
glArrayElement(3)
glArrayElement(5)
glEnd()
glBegin(GL_TRIANGLES)
glColor3f(color[2*3], color[2*3 + 1], color[2*3+2])
glVertex2f(color[2*2], color[2*2 + 1])
glColor3f(color[3*3], color[3*3 + 1], color[3*3+2])
glVertex2f(color[3*2], color[3*2 + 1])
glColor3f(color[5*3], color[5*3 + 1], color[5*3+2])
glVertex2f(color[5*2], color[5*2 + 1])
glEnd()

Come potete vedere c’è un bel risparmio. Notate anche che il vertice k-esimo viene colorato con il colore k-esimo.

La seconda funzione che vediamo ci permette di semplificare il tutto ancora di più. Tale funzione è glDrawElements(mode, count, type, indices). Questa funzione è del tutto equivalente a questo codice:


glBegin(mode)
for i in range(count) :
    glArrayElement(indices[i])
glEnd()

Il parametro indices, come avrete notato, è un array di indici. Se indices contiene [0, 4, 6] verranno inseriti in sequenza il vertice 0, 4 e 6 contenuto nell’array di OpenGL. Notate anche che è un errore inserire glDrawElements() all’interno di un blocco glBegin/glEnd.

Una terza funzione è glDrawArray(mode, first, count). Questa funzione è equivalente a:


glBegin(mode)
for i in range(count) :
    glArrayElement(first + i)
glEnd()

La differenza principale con glDrawElements riguarda la scelta degli indici: mentre glDrawElements permette di campionare gli indici in ordine sparso all’interno dell’array di OpenGL, glDrawArray permette solamente di disegnare un gruppo di vertici contiguo.

Per il momento basta così. Vi lascio con un piccolissimo esempio pratico:


from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

def init():
    glClearColor(0, 0, 0, 0)
    glShadeModel(GL_SMOOTH)

def test():
    glutInit()

def display():
    glClear(GL_COLOR_BUFFER_BIT)

    glEnableClientState(GL_VERTEX_ARRAY)
    glEnableClientState(GL_COLOR_ARRAY)

    vertici = [25, 25,
        100, 325,
        175, 25,
        175, 325,
        250, 25,
        325, 325]

    colori = [1, 0.2, 0.2,
        0.2, 0.2, 1,
        0.8, 1, 0.2,
        0.75, 0.75, 0.75,
        0.35, 0.35, 0.35,
        0.5, 0.5, 0.5]

    glColorPointer(3, GL_FLOAT, 0, colori)
    glVertexPointer(2, GL_INT, 0, vertici)

    indici = [0, 1, 2, 3, 4, 5]

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indici)

    glDisableClientState(GL_COLOR_ARRAY)
    glDisableClientState(GL_VERTEX_ARRAY)
    glFlush()

def reshape(w, h):
    glViewport(0, 0, w, h)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(0, w, 0, h)

if __name__ == '__main__':
    glutInit()
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
    glutInitWindowSize(350, 350)
    glutInitWindowPosition(100, 100)
    glutCreateWindow("Array: Due Triangoli")
    init()
    glutDisplayFunc(display)
    glutReshapeFunc(reshape)
    glutMainLoop()

L’esempio è piuttosto semplice alla luce della spiegazione.

Con questo si conclude la prima parte del corso dedicata ai vertici. Nella prossima vedremo come manipolare la “telecamera” e gli oggetti. Analizzeremo quindi i 3 aspetti fondamentali della visualizzazione: composizione, inquadratura e proiezione.

Vi ricordo che per ulteriori chiarimenti sono sempre disponibile nei commenti, su LQH e se ci sono potete abusare di me all’indirizzo Jabber: thek3nger@jabber.linux.it

4 comments on “OpenGL – Modellare con gli Array

    • Primo errore: hai messo un 3 dove andava un 6.

      glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indici);

      Secondo: prendiamo sempre il codice di prima. Tu hai specificato GL_UNSIGNED_BYTE. Solo che poi specifichi gli indici in int!

      int indici[] = {0, 1, 2, 3, 4, 5};

      In Python questo non è un problema perché tutte le conversioni sono implicite. In C invece no. Devi quindi usare il tipo adatto o meglio ancora puoi usare quindi i tipi che ti fornisce proprio OpenGL. Ad esempio cambia quella riga con:

      GLubyte indici[] = {0, 1, 2, 3, 4, 5};

      • Funziona!
        Il 3 era rimasto per errore perché avevo fatto delle prove per capire bene le funzioni 😀

        • Bene. 🙂 Puoi trovare la lista di questi tipi definiti di OpenGL con una semplice ricerca. Ti conviene usarli sempre per questioni di portabilità. 🙂