Как я писал в пред. посте (Relict Engine: Шейдеры. Часть 1. Теория и вопросы), нам потребуется комбайн для перегона материала (или, если угодно, настроек шейдера) в spir-v формат. Для этого необходимо создать изначальный формат, желательно в текстовом виде, который и будет входной точкой в этот процесс.
Этот формат должен отвечать следующим требованиям:
В нем должно быть указано, с какими "сырыми" шейдерами компоновать этот материал
Иметь возможность обращаться к дополнительным файлам, содержащим кастомные функции
Иметь возможность самому определять дополнительные функции
Задавать входные опции
Определять параметры уже самого шейдера
Исходя из выше перечисленного, использовать GLSL или HLSL как есть не выйдет. Спецификации языков не обладают нужным функционалом. Поэтому получаем нечто похожее на вот это:
первый вариант синтаксиса материала
Что мы тут видим: Директива using Она определяет компоновку с сырыми шейдерами. Определяет домен, модель смешивания альфы и модель освещения (не путать с моделью затенения из пред. поста). Нейминг был взят из Unreal Engine, потом, возможно, поменяю на немного более логичные.
Директива in Она определяет входные переменные для материала, используя типы GLSL
Директива include Эта директива говорит комбайну, что следует обратится к функции, определенной в файле MaterialFunctions/getColor (Content/MaterialFunctions/getColor.minc проекта). Что важно, сам файл как самостоятельный ассет не будет использоваться, но его контент будет вставлен заместо директивы include при первичной компоновке.
Директива function Директива означает, что следующий текст является функцией. Сама функция должна быть написана на языке GLSL. В прочем, возможно, логичнее будет определить блоковую директиву, в которой код будет интерпретироваться как есть.
Директива set Эта директива передает конкретные значения в параметры "сырых" шейдеров. Иными словами, это и есть сам материал.
Шейдер это программа, написанная на одном из специализированных языков программирования, например GLSL, и выполняемая на GPU. Шейдеры бывают разных видов: вершинные, фрагментные, геометрические и компьют. Каждый создан для своей задачи и работают чуть по разному. Например, вершинный шейдер работает исключительно с вершинами (или набором точек) 3д модели, а фрагментный (или, как его еще называют, пиксельный), только с пикселями экрана или текстуры.
Для вывода графики на экран нам нужен как минимум вершинный и фрагментный шейдеры. Они будут исполнятся на каждый отдельно взятый объект. В случае вершинного для каждой вершины модели; в случае фрагментного для каждого пикселя экрана, занятого объемом результата вершинного шейдера.
А так-же есть две модели шейдинга (шейдинг ("затенение") - процесс в компьютерной графике, который придает объектам реалистичный вид, определяя их цвет и внешний вид в зависимости от освещения, текстур и материалов): Forward (или прямое затенение) - при этой модели каждый отдельный объект обрабатывается самостоятельно. Свет и тени будут считаться отдельно для каждого объекта. Deffered (или отложенное затенение) - отрисовка сцены происходит в несколько проходов с сохранением промежуточных данных в специальные текстуры, которые имеют общее название G-Buffer. А потом, отдельным проходом, единоразово будет просчитан свет и тень.
И у первой и у второй модели затенения есть свои плюсы и минусы. Так например, для разряженной сцены, где много "пустых" мест быстрее будет работать прямое затенение. Когда как для нагруженной сцены, особенно если есть много перекрывающихся объектов, отложенное наоборот покажет себя лучше.
Шейдеры в Relict Engine
Таким образом, в первую очередь нам нужно выбрать, какую модель затенения использовать в движке. Т.к. проект, для которого Relict Engine создается, связан с космосом, то логично предположить, что прямое затенение даст больше производительности, нежели отложенное. И это будет верно, пока мы говорим о "сферическом космосе в вакууме". Как только появляются туманности, низкие орбиты и нагруженные космопорты на этих орбитах, все становится менее очевидно. Поэтому, несмотря на кажущуюся простоту этого вопроса , ответить на него не так уж и просто.
Следующие вопросы, который надо решить, и которые не связаны с моделью затенения напрямую, а больше относятся к архитектуре движка, является: А как хранить шейдеры? Как передавать в них параметры?
В спецификации OpenGL 4.5 появилась возможность использовать бинарные шейдеры Spir-V, которые являются основным форматом шейдеров для Vulkan. И это удобно, мы сможем использовать один и тот-же шейдер как для OpenGL, так и для Vulkan без модификации. Но это касается "статичных" шейдеров. Их нельзя изменять, можно добавить только заранее известные переменные, которые можно будет передать в шейдер через функции движка или скрипта, а если мы хотим чего-то более сложного? Например передать не текстуру, а некую математическую функцию, которая будет определять цвет пикселя по своим собственным законам?
Таким образом, нам понадобится система, которая будет компоновать наши кастомные навороты, а так-же вводные данные, в виде переданных параметров с набором функций базовых шейдеров в некую отдельную сущность, которая будет представлять из-себя скомпонованный шейдер из множества отдельно взятых функций и точки входа. И желательно, все это все равно сохранить в Spir-V формат для удобства дальнейшего перехода на Vulkan. Более того, в зависимости от выбранной модели затенения, выходной результат так-же должен отличаться. Так например, если мы для Основного цвета (Diffuse Color) не будем использовать текстуру, а некую мат. функцию, то нам нужно будет скомпоновать эту функцию с шейдером G-Buffer, в случае использования отложенного затенения, когда как в случае прямого затенения, с итоговым фрагментным шейдером.
Иными словами только для подготовки шейдера к компиляции в кэш нам потребуется целый промежуточный комбайн. Этот комбайн будет работать с текстом, компонуя и генерируя исходный glsl код из пред написанных сниппетов и нашего материала. А на выходе должен быть Asset, уже в формате Spir-V, который и будет использоваться для компиляции уже в машинный код и исполнения на GPU.
Здравствуйте, дорогие Пикабушники и Пикабушницы! 👋
Я сделал шейдер для травы в Unity с помощью Shader Graph.
А что такое шейдеры?
Если вы не знакомы, шейдеры — это небольшие программы, которые работают на видеокарте и отвечают за то, как объекты отображаются в игре или приложении. Благодаря шейдерам мы видим не просто геометрию объектов, но и их текстуры, освещение, тени и разные интересные визуальные эффекты.
Что такое Shader Graph?
Shader Graph — это инструмент Unity, который позволяет создавать шейдеры через визуальный интерфейс. Вместо написания кода мы соединяем узлы в графе и задаём их поведение. Это идеальный инструмент для тех, кто хочет создавать крутые визуальные эффекты, но не хочет углубляться в программирование шейдеров на языках вроде HLSL.
О шейдере
В своём проекте я сделал шейдер травы, и вот несколько его особенностей:
🌬 Ветер — трава красиво колышется на ветру благодаря текстуре шума, которая задаёт направление и интенсивность движения.
🚶♂️ Реакция на персонажей — когда персонаж идёт по траве, она расступается под его ногами, создавая эффект присутствия.
🎨 Изменение цвета — шейдер плавно меняет цвет травы в зависимости от расстояния до камеры. Ближняя трава выглядит ярче, более насыщенной по цвету, а дальняя принимает приглушённые оттенки. Это помогает создать глубину сцены.
🛠 Интерактивные параметры — в Shader Graph я настроил множество параметров, которые можно изменять прямо в редакторе: силу ветра, цвет, интенсивность реакции на персонажей и многое другое.
В видео я показываю сам граф шейдера и демонстрирую, как можно настраивать его параметры для достижения разных эффектов.
Для чего всё это?
Подобный шейдер можно использовать в сценах с открытым миром, чтобы добавить динамики и реалистичности. Реалистичная трава заметно оживляет любую игровую сцену и может стать важным элементом атмосферы.
Если вам интересно посмотреть, как всё это выглядит, или вы думали о создании подобных шейдеров, заходите в видео на ютубе:
Или в ВК:
Буду рад вашему мнению, вопросам или идеям! Спасибо за внимание! 😊