В системе материалов на основе графиков, как я могу поддерживать различные типы ввода и вывода?


11

введите описание изображения здесь

Я пытаюсь обернуть вокруг головы , как материальные системы , как это , это реализуется. Эти мощные и удобные для пользователя графоподобные системы, по-видимому, довольно распространены как метод, позволяющий программистам и непрограммистам быстро создавать шейдеры. Однако из моего относительно ограниченного опыта в графическом программировании я не совсем уверен, как они работают.


Фон:

Поэтому, когда я ранее программировал простые системы рендеринга OpenGL, я обычно создаю класс Material, который загружает, компилирует и связывает шейдеры из статических файлов GLSL, которые я создал вручную. Я также обычно создаю этот класс как простую оболочку для доступа к унифицированным переменным GLSL. В качестве простого примера представьте, что у меня есть базовый вершинный шейдер и фрагментный шейдер с дополнительной равномерной Texture2D для передачи текстуры. Мой класс Material просто загрузит и скомпилирует эти два шейдера в материал, и с этого момента он предоставит простой интерфейс для чтения / записи формы 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 , вы увидите, что каждое из них имеет входные и выходные соединения различных типов. Некоторые узлы выводят с плавающей точкой, некоторые выводят vector2, некоторые выводят vector4 и т. Д. Как я могу улучшить указанные выше узлы и соединения, чтобы они могли поддерживать различные типы ввода и вывода? Будет ли подклассы MatNode_Out с MatNode_Out_Float и MatNode_Out_Vec4 (и так далее) разумным выбором?

2.) Наконец, как эта система относится к шейдерам GLSL? Снова глядя на UE4 (и аналогично для других систем, связанных выше), пользователь должен в конечном итоге подключить некоторый узел материала к большому узлу с различными параметрами, которые представляют параметры шейдера (основной цвет, металличность, блеск, излучательная способность и т. Д.) , Мое первоначальное предположение заключалось в том, что в UE4 был какой-то жестко закодированный «мастер-шейдер» с различными формами, и все, что пользователь делает в своем «материале», просто передается «мастер-шейдеру», когда они подключают свои узлы к « мастер-узел ».

Однако в документации UE4 говорится:

«Каждый узел содержит фрагмент кода HLSL, предназначенный для выполнения конкретной задачи. Это означает, что когда вы создаете материал, вы создаете код HLSL с помощью визуальных сценариев».

Если это правда, генерирует ли эта система настоящий шейдерный скрипт? Как именно это работает?


1
Связанные с вашим вопросом: gameangst.com/?p=441
гламперт

Ответы:


10

Я постараюсь ответить, насколько мне известно, с небольшими знаниями о конкретном случае UE4, а скорее об общей технике.

Основанные на графике материалы - это столько же программирования, сколько и написание кода самостоятельно. Это просто не похоже на людей, не имеющих опыта работы с кодом, что делает его на вид проще. Поэтому, когда дизайнер связывает узел «Add», он в основном пишет add (value1, value2) и связывает вывод с чем-то другим. Это то, что они имеют в виду, что каждый узел будет генерировать код HLSL, будь то вызов функции или просто простые инструкции.

В конце концов, использование графа материала похоже на программирование сырых шейдеров с библиотекой предопределенных функций, которые делают некоторые полезные вещи, а также то, что делает UE4. Он имеет библиотеку кода шейдера, которую шейдерный компилятор примет и вставит в окончательный исходный код шейдера, когда это применимо.

В случае UE4, если они утверждают, что он преобразован в HLSL, я предполагаю, что они используют инструмент преобразования, который способен преобразовывать байтовый код HLSL в байтовый код GLSL, поэтому его можно использовать на платформах GL. Но в других библиотеках просто есть несколько шейдерных компиляторов, которые будут считывать график и напрямую генерировать необходимые источники языка шейдинга.

График материала также является хорошим способом абстрагироваться от специфики платформы и сосредоточиться на том, что важно с точки зрения художественного направления. Поскольку он не привязан к языку и имеет гораздо более высокий уровень, его проще оптимизировать для целевой платформы и динамически внедрять в шейдер другой код, например легкую обработку.

1) Теперь, чтобы ответить на ваши вопросы более прямо, у вас должен быть управляемый данными подход к проектированию такой системы. Найдите плоский формат, который можно определить в очень простых структурах и даже определить в текстовом файле. По сути, каждый граф должен быть массивом узлов с типом, набором входов и выходов, и каждое из этих полей должно иметь локальный link_id, чтобы убедиться, что соединения с графом однозначны. Кроме того, каждое из этих полей может иметь дополнительную конфигурацию к тому, что поддерживает поле (например, какой диапазон типов данных поддерживается).

При таком подходе вы можете легко определить поле узла как (float | double) и позволить ему выводить тип из соединений или принудительно вводить тип в него, без иерархий классов или чрезмерного проектирования. Вы должны спроектировать эту структуру данных графа так, как вам хочется. Все, что вам нужно, - это то, что у него достаточно информации, чтобы генератор кода не имел двусмысленности и, следовательно, потенциально неправильно обрабатывал то, что вы хотите сделать. Важно то, что на уровне базовой структуры данных вы сохраняете ее гибкость и сосредоточены на решении задачи определения материала в одиночку.

Когда я говорю «определить материал», я очень конкретно имею в виду определение поверхности сетки, помимо того, что обеспечивает сама геометрия. Это включает использование дополнительных атрибутов вершин для настройки аспекта поверхности, добавления смещения к ней с помощью карты высот, изменения нормалей с помощью пиксельных нормалей, изменения физических параметров, изменения BRDF и т. Д. Вы не хотите описывать что-либо еще, например HDR, тональное отображение, анимацию скинов, обработку света или многое другое в шейдерах.

2) Затем шейдерный генератор рендерера должен пройти через эту структуру данных и, прочитав ее информацию, собрать набор переменных и связать их вместе, используя готовые функции и введя код, который вычисляет освещение и другие эффекты. Просто помните, что шейдеры отличаются не только от разных графических API, но также и от разных средств визуализации (для отложенного рендеринга или рендера на основе плиток требуется прямой шейдер), а с такой системой материалов вы можете абстрагироваться от неприятного слой низкого уровня, и сосредоточиться только на описании поверхности.

Для UE4 они составили список вещей для конечного узла вывода, о котором вы упомянули, который, по их мнению, описывает 99% поверхностей в старых и современных играх. Они разработали этот набор параметров на протяжении десятилетий и доказали это безумным количеством игр, которые Unreal выпустил до сих пор. Поэтому вам будет хорошо, если вы будете делать все так же, как нереально.

Чтобы подвести итог, я предлагаю файл .material только для обработки каждого графика. Во время разработки он может содержать текстовый формат для отладки, а затем упаковываться или компилироваться в двоичный файл для выпуска. Каждый .material будет состоять из N узлов и N соединений, во многом как база данных SQL. Каждый узел будет иметь N полей, с именем и некоторыми флагами для принятых типов, если его ввод или вывод, если типы выведены и т. Д. Структура данных времени выполнения для хранения загруженного материала была бы такой же плоской и простой, поэтому Редактор может легко адаптировать его и сохранить обратно в файл.

А затем оставьте всю тяжелую работу для последнего поколения шейдеров, что на самом деле является самой сложной частью. Прекрасной частью является то, что ваш материал остается независимым от платформы рендеринга, теоретически он будет работать с любой техникой рендеринга и API, если вы представляете материал на соответствующем языке затенения.

Дайте мне знать, если вам нужны дополнительные детали или исправления в моем ответе, я потерял обзор по всему тексту.


Я не могу отблагодарить вас за то, что вы написали такой сложный и отличный ответ. Я чувствую, что у меня есть отличное представление о том, куда я должен идти отсюда! Спасибо!
MrKatSwordfish

1
Нет проблем, приятель, не стесняйтесь сообщать мне, если вам нужна дополнительная помощь. На самом деле я работаю над чем-то, что эквивалентно моим собственным инструментам, поэтому, если вы хотите обменяться мыслями, будьте моим гостем! Хорошего дня: D
Grimshaw
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.