OpenGL orientato agli oggetti


12

Ho usato OpenGL per un po 'e ho letto un gran numero di tutorial. A parte il fatto che molti di loro usano ancora la pipeline fissa, di solito lanciano tutte le inizializzazioni, i cambiamenti di stato e il disegno in un file sorgente. Questo va bene per l'ambito limitato di un tutorial, ma sto facendo fatica a capire come ridimensionarlo a un gioco completo.

Come dividi il tuo utilizzo di OpenGL tra i file? Concettualmente, posso vedere i vantaggi di avere, diciamo, una classe di rendering che riproduce semplicemente le cose sullo schermo, ma come funzionano cose come shader e luci? Dovrei avere lezioni separate per cose come luci e shader?


1
Esistono giochi open source e tutorial più grandi che hanno più codice. Dai un'occhiata ad alcuni di essi e guarda come è organizzato il codice.
MichaelHouse

Sei un principiante quando si tratta di rendering dei motori?
Samaursa,

Non è possibile rispondere ragionevolmente a questa domanda senza una limitazione del campo di applicazione. Cosa stai cercando di costruire? Che tipo di gioco, che tipo di grafica ha, ecc.?
Nicol Bolas,

1
@Sullivan: "Pensavo che questo tipo di concetti si applicasse a quasi tutti i giochi." Non lo fanno. Se mi chiedi di creare Tetris, non mi preoccuperò di creare un grande strato di wrapper o qualcosa attorno a OpenGL. Usalo direttamente. Se mi chiedi di creare qualcosa come Mass Effect, avremo bisogno di più livelli di astrazione attorno al nostro sistema di rendering. Lanciare il motore Unreal a Tetris sta usando un fucile da caccia per cacciare le mosche; rende il compito molto più difficile della semplice codifica manuale. Dovresti costruire un sistema tanto grande e complesso quanto ti serve e non più grande.
Nicol Bolas,

1
@Sullivan: l'unica complessità che la tua domanda implica è dove parli della suddivisione su più file. Il che è qualcosa che farei anche a Tetris. E ci sono molti livelli di complessità tra Tetris e il motore Unreal. Quindi ancora: di quale stai chiedendo?
Nicol Bolas,

Risposte:


6

Penso che OO OpenGL non sia così necessario. È diverso quando parli di shader, modello, ecc.

Fondamentalmente dovresti fare prima l'inizializzazione del gioco / motore (e altre cose). Quindi caricare texture, modelli e shader su RAM (se necessario) e Buffer Objects e caricare / compilare shader. Successivamente, l'utente, nella struttura dei dati o nella classe di shader, modello, ha ID int di shader, oggetti modello e texture buffer.

Penso che la maggior parte dei motori abbia componenti del motore e ognuno di essi ha determinate interfacce. Tutti i motori che ho esaminato hanno alcuni componenti come Renderer o SceneManager o entrambi (dipende dalla complessità del gioco / motore). Quindi puoi avere la classe OpenGLRenderer e / o DXRenderer che implementano l'interfaccia Renderer. Quindi se hai SceneManager e Renderer puoi fare alcune delle seguenti cose:

  • Attraversa SceneManager e invia ogni oggetto a Renderer per il rendering
  • Incapsula lettere maiuscole in alcuni metodi di SceneManager
  • Invia SceneManager a Renderer in modo che lo gestisca

Il renderer probabilmente chiamerebbe la funzione di disegno dell'oggetto che chiamerebbe la funzione di disegno di ogni mesh composta e la mesh legherebbe l'oggetto texture, legerebbe shader, chiamerebbe la funzione di disegno OpenGL e quindi inutilizzerebbe shader, texture e oggetti buffer dati oggetto.

NOTA: questo è solo un esempio, dovresti studiare SceneManager in modo più dettagliato e analizzare il tuo caso d'uso per vedere qual è la migliore opzione di implementazione

Ovviamente, avresti altri componenti del motore, come MemoryManager , ResourceLoader ecc., Che si occuperebbero dell'utilizzo della memoria video e RAM, in modo da poter caricare / scaricare determinati modelli / shader / trame secondo necessità. I concetti per questo includono la memorizzazione nella cache, il mapping della memoria ecc. Ecc. Ci sono molti dettagli e concetti su ciascun componente.

Dai un'occhiata a una descrizione più dettagliata di altri motori di gioco, ce ne sono molti e la loro documentazione è praticamente disponibile.

Ma sì, le lezioni semplificano la vita; dovresti usarli totalmente e ricordare incapsulamento, eredità, interfacce e altre cose interessanti.


Questa è la risposta che stavo cercando. Tuttavia, quando dici "... carica trame, modelli e shader su RAM (se necessario) e Buffer Objects e carica / compila shader ..." mi riporta alla mia domanda originale; dovrei usare oggetti per incapsulare cose come modelli e shader?
Sullivan,

L'oggetto è un'istanza di una classe e "dovresti usarli totalmente".
edin-m,

Scusa, volevo chiedere "come dovrei usare gli oggetti". Colpa mia.
Sullivan,

Bene, dipende dal tuo design di classe. Qualche esempio di base sarebbe intuitivo, qualcosa del tipo: object3d (class) has mesh (class); ogni maglia ha materiale (classe); ogni materiale ha trama (può essere di classe o solo id) e shader (classe). Ma leggendo di ogni componente vedrai che ci sono approcci diversi ma uguali per creare SceneManager, Renderer, MemoryManager (tutti questi possono essere a classe singola o multipli, ereditati ecc. Ecc.). Decine di libri sono scritti su questo argomento (architettura del motore di gioco) e per rispondere in dettaglio alla domanda si potrebbe scrivere uno.
edin-m,

Penso che questa sia la migliore risposta che sto per ottenere. Grazie per l'aiuto :)
Sullivan,

2

OpenGL ha già alcuni concetti "Object".

Ad esempio, qualsiasi cosa con un ID può passare attraverso come oggetto (ci sono anche cose specificatamente chiamate "Oggetti"). Buffer, trame, oggetti buffer vertici, oggetti array vertici, oggetti frame buffer e così via. Con un po 'di lavoro puoi avvolgere le classi attorno a loro. Ti dà anche un modo semplice per tornare alle vecchie funzioni obsolete di OpenGL se il tuo contesto non supporta le estensioni. Ad esempio un oggetto VertexBuffer potrebbe ricorrere all'utilizzo di glBegin (), glVertex3f () e così via.

Esistono alcuni modi in cui potrebbe essere necessario allontanarsi dai concetti OpenGL tradizionali, ad esempio probabilmente si desidera archiviare metadati sui buffer negli oggetti buffer. Ad esempio se il buffer memorizza i vertici. Qual è il formato dei vertici (ovvero posizione, normali, texcoords e così via). Quali primitivi utilizza (GL_TRIANGLES, GL_TRIANGLESTRIP, ecc ...), informazioni sulla dimensione (quanti galleggianti sono memorizzati, quanti triangoli rappresentano, ecc ...). Giusto per semplificare il loro inserimento nei comandi degli array di disegno.

Ti consiglio di guardare OGLplus . Sono i collegamenti C ++ per OpenGL.

Anche glxx , questo è solo per il caricamento delle estensioni però.

Oltre a racchiudere l'API OpenGL, dovresti cercare di creare un livello leggermente superiore su di esso.

Ad esempio una classe di gestione dei materiali responsabile di tutti gli shader, caricandoli e utilizzandoli. Inoltre sarebbe responsabile per il trasferimento di proprietà a loro. In questo modo puoi semplicemente chiamare: materials.usePhong (); material.setTexture (sometexture); material.setColor (). Ciò consente una maggiore flessibilità poiché è possibile utilizzare elementi più recenti come gli oggetti buffer uniformi condivisi per avere solo 1 buffer di grandi dimensioni contenente tutte le proprietà utilizzate dagli shader in 1 blocco, ma se non è supportato, è possibile ricorrere al caricamento su ciascun programma shader. Puoi avere 1 grande shader monolitico e scambiare tra diversi modelli di shader usando routine uniformi se è supportato o puoi ricorrere all'utilizzo di un gruppo di diversi shader piccoli.

Puoi anche cercare di spendere dalle specifiche GLSL per scrivere il tuo codice shader. Ad esempio, #include sarebbe incredibilmente utile e molto facile da implementare nel codice di caricamento dello shader (è disponibile anche un'estensione ARB ). Puoi anche generare il tuo codice al volo in base alle estensioni supportate, ad esempio utilizzare un oggetto uniforme condiviso o ricorrere all'utilizzo di uniformi normali.

Infine, vorrai un'API della pipeline di rendering di livello superiore che esegua operazioni come grafici di scene, effetti speciali (sfocatura, bagliore), elementi che richiedono passaggi di rendering multipli come ombre, illuminazione e così via. Inoltre, un'API di gioco che non ha nulla a che fare con l'API grafica ma si occupa solo di oggetti in un mondo.


2
"Ti consiglio di guardare OGLplus. Sono i collegamenti C ++ per OpenGL." Vorrei raccomandare contro quello. Adoro RAII, ma è del tutto inappropriato per la maggior parte degli oggetti OpenGL. Gli oggetti OpenGL sono associati a un costrutto globale: il contesto OpenGL. Quindi non saresti in grado di creare questi oggetti C ++ fino a quando non avessi un contesto, e non saresti in grado di eliminare questi oggetti se il contesto è già stato distrutto. Inoltre, dà l' illusione di poter modificare gli oggetti senza cambiare lo stato globale. Questa è una bugia (a meno che tu non stia usando EXT_DSA).
Nicol Bolas,

@NicolBolas: Non posso controllare per vedere se c'è un contesto e inizializzare solo allora? O forse consentire solo ai gestori di creare gli oggetti che richiedono un contesto OpenGL? --- a proposito, grande set di tutorial (link nel tuo profilo)! In qualche modo non mi sono mai imbattuto in loro durante la ricerca di OpenGL / Grafica.
Samaursa,

@Samaursa: a che fine? Se hai un gestore per oggetti OpenGL, allora ... non hai davvero bisogno che gli oggetti si prendano cura di se stessi; il manager può farlo per loro. E se li inizializzi solo quando esiste un contesto, cosa succede se provi a usare un oggetto che non è stato inizializzato correttamente? Crea fragilità nel tuo codice. Né cambia nulla di ciò che ho detto sulla capacità di modificare questi oggetti senza toccare lo stato globale.
Nicol Bolas,

@NicolBolas: "non saresti in grado di creare questi oggetti C ++ fino a quando non avessi un contesto" ... è diverso dall'uso dei costrutti OpenGL nudi? Ai miei occhi, la oglplus::Contextclasse rende questa dipendenza altamente visibile - sarebbe un problema? Credo che aiuterà i nuovi utenti OpenGL a evitare molti problemi.
xtofl

1

Nel moderno OpenGL puoi quasi totalmente separare gli oggetti renderizzati l'uno dall'altro, usando diversi programmi di shader e vaos. E anche l'implementazione di un oggetto può essere separata in molti livelli di astrazione.

Ad esempio, se si desidera implementare un terreno, è possibile definire una TerrainMesh il cui costruttore crea i vertici e gli indici per il terreno e li imposta in arraybuffer e, se gli si assegna una posizione di attributo, frantuma i dati. Dovrebbe anche sapere come renderlo e dovrebbe avere cura di ripristinare tutti i cambiamenti di contesto che ha fatto per impostare il rendering. Questa stessa classe non dovrebbe sapere nulla del programma shader che lo renderà, e non dovrebbe sapere nulla di nessun altro oggetto nella scena. Al di sopra di questa classe è possibile definire un Terreno, che è a conoscenza del codice dello shader, e il suo compito è quello di creare la connessione tra lo shader e TerrainMesh. Questo dovrebbe significare ottenere attributi e posizioni uniformi e caricare trame e cose del genere. Questa classe non dovrebbe sapere nulla sull'implementazione del terreno, sull'algoritmo LoD che utilizza, è solo responsabile dell'ombreggiatura del terreno. Oltre a ciò, è possibile definire la funzionalità non OpenGL come behivour e rilevamento delle collisioni e simili.

Per quanto riguarda il punto, anche se OpenGL è progettato per essere utilizzato a basso livello, puoi comunque costruire livelli indipendenti di astrazione, che ti consentono di scalare fino ad applicazioni con una dimensione di un gioco Unreal. Ma il numero di livelli desiderati / necessari dipende in realtà dalle dimensioni dell'applicazione desiderata.

Ma non mentire a te stesso su queste dimensioni, non provare a imitare il modello a oggetti di Unity in un'applicazione a 10k line, il risultato sarà un disastro completo. Costruisci i livelli gradualmente, aumenta solo il numero di livelli di astrazione, quando è necessario.


0

ioDoom3 è probabilmente un ottimo punto di partenza, in quanto puoi fare affidamento su Carmack per seguire una buona pratica di programmazione. Inoltre credo che non usi il megatexturing in Doom3, quindi è relativamente semplice come render pipe.


6
"come puoi fare affidamento su Carmack per seguire una buona pratica di programmazione" Oh amico, è una buona idea. Carmack segue la pratica del codice C ++. È una rivolta! Oh ... non stavi scherzando. Um ... hai mai veramente guardato il suo codice? È un programmatore C ed è così che pensa. Il che va bene per C, ma la domanda riguarda C ++ .
Nicol Bolas,

Vengo da uno sfondo C quindi il suo codice ha molto senso per me: P e sì, ho trascorso un po 'di tempo a lavorare su giochi basati su terremoti.
chrisvarnz,
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.