Livello di astrazione corretto per un componente di rendering 3d?


8

Ho visto molte domande in quest'area ma non questa domanda esatta, quindi mi scuso se questo è un duplicato.

Sto realizzando un piccolo gioco 3D. Ad essere onesti, è solo un piccolo progetto di hobby e probabilmente non si rivelerà un vero gioco, sarò felice di fare una bella demo grafica e conoscere il rendering 3d e il design c ++.

Il mio intento è quello di utilizzare direct3d9 per il rendering in quanto ne ho una piccola esperienza e sembra soddisfare i miei requisiti. Tuttavia, se ho imparato una cosa come programmatore, è chiedere " c'è qualche ragione plausibile che questo componente possa essere sostituito da una diversa impiantazione " e se la risposta è sì, allora devo progettare un'astrazione e un'interfaccia adeguate a quel componente . Quindi, anche se intendo impiantare d3d9, devo progettare un'interfaccia 3d che potrebbe essere implementata per d3d11, opengl ...

La mia domanda è: a che livello è meglio farlo? Sto pensando che un'interfaccia in grado di creare e successivamente disegnare

  • Buffer di vertici e buffer di indice
  • Textures
  • "Shader" di vertici e pixel
  • Qualche rappresentazione dello stato del disegno (metodi di fusione, ecc ...)

In altre parole, un'interfaccia di livello abbastanza basso in cui il mio codice per disegnare ad esempio un modello animato userebbe l'interfaccia per ottenere buffer di vertici astratti ecc. Temo però che sia un livello troppo basso per astrarre tutte le funzionalità di cui ho bisogno in modo efficiente.

L'alternativa è farlo a un livello superiore in cui l'interfaccia può disegnare oggetti, animazioni, paesaggi ecc. E implementarli per ciascun sistema. Sembra più lavoro, ma immagino sia più flessibile.

Quindi questa è davvero la mia domanda, quando astraggiamo il sistema di disegno, quale livello di interfaccia funziona meglio?


Potrebbe non rispondere completamente alla tua domanda, ma ti consiglio di guardare alle fonti di motori DirectX + OpenGL come OGRE e Irrlicht per vedere come l'hanno astratta.
Il comunista Duck il

Risposte:


8

Ci scusiamo per la lunghezza di questa risposta!

"c'è qualche ragione immaginabile che questo componente possa essere sostituito da una diversa impiantazione" e se la risposta è sì, allora ho bisogno di progettare una corretta astrazione e interfaccia a quel componente.

"Qualsiasi ragione immaginabile" è una posizione troppo estrema da prendere. Quasi ogni funzione può essere parametrizzata in qualche modo, ogni modulo ha una strategia o una politica diversa e ogni costante ottimizzata. Se fornisci un ulteriore livello di astrazione per ognuno di questi, finirai per scrivere il doppio del codice per nessun guadagno immediato, solo un potenziale guadagno in seguito se mai hai bisogno di quella flessibilità.

Se segui il percorso di fornitura delle interfacce in modo che ognuna di queste cose possa essere una facciata per una delle diverse opzioni, devi decidere come fornire queste diverse opzioni, inclusi i valori predefiniti, e quando provi a renderlo semplice dato che l'oggetto originale era spesso finisci con un intero livello in più. Questo può (e fa) diventare assurdo in molti software "enterprise" del mondo reale - posso suggerire umilmente di leggere questo pezzo di satira che purtroppo colpisce troppo vicino a casa in molti casi: Why Hate Frameworks .

Il più grande argomento contro questo è la complessità. Facciamo il presupposto (probabilmente errato) che esista una buona interfaccia che possa applicarsi ugualmente bene a due API di rendering 3D e che qualcuno sia in grado di descriverti in una risposta qui. Lavoreresti quindi con questa interfaccia che, per la sua natura multiuso, coprirebbe senza dubbio elementi che DX9 non utilizza, ad esempio la pletora di estensioni API disponibili in OpenGL ma non in DirectX 9. Questo è un'ulteriore complicazione che renderà il codice più difficile da comprendere e mantenere e rallenterà il processo di apprendimento. È abbastanza brutto quando si utilizza una libreria multi-piattaforma esistente come SDL, perché le persone incontrano per sempre strane funzioni e restrizioni che esistono puramente per risolvere qualcosa su una piattaforma, ma tu " Non solo devi imparare perché c'è quella parte dell'interfaccia ma anche come può o meno interagire con le parti che stai per scrivere. per esempio. Finiresti per scrivere oggetti astratti che incapsulano aspetti delle estensioni OpenGL che sono necessari nel tuo codice di rendering principale, anche se non hai ancora modo di usarli.

Tuttavia, in futuro, una volta che sei competente per il funzionamento del sistema DX9, puoi esaminare come funziona il sistema OpenGL. Vedrai quindi come adattare il tuo sistema esistente per far funzionare entrambe le parti, e lo farai, con la pratica esistenza del tuo percorso di codice DX9 esistente come efficace test unitario per garantire che il refactoring sia corretto.

Sei fortunato nel fatto che stai lavorando con uno dei materiali più fluidi e malleabili noti all'ingegneria: il codice sorgente memorizzato come dati del computer. Abbraccia quella benedizione apportandole delle modifiche quando sai di averne bisogno, piuttosto che farle con mesi di anticipo e caricarti nel frattempo dovendo scrivere a un'astrazione da cui non stai ancora guadagnando.


Ecco come mi sento, ma allo stesso tempo il consiglio comune che vedo è che astrarre il codice specifico della piattaforma per un gioco è facile da fare e dovrebbe essere fatto ... Non so cosa pensare.
Rei Miyasaka,

5

Tuttavia, se ho imparato una cosa come programmatore, è chiedere "c'è qualche ragione plausibile che questo componente possa essere sostituito da una diversa impiantistica" e se la risposta è sì, allora devo progettare un'astrazione e un'interfaccia adeguate a quel componente .

Questo non risponde davvero alla tua domanda, ma dal mio esperienza di programmatore, la generalizzazione prematura è altrettanto negativa dell'ottimizzazione prematura. Raramente ottieni qualcosa e devi mantenerlo nel corso del progetto.

Il mio consiglio è di utilizzare direttamente il codice client D3D9 nel progetto e, quando si determina che è necessario cambiare interfaccia, creare un'interfaccia con tutto ciò che si sta attualmente utilizzando. I programmatori sono terribili nel predire ciò di cui potrebbero aver bisogno, doppiamente nel tuo caso in cui non hai una conoscenza terribile del materiale, quindi in questo modo stai facendo la minima quantità di lavoro.

Vedi anche: http://c2.com/xp/YouArentGonnaNeedIt.html


Il problema è che se in seguito deve cercare un'astrazione, deve riscrivere tutto il suo codice di interfaccia. E se D3D9 è stato incluso globalmente in tutto il suo progetto, sarà dannatamente difficile riformattare tutto quel codice.
DeadMG

1
Dovrà comunque riscrivere tutto quando ha un caso d'uso che non è coperto da qualsiasi ipotesi faccia ora, comunque. Il refactoring dovrebbe essere abbracciato, specialmente per un progetto di apprendimento.
Tetrad

Sto esagerando un po 'il caso. Anch'io odio l'astrazione inutile. Ma in questo caso posso dire che in realtà c'è una buona possibilità che vorrò farlo con un'implementazione diversa ...
JohnB

4

Hm, sembra che non abbia abbastanza reputazione per pubblicare commenti alle risposte.

Sia Kylotan che Tetrad hanno dato risposte eccellenti. Quello che vorrei aggiungere è suggerire di separare il codice di rendering dalla logica di gioco - questo servirà a diversi scopi:

  • il tuo codice sarà più pulito
  • sarà più facile apportare modifiche al renderer senza riscrivere metà della logica di gioco ogni volta
  • alla fine, fornire moduli di rendering completamente diversi è più semplice in questo modo

Come bonus aggiuntivo, in questo modo costruirai la tua astrazione gradualmente mentre lavori al tuo progetto. Come dici tu, questo è un progetto di apprendimento, quindi prendilo con calma e rendilo semplice, aggiungendo complessità e astrazioni mentre procedi invece di fare grandi progetti poiché probabilmente non hai esperienza per farlo correttamente inizialmente (come anche Tetrad ha suggerito).


Oh davvero! Ma la domanda è: a che livello lo separo? A livello di disegno di un'intera città, o a livello di disegno di un triangolo? O dove nel mezzo
JohnB

Un modo ragionevole sarebbe mantenere separato il rendering delle chiamate API. Probabilmente si tratterebbe di "disegnare un edificio o un'auto" nella tua analogia (considerando che i tuoi edifici e le tue auto sono oggetti singoli :)). Un esempio: vuoi animare il tuo oggetto (per semplicità supponiamo che solo uno possa essere giocato alla volta). Un oggetto di gioco potrebbe avere un elenco di sequenze disponibili e la loro lunghezza e impostare l'animazione attiva e la sua velocità mentre il renderer gestirà, bene, la parte di rendering. Potrebbe anche essere necessario recuperare i dati dal renderer, ad esempio la forma della collisione dell'oggetto potrebbe cambiare durante l'animazione.
Gilead,

1

Non puoi scrivere un'astrazione di basso livello su questo tipo di cose, perché i dettagli di uno shader sono completamente diversi tra D3D9, 11, OGL, ecc. È semplicemente impossibile.

Puoi controllare le astrazioni implementate in, diciamo, SFML, SDL, OGRE se vuoi vedere cosa altre persone hanno scelto di fare per rendere le astrazioni.


A meno che gli shader non facciano parte dell'astrazione.
user253751

1

Ho provato a scrivere un'API che avvolga ordinatamente D3D e OGL. È un'impresa enorme, ho imparato. Molte delle loro differenze possono essere riconciliate. Ad esempio, caricamento di shader. Incapsulo efficacemente il codice shader D3D e OGL nel mio formato. IE: In D3D, è necessario specificare il profilo dello shader, quindi il file stesso contiene il profilo dello shader. Quando la sorgente shader incapsulata viene consegnata alla mia API, chiama il metodo di creazione shader D3D appropriato con il profilo shader appropriato e simili.

Tuttavia, ci sono ancora molte differenze che non ho risolto, né potrei mai, perché ho deciso che un'astrazione di livello superiore è più facile da progettare e soddisfacente per le mie esigenze. Attualmente sto lavorando a qualcosa di simile al framework D3DXEffect. Gli oggetti hanno una tecnica di rendering, che contiene passaggi di rendering, che hanno un set di stati di rendering, un set di shader e richiedono determinati input. Questo mi permette di sottrarre più API.


1

Non so perché le persone passino automaticamente alla generalizzazione / ottimizzazione prematura quando sorgono domande come queste. C'è qualcosa da dire sull'essere preventivi e realizzare quando è probabile che un componente in un sistema venga scambiato. Tutte le risposte su "Non preoccuparti e codice via fino a quando non è necessario cambiarlo" generalmente mettono le persone nei guai nel mondo reale poiché portano cambiamenti più grandi lungo la strada o lasciano codice che dovrebbe essere progettato in modo diverso per iniziare con, ma è stato lasciato perché "funziona". Pensare in anticipo e sapere dove fermarsi prima di pensare troppo è un'abilità in sé.

Sarebbe molto più semplice riformattare le interfacce per adattarsi alle differenze tra DX / OGL, piuttosto che introdurre una nuova implementazione (ad esempio DX) in un sistema che è strettamente codificato per funzionare con OGL. L'astrazione del codice dovrebbe avvenire comunque ad un certo livello in quanto non si vorrebbe mescolare il codice grafico con il codice della logica di gioco ovunque, o ripetere il codice. Se sto lavorando a un gioco e sto iniziando con OpenGL (perché ho esperienza con esso), ma so anche che mi piacerebbe finalmente ottenere il gioco su Xbox / Windows (DX), sarebbe sciocco per me non tenerne conto durante la progettazione della mia API grafica. L'idea di refactoring di qualcosa di complesso come un gioco e l'introduzione di nuove API grafiche è noiosa e soggetta a errori.

Dovresti considerare come utilizzare l'API e crearla man mano che le tue esigenze / conoscenze cambiano durante il processo di sviluppo del gioco. Non stai facendo un'astrazione su OGL / DX di basso livello, ma piuttosto creando un'API per aiutarti a caricare la geometria, caricare / compilare shader, caricare trame, ecc.

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.