Note
Struttura
Gestione di Interfacce con OpenGL
Claudio Rocchini
Aspetti di OpenGL
Vedremo alcuni aspetti di OpenGL che risultano utili nello sviluppo di interfacce.
Inoltre vedremo come si possono implementare alcune funzioni utili per l’interazione con l’untente.
Picking di un Punto 3D
Un’operazione tipica e’ la lettura delle coordinate spaziali di un punto, ad esempio dopo che l’utente ha cliccato sulla finestra OpenGL.
Passi fondamentali:
Recupero matrici di trasformazione correnti
Lettura Z-Buffer
Proiezione inversa
Picking di un Punto 3D
GLdouble modelMatrix[16];
GLdouble projMatrix[16];
GLint    viewport[4];
// Recupero matrici e viewport
glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
glGetIntegerv(GL_VIEWPORT,viewport);
// ATTENZIONE: ribaltamento Y!!!!!
sy = viewport[3]-1-sy;
Picking di un Punto 3D
// Lettura z buffer
GLfloat  qz;
// Seleziono il buffer corretto
glReadBuffer(GL_FRONT);
glReadPixels(sx,sy,1,1,GL_DEPTH_COMPONENT,
             GL_FLOAT,&qz);
Picking di un Punto 3D
if(qz==1.0){ // Proiezione inversa
x = y = z = 0.0;
return false;
} else {
GLdouble objz = qz;
GLdouble tx,ty,tz;
gluUnProject( GLdouble(sx), GLdouble(sy), objz,
modelMatrix, projMatrix, viewport,
&tx, &ty, &tz);
return true;
}
Picking di un Punto 3D
Il codice di picking puo’ essere ottimizzato leggendo le matrici di proiezione e la viewport solo nel momento in cui cambiano:
La viewport e la projection matrix, di solito, vengono aggiornate solo al resize della finestra
La modelview viene aggiornata quando si cambia punto di visuale (rotazione, zoom, etc).
Picking di un Punto 3D
In alcuni casi e’ impossibile eseguire il picking:
Se non si usa lo z-buffer
(Solo per le schede 3D di ultima generazione), se si e’ attivato l’antialiasing delle immagini (lo z-buffer e’ incosistente).
Selezione
Per selezione si intende l’identificazione di un oggetto OpenGL a partire dalla sua visualizzazione.
Per realizzarla si usano le funzionalita’ native di OpenGL.
Utilizzo tipico: dopo il click dell’utente e’ necessario identificare l’oggetto puntato.
Selezione
Il Meccanismo della selezione OpenGL e’ il seguente:
Si sceglie la zona su cui operare la selezione (tipicamente un cono di vista centrato sulla posizione di click del mouse).
Si renderizza solo la zona interessata, badando a dare un nome ad ogni oggetto renderizzato.
Tutti i nome degli oggetti renderizzati nella zona selezionata sono memorizzati in un buffer.
Selezione 1
// Definizione del buffer di selezione
const GLsizei MAXSBUF = 512;
GLuint sbuf[MAXSBUF];
GLint  buflen;
// Preparativi generali
// Seleziona il buffer di picking
glSelectBuffer( MAXSBUF, sbuf );
glRenderMode( GL_SELECT ); // Modalita' select
Selezione 2
// Rovesciamento coordinate
y =  viewport[3] - viewport[1] - 1 - y;
// Preparazione matrice di selezione
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPickMatrix(x, y, wx,wy, wextra.viewport);
gluPerspective(prospAngle,aspect,nearPlane,farPlane);
glMatrixMode( GL_MODELVIEW );
Selezione 3
glInitNames();
glLoadName(0);
// Rendering con nome
…
// Chiusura modalita' selezione
buflen = glRenderMode( GL_RENDER );
// Interpretazione dei risultati
…
Selezione 4
// Esempio di rendering con nome (oggetto)
Int name = 7;
glPushName(name);
glBegin(GL_TRIANGLES);
glVertex3fv(v[0]);
glVertex3fv(v[1]);
glVertex3fv(v[2]);
glEnd();
glPopName();
Selezione 5
// Esempio di rendering con nome (triangoli)
glBegin(GL_TRIANGLES);
for(int I=0;I<v.size();++I)
{
glPushName(I+1);
glVertex3fv(v[I][0]);
glVertex3fv(v[I][1]);
glVertex3fv(v[I][2]);
glPopName();
}
glEnd();
Selezione
Intepretazione del buffer risultato:
// Parsing dello stack
pr.resize(buflen);
int j = 0;
for(int i=0;i<buflen;++i){
int n = sbuf[j++]; // Namestack size
pr[i].minDepth = sbuf[j++]/double(UINT_MAX);
pr[i].maxDepth = sbuf[j++]/double(UINT_MAX);
pr[i].nameStack.resize(n);
for(int k=0;k<n;++k)
pr[i].nameStack[k] = sbuf[j++];
}
Dragging in OpenGL
E’ utile implementare la funzione di dragging, passi fondamentali:
Salvare lo stato OpenGL attuale
Selezionare la modalita’ di disegno diretta (front buffer) e in modalita’ xor
Selezionare modalita’ di proiezione diretta (coordinate 2d = coordinate pixel)
Eseguire il disegno del drag senza glClear (lo sfondo e’ disegnato una sola volta)
Dragging 1
// Salvataggio stato opengl
glGetIntegerv( GL_DRAW_BUFFER, &oldDrawBuffer );
glGetIntegerv( GL_DEPTH_FUNC, &oldDepthFunc );
glGetIntegerv( GL_LINE_STIPPLE_PATTERN,
&oldLineStippleP );
glGetIntegerv( GL_LINE_STIPPLE_REPEAT ,
& oldLineStippleR );
Dragging 2
glDrawBuffer( GL_FRONT ); // Modalita’ disegno
glDepthFunc( GL_ALWAYS );
glLineStipple( 1, 0xF6F6 );
glMatrixMode( GL_MODELVIEW ); // Vista
glPushMatrix();
glLoadIdentity();
glMatrixMode( GL_PROJECTION ); // Proiezione
glPushMatrix();
glLoadIdentity();
glOrtho( 0, vport[2], 0, vport[3], 0, 1 );
Dragging 3
// Colore disegno: bianco
glGetMaterialfv( GL_FRONT, GL_EMISSION ,oldEColor );
glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, white );
// Abilitazione motalita’ xor tramite la funzione
// blending: colore = 1 – colore_sfondo;
BlendingIsE = glIsEnabled( GL_BLEND );
glEnable( GL_BLEND );
glGetIntegerv( GL_BLEND_SRC, & BlendFun1 );
glGetIntegerv( GL_BLEND_DST, & BlendFun2 );
glBlendFunc( GL_ONE_MINUS_DST_COLOR, GL_ZERO );
Dragging 4
// Dragging vero e proprio
glBegin(GL_LINE_LOOP);
glVertex2i( ox,oy ); glVertex2i( ox,dy );
glVertex2i( dx,dy ); glVertex2i( dx,oy );
glEnd();
dx = x;
dy = y;
glBegin(GL_LINE_LOOP);
glVertex2i( ox,oy ); glVertex2i( ox,dy );
glVertex2i( dx,dy ); glVertex2i( dx,oy );
glEnd();
glFinish();
Dragging 5
// End drag: ripristino stato OpenGL
glDrawBuffer( oldDrawBuffer );
glDepthFunc( oldDepthFunc );
glLineStipple( oldLineStippleR, oldLineStippleP );
glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, oldEC );
if( !BlendingIsE ) glDisable( GL_BLEND );
glBlendFunc( BlendFun1, BlendFun2 );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
glPopMatrix();
Stampa di testi
Per visualizzare stringhe sul render OpenGL:
Se si utilizza glut c’e’ la funzione apposita
In MFC, si puo’ utilizzare il codice:
SelectObject(we.ghDC, GetStockObject (SYSTEM_FONT));
wglUseFontBitmaps(we.ghDC, 0, 256, FONT_BASE);
…
glListBase(FONT_BASE);
CallLists (strlen(str), GL_UNSIGNED_BYTE,str);
Librerie OpenGL ad alto livello
Nel corso degli anni sono state sviluppate una serie di librerie ad alto livello per l’utilizzo di OpenGL.
Quasi tutte si basano sulla definizione di un grafo di scena, che rappresenta gli oggetti da visualizzare, le loro propieta’ ed eventuali azioni su di essi.
Lo sviluppatore storico di queste librerie e la Silicon Graphics. Negli ultimi tempi la crisi finanziaria della SGI ha lascito aperto il campo.
Pregi e difetti
Tutte le librerie HL OpenGL hanno pregi e difetti:
Pregi:
Sviluppo veloce di applicazioni complesse
Presenza di tool non banali da implementare (manipolatori tridimensionali, semplificatori automatici di geometrie, …)
Difetti:
Incremento della memoria utilizzata (Per SGI forte incremento!)
Scarsa flessibilita’: difatto per lo sviluppo di applicazioni commerciali, risulta spesso piu’ semplice ripartire da OpenGL.
Storiografia
Sviluppo SGI:
OpenInventor ( -> VRML 1.0 -> VRML 2.0)
CosmoSoftware (CosmoPlayer)
SGI Performer
SGI Optimizer
SGI Volumizer
…
Vari tentativi da parte di altre ditte di porting o risviluppo del codice (TGS,…)
Storiografia
Sviluppi OpenSource, Shareware: infiniti.
Google: “opengl scene graph”: 7500 hits
Solo su sourceforge ci sono decine di progetti in corso di sviluppo.
In realta’ realizzare una libreria decente comporta una grossa mole di lavoro.
Un esempio di HL OpenGL
GCR: libreria grafica OpenGL ad alto livello.
Implementa uno scene graph simile ad OpenInventor
Caratteristiche salienti:
E’ freeware, open source, ed e’ a disposizione di questo corso per studio o modifica
E’ parzialmente implementata
E’ infinitamente rozza
Contatti
Claudio Rocchini
Visual Computing Group
ISTI-CNR,
Area della Ricerca di Pisa
Email: rocchini@iei.pi.cnr.it
Web: http://vcg.iei.pi.cnr.it/~rocchini