Il modo più efficace per disegnare il vertice con OpenGL


8

Sto scrivendo un gioco 3D OpenGL. Ci saranno tonnellate di triangoli per il terreno e gli oggetti in uso.

Sto studiando dalla guida ufficiale di OpenGL e il primo metodo presentato è quello di chiamare una funzione glVertexdopo il glBeginper ogni vertice che si desidera disegnare.

Tuttavia, questo metodo sembra piuttosto antico e inefficiente quando devi disegnare migliaia di triangoli. Immagino che ci siano metodi per memorizzare nella memoria della scheda grafica le informazioni che non cambiano in ogni fotogramma in modo che quando si esegue il rendering di ogni fotogramma le informazioni siano già lì. Mentre per informazioni più "instabili" probabilmente devi inviare nuovi dati di vertici ogni frame e quindi una tecnica diversa potrebbe essere più efficiente.

Venendo alla mia domanda: vorrei una chiara spiegazione delle più moderne ed efficienti funzioni e tecniche OpenGL utilizzate per inviare informazioni sui vertici all'hardware e dare istruzioni per il rendering.

Quali sono i modi migliori e gli strumenti OpenGL per inviare l'ordine per il rendering dei dati dei vertici che raramente cambiano durante i frame (penso al terreno e agli oggetti) e qual è il modo migliore per inviare i dati dei vertici che cambiano molto durante i frame?


Hai ragione, glBegin () e glEnd () fanno parte del rendering in modalità immediata e piuttosto vecchio stile. Oggi si utilizzerebbero ad esempio i VBO. Tuttavia, come e esattamente cosa usi dipende dal tuo scenario!
Thalador,

Sono anche uno studente che cerca di imparare OpenGL. So che dovrai usare Vertex Buffer Objects (VBO) per disegnare i vertici più velocemente. Anche se non so abbastanza per dirti come fare, posso darti un link a un ottimo libro (online gratuito) che lo fa. L'ho scremato e ho creato un piccolo triangolo ma è davvero complesso. Esercitazione su OpenGL moderna di Arcsynthesis In questo momento sto esaminando le esercitazioni di LazyFoo. Iniziano con la pipeline a funzioni fisse glBegin (), glEnd (), quindi si spostano in OpenGL più moderni utilizzando la pipeline a funzioni fisse come base. J j
benbot,

Risposte:


19

In generale, la risposta è "dipende". Il modo in cui uno invierebbe gli aggiornamenti delle particelle è un po 'diverso dal modo in cui invieresti una serie di modelli con skin GPU.

Buffer vertici / indice

In generale, tuttavia, tutto ciò che oggigiorno viene svolto con un VBO ( oggetto buffer vertici ). La vecchia API in modalità immediata ( glBegin/ glEnd) è probabilmente implementata solo come un fat wrapper attorno al sistema di buffer dei vertici interno del driver.

Per oggetti statici, crea un VBO statico e riempilo con i tuoi dati di vertice. Se uno dei vertici è condiviso (in genere il caso), probabilmente si desidera anche creare un buffer di indice. Ciò riduce la necessità di inviare gli stessi dati più di una volta per cambiare mesh (potenzialmente risparmiando sulla larghezza di banda di trasferimento) e sull'elaborazione (risparmiando sui tempi di ombreggiatura dei vertici). Si noti che si disegna con diverse funzioni quando si eseguono disegni indicizzati vs non indicizzati.

Per oggetti dinamici, fai lo stesso, anche se con un set dinamico di buffer.

Note avanzate

Per pezzi più grandi come il terreno, probabilmente non dovrai spezzare la maglia in più pezzi. Rendere la GPU renderizzata di cento milioni di triangoli quando ne sono visibili solo duecentomila è un enorme spreco, specialmente se non sono ordinati e c'è un sacco di inversioni di shader di frammenti in eccesso e sprecate. Suddividere la mesh in grossi pezzi e rendere frustranti solo quelli che si trovano all'interno della vista. Ci sono anche varie tecniche di abbattimento più avanzate che puoi usare per eliminare pezzi che potrebbero essere nel frustrazione ma che sono interamente dietro una collina o un edificio o qualcosa del genere. Mantenere il conto alla rovescia delle chiamate di disegno è buono, ma c'è un equilibrio da trovare (che devi trovare per la tua app / hardware specifico) tra minimizzare le chiamate di disegno e minimizzare il disegno della geometria nascosta.

Una delle cose chiave da tenere a mente con un buffer GPU è che non puoi scrivergli mentre la GPU sta leggendo da esso. Devi far sapere al conducente che va bene scartare la vecchia copia del buffer (quando ha finito) e dartene una nuova (se quella vecchia è occupata). Ovviamente c'è da molto tempo che OpenGL non ha alcuna funzione per farlo (ora c'è InvalidateBufferData per GL 4.3 e alcune implementazioni precedenti come estensione). Piuttosto, esiste un comportamento non standard ma comune implementato dalla maggior parte dei driver. Fare questo per scartare il buffer prima di aggiornarlo:

glBindBuffer(GL_ARRAY_BUFFER, my_vbo);
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_DYNAMIC_DRAW);

Certamente cambia GL_ARRAY_BUFFEReGL_DYNAMIC_DRAW ai valori appropriati per il buffer. I buffer statici non verranno aggiornati (o non dovrebbero esserlo), quindi è improbabile che tu debba preoccuparti di scartarli per tale buffer.

Si noti che potrebbe essere più veloce da usare glBufferDatao glBufferSubDataoppure potrebbe essere più veloce con glMapBuffer. Dipende molto dal driver e dall'hardware. L'hardware del PC di generazione attuale sarà probabilmente il più veloce glBufferDatama test per essere sicuro.

Un'altra tecnica consiste nell'utilizzare instancing . L'istanza consente di effettuare una singola chiamata di disegno che disegna più copie dei dati in un buffer vertice / indice. Se avessi, diciamo, 100 rocce identiche, allora vorresti disegnarle tutte in una volta anziché fare 100 estrazioni indipendenti.

Durante l'installazione, è necessario inserire i dati per istanza in un altro buffer (come la posizione dell'oggetto di ciascun individuo). Può essere un buffer uniforme ( buffer costante nella terminologia D3D) o un buffer di trama o un attributo vertice per istanza. Ancora una volta, ciò che è più veloce dipende. Gli attributi per istanza sono probabilmente più veloci e sicuramente molto più facili da usare, ma molte implementazioni GL comuni non supportano ancoraglBindingAttribDivisor quindi dovrai vedere se è disponibile per l'uso e se è davvero più veloce (alcuni driver più vecchi emulavano l'istanza aggiungendo buffer e alla fine è stato più lento utilizzare istanziamenti su di essi piuttosto che effettuare chiamate di disegno indipendenti e non esiste un modo standard per scoprire ... le gioie dell'utilizzo di OpenGL).

Esistono anche algoritmi per l' ottimizzazione della cache dei vertici , che è l'atto di ordinare vertici / indici nei buffer per giocare con la cache dei vertici sulle moderne GPU. Una GPU eseguirà lo shader solo per un vertice, quindi lo memorizzerà nella cache dei vertici, ma potrebbe essere necessario sfrattarlo troppo presto per fare spazio ad altri vertici. (Supponiamo che due triangoli condividano entrambi un vertice ma ci sono altri 100 triangoli disegnati tra loro; il vertice condiviso finirà probabilmente per essere sprecato valutato due volte dallo shader di vertice.)

Alcune di queste funzionalità richiedono una versione sufficientemente nuova di GL o GLES. GLES2 non supportava l'istanziamento, ad esempio.

Profilo sempre

Ancora una volta, se ti preoccupi delle prestazioni, testa ogni possibile metodo e vedi quale è più veloce per la tua app sull'hardware di destinazione. Non solo i diversi hardware / driver di diversi produttori saranno diversi, ma alcune intere classi di hardware sono innatamente diverse. Una tipica GPU mobile è una bestia molto diversa da una tipica GPU desktop discreta. Le tecniche che sono "migliori" su uno non saranno necessariamente migliori su un altro.

Quando si tratta di prestazioni, sii sempre uno scettico .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.