LA LIBRERIA GRAFICA OPENGL - II Parte

Traformazioni geometriche

OpenGL compie questa sequenza (pipeline) di trasformazioni geometriche su ogni primitiva:
     +------------------------+ 
     |[3D] Object coordinates | sistema di riferimento locale in cui e'
     |(o modeling coordinates)| definito un singolo oggetto della scena
     +------------------------+
TRASFORMAZIONI DI| collocano il singolo oggetto all'interno della scena
MODELLAZIONE     | (in generale una trasf. diversa per ogni oggetto)
                 V
     +------------------------+ sistema di riferimento globale di tutta
     |[3D] World coordinates  | la scena
     +------------------------+
TRASFORMAZIONE DI| posiziona la scena davanti alla telecamera
VISTA            | 
                 V
     +------------------------+ sistema di riferimento solidale con la
     |[3D] View coordinates   | telecamera: occhio in (0,0,0), che guarda verso
     |  (o eye coordinates)   | la direzione negativa dell'asse z
     +------------------------+
TRASFORMAZIONE DI| definisce tipo di trasformazione (parallela o 
PROIEZIONE       | prospettica) e volume di vista
                 V
     +------------------------+ sistema di riferimento normalizzato nel cubo
     |[3D] Normalized         | con x,y,z tra -1 e +1, contiene (deformato
     | projection coordinates | dalla trasf. di proiezione) il volume di vista
     +------------------------+ 
TRASFORMAZIONE DI| fatta automaticamente da OpenGL, il programma controlla
VIEWPORT         | l'estensione della viewport con glViewport
                 V
     +------------------------+
     |[2D] Device coordinates | sistema di riferimento della finestra sullo
     |(o viewport coordinates)| schermo (coordinate 2D espresse in pixel)
     +------------------------+

Come si specificano le trasformazioni

OpenGL tiene 2 matrici di trasformazione nel suo stato corrente: Sono matrici 4x4 (matrici in coordinate omogenee 3D), a queste matrici vanno soggette tutte le primitive che vengono rese:

Vi sono primitive per modificare le matrici correnti. Sono le stesse su entrambe le matrici. Bisogna dichiarare esplicitamente su che matrice si vuole agire.

Per modificare la matrice corrente, OpenGL fornisce funzioni che permettono di definire una trasformazione complessa come composizione di trasformazioni elementari. Le trasformazioni elementari sono: Posso ottenere qualsiasi trasformazione componendo queste.

Importante: la composizione delle matrici avviene moltiplicando la nuova matrice a destra di quella corrente, quindi PRIMA viene eseguita la trasformazione corrispondente alla NUOVA matrice e DOPO la trasformazione corrispondente alla VECCHIA matrice.

In pratica le trasformazioni sono eseguite IN ORDINE INVERSO a come le specifico nel codice.

Esempio:

glLoadIdentity(); matrice identita' I
glTRanslate(...); matrice = I T = T dove T = matrice di traslazione 
glRotate(...); matrice = T R dove R = matrice di rotazione 
Risultato: nell'ordine prima ruoto poi traslo i punti

Esempio di trasfomazione composta

Rotazione attorno a un punto C = (xC,yC,zC) diverso da origine:
  1. traslo di (-xC,-yC,-zC) per portare C nell'origine del nuovo riferimento
  2. ruoto dell'angolo alpha voluto attorno a origine
  3. traslo di (xC,yC,zC) per portare C alla posizione originale
Nel codice:

Stack delle matrici

Le matrici MODELVIEW e PROJECTION fanno parte dello stato corrente. Cambiare la matrice corrente influisce su tutte le primitive da quel momento in poi. Se voglio modificarla momentaneamente per disegnare un certo gruppo di primitive (es. quelle che formano un oggetto), e poi ripristinarla, devo usare glPushMatrix e glPopMatrix. Tipicamente questo succede per le trasformazioni di modellazione con la MODELVIEW.

Esempio:

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix(); /* salva la matrice corrente su stack */
  ... modifiche alla matrice ...
  ... disegnamento primitive sotto influsso della matrice modificata ...
  glPopMatrix(); /* rispristina la matrice precedente */
  ... le primitive disegnate qui sono soggette alla matrice
      senza le modifiche fatte tra push e pop ...

Trasformazioni di modellazione e di vista

In OpenGL sono controllate agendo sulla stessa matrice, la MODELVIEW. Cio' significa che nel codice sono invocate PRIMA le funzioni OpenGL per la trasformazione di vista e DOPO quelle per le trasformazioni di modellazione (ordine inverso!).

Trasformazioni di modellazione

Agiscono su ogni singolo oggetto separatamente per collocarli all'interno della scena.

Definite agendo sulla matrice MODELVIEW, tipicamente tra glPushMatrix e glPopMatrix affinche' la trasformazione agisca solo sull'oggetto in questione.

Nell'ordine: dimensiono (glScale), oriento (glRotate), posiziono (glTranslate) ciascuno degli oggetti per comporre la scena.

Trasformazione di vista

Agisce sull'intera scena per posizionarla davanti alla telecamera.

Definita agendo sulla matrice MODELVIEW, senza Push e Pop.

Oriento (glRotate), posiziono (glTranslate) la scena davanti alla telecamera (o la telecamera davanti alla scena).

Oppure: uso la funzione di utilita' gluLookAt, che mi permette di definire la trasformazione di vista in modo piu' intuitivo.

Trasformazione di proiezione

Definisce il tipo di proiezione e il volume di vista. Agisco sulla matrice PROJECTION con una fra:

Trasformazione di viewport

Il contenuto del volume di vista viene mappato (deformandolo) nel cubo normalizzato delle coordinate di proiezione.

Questo cubo viene poi appiattito schiacciandolo nella direzione z e mappato nelle coordinate 2D della viewport grafica (finestra sullo schermo).

Di default OpenGL usa l'intera area della finestra. Posso invece specificare esplicitamente una viewport (es. piu' piccola) con

Conservazione dell'aspect ratio

Se l'aspect ratio (rapporto tra dimensioni x e y) della viewport e quella del volume di vista non sono uguali, l'immagine della scena viene deformata allungandola in verticale o in orizzontale.

Per preservare nell'immagine le proporzioni della scena, devo mantenere le due aspect ratio uguali tra loro:

Esempio

La finestra iniziale ha dimensione 400x400 pixel, le trasformazioni iniziali sono: Aspect ratio = 1.0 perche' finestra quadrata.

Se la finestra viene deformata a 400x200

Display list

In OpenGL una primitiva grafica e' definita da una lista di vertici racchiusi tra glBegin e glEnd, piu' eventualmente altre istruzioni che specificano attributi (normali, colore...).

In condizioni normali, una primitiva viene mandata dal client al server, che la visualizza immediatamente, e poi la "dimentica" (ovvero non la memorizza). Se il client vuole ridisegnare la stessa primitiva una seconda volta, deve reinviarla, con spesa di tempo.

Display list = serie di primitive di OpenGL a cui e' stato assegnato un nome (identificatore numerico).

Una display list viene visualizzata usando i parametri dello stato corrente di OpenGL. Cosi' la stessa primitiva, inserita in una display list, puo' essere chiamata con stato corrente modificato, dando luogo a un effetto diverso.

Per esempio, tra una chiamata e l'altra della display list posso cambiare colori, modalita' di visualizzazione wireframe/solid, spessore di linea, trasformazioni geometriche.

Posso disegnare un'intera scena (es. composta da parallelepipedi di dimensioni e colori diversi) usando un'unica display list che viene scalata/traslata/colorata in modo diverso.

Istruzioni per display list

Nota: Una display list viene visualizzata sotto l'influsso dello stato corrente al momento in cui chiamo glCallList. Questo non vieta di modificare lo stato corrente dentro la display list stessa. Tenere pero' presente che, in questo caso, dopo ogni chiamata alla display list trovero' lo stato modificato. Molto meglio cambiare lo stato localmente alla display list, usando glPush... e glPop...

Esempio

/* definisce display list contenente un quadrato */
mylist = glGenLists(1);
glNewList(mylist,GL_COMPILE);
glBegin(GL_QUADS);
  glVertex2f(0.0,0.0);
  glVertex2f(1.0,0.0);
  glVertex2f(1.0,1.0);
  glVertex2f(0.0,1.0);
glEnd();
glEndList();
...
/* disegna piu' quadrati chiamando la display list varie volte */
glMatrixMode(GL_MODELVIEW);
for (i=0; i<5.0; i++)
{
  glPushMatrix();
    glTranslatef(0.2*i,0.0,0.0);
    glScalef(0.1,0.1,1.0); 
    glCallList(mylist);
  glPopMatrix();
}
Quest'esempio e' contenuto nel file Quadra.java (il quale richiede anche BasicGL.java).

Illuminazione

A luci disabilitate, ogni vertice ha il suo colore (assegnato con glColor), e ogni punto facente parte di una primitiva 1D (GL_LINES,...) o 2D (GL_TRIANGLES, GL_POLYGON,...) ha colore ottenuto interpolando quello dei vertici della primitiva.

Ne segue che una primitiva ha un unico colore uniforme se i suoi vertici hanno tutti lo stesso colore. Quindi in 3D se tutte le facce di un solido hanno stesso colore, il solido appare come un'unica macchia di colore senza effetto di tridimensionalita'.

A luce abilitata, ogni punto e' colorato con:

Fattori che entrano in gioco:

Tipi di luce

Ogni sorgente luminosa contiene queste tre componenti di luce, e ciascuna componente puo' avere colore e intensita' diversa. Inoltre e' prevista una luce ambiente "di sfondo", indipendente dalle sorgenti luminose (attiva anche a sorgenti luminose spente).

Ogni materiale ha una sensibilita' particolare a ciascuna delle tre componenti della luce. Inoltre esistono materiali in grado di emettere luce propria (luce emessa).

Normali

Nel caso di luce diffusa, l'intensita' dell'illuminazione dipende dall'angolo formato dalla direzione della luce con la normale alla superficie.

OpenGL NON calcola da solo le normali, bisogna dargliele specificandole nella primitiva stessa.

Assegnazione della normale corrente

glNormal3f(tre float) oppure glNormal3fv(vettore di tre float)

Varie forme analogamente a quanto visto per glVertex...

Va chiamata tra glBegin e glEnd e influisce tutti i vertici che seguono fino a che la normale corrente non viene modificata di nuovo.

Tipicamente:

In realta' la normale e' uno di quegli attributi OpenGL che valgono "vertice per vertice" (come anche il colore). Posso assegnare una normale diversa a ogni vertice della stessa faccia. La normale per ciascun punto interno della faccia e' allora interpolata, cosicche' la normale cambia gradualmente all'interno della faccia.

Per default la normale corrente e' (0,0,0) e gli oggetti appaiono "piatti" (colorati di colore uniforme anche in presenza di luce diffusa, nonostante l'inclinazione diversa delle facce).

Proprieta' di materiale

Stabiliscono, per le primitive immediatamente seguenti: Sono assegnate con
glMaterial(faccia, nome_parametro, valore_parametro);
La funzione ha 4 varianti: glMaterialf, glMateriali, glMaterialfv, glMaterialiv, dove
nome_parametro       valore (scalare o vettore)
-------------------------------------------------------------------------
GL_AMBIENT           vettore di componenti RGBA, default (0.2,0.2,0.2,1)
                     frazione di luce ambiente ricevuta che la primitiva 
                     riflette, per ogni colore
-------------------------------------------------------------------------
GL_DIFFUSE           vettore di componenti RGBA, default (0.8,0.8,0.8,1)
                     frazione di luce diffusa ricevuta che la primitiva 
                     riflette, per ogni colore
-------------------------------------------------------------------------
GL_SPECULAR          vettore di componenti RGBA, default (0,0,0,1)
                     frazione di luce speculare ricevuta che la primitiva 
                     riflette, per ogni colore
-------------------------------------------------------------------------
GL_AMBIENT_AND_DIFFUSE  per assegnare in un colpo GL_AMBIENT e GL_DIFFUSE
                        con lo stesso vettore RGBA
-------------------------------------------------------------------------
GL_EMISSION          vettore di componenti RGBA, default (0,0,0,1)
                     componenti della luce emessa dalla primitiva stessa
-------------------------------------------------------------------------
GL_SHININESS         intero tra 0 e 128
                     intensita' della luce speculare riflessa
-------------------------------------------------------------------------
Nota: componenti RGB = 0,0,0 implicano luce "nera", ovvero nessuna luce (o nessuna componente di quel tipo nella luce).

Proprieta' delle sorgenti luminose

Posso definire una o piu' sorgenti luminose e, dopo averle definite, abilitarle/disabilitarle (per default disabilitate).

Tipi di sorgenti

Ogni sorgente e' caratterizzata da NOTA:
La posizione della luce e' soggetta alle trasformazioni come tutte le primitive geometriche, in particolare e' soggetta alla matrice MODELVIEW corrente. E' possibile ottenere luci che si spostano facendo precedere la definizione della loro posizione da una qualche trasformazione. Invece, trasformazioni poste dopo la definizione della luce (ma prima della definizione degli oggetti della scena) non cambiano la posizione della luce ma muovono solo la scena a luce ferma.

Definizione di sorgenti luminose

glLight(luce, nome_parametro, valore_parametro);
nome_parametro       valore (scalare o vettore)
-------------------------------------------------------------------------
GL_AMBIENT           vettore di componenti RGBA, default (0,0,0,1)
                     valori di luce ambiente emessa dalla sorgente
-------------------------------------------------------------------------
GL_DIFFUSE           vettore di componenti RGBA, default (1,1,1,1) per
                     GL_LIGHT0 e (0,0,0,1) per le altre
                     valori di luce diffusa emessa dalla sorgente
-------------------------------------------------------------------------
GL_SPECULAR          vettore di componenti RGBA, default (1,1,1,1) per
                     GL_LIGHT0 e (0,0,0,1) per le altre
                     valori di luce speculare emessa dalla sorgente
-------------------------------------------------------------------------
GL_POSITION          vettore di coordinate (x,y,z,w)
                     posizione della luce, w=1 per posizione in un punto
                     (x,y,z) al finito, w=0 per posizione all'infinito 
                     nella direzione del vettore (x,y,z)
-------------------------------------------------------------------------
... altri parametri per le luci spot (di cui non ci occupiamo) ...
-------------------------------------------------------------------------
... altri parametri per specificare come la luce e' attenuata con
    l'aumentare della distanza dalla sorgente (non ce ne occupiamo) ...
-------------------------------------------------------------------------

Modello di illuminazione

Il modello di illuminazione controlla
glLightModel(nome_parametro, valore_ parametro) 
Anche questa funzione ha 4 versioni: glLightModelf, glLightModeli, glLightModelfv, glLightModeliv.
nome_parametro             valore 
-------------------------------------------------------------------------
GL_LIGHT_MODEL_AMBIENT     vettore di componenti RGBA della luce ambiente
                           "di sfondo" (default 0.2,0.2,0.2,1)
-------------------------------------------------------------------------
GL_LIGHT_MODEL_TWO_SIDE    0 oppure 1 (default = 0)
      0 --> illumina solo front faces (in oggetti "chiusi" le back faces
            non si vedono...)
-------------------------------------------------------------------------
GL_LIGHT_MODEL_LOCAL_VIEWER       0 oppure 1 (default = 0)
      0 --> non tiene conto della posizione del punto di vista nel 
       calcolo delle riflessioni speculari (meno accurato ma piu' veloce)
-------------------------------------------------------------------------

Nota bene

In presenza di luci, il colore apparente di un oggetto e' determinato dal colore della luce che riflette, e apparentemente glColor non ha effetto.

Si puo' usare l'istruzione glColorMaterial per far si' che i parametri di materiale seguano il valore attuale del colore assegnato con glColor

Abilitazioni per le luci

Inoltre in presenza di illuminazione con luce diffusa puo' essere utile:

Cosa fare per ottenere una scena illuminata