Pipeline di rendering del motore: rendere generici gli shader


10

Sto cercando di creare un motore di gioco 2D usando OpenGL ES 2.0 (iOS per ora). Ho scritto il livello Applicazione in Obiettivo C e un RendererGLES20 autonomo separato in C ++. Non viene effettuata alcuna chiamata specifica GL al di fuori del renderer. Funziona perfettamente.

Ma ho alcuni problemi di progettazione quando uso gli shader. Ogni shader ha i suoi attributi e uniformi unici che devono essere impostati appena prima della chiamata principale (in questo caso glDrawArrays). Ad esempio, per disegnare una geometria farei:

void RendererGLES20::render(Model * model)
{
    // Set a bunch of uniforms
    glUniformMatrix4fv(.......);
    // Enable specific attributes, can be many
    glEnableVertexAttribArray(......);
    // Set a bunch of vertex attribute pointers:
    glVertexAttribPointer(positionSlot, 2, GL_FLOAT, GL_FALSE, stride, m->pCoords);

    // Now actually Draw the geometry
    glDrawArrays(GL_TRIANGLES, 0, m->vertexCount);

    // After drawing, disable any vertex attributes:
    glDisableVertexAttribArray(.......);
}

Come puoi vedere questo codice è estremamente rigido. Se dovessi usare un altro shader, per esempio l'effetto a catena, avrei bisogno di passare uniformi extra, attributi di vertice ecc. In altre parole, dovrei cambiare il codice sorgente di rendering di RendererGLES20 solo per incorporare il nuovo shader.

C'è un modo per rendere l'oggetto shader totalmente generico? Mi piace Cosa succede se voglio solo cambiare l'oggetto shader e non preoccuparmi della ricompilazione della fonte di gioco? Qualche modo per rendere agnostico il renderer di uniformi e attributi ecc.? Anche se dobbiamo trasferire i dati alle uniformi, qual è il posto migliore per farlo? Classe di modello? La classe del modello è a conoscenza delle uniformi e degli attributi specifici dello shader?

Spettacoli seguenti: Classe attori:

class Actor : public ISceneNode
{
    ModelController * model;
    AIController * AI;
};

Classe controller modello: class ModelController {class IShader * shader; int textureId; tinta vec4; float alpha; struct Vertex * vertexArray; };

La classe Shader contiene solo l'oggetto shader, compilando e collegando le sub-routine ecc.

Nella classe Game Logic sto realizzando il rendering dell'oggetto:

void GameLogic::update(float dt)
{
    IRenderer * renderer = g_application->GetRenderer();

    Actor * a = GetActor(id);
    renderer->render(a->model);
}

Si noti che anche se Actor estende ISceneNode, non ho ancora iniziato a implementare SceneGraph. Lo farò non appena risolverò questo problema.

Qualche idea su come migliorare questo? Modelli di progettazione correlati ecc.?

Grazie per aver letto la domanda.



Non sono sicuro che si tratti di un duplicato esatto, ma penso alla cosa che stai cercando di fare.
Sean Middleditch,

Risposte:


12

È possibile rendere il tuo sistema shader più guidato dai dati, in modo da non disporre di un codice così specifico per lo shader in circolazione per uniformi e formati di vertici, ma piuttosto impostarli a livello di programmazione in base ai metadati collegati agli shader.

Innanzitutto la dichiarazione di non responsabilità: un sistema basato sui dati può semplificare l'aggiunta di nuovi shader, ma d'altra parte comporta costi in termini di maggiore complessità del sistema, il che rende più difficile la manutenzione e il debug. Quindi è una buona idea riflettere attentamente sulla quantità di dati guidata per te (per un piccolo progetto, la risposta potrebbe essere "nessuna") e non cercare di costruire un sistema eccessivamente generalizzato.

Ok, parliamo prima dei formati (attributi) dei vertici. È possibile creare una descrizione dei dati creando una struttura che contenga i dati a cui passare glVertexAttribPointer- l'indice, il tipo, la dimensione, ecc. Di un singolo attributo - e disponendo di una matrice di tali strutture per rappresentare l'intero formato del vertice. Date queste informazioni, è possibile impostare a livello di codice tutto lo stato GL relativo agli attributi del vertice.

Da dove provengono i dati per popolare questa descrizione? Concettualmente, penso che il modo più pulito sia far appartenere allo shader. Quando si creano i dati del vertice per una mesh, si cerca quale shader viene utilizzato sulla mesh, si trova il formato del vertice richiesto da quello shader e si crea il buffer dei vertici di conseguenza. Hai solo bisogno di un modo per ogni shader per specificare il formato del vertice che si aspetta.

Esistono diversi modi per farlo; ad esempio, potresti avere un set standard di nomi per gli attributi nello shader ("attrPosition", "attrNormal", ecc.) più alcune regole codificate come "position is 3 float". Quindi si utilizza glGetAttribLocationo è simile per interrogare quali attributi utilizza lo shader e si applicano le regole per creare il formato del vertice. Un altro modo è avere uno snippet XML che definisca il formato, incorporato in un commento nella fonte dello shader ed estratto dai tuoi strumenti, o qualcosa del genere.

Per le uniformi, se è possibile utilizzare OpenGL 3.1 o versioni successive è consigliabile utilizzare oggetti buffer uniformi (l'equivalente OpenGL dei buffer costanti di D3D). Ahimè, GL ES 2.0 non ha quelli, quindi le divise devono essere gestite individualmente. Un modo per farlo sarebbe quello di creare una struttura contenente la posizione uniforme per ciascun parametro che si desidera impostare: la matrice della telecamera, la potenza speculare, la matrice mondiale ecc. Anche le posizioni del campionatore potrebbero essere qui. Questo approccio dipende dalla presenza di un set standard di parametri condivisi tra tutti gli shader. Non tutti gli shader devono usare ogni singolo parametro, ma tutti i parametri dovrebbero essere in questa struttura.

Ogni shader avrebbe un'istanza di questa struttura e quando carichi uno shader lo interrogheresti per le posizioni di tutti i parametri, usando glGetUniformLocatione nomi standardizzati. Quindi, ogni volta che devi impostare un'uniforme dal codice, puoi verificare se è presente nello shader e cercare la sua posizione e impostarla.

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.