Моя борьба с Шейдерами на Unity Shader Graph
Всем примет, меня зовут Даниил и я разработчик мобильной игры Неко Скольжение. Путь разработки даже такой небольшой игры для одного программиста наполнен трудностями и сегодня я хочу рассказать вам как я с шейдерами боролся.
Для тех, кто не знает, шейдеры – это небольшие программы для графического процессора, с инструкциями, как отрисовывать тот или иной объект. Сам я с шейдерами раньше не работал и писать для них код стоило бы мне множества усилий. Поэтому, Юнитевский Shader Graph стал для меня спасением. Правда и здесь, мне кажется, я мог сделать лучше.
Взглянем на граф шейдера обычного кота.
Для него пришлось писать мега-шейдер, который сразу бы позволил:
— Добавить засветку текстуры определённым цветом;
— Добавить эффект растворения текстуре;
— Позволить произвести заливку текстуры конкретным цветом с анимацией относительно конкретной точки.
Когда кота засасывает чёрная дыра, он должен сначала полностью стать белым, и только потом улететь в дыру. Один объект белого цвета, который бы расширялся и обрезался маской на котах, было бы тяжело правильно реализовать, учитывая, что маски на момент решения задачи уже использовались в другом месте. Да и делать маску под каждое состояние кота казалось трудозатратной задачей для небольшой механики.
Самой сложной задачей, в данном случае, оказалась задача определения точки, с которой текстура бы заливалась цветом. Путём кучи математических операций удалось достичь, чтобы на спрайтах разного размера и в разном положении, наложение цвета происходило бы с одинаковой точки и одинакового размера.
Однако, без проблем не обошлось. В шейдере можно заметить внешние параметры Bounds Min и Bounds Max. Те, кто работал с Shader Graph, знают, что в нём есть отдельная нода объекта, в которой уже хранятся эти параметры, так зачем же я их передаю? А ответ простой – батчинг. Как оказалось, когда Юнити пакует несколько спрайтов с одним шейдером на отрисовку, у них становятся общие границы, из-за чего точка заливки цветом сдвигается, и приходится выдумывать вот такие велосипеды, чтобы всё починить.
Ещё одним испытанием для меня стало написание шейдера эффекта искажения пространства от чёрной дыры. Очевидно, что мне необходимо было брать отрендеренное изображение при первом проходе и вносить искажения в него. Однако уже на этом этапе возникли некоторые сложности, так как мало того, что искажение изображения за материалом для 2D и 3D шейджеров работает по-разному, так ещё и в Юнити существуют _CameraOpaqueTexture и _CameraSortingLayerTexture, и первоначально мне попадались гайды с первой, поэтому мне долго пришлось разбираться с настройкой.
Но эффект того стоил, выглядит он довольно неплохо для человека, который имеет небольшой опыт работы с графом.


Котиков как будто засасывает внутрь чёрной дыры.
Хотя, даже с законченной текстурой есть проблемы. В одной из минорных версий Юнити, они в очередной раз сломали Vulkan API, из-за чего пара пользователей пожаловалась на чёрный экран при запуске, а в Firebase приходил краш. Я решил, что можно выключить Vulkan API, и собрать версию так. Но после этого, почему-то, сломалось определение позиции для применения эффекта.
Возможно, что-то в матрице преобразования не работает или ещё какие-то проблемы, но эффект стал работать неправильно. Я попытался разобраться в конкретных причинах, но пока что не смог понять, что не так и как заставить его работать правильно, поэтому просто поднял минорную версию Юнити до самой актуальной (мне всё равно нужно было это сделать для асинхронного инстатиирования объектов), и решил последить, будут ли ещё жалобы на чёрный экран.
На данный момент, это были два моих самых проблемных шейдера, но если вам интересны какие-либо ещё аспекты разработки игры или предложения по поводу правильного составления графа шейдеров – я всегда буду рад прочитать.