Мэтью Ньюкомб, ведущий разработчик игр Assemble With Care @ ustwo

Assemble With Care - 18-месячный проект, недавно выпущенный на Apple Arcade. Медитативная тактильная игра, в которой вы восстанавливаете предметы, которые имеют сентиментальную ценность для их владельцев, и исследуете их отношения друг с другом.

Для всех, кому интересно узнать больше, вот наш трейлер, и вы можете найти его в Apple Arcade здесь.

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

В частности, я буду проходить через:

  • Искусство сборки с осторожностью - каковы были художественные цели и задачи и откуда мы черпали вдохновение.
  • Оживление неодушевленных - как сочетание некоторых базовых методов шейдера и традиционных художественных подходов может создать ощущение тонкой «раскрашенной вручную»
  • Ориентация на игрока - как мы художественно уменьшаем акцент на аспектах сцены, чтобы направлять внимание игроков на протяжении всего процесса.
  • Цвет в тени - как мы используем тени и текстурирование для тщательного управления цветом по всей сцене.
  • Шейдеры. Повсюду разбросаны небольшие фрагменты кода для справки.

Немного контекста

После выхода Долины монументов 2 в июне 2017 года мы потратили несколько месяцев на прототипирование целого ряда идей. Мы остановились на ядре того, что станет Assemble в начале 2018 года, и создали небольшую команду вокруг этого, чтобы в конечном итоге выпустить его 19 сентября 2019 года.

Assemble - это огромная командная работа многих замечательных людей, и представленная здесь работа далеко не только моя. В частности, спасибо Максу ван дер Мерве. Как технический художник (прописными буквами) на Assemble, Макс задумал и заложил основу для многих идей и техник, представленных здесь. С тех пор Макс ушел, чтобы прототипировать захватывающий материал @thelinestudio.

Искусство сборки с осторожностью

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

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

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

Оживление неодушевленного

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

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

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

Смещение вершины

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

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

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

Симплексный шум. Последний вариант использует симплексный шум. Шумовые функции уже некоторое время используются в играх в качестве инструмента и имеют массу полезных применимых случаев, например, если вы когда-либо делали дрожание камеры, это отличный доклад на GDC от Белки Эйзерло. Использование симплексного шума имеет то же преимущество, что и синус, в том, что вершины рядом друг с другом имеют одинаковые значения, поэтому форма чашки лучше сохраняется, но также есть достаточно случайности в образце, так что глаз не может воспринимать узор, движущийся через чашки, и когда мы уменьшаем амплитуду, как показано ниже, создается впечатление, что модель была сформирована вручную с дефектами. Позже мы объединим это с техникой анимации, но это первый этап.

Итак, как нам это реализовать? Есть два распространенных способа сэмплирования симплексного шума. Первый - алгоритмический - есть хорошее изучение этого здесь и довольно хорошо оптимизированная библиотека hlsl, которую вы можете использовать прямо в своих шейдерах.

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

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

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

Приближаемся к ощущению анимации «нарисованного от руки»

Разобравшись с подходом смещения вершин, мы хотим использовать его, чтобы оживить объекты, чтобы они выглядели живописно. Чтобы достичь этого, нам не нужно постоянное движение, так как оно заставляет все чувствовать себя плавно, поэтому мы запускаем смещение вершин с фиксированной частотой кадров (для Assemble мы запускаем смещения нашей модели и смещения текстуры uv все со скоростью 5 кадров в секунду). Сделать это в шейдере очень просто. В предыдущем фрагменте шейдера SimplexWobble мы проходили через inTime, и для вычисления этого значения при заблокированной частоте кадров мы используем:

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

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

Здесь я оставил старую версию Assemble работать на iPad на ночь, а вернувшись на следующий день, вы можете увидеть серьезные проблемы с пикселизацией.

Если вы используете Time.y в своих шейдерах, вам стоит подумать о том, чтобы со временем заменить его чем-нибудь стабильным. Для Assemble мы фактически переключились на то, чтобы скрипт вычислял пошаговое время цикла каждую секунду с помощью Time.deltaTime и передавал его в наши шейдеры с помощью Shader.SetGlobalFloat

Применение мазка кистью

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

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

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

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

Чтобы добиться ощущения движения и кипения, как при колебании вершины, во фрагментном шейдере мы сдвигаем изменяющиеся UV на значение Stepped Time, которое мы вычислили ранее. Это означает, что положение UV немного отличается каждые 1/5 секунды, а текстурирование смещается синхронно со смещением вершины, так что они сливаются вместе.

В шейдере это действительно просто, мы добавляем steppedTime, вычисленное так же, как показано ранее, к UV, передаваемым в сэмплер текстуры кисти. Затем мы перемещаемся между этим и основным диффузным цветом, чтобы получить окончательный результат.

Тональное отображение

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

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

Подход тонкий, но эффективный, вот сравнение, показывающее сочетание этих трех техник.

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

Итак, чтобы выделить различные компоненты этой техники.

Обесцвечивание. Все, что находится на полу, мы возвращаем к оттенкам серого.

Минимальный уровень черного - вместо обесцвечивания в сторону реального черного, мы обесцвечиваем его в сторону серого (ограничивая, насколько близко могут быть цвета # 000000), это позволяет нам визуально сопоставить части на полу с фоновым тоном, ударяя вернуть их визуальный приоритет.

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

Что касается кода шейдера, мы запускаем его во фрагментном шейдере после того, как на предыдущих шагах был задан текстурированный диффузный цвет кисти:

Яркость - это встроенная функция шейдера единства, которая преобразует значение цвета в оттенки серого.

Наша функция «RemapLuminanceLevels» переназначает значения цвета из одного диапазона (от белого к черному) в другой (от белого к серому), мы переназначаем число с плавающей запятой, подобное этому, с помощью следующего hlsl.

Тени и цвет

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

На полу тени, отбрасываемые на эту поверхность, имеют этот поразительный розово-фиолетовый градиент, где свет падает на эту поверхность, тона более светлые, персиковые и не переходят вниз по кадру. Вот текстура тени и текстура света, которые мы используем для этого.

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

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

Собираем все вместе

Здесь вы можете увидеть, как методы, которые мы использовали в этой статье, объединяются шаг за шагом, создавая окончательный визуальный эффект, который вы можете увидеть во время игры в Assemble With Care. Хотя каждый из этих шагов по отдельности относительно прост, сочетание и балансирование каждого из них друг с другом в конечном итоге привело нас к живописному художественному стилю, которым мы были действительно довольны.

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

Спасибо за прочтение!

Мэтт @Matthew_C_Newcombe