Próbuję owinąć głowę wokół jak systemy materialne, takie jak ta , to są realizowane. Te wydajne i przyjazne dla użytkownika, podobne do wykresów systemy wydają się być stosunkowo powszechne jako metoda umożliwiająca programistom i nieprogramiście szybkie tworzenie programów cieniujących. Jednak z mojego stosunkowo ograniczonego doświadczenia w programowaniu graficznym nie jestem do końca pewien, jak działają.
Tło:
Tak więc, kiedy wcześniej programowałem proste systemy renderowania OpenGL, zwykle tworzę klasę materiałów, która ładuje, kompiluje i łączy shadery ze statycznych plików GLSL, które ręcznie utworzyłem. Zwykle tworzę tę klasę jako proste opakowanie do dostępu do zmiennych mundurowych GLSL. Jako prosty przykład wyobraź sobie, że mam podstawowy moduł cieniujący wierzchołek i moduł cieniujący fragmenty, z dodatkowym jednolitym Texture2D do przekazywania tekstury. Moja klasa materiałów po prostu ładowałaby i kompilowała te dwa moduły cieniujące w materiał, i od tego momentu ujawniałaby prosty interfejs do odczytu / zapisu munduru Texture2D tego modułu cieniującego.
Aby uczynić ten system nieco bardziej elastycznym, zwykle piszę go w sposób, który pozwala mi próbować przekazać mundury o dowolnej nazwie / typie [np .: SetUniform_Vec4 („AmbientColor”, colorVec4); który ustawiłby mundur AmbientColor na konkretny wektor 4d o nazwie „colorVec4”, jeśli ten mundur istnieje w 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..
}
To działa , ale wydaje się, jakby zły system ze względu na fakt, że klient Materiałowych klasa ma do mundurów dostępowych na samą wiarą - użytkownik musi być nieco świadomi mundurów, które są w każdym obiekcie materialnym, ponieważ są one zmuszone do przekazać je według nazwy GLSL. To nie jest wielka sprawa, gdy tylko 1-2 osoby pracują z systemem, ale nie mogę sobie wyobrazić, że ten system w ogóle skalowałby się bardzo dobrze, a zanim podejmę kolejną próbę programowania systemu renderującego OpenGL, chcę wyrównać trochę wyżej.
Pytanie:
Właśnie tam jestem, więc próbowałem zbadać, w jaki sposób inne silniki renderujące radzą sobie z ich systemami materiałowymi.
To podejście oparte na węzłach jest świetne i wydaje się być niezwykle powszechnym systemem do tworzenia przyjaznych dla użytkownika systemów materiałowych w nowoczesnych silnikach i narzędziach. Z tego, co mogę powiedzieć, są oparte na strukturze danych wykresu, gdzie każdy węzeł reprezentuje pewien aspekt cieniowania twojego materiału, a każda ścieżka reprezentuje pewien rodzaj relacji między nimi.
Z tego, co mogę powiedzieć, wdrożenie tego rodzaju systemu byłoby tak proste, jak klasa MaterialNode z różnymi podklasami (TextureNode, FloatNode, LerpNode itp.). Gdzie każda podklasa MaterialNode miałaby MaterialConnections.
class MaterialConnection
{
MatNode_Out * fromNode;
MatNode_In * toNode;
}
class LerpNode : MaterialNode
{
MatNode_In x;
MatNode_In y;
MatNode_In alpha;
MatNode_Out result;
}
To bardzo podstawowy pomysł, ale nie jestem pewien, jak działa kilka aspektów tego systemu:
1.) Jeśli spojrzysz na różne „wyrażenia materiałowe” (węzły), których używa Unreal Engine 4 , zobaczysz, że każdy z nich ma połączenia wejściowe i wyjściowe różnego rodzaju. Niektóre węzły wyjściowe są zmiennoprzecinkowe, niektóre wektor wyjściowy2, niektóre wektor wyjściowy4 itd. Jak mogę ulepszyć powyższe węzły i połączenia, aby mogły obsługiwać różne typy danych wejściowych i wyjściowych? Czy podklasowanie MatNode_Out z MatNode_Out_Float i MatNode_Out_Vec4 (i tak dalej) byłoby dobrym wyborem?
2.) Wreszcie, w jaki sposób ten system odnosi się do shaderów GLSL? Patrząc ponownie na UE4 (i podobnie w przypadku innych systemów połączonych powyżej), użytkownik musi ostatecznie podłączyć jakiś węzeł materiału do dużego węzła z różnymi parametrami reprezentującymi parametry modułu cieniującego (kolor podstawowy, metalowość, połysk, emisyjność itp.) . Moje pierwotne założenie było takie, że UE4 miał jakiś zakodowany „master shader” z różnymi mundurami, a wszystko, co użytkownik robi w swoim „materiale”, jest po prostu przekazywane do „master shadera”, kiedy podłączają swoje węzły do „ węzeł główny ”.
Jednak dokumentacja UE4 stwierdza:
„Każdy węzeł zawiera fragment kodu HLSL, wyznaczony do wykonania określonego zadania. Oznacza to, że tworząc materiał, tworzysz kod HLSL za pomocą skryptów wizualnych”.
Jeśli to prawda, czy ten system generuje prawdziwy skrypt cieniujący? Jak dokładnie to działa?