LA LIBRERIA GRAFICA OPENGL - I Parte
Libreria grafica:
Pacchetto software per lo sviluppo di applicazioni grafiche.
API (Application Program Interface) composta da un insieme
di funzioni e procedure delle quali esiste una precisa specifica.
- indipendenza da periferiche di input e di output
- portabilita' simile a quella dei linguaggi di programmazione
Funzionamento di una libreria grafica:

OPEN GL (Open Graphics Library)
Libreria grafica progettata per rendering in tempo reale e sviluppata
(GL) per Silicon Graphics. Scritta in C.
Organizzazione della libreria per sistema X:

- GL: libreria di base, contiene primitive geometriche
di base, attributi, trasformazioni, colore, illuminazione...
- GLU (Graphics Library Utilities):
implementata sopra GL, contiene primitive geometriche piu' evolute
(es. sfere) ed altro
- GLX: estensione OpenGL al sistema X, consente di
collegare OpenGL ad una finestra di rendering su una macchina
che usa il sistema X
- GLUT (Graphics Library Utility Toolkit):
funzionalita' minimali per dotare un programma che fa
grafica in OpenGL di un'interfaccia utente in ambiente X
Una organizzazione simile vale per l'ambiente Microsoft Windows.
Le funzioni GL / GLU / GLX / GLUT iniziano con "gl" / "glu" / "glx" /
"glut".
Primitive geometriche
Primitive di base
Contenute nella libreria GL, consentono di disegnare oggetti a
facce piane.
Una primitiva e' specificata da:
- lista di vertici che sono l'informazione geometrica
- tipo di primitiva che stabilisce come tali vertici
devono essere connessi per formare l'oggetto (informazione topologica)
Ciascun vertice e' specificato dandone le coordinate
in un sistema di riferimento locale alla primitiva
(coordinate di modellazione).
Come si definisce una primitiva
glBegin(tipo primitiva);
glVertex(...) per ogni vertice che forma la primitiva
glEnd();
La funzione glVertex ha piu' varianti secondo il numero e
il tipo di argomenti. Es:
-
glvertex2f(due float); glvertex2fv(array di due float);
per vertici specificati in 2D (implicitamente z=0)
-
glvertex3f(tre float); glvertex3fv(array di tre float);
per vertici specificati in 3D
Tipi di primitive
- GL_POINTS
L'oggetto e' un insieme di punti sparsi.
Ogni vertice e' preso singolarmente.
- GL_LINES
L'oggetto e' un insieme di segmenti di retta. Ogni
coppia di vertici consecutivi della lista definisce un segmento.
- GL_LINE_STRIP
L'oggetto e' una spezzata poligonale aperta.
I vertici della lista sono connessi in catena.
- GL_LINE_LOOP
L'oggetto e' una spezzata poligonale chiusa.
Come sopra ma l'ultimo vertice e' connesso al primo.
- GL_TRIANGLES
L'oggetto e' un insieme di triangoli.
Ogni tre vertici consecutivi della lista definiscono un triangolo.
- GL_TRIANGLE_FAN
L'oggetto e' un ventaglio di triangoli che condividono un
vertice. I primi tre vertici formano il primo triangolo, ogni
nuovo vertice forma un triangolo con il vertice precedente ed il
primo vertice della lista.
- GL_TRIANGLE_STRIP
L'oggetto e' una striscia di triangoli dove due triangoli
consecutivi hanno in comune un lato. I primi tre vertici
formano il primo triangolo, ogni nuovo vertice forma un
triangolo con i due vertici immediatamente precedenti.
- GL_QUADS
L'oggetto e' un insieme di quadrilateri. Ogni
quattro vertici consecutivi della lista definiscono un quadrilatero.
- GL_QUAD_STRIP
L'oggetto e' una striscia di quadrilateri
ciascuno dei quali ha un lato in comune con il quadrilatero precedente.
I primi 4 vertici formano il primo quadrilatero, ogni due nuovi vertici
formano un quadrilatero coi precedenti due.

- GL_POLYGON
L'oggetto e' un poligono. La lista di vertici specifica
la spezzata poligonale di contorno del poligono.
In un poligono i lati sono gli stessi che avrei con GL_LINE_LOOP.
Nota su quadrilateri e poligoni
OpenGL garantisce trattamento corretto solo per poligoni (e quadrilateri
come caso particolare) semplici e convessi.
-
Un poligono e' detto semplice se i suoi lati si intersecano
al piu' nei vertici estremi.
-
Un poligono e' detto convesso se dati due punti interni
qualunque P1 e P2 il segmento P1P2 e' interno al poligono.
Alcune configurazioni di vertici per GL_QUADS, GL_QUAD_STRIP e
GL_POLYGON possono dare luogo a poligoni intrecciati o non convessi
e quindi a risultati errati.
Esempio
Questo definisce un triangolo giacente sul piano z=0:
glBegin(GL_TRIANGLES);
glVertex2f(-0.5,0.0);
glVertex2f(0.5,0.0);
glVertex2f(0.0,0.5);
glEnd();
Primitive piu' avanzate
Contenute nella libreria GLU ed implementate a
partire dalle primitive di base di GL.
-
Oggetti a facce curve (sfere, cilindri) realizzati
approssimandoli mediante faccette piane.
-
Superfici curve di tipo NURBS che saranno automaticamente
trasformati in oggetti a facce piane per la visualizzazione.
Esempio
gluSphere(aux ,raggio, numero_fette, numero_strati)
dove aux e' un puntatore ad un oggetto ausiliario
usato da OpenGL,
la sfera viene approssimata mediante poligoni dividendola
verticalmente in fette ed orizzontalmente in strati,
controllo la precisione
dell'approssimazione specificando quante fette e strati voglio usare.
Attributi pittorici
Lo stato corrente di OpenGL contiene i valori
degli attributi.
L'aspetto di una primitiva geometrica dipende dal valore
corrente degli attributi al momento in cui viene eseguita.
Una volta che e' stato impostato un certo valore per un attributo,
questo influisce sul modo in cui sono
disegnate tutte le primitive che seguono fino a che
il valore non viene nuovamente cambiato.
Portata degli attributi
- attibuti a livello delle primitive
- attributi a livello dei singoli vertici
Attributi a livello delle primitive
Attributi che influiscono su una primitiva nel suo complesso
-
POINT SIZE = grandezza del marchio per i punti.
-
LINE WIDTH = spessore per le linee.
-
POLYGON MODE = modalita' di rendering per i poligoni e
le altre primitive bidimensionali:
disegnare l'area piena, solo i lati di contorno, solo i vertici.
Nello spazio un poligono ha due facce (front e back), posso assegnare
una modalita' di visualizzazione diversa nelle due facce.
Vanno assegnati al di fuori della primitiva (= fuori da glBegin/glEnd).
Vanno assegnati prima della primitiva,
in modo che al momento in cui si incontra la primitiva i
valori siano quelli voluti.
Istruzioni per impostare gli attributi:
-
glPointSize(valore);
assegna grandezza dei punti in pixel, default = 1.0.
-
glLineWidth(valore);
assegna spessore delle linee in pixel, default = 1.0.
-
glPolygonMode(faccia,modo);
assegna il modo in cui sono disegnate le primitive
bidimensionali.
modo e' uno tra:
- GL_POINT: vengono disegnati solo i vertici
- GL_LINE: vengono disegnati solo i lati
- GL_FILL: viene disegnata tutta l'area
faccia indica a quale delle due facce definite dalla
primitiva si applica il modo indicato:
- GL_FRONT = la faccia al "dritto" (senso antiorario dei vertici)
- GL_BACK = la faccia al "rovescio" (senso orario dei vertici)
- GL_FRONT_AND_BACK = entrambe
Attributi a livello dei vertici
Attributi che agiscono "vertice per vertice" anche all'interno della
stessa primitiva
- materiale (determina il colore)
- vettore normale
Possono essere cambiati all'interno di una coppia glBegin/glEnd.
Possono anche essere assegnati fuori (prima della primitiva) se
l'attributo assume lo stesso valore per tutti i vertici della primitiva.
Se i vertici di una primitiva hanno valori diversi dell'attributo,
il valore dell'attributo in un punto interno alla primitiva viene
interpolato ed assume un valore ottenuto come media dei valori
nei vertici, pesata rispetto alla distanza del punto dai vertici.
Esempio: un segmento con un vertice rosso e uno giallo assumera'
al suo interno toni di arancio degradanti dal rosso verso il giallo).
Vedremo materiali e normali quando parleremo di illuminazione.
Per adesso diciamo solo che, per far si' che un vertice
brilli di luce propria di un certo colore, bisogna chiamare:
- glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, colore);
dove colore e' un vettore di 4 float che specificano il colore
della luce emessa nel sistema RGBA.
Definizione di primitiva con attributi
Si devono scrivere nell'ordine:
- comandi per assegnare attributi globali della primitiva
- glBegin con tipo della primitiva
- lista di vertici ed attributi vertice-per-vertice
- glEnd
Esempio che disegna un triangolo pieno con colore rosso, verde, blu
nei tre vertici e sfumato di conseguenza all'interno
(il colore e' dovuto a luce propria emessa dal triangolo):
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBegin(GL_TRIANGLES);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, red);
glVertex2f(-0.5,-0.5);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, green);
glVertex2f(0.5,-0.5);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, blue);
glVertex2f(0.0,0.5);
glEnd();
dove red, green, blue sono tre vettori:
GLfloat red[4] = {1.0,0.0,0.0,1.0};
GLfloat green[4] = {0.0,1.0,0.0,1.0};
GLfloat blue[4] = {0.0,0.0,1.0,1.0};
Stack degli attributi
Esiste il modo di cambiare temporaneamente il valore di un
attributo e poi ripristinare lo stato precedente.
Esempio: cambiare il colore corrente in rosso,
disegnare qualcosa in rosso e poi ripristinare il colore precedente
qualunque esso sia.
Se voglio modificare il valore di un attributo momentaneamente per
disegnare una certa primitiva, salvando il valore precedente per poi
ripristinarlo, uso glPushAttrib e glPopAttrib.
Esempio che disegna un triangolo rosso, e anche tutto cio' che disegno
in seguito sara' rosso (se non cambio colore nel frattempo):
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, red);
glBegin(GL_TRIANGLES);
glVertex2f(-0.75,0.0);
glVertex2f(-0.25,0.0);
glVertex2f(-0.5,0.5);
glEnd();
Esempio che disegna un triangolo in rosso e poi ripristina il colore
precedente (cio' che disegno dopo sara' del
colore che avevo prima di assegnare il rosso):
/* salva su stack attributi relativi alla luce */
glPushAttrib(GL_LIGHTING_BIT);
/* imposta temporaneamente luce emessa di colore rosso */
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, red);
glBegin(GL_TRIANGLES);
glVertex2f(-0.75,0.0);
glVertex2f(-0.25,0.0);
glVertex2f(-0.5,0.5);
glEnd();
/* ripristina condizioni precedenti */
glPopAttrib();
Testo
OpenGL supporta due forme: STROKE e RASTER.
-
STROKE: caratteri sono costruiti con primitive grafiche.
Puo' essere definito al livello di dettaglio di qualsiasi oggetto.
Puo' essere manipolato con le trasformazioni geometriche e visualizzato
come le primitive geometriche.
-
RASTER: caratteri sono definiti da rettangoli di bit detti bit-blocks.
Un carattere raster viene messo direttamente nel frame buffer mediante
una operazione di bit-block-transfer (bitblkt), che muove il
blocco di bit usando una singola istruzione.
Semplice e veloce.
Produce scritte in "sovraimpressione".
Trasformazioni geometriche
Nel prossimo capitolo vedremo:
-
Trasformazioni di modellazione e di vista.
Per comporre la scena (collocare i vari oggetti,
definiti in un sistema di riferimento locale,
inserendoli in un sistema di riferimento comune),
e per porre la scena davanti alla telecamera
-
Trasformazioni di proiezione.
Definiscono il tipo di proiezione (ortogonale o prospettica)
e il volume di vista (quanta parte di spazio viene inquadrata
dalla telecamera)
-
Trasformazioni di viewport.
Per mappare il tutto nella finestra 2D
Per il momento consideriamo una situazione semplificata:
- Evitiamo le trasformazioni di modellazione, usando
una scena composta di oggetti
gia' definiti in uno spazio di riferimento comune, e contenuti
nel volume di vista di default (cioe' il cubo -1 +1)
- Come trasformazioni di vista e proiezione
usiamo quelle di default, ovvero:
- proiezione e' ortografica, punto di vista sta sull'asse z e
guarda in giu' lungo l'asse z
- volume di vista e' un cubo con x,y,z tra -1 e +1
- il tutto viene mappato nell'intera finestra grafica (cio' implica
una deformazione se la finestra non e' quadrata)
In tal modo non e' necessario definire trasformazioni. Vedremo le
trasformazioni in seguito.
Disegnamento della scena OpenGL
(Confrontare il corpo della funzione display nell'esempio
es1.c).
-
Abilitare le funzioni di OpenGL non di default:
- glEnable(GL_DEPTH_TEST); per eliminazione delle parti nascoste
- glEnable(GL_LIGHTING); per illuminazione
-
Pulire il foglio:
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
Definizione delle trasformazioni (in questo esempio manca
perche' usiamo le trasformazioni di default)
-
Disegno:
- alternanza di definizioni di primitive e impostazioni di attributi
- per ottenere un effetto locale (temporaneo),
le impostazioni di attributi possono essere racchiuse tra
glPushAttrib / glPopAttrib
- l'esempio disegna tre triangoli, una poligonale aperta e una serie
di punti
OpenGL e GLUT
OpenGL assume di avere a disposizione una finestra di output su cui
disegnare.
Tale finestra deve essere richiesta dialogando col window system / window
manager.
OpenGL non prevede funzioni per questo:
bisogna affiancare ad OpenGL un toolkit.
Glut e' un toolkit prototipale scritto
per X window system che permette di ottenere finestre per
disegnare in OpenGL e di gestirle.
Glut non e' un pacchetto per la realizzazione di interfacce grafiche,
non fornisce sofisticati componenti di interfaccia
(bottoni, campi di testo...).
Le funzionalita' di Glut sono piu' limitate:
- ottenere finestre (in cui OpenGL possa fare rendering)
- gestire tali finestre:
- ridisegnare contenuto grafico della finestra quando
essa viene nascosta da un'altra, oppure iconificata, e poi
torna ad essere visibile
- eseguire animazione (aggiornamento in continuo dell'immagine
visualizzata)
- catturare eventi generati dall'utente all'interno della
finestra (pressione di tasti della tastiera, movimento e
click del mouse)
- altre cose: gestione del cursore (forma...),
rendering di testo
- immettere opzioni tramite menu' pop-up
Aprire una finestra di rendering
Sequenza di istruzioni da chiamare nell'ordine (tipicamente nel main):
1. glutInit(&argc, argv);
Stabilisce connessione con X server.
Chiamata con gli stessi argomenti con cui il main program e' invocato
da command line.
2. glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
Stabilisce i requisiti della finestra che si vuole aprire.
Qui i requisiti sono:
una finestra abbia double buffer, RGBA color mode,
e z-buffer per l'eliminazione delle parti nascoste.
3. glutInitWindowSize(dimX,dimY);
Stabilisce le dimensioni della finestra da aprire (in pixel).
4. win = glutCreateWindow(etichetta);
Crea la finestra e ritorna un identificatore intero per tale finestra.
Etichetta e' una stringa che comparira' come titolo della finestra.
Gestire una finestra
Glut permette al programma di associare ad una finestra funzioni
(dette callback) che vengono chiamate automaticamente quando
avvengono certi eventi.
Le principali callback previste:
- Display function:
chiamata quando la finestra deve essere redisegnata
(es: quando diventa nuovamente visibile dopo essere stata
iconificata o nascosta da un'altra finestra).
Contiene le istruzioni OpenGL per redisegnare
il contenuto grafico della finestra.
- Idle function:
chiamata "continuamente" per
eseguire per animazione (es: sposta l'oggetto e lo redisegna)
- Keyboard function / Special key function:
chiamata quando utente preme un tasto ASCII / un tasto speciale
avendo il focus sulla finestra.
- Mouse function:
chiamata quando utente preme o rilascia un tasto del mouse
nella finestra.
- Motion function / Passive motion function:
chiamata quando utente muove il mouse tenendo un tasto premuto
/ senza tenere tasti premuti.
- Visibility function:
chiamata quando la finestra passa da visibile a non
visibile o viceversa
(tipicamente disattiva / riattiva animazione).
Le funzioni vengono registrate con glutXXXXXFunc(funzione);
e deregistrate con glutXXXXXFunc(NULL);
dove XXXXX e' il nome della funzione che si sta registrando.
Esempi:
glutDisplayFunc(f1);
glutIdleFunc(f2);
dove f1, f2 sono funzioni C del tipo void f(void).
Alcune delle funzioni callback accettano argomenti, che sono passati
automaticamente dal sistema alla funzione chiamata.
Ciclo degli eventi
L'istruzione glutMainLoop()
entra in un ciclo senza fine, interamente gestito da Glut, nel quale
- attende un evento a cui sia stata associata una callback
- attiva la callback corrispondente
- torna in attesa
Va chiamato come ultima istruzione del main, dopo avere registrato le
callback. Il loop non termina mai. Il programma puo' terminare eseguendo
exit all'interno di una funzione callback.
Nota: se e' stata registrata una Idle callback, questa viene chiamata
ad ogni ciclo.
Codice OpenGL in Glut
Il codice OpenGL trova posto nella display callback.
Non mettere codice OpenGL dentro altre callback.
Se una callback deve ridisegnare la scena
(es. idle callback per animazione), questo si ottiene
chiamando la funzione glutPostRedisplay();
che provoca l'esecuzione della display callback.
Altro su GLUT
Altre informazioni su Glut
(callback e menu' pop-up).