La struttura di una animazione reale, non differisce
molto da questa descrizione. Solitamente, è più facile ridisegnare
l'intero buffer dall'inizio per ogni fotogramma, piuttosto che
cercare di capire cosa deve essere ridisegnato. Ciò è verissimo per
i simulatori di volo, nel quale un cambiamento dell'orientamento del
mezzo, costringe al riposizionamento di tutto ciò che si vede fuori
dall'abitacolo. Nella maggior parte delle animazioni, gli oggetti
sono ridisegnati mediante semplici trasformazioni (un cambio del
punto di vista dell'osservatore, o un veicolo che si muove lungo la
strada, un oggetto ruotato leggermente). Se è necessario un lungo
processo di calcolo per le operazioni non grafiche, il tasso di
fotogrammi subisce un brusco calo. Da tenere in mente quindi, che il
tempo di attesa dopo la routine scambia_i_buffer() , viene
usato per questi calcoli. OpengGL non ha una routine di comando
scambia_i_buffer() in quanto
ciò è legato all'hardware disponibile ed al sistema di gestione
delle finestre nella macchina sulla quale il programma viene
eseguito.
In questo blog si parla di programmazione con il linguaggio C++, i post cercheranno di essere molto esplicativi sull'argomento trattato.
Visualizzazione post con etichetta funzioni virtuali. Mostra tutti i post
Visualizzazione post con etichetta funzioni virtuali. Mostra tutti i post
venerdì 29 marzo 2013
lunedì 4 marzo 2013
GLUT, OpenGL Utility Toolkit
Come si sa OpenGL non contiene comandi di rendering,
ma è progettato per essere indipendente dal sistema operativo e
dalla gestione delle finestre. Di conseguenza, non contien comandi
per aprire le finestre, o leggere eventi dal mouse o dalla tastiera.
Sfortunatamente, è impossibile scrivere un programma di grafica
completo senza almeno aprire una finestra, e i programmi più utili
richiedono una forma di input da parte dell'utente per operare i
comandi del sistema operativo e del sistema a finestra. In molti
casi, un programma completo crea gli esempi più interessanti, in
questo GLUT semplifica il sistema di gestione delle finestre,
dell'input e così via. Se si possiede una implementazione di GLUT e
OpenGL sul sistema, gli esempi mostrati saranno eseguiti senza
problemi. Inoltre, i comandi di disegno delle OpenGL sono limitati al
disegno di geometrie semplici quali punti, linee e poligoni; GLUT
include alcune routine che permettono di creare oggetti 3D più
complicati quali sfere, toroidi, teiere. In questo modo le immagini
mostrate dai programmi sono rese più interessanti. GLUT può essere
non molto soddisfacente come applicazione, ma può essere un buon
punto di partenza per imparare OpenGL.
Gestione della Finestra
Cinque routine che eseguono i compiti necessari a gestire la finestra.- glutInit(int *argc, char **argv) inizia GLUT e processa ogni argomento di comando (per X, possono esserci opzioni sullo schermo e la geometria). glutInit() deve essere chiamata come prima GLUT routine.
- glutInitDisplayMode(unsigned int modo) specifica se si vuole usare RGBA o modello di colore indicizzato. Si può anche specificare se si vuole finestra a singolo o doppio buffer. (Se si lavora con la mappa dei colori ne si vorrà caricare uno; usare glutSetColor() per farlo.) Infine si può usare la routine per indicare se si vuole una finestra associata ad un tipo di profondità, tratto, buffer di accumulazione. Per esempio si vuole creare una finestra con doppio buffering, e colore RGBA, bisogna chiamare: glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH).
- glutInitWindowPosition(int x, int y) specifica localizzazione nello schermo partendo dall'angolo in lato a sinistra.
- glutInitWindowSize(int larghezza int taglia) specifica larghezza e altezza della finestra.
- int glutCreateWindow(char *stringa) crea la finestra del contesto OpenGL. Restituisce un indicatore unico per la finestra. Avvertenza, sino a quando glutMainLoop() viene chiamato la finestra non è visualizzata.
Le chiamate alla Visualizzazione
glutDisplayFunc(void (*func)(void)) è la prima e la più importante funzione di chiamata alla visualizzazione. Non appena GLUT determina il contesto della finestra che deve essere visualizzata, la chiamata alla funzione registrata mediante glutDisplayFunc() viene eseguita. Pertanto le routines di cui si ha bisogno per ridisegnare la scena nello schermo attraverso il richiamo della funzione.
Se il programma cambia i contenuti della finestra, a volte bisogna richiamare glutPostRedisplay(void), il quale fornisce una scappatoia a glutMainLoop() funzione per essere richiamata mediante questa nuove opportunità.
Eseguire il programma
L'ultima funzione da richiamare è glutMainLoop(void). Tutte le finestre che sono state create vengono mostrare, e il rendering di queste è ora effettivo. Il processo degli eventi comincia ed avviata la registrazione degli eventi a schermo. Una volta entrati nell'anello, non se ne esce.
domenica 30 dicembre 2012
Definire funzioni membro virtuali
Un oggetto di una classe derivata è anche un membro
di una classe di base. Per esempio, il programma Capo virtuale,
l'oggetto Capo è anche un oggetto Nemico. Tutto ciò
ha senso in quando si tratta solo di un tipo di nemico speciale.
Inoltre ha senso in quanto l'oggetto Capo ha tutti i membri
dell'oggetto Nemico. Bene, e allora? Poiché una classe
derivata è anche un membro della classe di base, si può usare un
puntatore alla classe di base per puntare un oggetto della classe
derivata. Questo è ciò che faccio in main() con la seguente
istruzione, la quale istanzia un oggetto Capo su heap e crea
un puntatore a Nemico che punti all'oggetto Capo.
Nemico* pCattivo = new
Capo();
Cosa vuol dire tutto questo? E' utile a permetterci
di gestire l'oggetto senza doverne conoscere il tipo esatto. Per
esempio di può avere una funzione che accetta un puntatore a Nemico
e può funzionare sia con Nemico che con Capo oggetti. La funzione
non deve conoscere l'esatta tipologia di oggetto passato, può
funzionare con oggetti che producono risultati diversi a seconda di
quale sia utilizzato. Ma ora analizziamo cosa accade nella successiva
riga di codice.
pCattivo - > Fastidio();
Invece mostra una linea di testo Il nemico vuole
combatterti. Quindi non è stata
richiamata la funzione Fastidio()
dell'oggetto Capo ma
dell'oggetto Nemico,
nonostante sia stata superata dalla funzione Fastidio()
nella classe di Capo.
Questo accade come risultato di un legame precedente nel
quale l'esatta funzione è legata al tipo di puntatore, in questo
caso Nemico. Ciò di
cui si ha bisogno è una funzione membro chiamata sul tipo di oggetto
puntato, e non fissato dal puntatore. In questo si ottiene la
flessibilità del legame successivo
attraverso funzioni virtuali,
che ottiene con il comportamento polimorfico. Per creare una funzione
membro virtuale, si aggiunge semplicemente la parola chiave virtual
prima del nome della funzione nella sua dichiarazione. Questo è ciò
che viene fatto in Nemico
con la seguente istruzione.
void virtual VFastidio()
const
{ cout << “Il
nemico vuole combatterti.\n”; }
Questo significa che VFastidio() è una
funzione virtuale. E' virtuale in Nemico, e viene ereditata in
Capo. Questo impila che VFastidio() in Capo
possa superare con la seguente istruzione la funzione ereditata. La
corretta versione di VFastidio() viene chiamata (in base al
tipo di oggetto) e non sarà fissata dal tipo di puntatore.
void virtual VFastidio()
const
{ cout << “Il capo
vuole mettere fine alla tua esistenza.\n”; }
La prova del comportamento polimorfico viene in
main() con la seguente istruzione, nel quale la funzione
VFastidio() definita in Capo viene chiamata ed il testo
“Il capo vuole mettere fine alla tua esistenza”. Viene mostrato
su schermo.
pCattivo - > VFastidio();
Definire Distruttori virtuali
Quando si usa un puntatore alla classe di base per
puntare un oggetto di una classe derivata, si può avere un
potenziale problema. Quando viene cancellato il puntatore, solo il
distruttore della classe viene chiamato per l'oggetto. Questo può
condurre a risultati disastrosi poiché il distruttore della classe
derivata è necessario per liberare la memoria ( come fa il
distruttore di Capo). La soluzione come si può indovinare, è
di rendere il distruttore della classe virtuale. In questo modo viene
chiamato il distruttore della classe, il qual conduce (sempre) alla
chiamata del distruttore della classe di base, dando ad ogni classe
la possibilità di ripulirsi. La teoria viene messa in pratica quando
si crea il distruttore virtuale della classe Nemico.
virtual ~Nemico(){ cout <<
“m_pDanno cancellato”; delete m_pDanno;}
Quando viene cancellato il puntatore che punta alla
classe Capo con la seguente istruzione, il distruttore della
classe Capo viene chiamato, il quale libera la memoria su heap
dei dati m_pMultidanno e mostra il messaggio.
delete pCattivo;
Quindi viene chiamato il distruttore di Nemico,
il quale libera la memoria su heap da m_pDanno e mostra
il messaggio m_pDanno cancellato. L'oggetto è distrutto, e
tutte le memorie associate ad esso sono cancellate.
Iscriviti a:
Post (Atom)