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:
glArrayElement(2)
glArrayElement(3)
glArrayElement(5)
glEnd()
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
Grazie ancora per questi articoli! 😀
Ho tradotto in C anche questo, ma non mi funziona :'(
Se hai voglia e tempo puoi dargli un’occhiata? 🙂
http://stylecode.altervista.org/opengl5.c
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 inint
!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à. 🙂