|
|
|
|
Vedremo come si realizza una applicazione OpenGL
in ambiente Windows, utilizzando le Microsoft Foundation Class. |
|
|
|
|
Le Microsoft Foundation Class formano la nota
libreria di sviluppo di applicazioni per ambiente Windows. Vedremo come
siano state realizzate delle opportune chiamate di libreria per
l’interfacciamento di OpenGL. |
|
|
|
|
OpenGL è il famoso standard per la
visualizzazione grafica bi e tridimensionale. E’ supportato da tutte le
piattaforme. Le schede grafiche di ultima generazione lo implementano in
hardware. |
|
|
|
|
Una introduzione ad OpenGL e’ fuori dagli scopi
di questa presentazione. |
|
Ne evidenzieremo le caratteristiche necessarie
all’interfacciamento con il sistema a finestre. |
|
http://www.opengl.org |
|
|
|
|
I comandi di OpenGL sottintendono un contesto
grafico di default, la seguente serie di comandi visualizza un triangolo: |
|
|
|
glBegin(GL_TRIANGLES); |
|
glVertex3f(0,0,-10); |
|
glVertex3f(10,0,-10); |
|
glVertex3f(0,10,-10); |
|
glEnd(); |
|
|
|
|
In ogni applicazione Windows si puo’ definire un
contesto OpenGL corrente che riceve i comandi grafici. |
|
Un applicazione multifinestra puo’ alternare
vari contesti grafici OpenGL. |
|
|
|
|
Una volta costruita una applicazione standard
MFC sono necessarie alcune aggiunte per l’utilizzo di OpenGL all’interno di
una finestra. |
|
|
|
|
|
|
|
|
Scelta del formato pixel. |
|
Creazione di un contesto OpenGL (GLRC). |
|
Creazione di un Device contest (DC) associato ad
una finestra. |
|
Collegamento fra GLRC e DC. |
|
|
|
|
Aggiungere nel file header della view: |
|
|
|
#include <GL\\gl.h> |
|
#include <GL\\glu.h> |
|
|
|
|
|
|
|
|
File di |
|
Libreria |
|
aggiuntivi |
|
|
|
|
I seguenti dati sono aggiunti alla classe view: |
|
|
|
CPalette m_cPalette; // Palette Personale |
|
CPalette *m_pOldPalette; // Vecchia Palette |
|
CClientDC *m_pDC; // DC della finestra |
|
HGLRC m_hrc; // Contesto OpenGL |
|
|
|
|
|
|
In caso di gestione multifinestra e’ necessario
aggiunge alla view il seguente: |
|
|
|
// IL proprietario del contesto GL |
|
static CGlwrapperView * m_glOwner; |
|
|
|
Nel file …view.cpp si dichiara: |
|
|
|
CGlwrapperView * CGlwrapperView::m_glOwner = 0; |
|
|
|
|
|
|
// E’ importante ricordarsi di inizializzare le
variabili nel costruttore della classe. |
|
CGlwrapperView::CGlwrapperView() |
|
{ |
|
m_pOldPalette = 0; |
|
m_pDC
= 0; |
|
m_hrc
= 0; |
|
} |
|
|
|
|
inline BOOL SetGL() |
|
{ if(m_glOwner!=this) |
|
{
if(!wglMakeCurrent(m_pDC->GetSafeHdc(),m_hrc)) |
|
{ |
|
AfxMessageBox("GlMakeCurrent
Error"); |
|
return FALSE; |
|
} |
|
m_glOwner = this; |
|
} |
|
return TRUE; |
|
} |
|
|
|
|
Attraverso il Class Wizard e’ necessario
inserire alcuni gestori di messaggi |
|
|
|
|
I gestori necessari sono: |
|
Create |
|
PrecreateWindow(*) |
|
OnDraw(*) |
|
OnSize |
|
OnEraseBkgnd |
|
OnDestroy |
|
|
|
(*) Gia’ dichiarati dall’Application Wizard |
|
|
|
|
|
|
//Creazione finestra |
|
|
|
if( !CWnd::Create(lpszClassName,
lpszWindowName, dwStyle, |
|
rect, pParentWnd, nID,
pContext) ) |
|
return FALSE; |
|
|
|
|
|
|
|
|
// Creazione DC (Contesto grafico standard) |
|
|
|
m_pDC = new CClientDC(this); |
|
ASSERT(m_pDC != NULL); |
|
|
|
// Setta il pixel format e crea la palette |
|
if (!SetupPixelFormat(m_pDC)) |
|
return FALSE; |
|
|
|
|
|
|
// Creazione palette (se necessaria) |
|
if( CreateRGBPalette(m_pDC,m_cPalette) ) |
|
{ |
|
m_pOldPalette = m_pD->SelectPalette(
&m_cPalette, FALSE); |
|
m_pDC->RealizePalette(); |
|
} |
|
|
|
|
// Crea e setta il contesto OPENGL |
|
m_hrc = wglCreateContext( |
|
m_pDC->GetSafeHdc()); |
|
if(m_hrc==NULL) |
|
{ |
|
AfxMessageBox("OpenGL contest fail"); |
|
return FALSE; |
|
} |
|
|
|
|
// Inizializzazioni GL |
|
|
|
SetGL(); |
|
InitGL(); |
|
|
|
|
La palette serve solo in caso di schermo a 256
colori(obsoleto). |
|
Si realizza tramite le specifiche della
Microsoft. |
|
Si puo’ scaricare l’implementazione di default
dal sito: vcg.iei.pi.cnr.it/˜rocchini/corso.html |
|
|
|
|
Prima di inizializzare OpenGL e’ necessario
settare il tipo di supporto pixel, che comprende: |
|
Numero di pixel colore |
|
Profondita’ z-buffer |
|
Tipo di buffer (singolo/doppio) |
|
… |
|
I settaggi vengono inseriti nella struttura
PIXELFORMATDESCRIPTOR. |
|
|
|
|
|
|
// Esempio di codice di richiesta di formato |
|
if ( (pixelformat =
ChoosePixelFormat(pDC->GetSafeHdc(), &pfd)) == 0 ) |
|
{ |
|
AfxMessageBox("ChoosePixelFormat
failed"); |
|
return FALSE; |
|
} |
|
if (SetPixelFormat(pDC->GetSafeHdc(),
pixelformat, &pfd) == FALSE) |
|
{ |
|
AfxMessageBox("SetPixelFormat
failed"); |
|
return FALSE; |
|
} |
|
|
|
|
Le inizializzazioni di OpenGL non fanno parte
del sistema di finestre, ma sono state citate perche’ indispensabili per la
visualizzazione della finestra OpenGl (per evitare l’effetto “finestra
nera”). |
|
|
|
|
glClearDepth(1.0f); |
|
glShadeModel( GL_SMOOTH ); |
|
glEnable(GL_DEPTH_TEST); |
|
glEnable(GL_COLOR_MATERIAL); |
|
glDisable(GL_CULL_FACE); |
|
glEnable(GL_LIGHT0); |
|
fv4[0]=0.1f; fv4[1]=0.1f; fv4[2]=0.1f;
fv4[3]=1.0f; |
|
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fv4); |
|
glLightfv(GL_LIGHT0, GL_POSITION, fv4); |
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, fv4); |
|
glEnable(GL_LIGHTING); |
|
|
|
|
Alla fine dell’utilizzo delle strutture OpenGl
e’ importante chiamare le funzioni di rilascio delle risorse. |
|
Queste funzioni devono essere chiamate subito
prima la distruzione della finestra View. |
|
|
|
|
void CGlwrapperView::OnDestroy() |
|
{ |
|
SetGL(); |
|
glFinish(); |
|
wglMakeCurrent(NULL, NULL); |
|
if
(m_hrc) ::wglDeleteContext(m_hrc); |
|
if
(m_pOldPalette) |
|
m_pDC->SelectPalette(m_pOldPalette, FALSE); |
|
if (m_pDC) delete m_pDC; |
|
m_glOwner = NULL; |
|
CView::OnDestroy(); |
|
} |
|
|
|
|
// Lo stile della finestra deve contenere le
seguenti specifiche: |
|
BOOL CGlwrapperView::PreCreateWindow( |
|
CREATESTRUCT& cs) |
|
{ |
|
cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
CS_OWNDC; |
|
return CView::PreCreateWindow(cs); |
|
} |
|
|
|
|
// Questa callback impedisce al sistema di
disegnare lo sfondo bianco di defalt: |
|
|
|
BOOL CGlwrapperView::OnEraseBkgnd(CDC* pDC) |
|
{ |
|
return TRUE; |
|
} |
|
|
|
|
OnSize e’ chiamata alla creazione della Finestra
e ad ogni cambiamento di dimensione. |
|
Si deve occupare di comunicare ad OpenGL
l’effettiva dimensione della finestra (viewport) |
|
Deve settare il tipo di proiezione di
visualizzazione (ortogonale/prospettica) |
|
|
|
|
|
|
void CGlwrapperView::OnSize(UINT nType, int cx,
int cy) |
|
{ |
|
CView::OnSize(nType, cx, cy); |
|
if(cx!=0 && cy!=0) |
|
{ |
|
… gestione dimensione… |
|
} |
|
} |
|
|
|
|
const float fNearP = 0.1f; |
|
const float fFarP = 20.0f; |
|
const float fPAngle = 45.0f; |
|
SetGL(); |
|
glViewport(0, 0, cx, cy); |
|
GLfloat fAspect; |
|
if (cy) fAspect = GLfloat(cx)/cy; |
|
else fAspect = 1.0f; |
|
glMatrixMode(GL_PROJECTION); |
|
glLoadIdentity(); |
|
gluPerspective(fPAngle, fAspect, fNearP, fFarP); |
|
glMatrixMode(GL_MODELVIEW); |
|
|
|
|
OnDraw, finalmente, visualizza effettivamente il
contesto OpenGl. |
|
Deve contenere il codice utente per la
visualizzazione |
|
|
|
|
OpenGL e intrinsecamente asincrono, vale a dire
che la visualizzazione continua in parallelo con il programma. |
|
E’ possibile che il sistema chiami più volte
OnDraw, prima che la visualizzazione sia terminata. |
|
|
|
|
void CGlwrapperView::OnDraw(CDC* pDC) |
|
{ |
|
if(pDC==NULL) return; |
|
static BOOL bBusy = FALSE; |
|
if(bBusy) |
|
return; |
|
bBusy = TRUE; |
|
… SEZIONE PRINCIPALE… |
|
bBusy = FALSE; |
|
} |
|
|
|
|
SetGL(); // Setta il contesto corrente |
|
// Cancellazione buffer |
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
|
glLoadIdentity(); |
|
… COMANDI UTENTE… |
|
glFinish(); |
|
SwapBuffers(wglGetCurrentDC()); |
|
|
|
|
SetGL(); // Setta il contesto corrente |
|
glLoadIdentity(); |
|
… COMANDI UTENTE… |
|
glFinish(); |
|
SwapBuffers(wglGetCurrentDC()); |
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
|
|
|
|
|
|
Esempio di comandi utente (visualizzazione
triangolo): |
|
glBegin(GL_TRIANGLES); |
|
glVertex3f(0,0,-10); |
|
glVertex3f(10,0,-10); |
|
glVertex3f(0,10,-10); |
|
glEnd(); |
|
|
|
|
L’applicazione e’ finalmente pronta per essere
compilata ed eseguita. |
|
|
|
|
Nel sito web del corso e’ possibile scaricare il
sorgente del programma, che comprende la classe Trackball per La
manipolazione della vista. |
|
|
|
|
La visualizzazione della finestra OpenGl avviene
su richiesta del sistema, quando e’ necessario ridisegnarne il contenuto. |
|
In alcune applicazioni e’ necessario invece
ridisegnare la finestra OpenGL su richiesta dell’applicazione. |
|
|
|
|
Una prima possibilità è quella di usare un timer
(classe Ctimer), che periodicamente genera il messaggio WM_TIMER. |
|
Il messaggio può essere gestito
dall’applicazione per generare animazioni. |
|
Questa soluzione non è però efficace; infatti il
timer non è affidabile per intervalli inferiori al secondo. |
|
|
|
|
Una soluzione migliore è l’utilizzo della
funzione OnIdle, della classe Applicazione. |
|
Tale funzione viene chiamata tutte le volte che
il sistema ha finito le sue operazioni. |
|
|
|
|
Scaricare lo scheletro di applicazione
glwrapper, oppure rigenerarlo da zero e: |
|
Visualizzare grafici di funzioni da R2 a R. |
|
Visualizzare oggetti animati nel tempo,
utilizzando Onidle |
|
Realizzare una versione di graphedit
visualizzata in OpenGL. |
|
|
|
|
Claudio Rocchini |
|
Visual Computing Group |
|
Istituto Elaborazione Informazione |
|
Area della Ricerca di Pisa |
|
Email: rocchini@iei.pi.cnr.it |
|
Tel: 050-3152926 |
|