Пятничные факты #323 — Движущаяся вода

опубликовали Albert, Ernestas, posila

Движущаяся вода — Идея Albert

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

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

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

Движущаяся вода — Техническое искусство Ernestas

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

Но теперь мы полируем Factorio, стараясь сделать его настолько красивым, насколько мы можем, учитывая наши ограничения. Я рад немного поговорить с вами о том, как мы решаем сделать анимированую воду!

На Рождество 2018 года я решил подарить себе, решив сделать тайно анимацию воды. Исходя из прошлых разговоров с Альбертом, цель была ясна:

  • Это должно было выглядеть похоже на текущую воду.
  • Без фотореализма.
  • Это должно было быть очень дешево для GPU.

В прошлом я экспериментировал с этим дешевым шейдером облаков. Он использовал фрактальное броуновское движение и использовал только выборку текстуры шума, вместо обычного подхода для расчета шума Перлина. С низким числом итераций это было почти так же дешево, как рисование спрайта. Поэтому я запустил MonoGame (прекрасный фреймворк) и реализовал мир на основе плиток, используя ту же технику для шейдера, с той лишь разницей, что я ограничил значения шума для имитации более ярких и темных областей. Для шума я использовал наш водный спрайт.

Оригинал — Шум — Вместе

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

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

Решение отражений, прозрачности и пены привело к использованию цели рендера для сохранения информации для шейдера воды. Цель рендера — это просто текстура, на которой вы можете рисовать. Используя три канала RGB, я могу сохранять три вида информации. Красный для отражений, зеленый для прозрачности и синий для пены. Все рисунки аддитивны для учета нескольких плиток, нанесенных друг на друга. Таким образом, мы можем добавлять информацию не только для берега, но и для разного всего.

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

Движущаяся вода — Интеграция в игру posila

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

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

Это создало проблему с производительностью, так как шаг подготовки рендеринга плитки занимал 3 мс (почти 1/5 от общего времени кадра) при уменьшении масштаба. Почему это имеет значение?

Основной цикл игры проходит в 3 основных этапа:

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

Хотя обновление игры и рендеринг выполняются параллельно, ни один из них не может работать, в то время как подготовительный рендер работает(подробнее об этом в FFF-70). Таким образом, 3 мс дополнительного времени на подготовку означают, что на обновление можно потратить 3 мс, прежде чем частота обновлений упадет ниже 60 (и я даже не хочу упоминать, сколько времени потребуется для отладочной сборки, я просто скажу, что ожидайте много неприятных взглядов от коллег).

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

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

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

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

Создание модов

Увидев это, вы можете порадоваться возможностям создания модов. Ну, не просто пока. Система была настроена так, чтобы она позволяла определять различные эффекты плитки, но на данный момент она ограничена разрешением только 1, который используется для определения эффекта воды. Я планирую снять это ограничение в ближайшем будущем, но в итоге вы будете ограничены только изменением свойств эффекта, который мы произвели. Но это все еще может быть достаточно интересным из-за способности изменять «текстуру шума».

До сих пор нет планов поддержки пользовательских определений шейдеров до 1.0.

Как всегда, дайте нам знать, что вы думаете на нашем форуме.

Comments: