Технический постмортем «Помни...» студии Ice-Pick Lodge
Особенности рендеринга и оптимизация.
10 февраля вышла «Помни...» — авторская адвенчура Айрата Закирова, технического директора студии Ice-Pick Lodge. Главный герой игры — Миша, житель небольшого российского городка, пытающийся восстановить отношения со старыми друзьями.
Программист графики в Ice-Pick Lodge Николай Понамарёв в техническом посмортеме рассказывает, как устроен рендеринг изображения в «Помни...», а также об оптимизации игры.
Вступление
Вас приветствует Николай Пономарёв, программист графики и портирования в студии Ice-Pick Lodge. «Помни…» — первая коммерческая игра, в разработке которой я принял участие. Этот опыт дал мне понимание многих аспектов геймдева. В этом небольшом техническом постмортеме я поделюсь несколькими техниками рендеринга и оптимизаций.
Проект
«Помни…» была разработана на движке Unity версии 2021.1. Игра рассчитана на геймплей при 60 FPS на консолях PS4 с разрешением 1080p и Xbox One с разрешением 900p. Pro-версии и некстген-консоли работают с улучшенными настройками графики и разрешением.
Мы приняли решение отказаться от встроенного рендерера из-за неоптимальной производительности и сложности кастомизации (отсутствие SRP Batcher, отсутствие атласов для теней, невозможность кастомизировать использование Stencil-буфера с отложенным освещением). Поэтому проект работает на Scriptable Render Pipeline.
Это был мой первый опыт в написании SRP. На ранних этапах разработки тестировался метод рендеринга Tiled Deferred, но в нашем случае он оказался медленнее классического отложенного рендеринга (из-за небольшого количества источников света), поэтому мы остановились на SRP с классическим отложенным рендерингом. Я назвал её DRP (Deferred Render Pipeline) .
В GBuffer используется 4 текстуры (Emission, Albedo, World Normal, BRDF Info) с фотореалистичной моделью освещения, но без параметра металличности. SRP Batcher дал нам лучшую производительность на CPU, а использование техник с современной шейдерной моделью и атласы для теней улучшили производительность на GPU. Также это дало возможности по кастомизации, например, кастомный скайбокс и внесение эффекта сепии в Uber-шейдер наложения постэффектов.
Мой DRP поддерживает эффекты SSAO, DOF, Color correction, tonemapping, vignette, dithering и TAA с динамическим апскейлингом.
Рендеринг
Расскажу о некоторых техниках рендеринга, которые мне запомнились.
Система Lens Flare
Встроенная в движок система бликов практически не может быть кастомизирована и скрывает блики при помощи рейкастов, не используя буфера глубины. Поэтому мы решили написать свою собственную.
Используя систему Multithreaded Job и Burst-компилятор, она обрабатывает и создает меши для бликов. На GPU вертексный шейдер считывает буфер глубины, чтобы постепенно скрывать и отсекать блики. Система получилась быстрой как на CPU, так и на GPU, блики плавно скрываются, могут существовать за границами экрана.
Запечённое глобальное освещение
Игра поддерживает смену дня и ночи, которая требует динамического глобального освещения. К счастью, «Мор» (2019), предыдущая игра студии, имела подобную систему, которая использовала уже существующую информацию в лайтмапах для сохранения данных о видимости глобального освещения. Её мы и позаимствовали, имплементировав в DRP.
Дополнительную информацию о рендеринге в «Море» вы можете узнать из лекции Андрея Сараева.
Процедурный свет в окнах домов
Для города требовалась какая-то простая имитация жизни жителей. Одной из составных частей этой имитации был свет в окнах домов, который менялся в зависимости от времени суток.
Обработка каждого окна на CPU заняла бы слишком много времени и потребовала бы большого количества draw calls. Поэтому она была переброшена на GPU и сделана процедурной. Каждое окно имеет рандомные значения, записанные в цвета, с помощью которых вертексный шейдер создаёт значения цвета, интенсивности и вероятности включения окна в ночное время.
Количество включенных ночью окон модулируется коэффициентом, зависящим от дня игры, чтобы к концу истории создать ощущение пустого города.
Освещение и рефракция дождя
Игра меланхоличная, в ней часто идёт дождь.
Осадки были созданы на встроенных в Unity партиклах. Первые варианты смотрелись плоско из-за отсутствия рефракции света, приходящего от сцены. Поэтому пришлось разработать простую технологию, имитирующую этот эффект. Из основной картинки мы генерируем набор мип-мапов (цветная мип-пирамида) с размытием гауссовым фильтром. Нужный уровень мип-мапа пирамиды (в зависимости от разрешения экрана) используется для имитации рефракции.
Намокание
Для большей реалистичности дождя требовалась имитация намокания поверхностей. Эффект реализован через изменение параметров Albedo и Smoothness в шейдерах объектов сцены, которые наполняют GBuffer.
Снег
В эпилоге игры наступает зима. Снег работает идентично намоканию в шейдерах объектов сцены. Но в отличие от влажности, снегу требовались текстуры, которые накладываются трипланарно.
В воспоминаниях, которые собирает игрок, время года могло плавно меняться с осени на зиму и обратно. Для постепенного появления снега использовалась текстура perlin-шума.
Чтобы проиллюстрировать забывание, понадобились следы на снегу.
Для них был создан дополнительный декальный GBuffer, в котором хранились мировая нормаль, глубина и AO. Вдобавок, благодаря технологии microshadowing (как она была описана в презентации Technical art of Uncharted 4) , следы получили эффект затенения от солнца на основе одной информации AO.
Деревья
Настроение осеннего угасания в игре создано за счёт листвы деревьев, редеющей день ото дня.
Для достижения этого эффекта используются атласы текстур с разной плотностью листвы. Регионы на атласе выбираются вертексным шейдером согласно мировой splatmap, где дизайнер «рисовал» уровень осени для каждой точки города.
Ветер сделан относительно стандартно: в вертексном шейдере берётся вектор от основания дерева на координаты вертекса и в зависимости от направления и силы ветра поворачивается через кватернионы. Листва колышется через смещение.
Для скрытия деревьев, перегораживающих камеру, шейдер дерева имеет эффект постепенной прозрачности через dithering, который очень хорошо работает с TAA.
Шейдеры проводов
Провод — это небольшой кубик с малым количеством вертексов и полигонов, который содержит в вертексном цвете маску начала и конца. Толщина, провисание, ветер и нормали процедурно создаются или реконструируются в вертексном шейдере.
Шейдер экрана телевизора
Новости о внешнем мире подаются в игре через телевизор.
Чтобы не выделять картинку телевизора в отдельную рендер-текстуру, все изображения накладываются напрямую в пиксельном шейдере (до восьми слоёв) . Дополнительно в нём применяются параллакс, хроматическая аберрация и тайлящаяся CRT-текстура для симуляции ЭЛТ-изображения.
Плавучесть
И напоследок — лодки и бумажные кораблики в озере. Они тоже были реализованы через вертексный шейдер. Перемещение не совсем точное по причине низкого числа вертексов воды, но при небольшой интенсивности волн работает достаточно хорошо.
VR Cinematography
Для анимаций некоторых катсцен был использован VR-шлем Oculus Rift, с помощью которого мы записали мокап анимации камер. Вся запись производится в отдельном проекте с упрощенной копией внутриигровой локации.
Оптимизация производительности
«Помни…» в основном лимитирована по CPU, которому нужно выполнять не только игровой код, но и SRP-код на основном потоке. Для снижения затрат на последнем потребовались некоторые оптимизации.
Разделенный рендеринг теней
Одна из самых значимых оптимизаций. Игра использует четыре каскада для теней, разделенных по дальности, два последних из которых рендерятся по очереди. В итоге за кадр отрисовываются только три каскада, а не четыре.
LOD и HLOD
Город имеет очень много активных объектов, которые могут нагружать CPU. Встроенная LOD-система в Unity частично отключает обработку рендеринга, но не отключает объекты. Чтобы решить эту проблему, была разработана кастомная LOD-система, которая отключает объекты в зависимости от дистанции до камеры.
Поверх неё была создана HLOD-система. Она заменяет группы объектов на единый, облегчая работу CPU и GPU. HLOD используют деревья и земля. В качестве верхнего LOD деревьев используются импостеры.
Некоторые объекты и алгоритмы в игре также обновляются не каждый кадр, как в примере с каскадами теней.
Частицы листьев
Изначально все листья были индивидуальными объектами, логика которых обрабатывалась на основном потоке. Позже была создана новая система, которая работает на партиклах и job-системе, что позволило отгрузить львиную долю обработки на отдельные потоки.
Динамическое разрешение
Мы используем динамическое разрешение вместе с TAA, что позволяет нам делать TAA-апскейлинг и лучше реконструировать изначальное изображение. Динамическое разрешение включается редко и потому практически незаметно, но позволяет сохранить стабильный FPS.
Мобильные платформы
С небольшими изменениями SRP без изменения геометрии удалось запустить игру на 60 FPS на производительных телефонах и 30 на относительно бюджетных. Версия для Switch использует консольные настройки графики и будет работать на 30 FPS.
На этом наш небольшой постмортем подходит к концу. Спасибо за уделённое на него время!
Комментарий недоступен
Выживание настоящей игровой инди-студии в России — вещь тяжёлая, утомительная, перемалывающая кости. Мы верим в людонарративную гармонию и не очень уважаем истории студий, которые выглядят мрачными лишь по обложке.
Существование студии должно быть сложным на грани выносимого — иначе теряется весь эффект.
Надеюсь, когда-нибудь об этом снимут сериал..)
Могло бы выйти драматично.
Где кампании для Мора за Самозванку и Бакалавра?!
"Ну чо ты как этот, нормально же сидели!"
Выглядит как трекопеечная поделка со дна Стима - а надо же, сколько технических нюансов пришлось решать...
игры это очень-очень тяжёлая стезя