Trasformazioni di vista e proiezione in 3D e Display list

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.

Trasformazioni di vista proiezione in 3D

In generale le trasformazioni di vista e quelle di proiezione vanno progettate assieme.

Viste ortografiche

Per esempio vogliamo guardare la scena in modo da avere l'asse x diretto verso la nostra destra, l'asse y diretto verso il nostro alto, l'asse z diretto dietro la nostra schiena. E' la situazione di default, ma proviamo a "rifarla a mano".

Supponiamo per esempio che la nostra scena sia centrata in C=(xC,yC,zC), che occupi un volume cubico di lato l e che il volume di vista da inquadrare sia un cubo concentrico a quello, di lato L>l.

  1. Sistemiamo la telecamera.
    Usiamo la funzione gluLookAt a cui dobbiamo passare la posizione della telecamera, la posizione del punto verso cui e' rivolta e la direzione del vettore che segna "l'alto".
    In proiezione ortografica non importa DOVE e' la telecamera, ma solo COME e' orientata.
    Possiamo collocarla in C e puntarla verso un punto T che stia sulla retta passante per C e parallela all'asse z, e tale che T abbia coordinata z minore di quella di C. Quindi scegliamo T=(xC,yC,zC-d) con d>0. L'alto e' dato dal versore dell'asse y (0,1,0).
    Istruzioni:
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(xC,yC,zC, xC,yC,zC-d, 0,1,0);

  2. Scegliamo l'ampiezza dell'inquadratura.
    Usiamo la funzione glOrtho a cui dobbiamo passare le coordinate x minima e massima, y minima e massima, z minima e massima del volume di vista da inquadrare.
    IMPORTANTE: queste coordinate sono espresse in un sistema di riferimento trasformato la cui origine e' il punto nel quale abbiamo messo la telecamera (coordinate di vista).
    Nel nostro caso tale punto e' il centro della scena C. Quindi se le coordinate minime e massime della scena prima (in coordinate del mondo) erano

    adesso bisogna contare che e' stato traslato tutto di (xC,yC,zC) unita' indietro. Le nuove coordinate estreme sono quindi Istruzioni:
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-L/2,L/2, -L/2,L/2, -L/2,L/2);

Viste prospettiche

Per esempio vogliamo un volume di vista (tronco di piramide) con asse parallelo all'asse z, base minore verso le z positive e base maggiore verso le z negative, le due basi sono quadrate.

Supponiamo per esempio che la nostra scena sia centrata in C=(xC,yC,zC) e che occupi un volume cubico di lato l.

  1. Sistemiamo la telecamera.
    Usiamo sempre la funzione gluLookAt a cui dobbiamo passare i parametri gia' detti sopra.
    In proiezione prospettica e' importante DOVE posizioniamo la telecamera, perche' diventera' il vertice della priramide che, troncata, sara' il nostro volume di vista.
    Mettiamo la telecamera in punto V che stia sulla retta passante per C e parallela all'asse z, e tale che T abbia coordinata z maggiore della massima coordinata z della scena. Quindi scegliamo V=(xC,yC,zC+d) con d>l/2. Puntiamo la telecamera verso C. L'alto e' dato dal versore dell'asse y (0,1,0).
    Istruzioni:
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(xC,yC,zC+d, xC,yC,zC, 0,1,0);

  2. Scegliamo l'ampiezza dell'inquadratura.
    Usiamo la funzione gluPerspective a cui dobbiamo passare l'angolo di apertura, la aspect ratio della base della piramide, e le distanze delle basi minore (near) e maggiore (far) del tronco di piramide dal punto di vista.
    L'angolo di apertura per sicurezza lo prendiamo abbastanza grande, per es. 60 gradi. L'aspect ratio e' 1 perche' vogliamo piramide a base quadrata. Le distanze near e far delle basi del tronco di piramide dal punto di vista vanno scelte in modo tale che tutta la scena sia compresa fra le due basi:

    direzione z    zV=zC+d          zC     
    <-----------------+-----+-------+-------+--------
                      |     |  l/2  |  l/2  |
                      |     |<----->|<----->|
                      |      d      |
                      |<----------->|
    
    Quindi: Istruzioni:
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60, 1, d - l/2 - a, d + l/2 + b); con a,b, > 0

Display list

In condizioni normali, le istruzioni OpenGL (specifiche di primitive, attributi, trasformazioni ecc.) vengono mandate dal programma al motore grafico di OpenGL, che la processa immediatamente per produrre l'immagine della scena, e poi le "dimentica" (non le memorizza). Se il programma vuole rieseguire la stessa sequenza di istruzioni una seconda volta, deve reinviarla, con spesa di tempo.

Display list = serie di istruzioni 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 colore, spessore di linea, trasformazioni geometriche ecc.

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

Parte da chiamare una sola volta all'inizio, definisce una 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();
Parte da chiamare ogni volta che occorre disegnare la display list, qui disegna piu' quadrati traslati uno rispetto all'altro, 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();
}