Приветствую, читатель! Меня зовут Сергей, я являюсь инди-разработчиком компьютерных игр. В моем портфолио имеется уже несколько инди-проектов, часть из которых была самостоятельно выпущена в Steam. Игра «Свет» или «The Light», вышедшая в 2012 году, была моей первой пробой пера, открывшей путь в мир игровой разработки. Проект распространялся бесплатно, но реакция публики и отзывы игроков подарили мне серьезную мотивацию для дальнейшей работы. «The Light» стал для меня чем-то вроде философской притчи о человечестве и его судьбе. Сюжет абстрактен и не преследует каких-то конкретных целей, это лишь возможность поразмышлять на обширную тему.

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



О чем речь?


В данной статье речь пойдет именно о проекте «Свет» и о проделанной работе, связанной с переносом игры на более новую версию движка Unity. Это подразумевает под собой большое количество технических нюансов. Статья будет представлена в двух частях.

Часть 1


Первоначальная версия игры в уже далеком 2012 году была собрана на Unity 4.2. Тогда в моем информационном поле еще не было разговоров о PBR материалах, рефлекшн пробах и других актуальных сейчас методах. Базовые шейдеры в Unity были весьма простыми и не блистали особой реалистичностью. Конечно, можно было дописать множество дополнительных элементов, вроде отражений по френелю, но тогда шейдерное программирование для меня было тайной за семью печатями. Основным используемым шейдером был Normal Bumped Specular и AlphaTest diffuse для объектов с прозрачностью (листья деревьев, фигурные металлические решетки).

Рилтайм тени отсутствовали, почти все освещение было запечено в текстуру и разрешение этих теней местами было очень низким. Запекание света ко всему прочему свело практически к нулю эффект рельефа от Bumped Specular шейдера, в результате чего при ближайшем рассмотрении объекты больше походили на плоский картон.

Картинку в значимой степени преображали постэффекты на камере, в частности Bloom и Color Correction. Они добавляли красок и разнообразия, хотя и были местами слишком навязчивыми.



На момент создания этого проекта я имел еще смутные представления об оптимизации, батчинге (объединении в один меш), окклюжн — куллинге (отсечении невидимых объектов) и т.д. Но в силу того, что технически сцена не была нагружена чем-то сложным, не имела рилтайм освещения, травы с альфаканалом и т.д. — игра вышла достаточно оптимизированной и неплохо работала на слабом железе, выдавая приятную для 2012 года картинку.

Спустя 7 лет на любое свое творение глядишь уже другими глазами. Технологии сильно ушли вперед, изменился инструментарий, последние версии движка Unity значительно отличаются от предшественников. В связи со всем сказанным выше перенос проекта с 4 версии на версию 2017 (решил остановится на ней в силу ряда причин) является достаточно долгой и кропотливой работой. К тому же, учитывая тот факт, что первоначальный проект и игрой полноценной называться не мог (большинство действий в игре осуществлялись с помощью одного скрипта с функцией триггера), нужно было с нуля писать всю логику, взаимодействие с объектами, инвентарь, систему меню, систему сохранений, достижений, настройки и т.д. В общем, меня ждал весьма масштабный объем работы!

Начало. Шейдеры и свет


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



Новый Standart

Было принято решение начать с замены старых шейдеров на новый PBR Standart. В предыдущей статье на тему разработки 35ММ я уже упоминал о новом типе PBR шейдеров (Physically Based Rendering), что подразумевает физический правильный рендеринг. Новый материал Standart уже не имеет привычного из прошлых версий o.Gloss и o.Specular, здесь у нас карта металличности (o.Metallic) и Smoothness. Также, имеется больше слотов для текстур различной категории. К примеру, у нас появляется возможность добавить карту Occlusion для мягких затенений. Эффект очень полезный, поскольку позволяет подчеркнуть объем и затенения в тех местах модели, куда света попадает меньше. Без этой карты и лайтмап — текстура объекты смотрятся плоско и нереалистично.



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



Растительность

После замены основных материалов я перешел к растительности. В оригинальной сборке игры был использован стандартный Alpha Test шейдер без спекуляра и Normal Map. Конечно же, данное положение вещей в 2019 году меня не устраивало. Можно было купить или найти на просторах интернета готовое решение, есть целые паки с шейдерами и готовыми моделями со множеством фитч, имитацией колыхания на ветру и т.д. Но уже традиционно я в таких вопросах пытаюсь разобраться сам и экспериментирую. Это что-то вроде спортивного интереса. За основу моего нового Vegetable шейдера был взят референс Toon Ramp из учебной статьи Unity.

Представленная модель освещения LightingRamp позволила отрисовать силуэт теней как на освещенной стороне полигона, так и на противоположной. Это имитировало способность листвы пропускать свет — транслюцентность.



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



К сожалению, в таком режиме автоматическая отрисовка задней стороны полигона методом Cull Off давала не очень корректный результат, поэтому Backface пришлось добавлять вручную в редакторе. Далее к шейдеру была добавлена карта Normal и Specular. Использовать модель освещения PBR шейдера и подключить Reflection Probes (зонды отражений) мне уже не удалось, но с помощью масок и Emission карты была добавлена имитация Ambient Occlusion. Ну и наконец, крайне необходимо было все это дело оживить и придать растительности движения. С помощью функции Vertex и той же маски нужные области плашек с листвой начинают оживать. За основу смещения вертексов взят пример Normal Extrusion with Vertex Modifier из мануала Unity.



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



Свет

Значительно был изменен подход к самому устройству освещения в сцене. В оригинале весь свет был запечен в лайтмапы, а рендеринг работал в режиме Forward. В ремейке важным аспектом было использование реалтайм света для возможности изменения времени суток. Режим рендера был сменен на Deferred. Главный источник света (солнце) использовался в режиме Mixed, направленное освещение и тени рисовались а реалтайме, а глобальное освещение запекалось в текстуры. Это позволяло менять уровень освещенности и направление, но при этом сохраняло эффект мягкого глобального освещения и рефлексов, которые всегда придают картинке дополнительного реализма. Запекание лайтмап-текстур происходило не для всех объектов, в основном для крупных и более-менее простых. Мелкие и сложные для создания развертки оставались динамичными и подсвечивались либо лайт -пробами, рефлекшн пробами, либо просто базовым амбиент лайтом ( который указывается в главных настройках освещения). Запекание осуществлялось Progressive лайтмапером.







День и ночь

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



Для оптимизации при минимальном уровне света (примерно при 0.1- 0.2) тени плавно отключались. Для смены времени требовалось учесть еще парочку важных моментов. На сцене присутствовали частицы- тополиный пух. Их материал не реагировал на свет ( на тот момент я еще не нашел подходящий шейдер), поэтому цвет материала пуха пришлось менять скриптом в зависимости от времени суток.

Также, при смене времени скрипт настраивал нужный цвет для постэффекта Global Fog, поскольку в дневное время туман должен выглядеть как светлая серо-голубая дымка, а в ночное иметь более темный, почти черный тон.



Модели и новый контент


Основная часть локации и базовые модели: модель главного здания, деревья, скамейки, фонарные столбы остались оригинальными. Многие другие пропсы были полностью или частично переделаны. Была переработана модель керосиновой лампы, создан новый дизельный генератор с отдельными анимируемыми элементами, новая модель кинопроектора и т.д.







Близлежащие строения, в которые раньше нельзя было попасть, были переработаны и стали доступны для игрока. Внутренняя планировка главного здания была также изменена. Помимо переработок старого контента был добавлен и новый, например модель автобуса ПАЗ, игровой автомат, автомат для продажи газировки, пазл — электрощиток, гермоворота в подвале, предметы инвентаря и т.д. Часть контента была создана самостоятельно, некоторые же модели были заказаны на аутсорсе.



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

Эффекты и нестандартные шейдеры.


Вода



С каждым новым проектом хочется учесть нюансы, на которые ранее закрывались глаза. Общая картина создается из отдельных деталей и даже не особо важные элементы могут повлиять на восприятие. Плюс, это в некотором роде челлендж — каждый раз улучшать что-то, с чем уже работал. Особенное удовлетворение доставляет самостоятельный поиск решений, а не использование готовых ассетов. Во всех предыдущих проектах мне приходилось работать с водой. Как правило это был достаточно простой вертексный шейдер, который не реагировал на игрока и освещение в сцене. За основу был взят Mirror Reflection с Unity Wiki, который являлся примером реализации зеркала.



Поверхность была динамичной за счет смещения вертексов (имитация волн), но всегда слишком однообразной и несколько скучной. Путем проб и ошибок для проекта Свет мне удалось создать Surface вариант аналогичного шейдера, который как и образец может: принимать текстуру зеркального отражения от скрипта Mirror, деформировать вертексы для имитации волн, записывать экран в Grabpass текстуру для создания преломлений под водой, иметь мягкие Alpha края при пересечении с геометрией ( depth fade). Также для эффекта реакции на игрока в шейдер передается информация о координатах положения игрока. В точке координат рисуется динамичное пятно, которое имитирует всплески, когда персонаж находится непосредственно в толще воды. Самое же главное, что позволил Surface шейдер — принимать освещение от любых источников света. Таким образом вода кажется более осязаемой, объемной субстанцией и позволяет поиграть с ней с помощью эффектов освещения.



Каустика

Еще одной важной деталью стало создание эффекта каустики — световых отражений, падающих на поверхности. В темноте затопленных водой подвальных туннелей этот прием был просто необходим. Эффект создан с помощью объекта Projector и светящегося материала с анимированной текстурой. В шейдере смешивается 2 текстуры каустики, которые смещаются в разных направлениях, в результате создается эффект динамики. В большинстве своих шейдеров для экономии я обычно применяю текстуры-маски, содержащие в себе 4 канала (RGBA) — каждый для определенной цели. В канале R может быть основная текстура, в канале G более мягкие световые пятна, а в канале B текстура шума для искажения рисунка каустики.



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



Частицы

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



Как уже отмечалось выше, почти все базовые поверхности в сцене используют модифицированный вариант Standart PBR shader. В некоторых добавлены маски с различными вариантами пятен для дополнительной детализации Albedo текстур. Поверхностям вроде асфальта или брусчатки добавлена маска пятен для имитации дождевых луж. Шейдер напольной плитки в главном здании имеет дополнительную карту для отражений, которые рисуются скриптом Mirror аналогично водной поверхности. Для оптимизации в прорисовку отражений включены лишь базовые и крупные объекты, а на определенном расстоянии рендер отражений выключается.

Туман/дым

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



Декали

Еще одним важным моментом стал поиск подходящего шейдера для декалей. В игре планировалось множество всевозможных граффити, которые чаще всего устанавливались с помощью старенького плагина decal system ( еще со старой версии Unity 4.6). Для граффити, надписей и знаков был создан большой атлас размером 4096 на 4096. Изображения на декалях полупрозрачные и отрисовываются в режиме Transparent alpha, поэтому в случае с динамическим освещением они не всегда выглядят адекватно, поскольку стандартный альфа шейдер не способен принимать тени.

Для решения задачи был создан специальный двухпроходный шейдер. В первом проходе рисуются темные части изображения, во втором светлые методом смешивания Blend DstColor One. Я могу не совсем верно понимать метод отрисовки, поэтому воздержусь от подробных объяснений, но желаемого результата удалось достичь: в тени изображение утопает во мраке, а на свету проявляется и даже играет красками. На производительность двухпроходность шейдера влияния не оказала, поскольку Decal system изначально объединяет все декали в один большой меш. Наверно есть более оптимальные способы, но такой вариант мне вполне подошел.





Земля/трава

Еще один двухпроходный шейдер был создан для поверхности земли. Сама территория на локации сделана геометрией в 3d max. Но для отрисовки травы дополнительно был создан террейн. Пришлось вручную подгонять высоты террейна в нужных местах, чтобы они соответствовали рельефу местности. Затем отрисовка самого террейна была выключена а на поверхность кистью наносилось несколько вариантов заготовленных мешей травы. Родная трава на террейне жутко нагружает сцену в плане производительности, поскольку она не батчится и каждый элемент создает дополнительный вызов отрисовки ( возможно кто-то меня поправит, если я не прав), но подобный метод крайне удобен в плане работы. Густота травы не высока и в тех местах где ее нет, очень бросается в глаза простота поверхности. Как раз в связи с этим в качестве эксперимента я использовал двухпроходный шейдер, о котором говорилось выше. Первый проход — обычная непрозрачная геометрия, второй — немного приподнятый дубликат поверхности в режиме Alpha test. Грубо говоря, дополнительно рисуется копия поверхности земли/ травы с жесткой прозрачностью и смещенными вверх вертексами. Такой метод несколько дополняет плоскую траву и создает иллюзию дополнительной детализации и объема.



Свет за окном

За окнами главного здания в некоторых местах были размещены односторонние полупрозрачные спрайты для имитации засветов. Также, в постэффекте Global Fog имеется параметр яркого белого тумана, который активируется по мере движения героя вглубь здания. То есть, при нахождении персонажа в темных коридорах корпуса, окружение за пределами плавно приобретает белесое свечение. Это создает достаточно красивый художественный эффект и позволяет подчеркнуть ближний и дальний план.



На этом первая часть статьи завершается. В данный момент идет подготовка материала для второй части. В ней я затрону тему постпроцессинга, работы со звуком, оптимизации и т.д.
Ссылка на вторую часть по мере готовности будет добавлена в конце этой статьи, а также опубликована в ВК и Инстаграме. Всем спасибо за внимание!