Sfondo:
Sto progettando un semplice sistema di rendering 3D per un'architettura di tipo sistema di componenti di entità usando C ++ e OpenGL. Il sistema è costituito da un renderer e un grafico di scena. Quando finisco la prima iterazione del renderer, potrei distribuire il grafico della scena nell'architettura ECS. Per ora è un segnaposto in un modo o nell'altro. Se possibile, i seguenti obiettivi sono per il renderer:
- Semplicità . Questo è per un progetto di ricerca e voglio essere in grado di cambiare ed espandere facilmente i miei sistemi (da qui l'approccio ECS).
- Prestazioni . La mia scena potrebbe avere molti piccoli modelli e anche grandi volumi con molta geometria. Non è accettabile acquisire oggetti dal contesto OGL e dalla geometria del buffer per ogni frame di rendering. Sto mirando alla localizzazione dei dati per evitare errori nella cache.
- Flessibilità . Deve essere in grado di eseguire il rendering di sprite, modelli e volumi (voxel).
- Disaccoppiato . Il grafico della scena può essere rifattorizzato nell'architettura ECS principale dopo aver scritto il mio renderer.
- Modulari . Sarebbe bello poter scambiare diversi renderer senza cambiare il mio grafico di scena.
- Trasparenza referenziale , nel senso che in qualsiasi momento posso dargli qualsiasi scena valida e renderò sempre la stessa immagine per quella scena. Questo obiettivo in particolare non è necessariamente richiesto. Ho pensato che avrebbe aiutato a semplificare la serializzazione delle scene (dovrò essere in grado di salvare e caricare scene) e offrirmi la flessibilità di scambiare scene diverse durante il runtime a scopo di test / sperimentazione.
Problema e idee:
Ho escogitato alcuni approcci diversi da provare, ma sto lottando su come memorizzare nella cache le risorse OGL (VAO, VBO, shader, ecc.) Per ciascun nodo di rendering. Di seguito sono riportati i diversi concetti di memorizzazione nella cache che ho pensato finora:
- Cache centralizzata. Ogni nodo scena ha un ID e il renderer ha una cache che mappa gli ID per renderizzare i nodi. Ogni nodo di rendering contiene VAO e VBO associati alla geometria. Una mancata cache acquisisce risorse e mappa la geometria su un nodo di rendering nella cache. Quando la geometria viene modificata, viene impostato un flag sporco. Se il renderer vede un flag di geometria sporco durante l'iterazione attraverso i nodi della scena, esegue il rebuffing dei dati utilizzando il nodo di rendering. Quando un nodo di scena viene rimosso, viene trasmesso un evento e il renderer rimuove il nodo di rendering associato dalla cache rilasciando risorse. In alternativa, il nodo è contrassegnato per la rimozione e il renderer è responsabile della sua rimozione. Penso che questo approccio raggiunga più da vicino l'obiettivo 6, considerando anche il 4 e 5. 2 soffre della complessità e perdita di localizzazione dei dati extra con le ricerche delle mappe invece dell'accesso alla matrice.
- Cache distribuita . Simile sopra ad eccezione di ogni nodo di scena ha un nodo di rendering. Questo ignora la ricerca della mappa. Per indirizzare la localizzazione dei dati, i nodi di rendering potrebbero essere memorizzati nel renderer. Quindi i nodi di scena potrebbero invece avere dei puntatori per renderizzare i nodi e il renderer imposta il puntatore su una cache mancata. Penso che questo tipo di imitazione di un approccio a componenti di entità, quindi sarebbe coerente con il resto dell'architettura. Il problema qui è che ora i nodi di scena contengono dati specifici dell'implementazione del renderer. Se cambio il modo in cui le cose vengono renderizzate nel renderer (come il rendering di sprite rispetto a volumi) ora devo cambiare il nodo di rendering o aggiungere più "componenti" al nodo della scena (il che significa cambiare anche il grafico della scena). Tra i lati positivi, questo sembra il modo più semplice per rendere operativo il mio renderer di prima iterazione.
- Metadati distribuiti . Un componente dei metadati della cache del renderer è archiviato in ciascun nodo di scena. Questi dati non sono specifici dell'implementazione ma contengono piuttosto un ID, un tipo e qualsiasi altro dato rilevante necessario alla cache. Quindi la ricerca nella cache può essere eseguita direttamente in un array usando l'ID e il tipo può indicare quale tipo di approccio di rendering usare (come sprite vs volumi).
- Visitatore + mappatura distribuita . Il renderer è un visitatore e i nodi scena sono elementi nel modello visitatore. Ogni nodo scena contiene una chiave cache (come i metadati ma solo un ID) che solo il renderer manipola. L'ID può essere utilizzato per l'array anziché per la ricerca di mappe generalizzate. Il renderer può consentire al nodo scena di inviare una diversa funzione di rendering in base al tipo di nodo scena e l'ID può essere utilizzato da qualsiasi cache. Un ID predefinito o fuori portata indica un errore nella cache.
Come risolveresti questo problema? O hai qualche suggerimento? Grazie per aver letto la mia bacheca di testo!