OpenGl: scheda grafica, buffers (Terminato) e glNormal
Buongiorno, sto giocando un pò con opengl e vorrei capire un pò meglio quello che sto facendo.
Le funzioni che disegnano vertici e poligoni, che impostano un tipo di prospettiva, ecc... sono esguite dalla cpu o dalla gpu della scheda grafica?
Grazie
Le funzioni che disegnano vertici e poligoni, che impostano un tipo di prospettiva, ecc... sono esguite dalla cpu o dalla gpu della scheda grafica?
Grazie
Risposte
Normalmente dalla GPU, ma dipende. Esistono infatti implementazioni delle OpenGL che utilizzano la CPU per disegnare la scena (si tratta principalmente di implementazioni pensate per sistemi non dotati di scheda grafica o senza driver appositi. Ci sono poi situazioni, sempre meno frequenti per fortuna, in cui alcuni driver utilizzano la CPU invece della GPU per emulare funzionalità non supportate dalla scheda video. Si trattava a volte di scelte di marketing. La specifica delle OpenGL non richiede infatti che le chiamate siano effettuate su un tipo di hardware specifico e non richiede che una certa operazione sia efficiente. Un driver può (ma era più comune in passato - soprattutto su schede video integrate Intel) dire di supportare qualcosa, per esempio una specifica versione delle OpenGL, accelerare in hardware solo alcune operazioni e disattivare tale accelerazione negli altri casi senza alcun tipo di avvertimento. Tali funzionalità capaci di disattivare l'accelerazione hardware potevano per esempio essere alcuni formati per le texture o il supporto agli shader..
Ormai la situazione è molto cambiata. Da dove stai imparando le OpenGL? Quale versione stai prendendo in considerazione? Personalmente credo che ormai le vecchie versioni delle OpenGL possano essere tranquillamente evitate e convenga passare direttamente alle versioni delle OpenGL successive alla 2.0. Forse anche successive alla 3.0. Probabilmente una buona scelta potrebbe essere partire dalle OpenGL ES 2 (la versione ridotta disponibile in ormai tutti gli smartphone e sul web grazie a WebGL..).
Ormai la situazione è molto cambiata. Da dove stai imparando le OpenGL? Quale versione stai prendendo in considerazione? Personalmente credo che ormai le vecchie versioni delle OpenGL possano essere tranquillamente evitate e convenga passare direttamente alle versioni delle OpenGL successive alla 2.0. Forse anche successive alla 3.0. Probabilmente una buona scelta potrebbe essere partire dalle OpenGL ES 2 (la versione ridotta disponibile in ormai tutti gli smartphone e sul web grazie a WebGL..).
Sto utilizzando la Programming Guide fornitaci dal prof. Stiamo iniziando e ha detto che per iniziare ci basta questa anche se è versione 1.1
Nella guida c'è scritto che la funzione che disegna viene chiamata quando c'è bisogno. Per capire quando viene chiamata ho inserito una printf ma non capisco in base a cosa viene chiamata.
Nella guida c'è scritto che la funzione che disegna viene chiamata quando c'è bisogno. Per capire quando viene chiamata ho inserito una printf ma non capisco in base a cosa viene chiamata.
Le chiamate nelle OpenGL sono asincrone. In pratica inseriscono uno o più comandi corrispondenti a quello che devi fare in una specie di coda e non hai modo di sapere quando il comando viene eseguito. In realtà nelle versioni più recenti ci sono alcuni sistemi per sapere quando alcune operazioni vengono eseguite ed è possibile usare glFinish che è sincrono e aspetta che la coda venga svuotata prima di tornare all'esecuzione della funzione, ma in pratica niente di tutto ciò viene usato (se non in casi particolari). E' comune nelle applicazioni grafiche essere un po' indietro rispetto allo stato interno del programma, ma è qualcosa con la quale si deve vivere ed è in ogni caso inevitabile.
"apatriarca":
Le chiamate nelle OpenGL sono asincrone. In pratica inseriscono uno o più comandi corrispondenti a quello che devi fare in una specie di coda e non hai modo di sapere quando il comando viene eseguito. In realtà nelle versioni più recenti ci sono alcuni sistemi per sapere quando alcune operazioni vengono eseguite ed è possibile usare glFinish che è sincrono e aspetta che la coda venga svuotata prima di tornare all'esecuzione della funzione, ma in pratica niente di tutto ciò viene usato (se non in casi particolari). E' comune nelle applicazioni grafiche essere un po' indietro rispetto allo stato interno del programma, ma è qualcosa con la quale si deve vivere ed è in ogni caso inevitabile.
Ti ringrazio di questa interessante discussione

Asincrone? La programmazione ad eventi non è basata sul meccanismo hw degli interrupt?
"apatriarca":
Normalmente dalla GPU, ma dipende. Esistono infatti implementazioni delle OpenGL che utilizzano la CPU per disegnare la scena (si tratta principalmente di implementazioni pensate per sistemi non dotati di scheda grafica o senza driver appositi. Ci sono poi situazioni, sempre meno frequenti per fortuna, in cui alcuni driver utilizzano la CPU invece della GPU per emulare funzionalità non supportate dalla scheda video. Si trattava a volte di scelte di marketing. La specifica delle OpenGL non richiede infatti che le chiamate siano effettuate su un tipo di hardware specifico e non richiede che una certa operazione sia efficiente. Un driver può (ma era più comune in passato - soprattutto su schede video integrate Intel) dire di supportare qualcosa, per esempio una specifica versione delle OpenGL, accelerare in hardware solo alcune operazioni e disattivare tale accelerazione negli altri casi senza alcun tipo di avvertimento. Tali funzionalità capaci di disattivare l'accelerazione hardware potevano per esempio essere alcuni formati per le texture o il supporto agli shader..
(...)
Per disegnare un triangolo sullo schermo, i passi sono a grandi linee i seguenti?
La CPU copia dalla Ram alla memoria della scheda video le tre cordinate di ogni vertice.
La GPU della scheda video calcola come disegnare il triangolo, con quale proiezione e in base alla grandezza come tagliarlo.
La natura asincrona delle OpenGL ha ben poco a che vedere con la programmazione ad eventi o gli hw interrupt. E' per certi versi simile alla programmazione web. Perché un comando venga eseguito sulla GPU è infatti necessario che tale comando e tutti i dati ad esso associati vengano inviati alla GPU attraverso il canale apposito. Una volta che la GPU ha ricevuto questi comandi li deve poi eseguire. Tutto questo richiede tantissimo tempo (dal punto di vista del processore) e non ha senso che la CPU aspetti che i comandi vengano eseguiti per continuare a fare qualcosa. E' per questo che le chiamate OpenGL ritornano subito al tuo programma dopo che il comando è stato mandato alla GPU ma non aspettano che tale comando sia stato eseguito ed in effetti potrebbe volerci tanto perché questo avvenga (potrebbero esserci altri comandi non ancora eseguiti in coda per esempio).
I passi per disegnare un triangolo sullo schermo sono tantissimi in una moderna scheda video. Tanto per darti una idea, dai una occhiata alla moderna pipeline delle OpenGL: http://www.seas.upenn.edu/~pcozzi/OpenG ... peline.pdf
I passi per disegnare un triangolo sullo schermo sono tantissimi in una moderna scheda video. Tanto per darti una idea, dai una occhiata alla moderna pipeline delle OpenGL: http://www.seas.upenn.edu/~pcozzi/OpenG ... peline.pdf
"apatriarca":
La natura asincrona delle OpenGL ha ben poco a che vedere con la programmazione ad eventi o gli hw interrupt. E' per certi versi simile alla programmazione web. Perché un comando venga eseguito sulla GPU è infatti necessario che tale comando e tutti i dati ad esso associati vengano inviati alla GPU attraverso il canale apposito. Una volta che la GPU ha ricevuto questi comandi li deve poi eseguire. Tutto questo richiede tantissimo tempo (dal punto di vista del processore) e non ha senso che la CPU aspetti che i comandi vengano eseguiti per continuare a fare qualcosa. E' per questo che le chiamate OpenGL ritornano subito al tuo programma dopo che il comando è stato mandato alla GPU ma non aspettano che tale comando sia stato eseguito ed in effetti potrebbe volerci tanto perché questo avvenga (potrebbero esserci altri comandi non ancora eseguiti in coda per esempio).
Capito.
Quindi l'inserimento dell'esecuzione delle funzioni callback (per esempio quella che disegna) in coda è sincrono con gli eventi mentre la loro esecuzione è asincrona?
E' corretto dire che gli eventi sono realizzati a basso livello con il meccanismo dell'interrupt?
I passi per disegnare un triangolo sullo schermo sono tantissimi in una moderna scheda video. Tanto per darti una idea, dai una occhiata alla moderna pipeline delle OpenGL: http://www.seas.upenn.edu/~pcozzi/OpenG ... peline.pdf
Quello che volevo chiedere è la conferma se ho capito la prima risposta: i calcoli su come visualizzare un triangolo a partire dai tre vertici sono quindi eseguiti dalla GPU?
Ma non ci sono funzioni di callback nelle OpenGL e non si programma ad eventi. Non capisco a cosa tu stia facendo riferimento nel tuo post. In che cosa verrebbero usati gli interrupt?
Come ti ho già spiegato non c'è alcuna garanzia che qualcosa sia accelerato da una GPU (esistono implementazioni software), ma è quello che normalmente avviene quando c'è una GPU. Con ogni probabilità i triangoli che disegni con le OpenGL sono disegnati dalla GPU.
Come ti ho già spiegato non c'è alcuna garanzia che qualcosa sia accelerato da una GPU (esistono implementazioni software), ma è quello che normalmente avviene quando c'è una GPU. Con ogni probabilità i triangoli che disegni con le OpenGL sono disegnati dalla GPU.
"apatriarca":
Ma non ci sono funzioni di callback nelle OpenGL e non si programma ad eventi. Non capisco a cosa tu stia facendo riferimento nel tuo post. In che cosa verrebbero usati gli interrupt?
Gli eventi per me sono per esempio: pressione di un tasto della tastiere, clic del mouse, ecc...
Quando un tasto (mouse o tastiera) viene premuto, il dispositivo manda un interrupt alla cpu che interrompe ciò che sta facendo ed esegue la funzione definita da chi ha scritto il sorgente.
Come ti ho già spiegato non c'è alcuna garanzia che qualcosa sia accelerato da una GPU (esistono implementazioni software), ma è quello che normalmente avviene quando c'è una GPU. Con ogni probabilità i triangoli che disegni con le OpenGL sono disegnati dalla GPU.
Capito. Nella migliore delle ipotesi sto utilizzando quindi la GPU.
Era solo per capire quali parti hw sto utilizzando.
Quando premi un tasto, informi la cpu che l'hai premuto, quindi lei manderà alla gpu le conseguenti modifiche della scena che verranno applicate non appena la gpu riterrà di doverlo fare. Non per niente, non sempre nei giochi si ha una risposta istantanea dei tasti.
Per prima cosa la gestione degli input da tastiera o da qualsiasi altra fonte non fa parte delle OpenGL. Normalmente si gestiscono gli input da tastiera e mouse attraverso il GUI toolkit usato per creare il context OpenGL (GLUT, SDL, Qt, Win32, GTK, wxWidgets, GLFW...). Non è detto che tali toolkit facciano usato di callback per la gestione di tali input e anche quando lo fanno, non fanno uso di interrupt hardware. Se tali interrupt vengono usati, è il sistema operativo a gestirli e a renderli disponibili attraverso la loro API.
Non capisco come funzionino i buffers (o meglio il single buffer).
Non capisco come mai col buffer singolo il colore di sfondo viene impostato quando premo il tasto 2 e non quando premo il tasto 1. Col buffer singolo dovrei avere un unico buffer e quindi il colore dovrebbe essere visualizzato non appena lo applico senza alcuno swap.
Codice: #include <GL/glut.h> // Header File For The GLUT Library #include <GL/gl.h> // Header File For The OpenGL32 Library #include <GL/glu.h> // Header File For The GLu32 Library #include <unistd.h> // Header file for sleeping. /* The number of our GLUT window */ int window; void keyb(unsigned char key, int x, int y) { printf("Tasto premuto: %c \n", key); //Colori di sfondo if(key == 'r') { printf("Imposto il colore rosso... \n"); glClearColor(1.0f, 0.0f, 0.0f, 0.0f); } else if(key == 'g') { printf("Imposto il colore verde... \n"); glClearColor(0.0f, 1.0f, 0.0f, 0.0f); } else if(key == 'b') { printf("Imposto il colore blu... \n"); glClearColor(0.0f, 0.0f, 1.0f, 0.0f); } //Disegno: imposto il colore else if(key == '1') { printf("Applico il colore ... \n"); glClear(GL_COLOR_BUFFER_BIT); } //Disegno: faccio lo swap else if(key == '2') { printf("Swappo i buffers ... \n"); glutSwapBuffers(); } } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); glutInitWindowSize(640, 480); glutInitWindowPosition(0, 0); window = glutCreateWindow("My first opengl app..."); glutKeyboardFunc(&keyb); glutMainLoop(); return 1; }
Output: Tasto premuto: g Imposto il colore verde... Tasto premuto: 1 Applico il colore ... Tasto premuto: 2 Swappo i buffers ...
Non capisco come mai col buffer singolo il colore di sfondo viene impostato quando premo il tasto 2 e non quando premo il tasto 1. Col buffer singolo dovrei avere un unico buffer e quindi il colore dovrebbe essere visualizzato non appena lo applico senza alcuno swap.
Credo di aver capito che l'errore sia sempre il solito.
La glClearColor non applica il colore di sfondo ma dice alla GPU: "appena puoi applica questo colore". Ma la GPU non esegue immediatamente. Se dopo eseguo la glutSwapBuffers (o la glFlush), allora queste forzano la GPU ad eseguire immediatamente l'azione che gli ho detto.
Nel caso di buffer doppio ci vuole la prima. Mentre con buffer singolo basta la seconda.
La glClearColor non applica il colore di sfondo ma dice alla GPU: "appena puoi applica questo colore". Ma la GPU non esegue immediatamente. Se dopo eseguo la glutSwapBuffers (o la glFlush), allora queste forzano la GPU ad eseguire immediatamente l'azione che gli ho detto.
Nel caso di buffer doppio ci vuole la prima. Mentre con buffer singolo basta la seconda.
Di fatto si usa sempre almeno il double buffering. Alcune applicazioni usano addirittura il triple buffering in cui l'applicazione non deve aspettare lo swap dei buffer prima di iniziare a disegnare una nuova scena. Nei casi in cui si faccia utilizzo della sincronia verticale (lo swap avviene quando il monitor decide di ripartire da capo nella visualizzazione del frame*) questa attesa potrebbe essere di alcuni millisecondi. A 60 Hz l'attesa può essere anche di 16 ms se la scena disegnata è molto semplice.
In ogni caso hai capito quale fosse il problema. Non c'è alcuna garanzia che i comandi che hai inviato alle OpenGL vengano eseguiti se non chiami glFlush (o una qualche altra funzione come glutSwapBuffer che chiama al suo interno glFlush).
* Nel caso dei vecchi monitor CRT, la scena viene disegnata una riga per volta e quando il raggio arriva alla fine dello schermo riparte da capo. Lo swap con la sincronia verticale avveniva quando questo si spostava dall'ultima riga alla prima. In questo modo non si avevano fastidiosi tagli orizzontali nella scena (soprattutto quando questa era molto dinamica). Il termine sincronia verticale deriva proprio da questo discorso. Nel caso dei monitor LCD non so in cosa consista esattamente, ma l'idea è più o meno la stessa. Evita che ci siano due immagini diverse mischiate sul monitor.
In ogni caso hai capito quale fosse il problema. Non c'è alcuna garanzia che i comandi che hai inviato alle OpenGL vengano eseguiti se non chiami glFlush (o una qualche altra funzione come glutSwapBuffer che chiama al suo interno glFlush).
* Nel caso dei vecchi monitor CRT, la scena viene disegnata una riga per volta e quando il raggio arriva alla fine dello schermo riparte da capo. Lo swap con la sincronia verticale avveniva quando questo si spostava dall'ultima riga alla prima. In questo modo non si avevano fastidiosi tagli orizzontali nella scena (soprattutto quando questa era molto dinamica). Il termine sincronia verticale deriva proprio da questo discorso. Nel caso dei monitor LCD non so in cosa consista esattamente, ma l'idea è più o meno la stessa. Evita che ci siano due immagini diverse mischiate sul monitor.
Grazie per le risposte
Sto procedendo con le lezioni.
Ho trovato un problema con la normale definita dalla glNormal. Non capisco perchè mi chiede le coordinate del vettore normale da assegnare al vertice.
Data una superficie la direzione della normale è fissata (perpendicolare alla superficie). Posso far variare solo modulo e verso. Quindi anzichè chiedermi le tre coordinate non dovrebbe chiedermi solo questi ultimi due?

Ho trovato un problema con la normale definita dalla glNormal. Non capisco perchè mi chiede le coordinate del vettore normale da assegnare al vertice.
Data una superficie la direzione della normale è fissata (perpendicolare alla superficie). Posso far variare solo modulo e verso. Quindi anzichè chiedermi le tre coordinate non dovrebbe chiedermi solo questi ultimi due?
Il problema è che tu non hai una superficie (ad esempio un triangolo), ma un vertice. Un vertice non definisce una superficie e quindi la direzione normale in quel punto non è ben definita. Il motivo per cui è utile/necessario definire una direzione normale per ogni vertice è per calcolare l'effetto che una sorgente luminosa ha sul quel punto. Nelle OpenGL "vecchie" infatti, l'effetto della luce viene calcolato solo nei vertici e poi viene interpolato all'interno di triangoli (o più generalmente poligoni).
Ricordati comunque che nella maggior parte dei casi la mesh è solo una approssimazione di quello che si vorrebbe disegnare e la normale nei vertici di tale mesh è normalmente presa come la normale sulla superficie che si vuole approssimare o attraverso una interpolazione (a volte pesata) tra le normale dei triangoli contenenti tale vertice (esistono diversi metodo con effetti diversi).
Ricordati comunque che nella maggior parte dei casi la mesh è solo una approssimazione di quello che si vorrebbe disegnare e la normale nei vertici di tale mesh è normalmente presa come la normale sulla superficie che si vuole approssimare o attraverso una interpolazione (a volte pesata) tra le normale dei triangoli contenenti tale vertice (esistono diversi metodo con effetti diversi).
"apatriarca":
Il problema è che tu non hai una superficie (ad esempio un triangolo), ma un vertice. Un vertice non definisce una superficie e quindi la direzione normale in quel punto non è ben definita. Il motivo per cui è utile/necessario definire una direzione normale per ogni vertice è per calcolare l'effetto che una sorgente luminosa ha sul quel punto. Nelle OpenGL "vecchie" infatti, l'effetto della luce viene calcolato solo nei vertici e poi viene interpolato all'interno di triangoli (o più generalmente poligoni).
(...)
Quindi la glNormal mi permette anche di definire "vettori normali" che non sono matematicamente perpendicolari?
Non viene segnalato nessun errore?
La normale definita con la glNormal non ha altri effetti? Ha effetto solo sulla luce e sulla mesh?
Disegno un triangolo. // glNormal(0, 0, 1); <------ Esempio di normale corretta glNormal(1, 1, 1); <------ Normale errata glVertex(0, 0, 0); glVertex(1, 0, 0); glVertex(0, 1, 0);
Un'ultima cosa: secondo come è stato scritto lo pseudo-codice sopra, la glNormal si applica solo al vertice sotto e non agli altri?
glNormal si applica a tutti i vertici che seguono la chiamata fino alla successiva chiamata a glNormal. Setta cioè un qualche tipo di stato all'interno delle OpenGL. Nel tuo caso quindi, (1,1,1) sarà la normale a tutti i vertici successivi. Puoi settare il valore che preferisci, non deve avere necessariamente legare con la geometria, ma ha poco senso fare diversamente. glNormal puoi chiamarlo prima di ogni vertice per settare normali differenti ai tre vertici del triangolo (viene usato per far apparire l'oggetto più liscio in quanto parte della nostra capacità di percepire le forme è legata all'ombreggiatura.