Trasformazioni di coordinate in OpenGL

Note integrative al corso di Grafica Interattiva, corso di Laurea in Informatica, nuovo ordinamento.
A cura di Paola Magillo, DISI, Universita' degli Studi di Genova.

Struttura (logica) di una scena OpenGL

Possiamo pensare una scena OpenGL come strutturata in modo gerarchico, cioe' composta da un insieme di oggetti, ciascuno dei quali puo' essere un aggregato di sotto-oggetti ecc.

Gli oggetti elementari (indivisibili) sono le singole primitive.

Un oggetto aggregato e' costituito da piu' oggetti (elementari o aggregati).

Questo porta a una gerarchia di aggregazione, che puo' essere rappresentata come un albero dove

Questa gerarchia non e' scritta nel codice del programma, e' nella mente della persona umana che progetta la scena.

Come si forma una scena

Ciascuna primitiva (oggetto elementare) e' definito dandone i vertici in un suo sistema di coordinate locale. I sistemi di coordinate locali di primitive diverse sono indipendenti.
Esempio: un quadrato e un triangolo entrambi definiti con baricentro in (0,0).

Un oggetto aggregato e' formato portando tutti i sotto-oggetti di cui e' formato in un sistema di coordinate comune.
Esempio: lascio il quadrato con baricentro in (0,0) ma sposto il triangolo sopra al quadrato (tipo tetto della casetta).

Continuo cosi' fino ad aver formato l'intera scena (pensata come oggetto aggregato complessivo).

Ruolo delle trasformazioni di modellazione

I sistemi di coordinate in cui sono definiti i singoli oggetti (a qualsiasi livello della gerarchia di aggregazione) si chiamano coordinate di modellazione e ogni oggetto ha le sue.

Il sistema di coordinate in cui e' definita l'intera scena (coordinate di modellazione dell'oggetto al vertice della gerarchia) si chiama coordinate del mondo ed e' unico.

Per portare ciascun oggetto dalle sue coordinate di modellazione alle coordinate di modellazione dell'oggetto suo padre, operiamo trasformazioni di modellazione.

Le trasformazioni di modellazione sono una sequenza di trasformazioni di tre tipi: traslazioni, rotazioni, scalature.

Quando la scena e' pronta

Ho portato tutti gli oggetti nel sistema di coordinate del mondo. Prima di scattare la foto devo:

Ruolo delle trasformazioni di vista

In OpenGL metto la scena davanti alla macchina fotografica, non viceversa.

La macchina fotografica ha un suo sistema di coordinate fisso, dette coordinate di vista, con

Le trasformazioni di vista portano la scena da coordinate del mondo a coordinate di vista. Compiono traslazioni e rotazioni.

Ruolo delle trasformazioni di proiezione

Scelgono che lente montare sulla macchina, il che determina

In OpenGL sono definite dando il volume di vista

Le trasformazioni di proiezione portano, deformandolo, il volume di vista a coincidere con il cubo di diagonale (-1,-1,-1) - (1,1,1).

Il sistema di coordinate interno al cubo e' detto coordinate normalizzate di proiezione o NPC

In proiezione ortografica la posizione del punto di vista funge solo da origine del sistema di riferimento, non influisce sulla forma del volume di vista.
In proiezione prospettica la posizione del punto di vista concorre a determinare la forma del volume di vista in quanto e' definisce l'origine del tronco di piramide.

Ruolo della viewport

Definisce il numero di righe e colonne del frame buffer, corrispondente alle dimensioni in pixel della finestra su schermo.

Ora OpenGL scatta la foto:

Riepilogo delle 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:

+-----+   +---------+   +-----+   +----------+   +-------+
|punto|   |matrice  |   |punto|   |matrice   |   |punto  |   
|  P  |-->|MODELVIEW|-->| Mm P|-->|PROJECTION|-->|Mp Mn P|-->...
+-----+   |   Mm    |   +-----+   |   Mp     |   +-------+
          +---------+             +----------+

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.

Ordine di scrittura delle trasformazioni

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 di trasfomazione composta

In OpenGL scalature e rotazioni avvengono sempre tenendo come punto fermo l'origine del sistema di modellazione corrente.

Per tenere fermo un altro punto C = (xC,yC,zC) diverso da origine:

  1. traslo di (-xC,-yC,-zC) per portare C nell'origine
  2. scalo o ruoto come desiderato, l'origine resta ferma
  3. traslo di (xC,yC,zC) per riportare l'origine in C

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 */
  ... modifico la matrice ...
  ... disegno 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.

Supponiamo che ciascun oggetto abbia coordinate locali con origine nel suo baricentro.
Di solito prima dimensiono (glScale), poi oriento (glRotate), infine 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 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:

In alternativa a glFrustum, posso usare la funzione di utilita' gluPerspective, che accetta parametri piu' intuitivi:

Semplificazione per scene 2D

La scena giace sul piano xy (z=0). Definiamo solo il rettangolo di base del volume di vista per la trasformazione ortografica:

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: