J'essaie de comprendre comment les systèmes matériels comme celui - ci sont mis en œuvre. Ces systèmes puissants et conviviaux de type graphique semblent être relativement courants comme méthode permettant aux programmeurs et aux non-programmeurs de créer rapidement des shaders. Cependant, à partir de mon expérience relativement limitée en programmation graphique, je ne sais pas exactement comment ils fonctionnent.
Contexte:
Ainsi, lorsque j'ai déjà programmé des systèmes de rendu OpenGL simples , je crée généralement une classe Material qui charge, compile et lie des shaders à partir de fichiers GLSL statiques que j'ai créés manuellement. Je crée également généralement cette classe comme un simple wrapper pour accéder aux variables uniformes GLSL. À titre d'exemple simple, imaginez que j'ai un vertex shader de base et un fragment shader, avec un Texture2D extra uniforme pour passer une texture. Ma classe Material chargerait et compilerait simplement ces deux shaders dans un matériau, et à partir de là, elle exposerait une interface simple pour lire / écrire l'uniforme Texture2D de ce shader.
Pour rendre ce système un peu plus flexible, je l'écris généralement d'une manière qui me permet d'essayer de passer des uniformes de n'importe quel nom / type [par exemple: SetUniform_Vec4 ("AmbientColor", colorVec4); qui définirait l'uniforme AmbientColor sur un vecteur 4d particulier appelé "colorVec4" si cet uniforme existe dans le matériau.] .
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..
}
Cela fonctionne, mais cela ressemble à un mauvais système car le client de la classe Material doit accéder aux uniformes sur la foi seule - l' utilisateur doit être quelque peu conscient des uniformes qui se trouvent dans chaque objet matériel car ils sont obligés de passez-les par leur nom GLSL. Ce n'est pas énorme quand ce n'est que 1-2 personnes qui travaillent avec le système, mais je ne peux pas imaginer que ce système évoluerait très bien du tout, et avant de faire ma prochaine tentative de programmation d'un système de rendu OpenGL, je veux mettre à niveau un peu.
Question:
C'est où j'en suis jusqu'à présent, j'ai donc essayé d'étudier comment d'autres moteurs de rendu gèrent leurs systèmes de matériaux.
Cette approche basée sur les nœuds est excellente et il semble que ce soit un système extrêmement courant pour créer des systèmes de matériaux conviviaux dans des moteurs et des outils modernes. D'après ce que je peux dire, ils sont basés sur une structure de données de graphique où chaque nœud représente un aspect shader de votre matériau et chaque chemin représente une sorte de relation entre eux.
D'après ce que je peux dire, l'implémentation de ce type de système serait aussi simple qu'une classe MaterialNode avec une variété de sous-classes (TextureNode, FloatNode, LerpNode, etc.). Où chaque sous-classe MaterialNode aurait des MaterialConnections.
class MaterialConnection
{
MatNode_Out * fromNode;
MatNode_In * toNode;
}
class LerpNode : MaterialNode
{
MatNode_In x;
MatNode_In y;
MatNode_In alpha;
MatNode_Out result;
}
C'est l' idée très basique , mais je suis un peu incertain sur la façon dont certains aspects de ce système fonctionneraient:
1.) Si vous regardez les différentes «expressions matérielles» (nœuds) utilisées par Unreal Engine 4 , vous verrez qu'elles ont chacune des connexions d'entrée et de sortie de différents types. Certains nœuds flottent en sortie, certains vecteur de sortie2, certains vecteur de sortie4, etc. Comment puis-je améliorer les nœuds et les connexions ci-dessus afin qu'ils puissent prendre en charge une variété de types d'entrée et de sortie? Le sous-classement de MatNode_Out avec MatNode_Out_Float et MatNode_Out_Vec4 (et ainsi de suite) serait-il un choix judicieux?
2.) Enfin, comment ce type de système est-il lié aux shaders GLSL? En examinant à nouveau UE4 (et de même pour les autres systèmes liés ci-dessus), l'utilisateur doit finalement brancher un nœud de matériau dans un grand nœud avec divers paramètres qui représentent les paramètres du shader (couleur de base, métal, brillant, émissivité, etc.) . Mon hypothèse initiale était que UE4 avait une sorte de «master shader» codé en dur avec une variété d'uniformes, et tout ce que l'utilisateur fait dans son «matériel» est simplement transmis au «master shader» lorsqu'il branche ses nœuds dans le « nœud maître ».
Cependant, la documentation UE4 indique:
"Chaque nœud contient un extrait de code HLSL, conçu pour effectuer une tâche spécifique. Cela signifie que lorsque vous construisez un matériau, vous créez du code HLSL via un script visuel."
Si c'est vrai, ce système génère-t-il un vrai script de shader? Comment est-ce que cela fonctionne exactement?