[Перевод] Высокопроизводительная система освещения для 2D-игр
Привет, меня зовут Александер Бирке (Alexander Birke), недавно я выпустил свою первую игру в Steam под названием Laser Disco Defenders. Мне кажется, было бы интересно раскрыть некоторые технические и дизайнерские решения, вошедшие в игру. Начну с собственной системы освещения, позволяющей работать со множеством двухмерных источников света даже на слабых компьютерах. LDD создана в Unity, но этот подход сработает в любом другом игровом движке, позволяющем создавать процедурные сетки (meshes).
Почему именно собственная система освещения?
Основная механика LDD заключается в том, что каждый выстреленный лазерный луч продолжает отражаться от стен и может попасть в игрока, поэтому мы назвали это «bullet hell своими руками». Визуальный стиль вдохновлялся эпохой диско и яркими танцполами того времени, поэтому я хотел, чтобы лазеры могли освещать окружение. Освещение также полезно, так как помогает игроку понять, что скоро в кадр попадёт лазерный луч. Игра была выпущена ещё и на PlayStation Vita, поэтому мне нужно было более производительное решение по сравнению со стандартным отложенным освещением (deferred lighting) Unity, использующим вызов отрисовки для каждого источника света, отрисовываемого в сцене. На экране одновременно запросто может быть до 30 лазерных лучей, с такой нагрузкой портативная консоль не справится. Ещё я хотел, чтобы освещение соответствовало форме лазерных лучей. Лазерный луч обычно длинный и тонкий, поэтому ни один из стандартных источников освещения Unity (direction, point, spotlight) не подходил.
Точечный источник слева не соответствует форме лазерного луча. Справа — нужный контур света.
Другие игровые элементы тоже должны были излучать свет, поэтому мне нужен был простой компонент освещения, помещаемый на игровые объекты, который система освещения должна была рендерить.
Должны поддерживаться не только лазерные выстрелы, но и другие источники света.
Основная идея использования процедурных сеток заключалась во «впечатывании» данных об освещении в буфер освещения, который затем сэмплировался в шейдерах. Это не отличается от принципа работы отложенного освещения, однако для моего решения требуется работа только в 2D, поэтому я мог рассчитать всё освещение за один проход отрисовки благодаря отрисовке освещения с помощью процедурной сетки.
Лазеры в игре рендерились как процедурная сетка, поэтому у меня уже была текстура, которую можно использовать для хранения уменьшения интенсивности света. Я назвал её (пам-пам-па-а-ам!)… лазерной таблицей!
Лазерная таблица. Сверху вниз: свечение, концы лучей, сами лучи. Отмеченные области имеют диффузное рассеивание для всех лазерных источников света.
Для выбранного визуального типа лазера мне нужны были длинные части луча, его концы и свечение, возникающее при попадании в стену. Кроме того, это свечение должно скрывать то, что каждый сегмент луча рендерится как одиночная четырёхугольная полоса, поэтому они не очень красиво накладываются друг на друга. Это небольшая хитрость, но зато визуально всё выглядит хорошо.
Схема генерирования сетки для отдельного луча. Для каждого сегмента нужны два четырёхугольника (quad) концов луча и ещё один для прямого сегмента посередине. Свету от свечения стены тоже нужен свой quad. Такая же топология используется для отрисовки самого лазера.
Для создания освещения каждого лазера я беру положения каждого сегмента и раздвигаю их вершины наружу, в зависимости от того, насколько далеко свет может распространяться от лазера. Затем создаются и добавляются к сетке вершины для других источников света. В LDD мне нужны были только круглые источники, но можно легко добавить и другие формы. В этих круглых источниках используется такое же диффузное свечение из лазерной таблицы и рендерится как четырёхугольники.
Видимая и скрытая сетка освещения. Обычно сетка освещения находится далеко под всем остальным в игре, поэтому при визуализации сцены не отображается. Здесь видны лазеры и пятна-препятствия, отбрасывающие свет.
Затем сетка рендерится в отдельном цветовом буфере. В Unity я для этого использовал камеру, имеющую те же расположение и размер, что и основная камера. Потом она рендерится в RenderTexture с помощью слоёв, чтобы исключить всё, кроме сетки освещения.
Сетка освещения и текстура, на которую она рендерится.
После этого цветовой буфер отправляется в графический процессор как обычная текстура, поэтому может использоваться из любого шейдера. Были созданы собственные шейдеры для спрайтов, частиц и всего остального, что требует освещения. В вершинном шейдере каждая вершина определяет положение своего viewport, поэтому может использоваться как UV-координаты при сэмплировании текстуры освещения в шейдере фрагмента. Самое интересное — это фоны игры. В них используется альфа-канал для определения величины отражаемости конкретного текселя. Это очень помогает добавить в игру больше глубины.
Пример фоновой текстуры в Laser Disco Defenders. Слева показаны цветовые каналы, справа — альфа-канал, используемый для отражаемости, обеспечивающий создание тёмных трещин на той текстуре пещеры. Там ещё есть скелет космического кита… просто потому что мы можем!
Система содержит пару приёмов оптимизации. Основной — это усечение по пирамиде видимости каждого источника света перед созданием для них вершин. Это уменьшает объём необходимых вычислений в процессоре и ограничивает количество вершин, передаваемых в графический процессор. Как можно видеть из изображения сетки освещения и текстуры, буфер освещения имеет гораздо меньшее разрешение, чем игра. Я выяснил, что его можно уменьшить в десять раз от игрового разрешения без каких-либо артефактов. При считывании текстуры билинейное сэмплирование в шейдерах обеспечивает достаточную для игры интерполяцию. Это уменьшает объём работы по заливке, используемой системой освещения, оставляя достаточно ресурсов рендеринга на создание парочки визуальных эффектов поверх неё.Заключение и дальнейшая работа
На консоли Vita игра обеспечивает приличное количество FPS, чем я очень горд, учитывая визуальную чёткость игры. Хороший уровень производительности был отмечен и в обзорах игры для ПК. Надеюсь, вы тоже согласитесь, что освещение значительно усиливает психоделический диско-стиль игры.
Система пока ограничена тем, что не может создавать тени. Вы можете добавить их, но ценой производительности. Если вы решите сделать тени, вам, скорее всего, не удастся провернуть фокус с буфером освещения низкого разрешения. Ещё система не может обеспечить отражения освещения. Их можно создать с помощью ещё одной лазерной таблицы, использующей цветовые каналы, представляющие направления, которые затем «впечатываются» в отдельный буфер направлений, но такая система уже сложнее того, что мне нужно было для Laser Disco Defenders.
Надеюсь, вам понравилась статья. В следующем посте я буду подробно рассматривать процедурную генерацию уровней, так что оставайтесь с нами!
Александер управляет бристольской игровой студией Out Of Bounds Games. Вы можете подписаться на его Twitter или на страницу в Facebook компании с самым корпоративным URL в мире. Ещё он организует Bristol Unity Meetup.