domenica 28 aprile 2013

Descrivere Punti, linee e poligoni

Questo articolo descrive come OpenGL realizza le primitive geometriche. Tutte le primitive geometriche sono descritte in base ai loro vertici, coordinate che descrivono i punti stessi, e i termini dei segmenti agli angoli dei poligoni. La sezione successiva descrive come queste primitive sono visualizzate e quale controllo si ha su questa operazione.

Cosa sono punti, linee e poligoni?

E' molto probabile che si abbia una buona idea in termini matematici della definizione di punto, linea e poligono. In OpenGL il significato è simile. La sola differenza è data dai limiti del calcolo basato sul computer. In ogni istruzione OpenGL, i calcoli a virgola mobile hanno dei limiti di precisione, e possono avere errori di routine; di conseguenza le coordinate di punti, linee e poligoni in OpenGL soffrono degli stessi problemi. Un altra differenza importante sorge dalle limitazioni del sistema di visualizzazione grafica. In uno schermo, l'unità visualizzabile minima è il pixel, che può valere meno di un centesimo di pollice , ma è molto lontano dal concetto matematico di infinitamente piccolo (per il punto) o infinitamente sottile (per la linea). Quando OpenGL effettua dei calcoli si assume che i punti siano vettori di numeri a virgola mobile. Comunque un punto è tipicamente ma non sempre disegnato come un singolo pixel. E molti punti con coordinate leggermente diverse possono essere disegnate sullo stesso pixel.

Punti
Un punto è rappresentato da un insieme di numeri in virgola mobile che rappresentano un vertice. Tutti i calcoli interni sono fatto come se i vertici fossero tridimensionali. Vertici specificati dall'utente come bidimensionali (cioè con solo coordinate x ed y) vengono assegnati a coordinata z pari a zero da OpenGL.

Avanzato
OpenGL lavora nelle coordinate omogenee della geometria proiettiva tridimensionale, quindi per calcoli interni, tutti i vertici sono rappresentati con quattro coordinate virgola mobile (x, y, z, w). Se w è diverso da zero, queste coordinate corrispondono al punto tridimensionale euclideo (x / w, y / w, z / w). È possibile specificare la coordinata w nei comandi OpenGL, ma di fatto avviene raramente. Se la coordinata w non è specificato, ottiene il valore 1,0.

Linee
In OpenGL, la linea terminata, si riferisce ad un segmento di linea, non la sua versione matematica che si estende all'infinito in entrambe le direzioni. Ci sono semplici modi per specificare una serie di segmenti di linea collegati, o anche un poligono, formato da serie di segmenti legati. In tutti i casi, però, le linee che costituiscono la serie connesse, sono specificate in termini di vertici come loro punti terminali.


Poligoni
I poligoni sono aree chiuse formate da anelli di segmenti, ove i segmenti sono specificati mediante i loro vertici come punti terminali. I poligoni sono riempiti con pixel di colore, ma possono anche essere disegnati come linee di contorno o insiemi di punti. I poligoni sono oggetti complessi, pertanto OpenGL pone dei limiti a ciò che può costituire un poligono. Per prima cosa i bordi dei poligoni non possono intersecarsi tra loro ( in matematica si parla di poligono semplice). Per seconda cosa, i poligoni devono essere convessi, e non possono avere tacche al loro interno. OpenGL non restringe il numero di segmenti che possono costituire un poligono convesso; ma un poligono con un foro non può essere descritto. Essi sono non convessi pertanto, non viene disegnato come lo si immagina. In alcuni sistemi i poligoni poco meno che convessi possono essere riempiti.
La ragione per cui OpenGL restringe i tipi di poligoni validi, è che più semplice fornire calcoli di rendering veloce solo per poche classi di poligoni. Per massimizzare le performance, OpenGL assume che tutti i poligoni siano semplici. Tuttavia molte superfici reali consistono di poligoni non semplici, e non convessi, o poligoni con fori. Sicché tutti i poligoni complessi possono essere generati da poligoni semplici, per costruire oggetti complessi, esistono funzioni nella libreria GLU.

Rettangoli
Dato che i rettangoli sono molti utilizzati nelle applicazioni grafiche, OpenGL fornisce una funzione per disegnare rettangoli pieni, questa istruzione viene chiamata glRect*(). La sintassi può essere:

void glRect{sifd}( valore x1, valore y1, valore x2, valore y2);
void glRect{sifd}( vettore *v1, vettore *v2);

Disegna un rettangolo definito dai punti degli angoli (x1, y1) e (x2,y2). Il rettangolo giace sul piano z=0, e i lati sono paralleli agli assi x e y. Se viene usata la forma vettoriale, i vertici sono definiti dai puntatori ai due vettori, i quali contengono le coppie di x, y.

Curve e superfici curve
Ogni linea curva può essere approssimata come una serie di rette collegate tra loro. Quindi suddividendo le linee curve e le superfici si approssimano usando una serie di segmenti piatti o poligoni, alcuni di dimensioni inferiori al pixel visualizzato sullo schermo. Anche se non esistono funzioni primitive per le curve, OpenGL consente un supporto diretto alla suddivisione e le disegna.

sabato 20 aprile 2013

Un sistema di coordinate in breve

Qualunque sia il sistema con il quale la finestra viene creata, o ridimensionata, il sistema invia una notifica all'utente. Anche se si sta usando GLUT, la notifica è automatica; questo tipo di routine è memorizzata dalla funzione glutReshapeFunc(). Bisogna registrare una chiamata alla funzione che:
  • Ristabilisca l'area del rettangolo ove viene calcolata l'immagine;
  • Definire il sistema di coordinate dell'oggetto che deve essere disegnato.
Un esempio di chiamata alla funzione di dimensionamento:

void riforma (int l, int a)
{
glViewport (0,0, (GLsizei) l, (Glsizei) a);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble) l, 0.0, (Gldouble) a);
}

Le funzioni di GLUT ricevono internamente due parametri, lunghezza ed altezza della finestra nuova, mossa, o ridimensionata; glViewport() aggiusta i pixel dell'immagine per il disegno dell'intera finestra. Nelle tre istruzioni successive, le coordinate del disegno sono impostate sull'angolo in basso a sinistra come (0,0) e sull'angolo in lato a destra come (l,a). Si può fare l'esempio con un foglio di carta millimetrata. I valori l e a definiti in riforma(), rappresentano il numero di righe e colonne dell'immagine. Quindi si fissa un asse sul foglio millimetrato mediante la funzione gluOrtho2D(), la quale fissa l'origine a (0,0) come l'angolo più in basso a sinistra. Pertanto quando vengono calcolati le linee, i punti ed i poligoni essi appariranno come oggetti bidimensionali in posizioni prevedibili.

sabato 13 aprile 2013

Forzare il disegno

Come già illustrato in precedenza, i sistemi grafici moderni possono essere pensati come una catena di montaggio. L'unità centrale di processo (CPU) invia i comandi di disegno. Tuttavia l'hardware installato deve effettuare altre operazioni e trasformazioni geometriche. Viene effettuato il clipping, seguita da ombreggiatura e texture. Infine i valori sono scritti in piani di bit per lo schermo. Nelle architetture ad alto livello queste operazioni sono effettuate da componenti hardware diverse, le quali sono state progettate per operazioni specifiche da completare velocemente. In questo modo la CPU non deve attendere che ogni comando di disegno sia completato per eseguirne un altro. Mentre la CPU si trova al vertice della catena di montaggio, le altre componenti effettuano le operazioni successive. Inoltre l'applicazione può essere eseguita più macchine contemporaneamente, attraverso connessioni di rete che costituiscono un legame client - server. Tutto questo può essere estremamente inefficiente se la rete esegue un comando per volta, in quanto vi può essere un notevole ritardo nella trasmissione. Solitamente il client raccogli un insieme di comandi prima di inviare il pacchetto di dati. Per questo motivo OpenGL fornisce il comando glFlush(), il quale forza il client ad inviare il pacchetto di rete anche se non fosse al completo. Dove non vi è una rete collegata tutti i comandi sono eseguiti immediatamente sul server, e glFlush() non ha alcun effetto. Comunque se si sta scrivendo una applicazione che possa lavorare bene con o senza rete, è bene aggiungere il comando glFlush() al termine di ogni immagine o scena. Da notare che glFlush() non attende che il fotogramma sia completo, solo forza il disegno ad essere eseguito, quindi garantisce che tutti i precedenti comandi esecutivi in un tempo finito e non vi siano altri comandi eseguiti. Altre situazioni alle quali glFlush() è utile:
  • Render via software che costruisce una immagine in memoria e non vuole aggiornare lo schermo continuativamente;
  • Implementare una raccolta dei comandi per ammortizzare i costi di avvio. Come citato nell'esempio della rete di macchine.

mercoledì 10 aprile 2013

Specificare un colore

Con OpenGL, la descrizione della forma di un oggetto che deve essere disegnato è indipendente dal suo colore. Qualunque sia la geometria dell'oggetto disegnato, userà lo schema di colore corrente. Lo schema di colore può essere semplice come “disegna tutto rosso scarlatto” oppure complicato come “ prendi un oggetto fatto di plastica blu, con un riflettore giallo puntato su di esso, in modo che vi sia una luce diffusa rossastra su tutto il resto”. In generale, il programmatore OpenGL deve impostare il colore o lo schema di colore per disegnare l'oggetto. Non appena lo schema di colore viene modificato, tutti gli oggetti saranno disegnati con quello schema di colore. Questo metodo aiuta OpenGL a raggiungere ottime performance che non si otterrebbero se non tenesse traccia dello schema di colore. Un esempio di pseudo-codice:
imposta_colore(rosso);
disegna_oggetto(A);

imposta_colore(verde);

disegna_oggetto(B);

disegna_oggetto(C);

Colorazione, ombreggiatura, illuminazione sono grandi argomenti che richiedono capitoli di libri, o lunghi paragrafi dedicati ad essi. Tuttavia, per disegnare semplici forme primitive, necessita solo una conoscenza di base su come impostare il colore corrente, questa informazione viene fornita nel paragrafo seguente. Per impostare il colore si utilizza il comando glColor3f(). Richiede 3 parametri il cui valore è compreso tra 0.0 e 1.0, virgola mobile. Questi rappresentano le componenti Rosso, Verde, Blu del colore. Utilizzare il valore 1.0 per un solo parametro, azzerando gli altri, crea un colore puro.
glColor3f(1.0, 0.0, 0.0);

Crea il colore rosso più luminoso del sistema di disegno, senza componenti verdi o blu. Tutti i parametri a 0 creano un colore nero, tutti a 1 il colore bianco. Qui vi sono gli otto comandi del colore che possono essere impostati per esempio.
glColor3f(0.0, 0.0, 0.0); nero

glColor3f(1.0, 0.0, 0.0); rosso

glColor3f(0.0, 1.0, 0.0); verde

glColor3f(1.0, 1.0, 0.0); giallo

glColor3f(0.0, 0.0, 1.0); blu

glColor3f(1.0, 0.0, 1.0); magenta

glColor3f(0.0, 1.0, 1.0); ciano

glColor3f(1.0, 1.0, 1.0); bianco

Come si può notare, nella precedente routine era stato spiegato come impostare il colore di pulizia dello schermo in glClearColor() , usa quattro parametri, i primi tre sono i parametri di glColor3f(); il quarto è il valore di alfa.

sabato 6 aprile 2013

Un kit di disegno indispensabile

Questo articolo spiega come azzerare la finestra e prepararla per il disegno, impostare i colori dell'oggetto da disegnare, e forza il completamento del disegno. Nessuno di questi argomenti ha che fare con il disegno in modo diretto, ma ogni programma che disegna oggetti geometrici deve gestire queste cose.

Azzerare la Finestra

Per disegnare sul computer, ci si comporta come se si dovesse disegnare su un foglio bianco, e poi si traccia l'immagine. Su di un computer, la memoria contiene l'ultima immagine utilizzata per riempire lo schermo, pertanto bisogna azzerarla usando un colore di sfondo prima di disegnare una nuova scena. Il colore utilizzato dipende dall'applicazione, per un programma di scrittura si usa un colore bianco (il colore della carta) prima di tracciare il testo. Se si sta disegnando una immagine vista nello spazio, il colore scelto può essere il nero, prima di disegnarne le stelle ed i pianeti. Alcune volte non è necessario riscrivere l'intera immagine ma solo alcune parti. A questo punto ci si può chiedere cosa voglia dire azzerare la finestra, non si potrebbe disegnare un rettangolo delle stesse dimensioni della stessa? Esiste una operazione più semplice , in quanto OpenGL semplifica con un comando speciale dedicato al solo azzeramento della finestra, inoltre su alcune macchine, l'hardware grafico consiste di buffer multipli contenenti i colori dei pixel che sono mostrati. Questi ed altri buffer devono essere ripuliti di tanto in tanto, pertanto è utile possedere un comando dedicato allo scopo.
Bisogna inoltre sapere che i colori dei pixel sono immagazzinati nell'hardware grafico come bitplane. Vi sono due modi per memorizzare, il primo (RGBA), rosso, verde, blu e alfa, immagazzina i valori direttamente come bitplane. Oppure esiste un singolo indice di valore che fa riferimento al colore di tabella. Il colore RGBA è quello comunemente usato. Un esempio di linea di codice nel quale la finestra viene azzerata con il nero è:
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
 
La prima linea di codice serve comunicare di azzerare lo schermo con il nero, il comando successivo ripulisce l'intera finestra con il colore corrente. Il singolo parametro glClear() indica quale buffer di memoria deve essere ripulito. In questo caso vien pulito solo il buffer del colore, dove viene conservata l'immagine. Tipicamente, viene impostato il colore una sola volta, all'inizio dell'applicazione, e l'operazione di pulizia avviene spesso. OpenGL tiene traccia del colore corrente di pulizia come variabile di stato. Se bisogna ripulire anche il buffer della profondità dell'immagine s i usa la sequenza di comandi:
glClearColor(0.0, 0.0, 0.0, 0.0);
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

In questo caso la chiamata a glClearColor() è la stessa di prima, il comando glClearDepth() specifica che il valore di ogni pixel dio profondità deve essere impostato, e il parametro glClear() consiste ora di OR bitwise, di tutti i buffer che devono essere puliti.