Я намагаюся обернути навколо голови , як матеріальні системи , як це , це реалізується. Ці потужні та зручні для користувачів системи, схожі на графіки, здаються відносно поширеними як метод, що дозволяє програмістам і непрограмістам швидко створювати шейдери. Однак, з мого відносно обмеженого досвіду роботи з графічним програмуванням, я не зовсім впевнений, як вони працюють.
Фон:
Отже, коли я раніше запрограмував прості системи візуалізації OpenGL, я, як правило, створюю клас Матеріал, який завантажує, компілює та посилає шейдери зі статичних GLSL-файлів, які я створив вручну. Я також зазвичай створюю цей клас як просту обгортку для доступу до єдиних змінних GLSL. Як простий приклад, уявіть, що у мене є основний шейдер вершини та шейдер фрагмента, з додатковим рівномірним Texture2D для передачі текстури. Мій клас "Матеріал" просто завантажить і компілює ці два шейдери в матеріал, і з цього моменту він відкриє простий інтерфейс для читання / запису уніформи Texture2D цього шейдера.
Щоб зробити цю систему трохи гнучкішою, я зазвичай пишу її таким чином, що дозволяє мені намагатися передати уніформу будь-якого імені / типу [тобто: SetUniform_Vec4 ("AmbientColor", colorVec4); який би встановити AmbientColor форму для конкретного 4d вектора під назвою «colorVec4» , якщо що рівномірна існує в матеріалі.] .
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..
}
Це працює, але це здається поганою системою через те, що клієнт класу Material має доступ до уніформи тільки на віру - користувач повинен бути дещо обізнаний з уніформами, які є у кожного матеріального об'єкта, тому що вони змушені передайте їх за своїм ім'ям GLSL. Це не велика справа, коли всього 1-2 люди працюють із системою, але я не можу уявити, що ця система взагалі дуже добре масштабується, і перш ніж зробити наступну спробу програмування системи візуалізації OpenGL, я хочу рівнятись трохи вгору.
Питання:
Ось де я зараз перебуваю, тому я намагався вивчити, як інші двигуни рендерингу обробляють свої матеріальні системи.
Цей підхід на основі вузлів чудовий і, здається, є надзвичайно поширеною системою для створення зручних у користуванні матеріальних систем у сучасних двигунах та інструментах. З того, що я можу сказати, вони засновані на структурі даних графіків, де кожен вузол представляє деякий шейдерний аспект вашого матеріалу, а кожен шлях представляє певний зв’язок між ними.
Як я можу сказати, реалізація такої системи була б таким же простим класом MaterialNode з різноманітними підкласами (TextureNode, FloatNode, LerpNode тощо). Де кожен підклас MaterialNode мав би MaterialConnections.
class MaterialConnection
{
MatNode_Out * fromNode;
MatNode_In * toNode;
}
class LerpNode : MaterialNode
{
MatNode_In x;
MatNode_In y;
MatNode_In alpha;
MatNode_Out result;
}
Це сама основна ідея, але я трохи не впевнений у тому, як би працювали кілька аспектів цієї системи:
1.) Якщо ви подивитесь на різні «Матеріальні вирази» (вузли), якими користується Unreal Engine 4 , ви побачите, що в кожному з них є вхідні та вихідні з'єднання різних типів. Деякі вузли виводять плаває, деякі вихідний вектор2, деякі вихідний вектор4 і т. Д. Як я можу вдосконалити вузли та з'єднання вище, щоб вони могли підтримувати різні типи введення та виведення? Чи буде підкласифікація MatNode_Out за допомогою MatNode_Out_Float та MatNode_Out_Vec4 (і так далі) бути мудрим вибором?
2.) Нарешті, як ця система стосується шейдерів GLSL? Подивившись знову на UE4 (як і на інші системи, пов'язані вище), користувачеві необхідно врешті-решт підключити якийсь вузол матеріалу у великий вузол з різними параметрами, що представляють параметри шейдера (базовий колір, металічність, блиск, емісію та ін.) . Моє первісне припущення було те, що UE4 мав якийсь жорсткий кодований "головний шейдер" з різноманітною формою, і все, що користувач робить у своєму "матеріалі", просто передається "головному шейдеру", коли вони підключають свої вузли до " головний вузол '.
Однак у документації UE4 зазначено:
"Кожен вузол містить фрагмент коду HLSL, призначений для виконання конкретного завдання. Це означає, що, будуючи Матеріал, ви створюєте код HLSL за допомогою візуального сценарію."
Якщо це правда, чи генерує ця система справжній сценарій шейдера? Як саме це працює?