Еще один demake–шейдер или рубка палитры с плеча в Unity
Для начала сразу оговорюсь – да, я в курсе, что подобного днища на маркетах Юнити и прочих систем овердофига и публика уже изрядно подустала от подобных сверхфишек. Но если есть чем поделиться – почему бы и не поделиться? В конце концов, если это пригодится хотя бы одному человеку – значит это было не зря. Ссылка на гитхаб - в конце поста.
Отмечу лишь, что я ни разу не программист шейдеров, и зачастую понятия не имею, что делаю и зачем, но методом проб и ошибок это начинает работать.
Участие в ряде конкурсов по игроделанью в роли криводела с каждым разом подталкивало меня к идее своего варианта олдскульного рендера, способного подвести картинку к теплым временам, когда игры выжигали глаза и рушили психику своей непреодолимой сложностью и кривым управлением. Святые времена.
Основной упор своей реализации я хотел сделать на минимизацию количества исходных файлов – чтобы из кода был только шейдер, без дополнительных управляющих скриптов, как во многих доступных ассетах. Весь эффект состоит из файла шейдера, файла палитры и файла-маски дизеринга.
Основная идея заключается в следующем – рендерим все, что происходит на сцене в рендер-текстуру какого-нибудь приятного олдскульного размера 320x200, которую затем пускаем под шейдер, задача которого – обрезать палитру до нужной и, если надо, наложить приятный дизер-эффект на нужные места. Потом рисуем нашу текстуру во весь экран.
Итак, создадим чистый проект в Unity. Так как я хочу стилизовать картинку под 4 цвета cga-палиры, 3D будет не особо уместно использовать для демонстрации (но это не значит, что в 3Д проектах этого нельзя делать – все в руках создателей). Выбираю шаблон 2D (URP).
Для затравки и тестов возьмем из интернетов какую-нибудь крутую картинку. Я нашел фото кибертянки из трейлера фильма Cyberbride, рейтинг которого 2,7 на imdb. Идеально.Импортируем ее в проект, сразу закидываем на сцену без каких-либо настроек. Теперь нам необходимо подготовить «железную» основу нашего будущего рендера, прежде чем мы начнем использовать шейдер. Создаем рендер-текстуру размером 320х200. Отключаем обязательно фильтрацию – олдскул все-таки.
Теперь берем камеру сцены и в блоке Output в параметр Output texture подкидываем нашу рендер-текстуру. В окне вывода Game сразу появится дурацкая надпись, что нет камер, рендерящих в экран. Жмем три вертикальные точки справа вверху и снимаем галочку с «Warn If No Cameras Rendering».
Теперь добавим на сцену Canvas. В блоке “Canvas Scaler“ в параметре UI Scale Mode выставим ему «Scale with Screen Size», а в Reference Resolution – наши 320х200. Добавим к этому Canvas дочерним элементом Raw Image. Ему в texture забрасываем нашу рендер-текстуру. И накидываем на него компонент Aspect Ratio Filter, у которого выставляем Mode в “Fit In Parent”, а сам Aspect Ratio ставим 1.6 (что соответствует 320х200).
Остался один необязательный момент, но для приятных ощущений лучше его сделать. Во вкладке Game, там, где красуется Free Aspect, добавим разрешение 640х400. Картинка станет приятнее, да и эффект удвоения на наших экранах все же выглядит поинтереснее. (если у вас 2к или 4к монитор, возможно лучше добавить утроенное, а то и учетверенное разрешение предпросмотра)
Все. База готова. Она стандартная, она обычная. Ничего нового нет. Можно конечно рендерить и в более высоком разрешении, но мне теплее старые добрые жесткие пиксели.
Если видим, что рендерится все так как надо, то можно подключать шейдер. Импортируем сам шейдер, картинку дизеринга и картинку с палитрой. У обоих картинок нужно выключить сжатие и фильтрацию, а у дизеринга нужно выставить Repeat в параметре Wrap Mode.
Создаем новый материал, указываем ему шейдер Rikovmike/LimitPaletteRaw, закидываем рендер-текстуру, палитру и дизер-картинку в соответствующие им параметры параметры и кидаем этот материал на RawImage, которая у нас в Canvas (чистую текстуру с него можно уже и убрать).
Изменения будут видны сразу. В параметрах шейдера нужно выставить количество цветов в палитре (почему руками – опишу чуть ниже), а также можно включить или выключить дизеринг и настроить его уровень.
Ну и на самом спрайте можно подкрутить яркость в параметре Color, если картинка будет засвеченной. У меня вышло так:
То же, но с палитрой sweetie16 с сайта lospec.com:
Теперь немного о том, как работает шейдер.
Во фрагментной части сначала цвет входящего пикселя сравнивается по очереди с каждым пикселем текстуры палитры. Сравнение происходит простым вычислением дистанции между векторами цветов и выбирается цвет с самой малой дистанцией – он и считается максимально приближенным к искомому в палитре. Подход спорный, но быстрый и достаточно точный. В рамках палитровых ограничений до 128 цветов особых тонкостей выборки цвета не должно вылезать.
Итак, найденный в палитре максимально близкий цвет подставляется на вывод вместо оригинального.
Далее следует блок обработки дизеринга. Зона дизеринга определяется особым условием – насколько «недолетел» проверяемый цвет до нужного, то есть какая финальная дистанция была рассчитана в момент окончательного утверждения выбранного цвета палитры. Дистанция в принципе варьируется от 0 до 1, так как все компоненты цвета (альфу мы тут не учитываем) изменяются в тех же пределах. Нетрудно выяснить и степень «недолета» цвета до нужного. Все эти непонятные недолеты превращаются в шашечки. Уровень недолетаемости определяется бегунком Dither Treshold, который по умолчанию равен 0.5. То есть если недолет был почти в половину цветового пространства – значит точно дизерить.
А вот цвета перехода определяются проще – для этого введены дополнительные переменные, хранящие «предыдущий» проверяемый в палитре цвет. И дальше в блоке обработки дизеринга сначала выбирается из текстуры дизеринга текущий по координатам сетки дизеринга пиксель. Если он белый, то рисуется найденный цвет, если он черный – рисуется предыдущий по дальности цвет.
Подход может быть и спорный, но дает достаточно правдоподобный эффект и работает на любых палитрах. Главное подобрать правильный Dither Treshold.
А теперь немного о параметре количества цветов.
Да, суть в том, что он используется в цикле обхода текстуры палитры попиксельно. И да, вместо него можно приспособить значение из _TexelSize текстуры палитры. Но мне показалось, что удобно все равно иметь в руках инструмент ручного ограничения палитры. В бОльшую сторону ничего плохого не случится, в меньшую же – есть интересные эффекты обрезки палитры. В любом случае, можно попробовать избавиться от этого параметра и взять количество цветов из ширины текстуры палитры.
Отмечу, что пробовал подсовывать под стандартные демки Unity, с небольшими твиками по свету выходило неплохо, например, в Lost Crypt:
![Еще один demake–шейдер или рубка палитры с плеча в Unity Unity, Шейдеры, Разработка, Игры, Демейк, Олдскул, Кривые руки, Gamedev, Гифка, Длиннопост](https://cs12.pikabu.ru/post_img/2022/10/18/7/1666091288137886634.jpg)
Скачать все необходимое (шейдер, дизер и палитру) можно тут:
https://github.com/rikovmike/LimitPaletteRaw
Теоретически, шейдер можно приспособить не только к Юнити, но я слишком ленив для проверки этого. Исходник шейдера я постарался разбавить комментариями, насколько смог :)
Water Simulation
Появилась возможность рассказать о том как мы создавали жидкость для TReload. Нам всего лишь нужно было залить уровни кислотой. Кислоты должно быть много, площади затопления огромные :) Один из финальных результатов:
Визуально кислота должна была представлять из себя грязную воду с желтым оттенком. Вот референсы:
Кислота должна поддерживать физическое взаимодействие с объектами, которые в нее брошены: рисовать волны, пену. Так же должна быть возможность видеть сквозь грязь. Возможно в уровнях будет небольшой ветер, но это неточно.
Разработка кислоты проводилась в несколько этапов:
- разрабатывались инструменты для работы с кислотой (в основном это инструмент рисования текстурных масок пены)
- разрабатывалась кислота (работали над шейдерами, материалами, логикой взаимодействия, звуковыми эффектами)
Инструмент рисования текстурных масок пены
Механизм рисования достаточно прост.
Условно есть 2 текстуры:
- текстура маски пены (далее маска)
- текстура кисти (далее кисть)
Задача состоит в том чтобы правильно произвести операцию Blit кисти с маской (использовать для кисти соответствующие “scale” и “offset”, чтобы корректно ее спроецировать в нужную область маски).
Чтобы можно было водить кистью по модели и рисовать, нужно чтобы координаты точки пересечения модели и кисти переводились в пространство UV.
Здесь есть 2 решения по части перевода координат:
- использовать “MeshColluder” и из него получать “texcoord.xy” области пересечения луча “Raycast”. В этом случае координаты будут уже приведены к “UV” виду, нам только останется проецировать текстуру кисти в нужную область маски.
- использовать “BoxCollider” и самостоятельно переводить “worldSpace” координаты кисти к UV координатам маски.
Мы использовали второй вариант:
- к модели кислоты добавляется “BoxColider”
- делается RayCast
- worldSpace точка пересечения луча кисти и кислоты переводится в “acidLocalSpace”
- далее эта точка переводится в “UV-space”. Для этого мы делим координаты точки пересечения на размеры кислоты:
Доработчки: механизм отмены (ctrl+z)
Для ввода механизма отмены пришлось изменить подход: была создана ортографическая камера, которая рендерит только слой кистей. Размеры камеры соответствовали размерам кислоты. В области пересечения кисти и маски создавался меш кисти, который рендерился камерой, а далее делался "Blit" с маской. Таким образом появилась возможность отменять действия.
Небольшая демонстрация работы системы рисования масок:
Волны
Нами предпринимались разные попытки создания волн:
- рисования волн на тектуре кислоты
- волны созданные геометрическим шейдером поверх кислоты
- тесселяция + GPU Instancing и волны
Справа на рисунке представлена волна, которая создана геометрическим шейдером. Волна слева - плоская.
Кстати, про кубики я писал здесь: Немного о дематериализации в нашей игре
В конечном счете мы отказались от таких волн и решили сделать реалистичные волны, которые учитывают интерференцию и генерируют пену в области распространения волны.
Как работает генерация волн
Опишу это простыми словами: есть 2-х мерное уравнение “колебаний”, которое нужно решать каждый кадр. Это уравнение позволяет генерировать распространение волн. С материалом по теме вы можете ознакомиться здесь: ссылка 1
А здесь еще один отличный материал: ссылка 2
Здесь крутой пример исходного кода для Unity: ссылка 3
Мой результат генерации волны (используется стандартная тесселяция от Unity и стандартный шейдер):
Но генерация волн это еще не все. Если у вас маленький бассейн, то примера с Github должно хватить. А если нужно рендерить море или океан, то возникает масса проблем оптимизации:
- оказывается Unity не поддерживает “Tessellation + GPU Instancing of Standard shaders”
- ближние участки кислоты должны быть высокополигональными (для этого нужно использовать систему “LOD”)
- дальние волны, пену можно не рендерить
- артефакты распространения волн
Самое важное я узнал в самом конце. Unity, почему “Tessellation + GPU Instancing” не не работают со стандартными шейдерами? Для решения этой проблемы пришлось посмотреть сгенерированный код Standard-шейдера, вытащить из него то что вам нужно и вставить это в “Fragment shader”.
Структура водной поверхности, распространение воды на соседние сегменты
Водная поверхность представляет из себя NxN объектов с “LOD”. По мере удаления, объекты с LOD подменяют друг друга так, что на расстоянии X вместо 4-х различных объектов с LOD, рисуется один:
То есть водная поверхность - это “умная” сетка из разных участков воды. Допустим, вода имеет размеры 8х8 и пусть источник волн возник в ячейке [2,4]:
Тогда мы резервируем соседние N ячеек (в моем примере по одной с каждой стороны) и проецируем текстуру распространения волн на этот участок общей водной поверхности. Проекция текстуры распространения волн показана красным. То есть мы растягиваем и смещаем текстуру для каждого участка воды. Поиск зоны отрисовки волн в Unity выглядит следующим образом:
Кстати, если источник волн на к краю воды, то мы располагаем текстуру с волнами так, чтобы она не уходила за границы воды (на видео этого нет).
А здесь мы спроецировали текстуру на которой должны рисоваться волны (настроили “tilling & offset”):
Таким образом распространение волны происходит на прилегающие соседние объекты, то есть за пределы одного участка воды.
Вот результаты работы симуляции воды и тесселяции:
Генерация волн от объектов сложной формы
До этого момента я упрощенно рассказал и сослался на литературу, описывающую то как генерировать волны в виде кругов. А что если в воду упадет параллелепипед, капсула или еще какой-то объект (в том числе и невыпуклый). В этом случае форма волн должна быть соответствующей.
Чтобы добиться “реалистичной” формы волн, мы поступили следующим образом:
- падающие в воду объекты рендерятся в текстуру _FallTex. (Ортографическая камера рендерит значения глубины упавших объектов умноженные на скорость падения обьекта)
- далее текстура _FallTex размывается и результат размытия передается в текстуру волн
То есть мы вмешиваемся в процесс симуляции воды, добавляем в симуляцию новые значения (новые источники волн).
Здесь показан результат симуляции волн от объектов сложной формы:
Распространение волн на дальние сегменты
Это одна из проблемных задач. Распространение волн осуществляется за счет использования дополнительных текстур. Игрок не способен летать над водной гладью со скоростью пули и присутствовать то в одном месте, то в другом. Поэтому есть возможность переставать генерировать те волны, которые “далеко”. А распространение тех волн, которые близко, нужно плавно переносить из одного водного сегмента на другой. Здесь видно как ведет себя вода при переходе между разными участками симуляции жидкости:
Чтобы передергов с водой не было, нужно иметь 2 текстуры симулирующие жидкость. Одна из них должна симулировать жидкость, а другая должна перекопировать на себя участок с волнами при перемещении игрока (то есть при смене центрального участка воды). Если этого не делать, то возможны такие баги:
Допустим упавший в воду объект поплыл из [2,4] в [3,4] :
Тогда, чтобы сымитировать цельность водной поверхности, мы копируем часть текстуры текущей симуляции жидкости и вставляем ее во вторую текстуру. Теперь делаем вторую текстуру основной и продолжаем распространение волн на этой текстуре.
Для чего мы проводим операцию копирования? Дело в том, что нам нужно начать новую симуляцию и отобразить в ней результат старой симуляции (на рисунке выше этот результат находится в желтой зоне).
Артефакты
Если объект - источник волн расположен на границе разных водных сегментов, то при копировании текстуры распространения волны могут возникнуть артефакты:
Эти артефакты связаны с тем, что текстура волн является “Clamp”. Для устранения данных артефактов, необходимо учитывать расположение объектов (проверять расположение относительно стыков) и, в случае необходимости, исключать часть объектов из процесса симуляции волн.
Возможны ситуации, когда возникают щели. Они решаются в тесселляцинном шейдере путем уменьшения высоты отклонения волн. То есть высота волн меняется в зависимости от расстояния до камеры игрока.
Вот мои тесты тесселяции и попытки объединения Tesselation + GPU Instancing в Standard shader:
Волны от объектов разной формы:
Если вы профи в своем деле — покажите!
Такую задачу поставил Little.Bit пикабушникам. И на его призыв откликнулись PILOTMISHA, MorGott и Lei Radna. Поэтому теперь вы знаете, как сделать игру, скрафтить косплей, написать историю и посадить самолет. А если еще не знаете, то смотрите и учитесь.
Немного о дематериализации в нашей игре
А мы все еще работаем в гараже над графической частью игры! Работа над скетчами вроде завершена! Набралась целая стопка изображений и чертежей игровых обьектов, идей по тому как это все подключается и взаимодействует, теперь осталось главное - собрать :)
Перед вами результат работы моего geometry - шейдера :
Шейдер позволяет использовать в игре эффкт дематериализации: рассыпание обьекта на кубики / исчезновения кубиками.
Как это работает в общем виде: в шейдер подается текстура - маска в которой содержатся пиксели - смещения.
Параметр _Burn смещает вершинки в зависимости от значений в текстуре и все это выглядит так, будто кубики двигаются.
А вот и обобщенное видео процесса:
В нашем мире всё (на самом деле почти всё) состоит из виртуальных кубиков, которые дематериализуются. Процесс дематериализации (наиболее приближенный к нашей игре) показан здесь:
Знаю, часто хочется получить готовый шейдер и использовать его в своем проекте.
Одна из версий шейдера (если кому пригодится) предсавлена здесь.
<Шейдер не должен работать на OGL и Metal>
Обновления по проекту обычно выкладываем здесь:
https://vk.com/treload
https://twitter.com/CGAleksey
https://www.instagram.com/cgaleksey/
На этом все, всем хорошего вечера :)
Puzzle of life - Проработка эффектов и анимаций взаимодействия
Основные эффекты для начальной сцены в игре. Также игрок будет часто взаимодействовать с объектами в локациях, чтоб находить предметы необходимые для принятия некоторых решений. В противном случае игрок может оказаться в не приятной ситуации например без револьвера, или ключа, которые были бы очень кстати в некоторых случаях.
Но самым важным будет найти фрагменты своих воспоминаний в виде пазла, чтоб узнать, как вы попали в этот беспробудный сон, и кто вы такой…
ENB Пресеты для Fallout NV которые стоит попробовать
(Решил сделать это поскольку сам долго времени потратил на нахождение нужного пресета, и хочу облегчить жизнь другим, потому-что оригинальное освещение игры много кому не нравится.
Постарался найти под самые разные вкусы, а ещё этот пост длиннее прошлого, поэтому тут более короткие описания)
Для начала:
ENB - особенная модификация для игр, позволяющая точно настроить сглаживание, свечение, замутнение окружения и другие настройки, а ещё это может очень сильно напрягать ваш ПК.
Что-бы они работали следует установить саму основу здесь.
Так-же для некоторых нужен вот этот плагин.
NEVADA ENB
Делает игру очень светлой, попытка сыграть на атмосфере вроде "Пустыня под аддским палящим солнцем"
https://www.nexusmods.com/newvegas/mods/63036?tab=files
(Есть так-же версия с более тёмными ночами, выбирайте нужную)
Rudy ENB
На этот раз попытка сделать всё немного более аутентично.
Old World ENB
"Пресет старого мира" черпает своё вдохновение от ранних фотоаппаратов 1950-х годов.
The ENB of the Apocalypse
Немного странное название, ведь никакой *мрачностью* там и не пахнет, да и светит как-то слишком сильно, однако я видел что некоторые играют с этим пресетом, так-что проверьте, может вам понравится.
Dreary ENB
Пресет сделанный под вдохновением сталкера и метро, не лорно конечно, но зато красиво.
DYNAMO ENB
Возможно это вполне лор-френдли пресет, без всего лишнего.
https://www.nexusmods.com/newvegas/mods/53521?tab=descriptio...
Beautification Project ENB
Если вам больше подходит освещение из четвёртой части, то этот пресет для вас.
https://www.nexusmods.com/newvegas/mods/65315?tab=descriptio...
Ну вроде и всё!
ENB Пресеты для Fallout 4 которые стоит попробовать
(Решил сделать это поскольку сам долго времени потратил на нахождение нужного пресета, и хочу облегчить жизнь другим, потому-что оригинальное освещение игры мягко говоря *не очень*.)
Для начала:
ENB - особенная модификация для игр, позволяющая точно настроить сглаживание, свечение, замутнение окружения и другие настройки, а ещё это может очень сильно напрягать ваш ПК.
Что-бы они работали следует установить саму основу ниже.
Empyreal ENB
Делает игру немного более светлой и бледной, немного изменяя эффект от ярких свечений, давая возможность взглянуть на игру немного по другому.
PRC - PhotoRealistic Commonwealth
Смысл можно увидеть в названии - это попытка сделать освещение более фотографичным, была поставлена задача на реалистичности, если по реализму надо ярче - делается ярче, если темнее - соответственно темнее.
Так-же есть бонусные ENB которые сделаны скорее ради скриншотов и особенности атмосферы, например возможность сделать картинку похожей на видео из VHS кассеты, ну или просто сделать всё чёрно-белым.
https://www.nexusmods.com/fallout4/mods/6796?tab=description
VOGUE ENB
Но если эти навороченные шейдры не для вас, то есть не напрягающий и простой пресет который был создан что-бы было как в оригинале, но лучше.
![ENB Пресеты для Fallout 4 которые стоит попробовать Fallout, Fallout 4, Enb, Игры, Компьютерные игры, Шейдеры, Освещение, Моды, Гифка, Длиннопост](https://cs11.pikabu.ru/post_img/2018/08/12/5/153406076617785195.jpg)
Меряем мир в сосисках!
Пройдя этот тест, вы узнаете, сколько нужно сосисок, чтобы спуститься по ним на дно Марианской впадины. А еще сколько их можно съесть, пока длится самый долгий в мире поцелуй. Не пропустите!