Sto cercando di avvolgere la mia testa intorno a come sistemi di materiali come questo , questa applicazione. Questi sistemi potenti e intuitivi, simili a grafici, sembrano essere relativamente comuni come metodo per consentire a programmatori e non programmatori di creare rapidamente shader. Tuttavia, dalla mia esperienza relativamente limitata con la programmazione grafica, non sono del tutto sicuro di come funzionino.
Sfondo:
Quindi, quando in precedenza ho programmato semplici sistemi di rendering OpenGL, in genere creo una classe Material che carica, compila e collega shader da file GLSL statici che ho creato manualmente. Di solito creo anche questa classe come un semplice wrapper per l'accesso alle variabili uniformi GLSL. Come semplice esempio, immagina di avere uno shader di vertice di base e uno shader di frammenti, con un Texture2D extra uniforme per passare una trama. La mia classe Materiale caricherà e compilerà semplicemente quei due shader in un materiale, e da quel momento in poi esponerebbe una semplice interfaccia per leggere / scrivere l'uniforme Texture2D di quello shader.
Per rendere questo sistema un po 'più flessibile, di solito lo scrivo in un modo che mi consente di provare a passare uniformi di qualsiasi nome / tipo [es: SetUniform_Vec4 ("AmbientColor", colorVec4); che imposterebbe l'uniforme AmbientColor su un particolare vettore 4d chiamato "colorVec4" se quell'uniforme esiste nel materiale.] .
class Material
{
private:
int shaderID;
string vertShaderPath;
string fragSahderPath;
void loadShaderFiles(); //load shaders from files at internal paths.
void buildMaterial(); //link, compile, buffer with OpenGL, etc.
public:
void SetGenericUniform( string uniformName, int param );
void SetGenericUniform( string uniformName, float param );
void SetGenericUniform( string uniformName, vec4 param );
//overrides for various types, etc...
int GetUniform( string uniformName );
float GetUniform( string uniformName );
vec4 GetUniform( string uniformName );
//etc...
//ctor, dtor, etc., omitted for clarity..
}
Questo funziona , ma ci si sente come un cattivo sistema a causa del fatto che il cliente della classe materiale deve uniformi di accesso sulla sola fede - l' utente deve essere in qualche modo a conoscenza delle divise che si trovano in ogni oggetto materiale, perché sono costretti a passali con il loro nome GLSL. Non è un grosso problema quando sono solo 1-2 persone che lavorano con il sistema, ma non posso immaginare che questo sistema si ridimensionerebbe molto bene e prima di fare il mio prossimo tentativo di programmare un sistema di rendering OpenGL, voglio livellare un pochino.
Domanda:
Ecco dove sono finora, quindi ho cercato di studiare come altri motori di rendering gestiscono i loro sistemi materiali.
Questo approccio basato sul nodo è eccezionale e sembra essere un sistema estremamente comune per la creazione di sistemi di materiali facili da usare in motori e strumenti moderni. Da quello che posso dire sono basati su una struttura di dati grafici in cui ogni nodo rappresenta un aspetto shader del tuo materiale e ogni percorso rappresenta una sorta di relazione tra di loro.
Da quello che posso dire, implementare quel tipo di sistema sarebbe una semplice classe MaterialNode con una varietà di sottoclassi (TextureNode, FloatNode, LerpNode, ecc.). Dove ogni sottoclasse MaterialNode avrebbe MaterialConnections.
class MaterialConnection
{
MatNode_Out * fromNode;
MatNode_In * toNode;
}
class LerpNode : MaterialNode
{
MatNode_In x;
MatNode_In y;
MatNode_In alpha;
MatNode_Out result;
}
Questa è l' idea di base , ma sono un po 'incerto su come funzionerebbero alcuni aspetti di questo sistema:
1.) Se osservi le varie "Espressioni materiali" (nodi) utilizzate da Unreal Engine 4 , vedrai che ognuna ha connessioni di input e output di vari tipi. Alcuni nodi output fluttuano, alcuni output vettore2, alcuni output vettore4, ecc. Come posso migliorare i nodi e le connessioni sopra in modo che possano supportare una varietà di tipi di input e output? La sottoclasse di MatNode_Out con MatNode_Out_Float e MatNode_Out_Vec4 (e così via) sarebbe una scelta saggia?
2.) Infine, come si collega questo tipo di sistema agli shader GLSL? Guardando di nuovo UE4 (e in modo simile per gli altri sistemi collegati sopra), l'utente deve infine collegare un nodo di un materiale in un nodo di grandi dimensioni con vari parametri che rappresentano i parametri dello shader (colore di base, metallizzazione, brillantezza, emissività, ecc.) . La mia ipotesi originale era che UE4 avesse una sorta di "master shader" codificato con una varietà di uniformi, e tutto ciò che l'utente fa nel suo "materiale" viene semplicemente passato allo "master shader" quando inserisce i suoi nodi nel " nodo principale ".
Tuttavia, la documentazione UE4 afferma:
"Ogni nodo contiene uno snippet di codice HLSL, designato per eseguire un'attività specifica. Ciò significa che mentre costruisci un Materiale, crei il codice HLSL tramite script visivi."
Se questo è vero, questo sistema genera un vero script shader? Come funziona esattamente?