[Программист анимаций компании Remedy Хенрик Энквист рассказал, как его команда создала убедительную симуляцию твидового пиджака главного героя игры в жанре хоррор-триллер Alan Wake.]
Главный персонаж нашего экшн-триллера — Алан Уэйк, писатель, попавший в кошмар, где он вынужден сражаться с тёмными силами и решать загадку исчезновения жены. Он не хорошо подготовленный герой боевика, а обычный человек.
Чтобы подчеркнуть характер персонажа, наш арт-директор хотел одеть его в старый твидовый пиджак с заплатами на локтях. Действие игры происходит в антураже реального мира, поэтому в отличие от фэнтезийной игры или космического шутера персонажи ограничены в применяемых инструментах. А это значит, что одежда наших персонажей становится гораздо более важной.
Чтобы передать иллюзию атмосферы триллера, пиджак Алана Уэйка должен быть как можно более правдоподобным. Пиджак должен развиваться на ветру и добавлять персонажу вспомогательные движения при перемещении по лесу. Как программист, я сразу же начал думать об использовании симуляции ткани.
Симуляция тканей использовалась во множестве игр до нас, но часто применяемые там методики давали ощущение шёлка или резины — неподходящих для нас материалов. Только совсем недавно начали появляться очень хорошие системы симуляции тканей сторонних компаний, но на момент, когда нам необходимо было стабильное решение, таких инструментов ещё не существовало, или они не отвечали нашим потребностям.
В этой статье я расскажу о проблемах, с которыми нам довелось столкнуться, и о решениях для создания нашей собственной симуляции тканей.
Риг пиджака
Пиджак моделировался вместе с остальной частью персонажа как обычный меш со скиннингом. Кости, управляющие мешем пиджака — это отдельный слой поверх обычного скелета. Рукава пиджака используют обычную схему для плеча и предплечья. И плечи, и предплечья разделены на одну основную кость и одну кость изгиба. Верхняя часть пиджака управляется привязками look-at constraints, а нижней частью управляет симуляция Верле.
Рисунок 1. Риг пиджака поверх обычного игрового скелета.
Верхняя часть пиджака
Кости пиджака имеют идущую сверху вниз иерархию (нижние являются дочерними элементами верхних), поэтому когда верхние кости движутся, нижние кости следуют за ними. У нас было искушение сделать нижние кости дочерними непосредственно к грудной клетке, но это бы вызвало потерю движения, особенно вертикального движения, когда персонаж приподнимает плечи.
В верхней части пиджака мы имитируем движение накладок на плечах, двигая кости надплечий при помощи look-at constraints по направлению к костям плеч. Благодаря этому накладки следуют за плечом, а при поднятии руки накладка поднимает остальную часть костей, как в настоящем пиджаке.
Look-at costraint применено к красному конусу
Следующими костями в цепочке является слой между верхней частью пиджака и симулируемой нижней частью. Эти кости управляются look-at constraints напрямую вниз для компенсации поворота, который создаётся плечами. Также мы добавили position constraints между левой и правой костями, чтобы компенсировать растяжение, возникающее при движении накладок на плечи.
Рисунок 2. Движение костей при поднимании руки персонажем.
Этого могло быть вполне достаточно для реализации ограничений в экспортере анимаций и запекания результатов в данные анимаций, но мы всё равно стремились управлять костями в игровом движке в реальном времени.
Благодаря этому мы могли бы сэкономить несколько байтов в данных анимаций, а также с лёгкостью переносить анимации между персонажами вне зависимости от того, есть ли на них пиджаки. Кроме того, движения надплечья, генерируемые игровой инверсной кинематикой (например, в момент прицеливания) при разрешении ограничений в реальном времени применялись бы правильно.
Нижняя часть пиджака
Решив задачу с верхней частью пиджака, мы перешли к симуляции нижней части. В большинстве игровых симуляций тканей используется привязка один к одному между вершинами в симуляции ткани и вершинами отрендеренного меша.
Мы же хотели сохранить точность меша пиджака, чтобы ему не мешали никакие определяемые программистом ограничения. Например, если бы мы решили использовать для симуляции ткани тот же меш, что и для рендеринга, то силуэт карманов и передней части пиджака был бы утерян.
Для придания пиджаку объёма можно было бы использовать карты нормалей, но мы чувствовали, что этого будет недостаточно. Мы хотели, чтобы наши художники моделировали пиджак так, как им этого хотелось, а затем позволить им использовать карты нормалей для добавления складок или других деталей, вместо того, чтобы возмещать утерянную геометрию.
Мы пришли к такому решению: создать меш ткани низкого разрешения для симуляции пиджака, а затем привязать его к костям скелета, используемого для управления мешем со скиннингом.
Рисунок 3. Сравнение силуэтов нашего пиджака и ткани, имеющей одинаковые вершины с симуляцией.
Физика Верле
Сначала мы рассмотрим физику Верле, а затем узнаем, как создать соответствие симуляции костям. В настоящее время физика Верле является стандартным решением для симуляции тканей в играх. Если вам незнакома методика Верле, то для начала рекомендую прочитать одну из этих статей на Gamasutra: Devil in the Blue Faceted Dress: Real Time Cloth Animation или Advanced Character Physics.
Рисунок 4. Сетка вершин 4x4 и ограничения (constraints) для одной из вершин.
Для остальных я вкратце повторю принцип работы. На рисунке 4 показан меш ткани и пружинные ограничения для одной из его вершин. Как видно из рисунка, каждая вершина меша соединена со всеми соседними вершинами, а также с их соседями.
Ограничения от непосредственных соседей называются ограничениями растяжения (stretch constraints) и обозначены синим. Длинные ограничения, обозначенные красным, называются ограничениями наклона/изгиба (shear/bend constraints).
Важно хранить эти ограничения в двух группах, потому что позже мы будем разрешать их с разными параметрами. Учтите, что в нашем пиджаке верхний ряд точек ткани привязывается скиннингом к персонажу и не будет управляться симуляцией.
Наличие меша в виде сетки не является обязательным требованием самого алгоритма, однако для симуляции ткани с такой топологией работать проще всего. Фундамент симуляции ткани состоит из двух частей. Первой частью является интегрирование Верле, в котором мы вычисляем для каждой вершины скорость и применяем её к позиции.
Vector3 vVelocity = vertex.vCurrentPosition - vertex.vPreviousPosition;
vertex.vPreviousPosition = vertex.vCurrentPosition;
vertex.vCurrentPosition += vVelocity * ( 1.0f - fDampingFactor ) + vAcceleration * fDeltaTime * fDeltaTime;
В нашем проекте vAcceleration
задавалась суммой гравитационной силы и ветра. Затухание использовалось и для подгонки внешнего вида пиджака, и для стабилизации симуляции. Высокий показатель затухания fDampingFactor
придаёт пиджаку ощущение очень лёгкой ткани, спускающейся медленно и плавно, а малый показатель затухания делает пиджак тяжелее, заставляя его дольше раскачиваться/колебаться после движения.
Вторая часть алгоритма заключается в разрешении пружинных констант (spring constraints) (этот процесс называется релаксацией). Для каждого ограничения мы притягиваем или отталкиваем вершины друг от друга, чтобы они удовлетворяли своим исходным длинам. Вот фрагмент кода в читаемом виде.
Vector3 vDelta = constraint.m_vertex1.m_vCurPos - constraint.m_vertex0.m_vCurPos;
float fLength = vDelta.length();
vDelta.normalize();
Vector3 vOffset = vDelta * ( fLength - constraint.m_fRestLength );
constraint.m_vertex0.m_vCurrentPosition += vOffset / 2.0f;
constraint.m_vertex1.m_vCurrentPosition -= vOffset / 2.0f;
Ограничения растяжения удерживают вершины ткани вместе, а ограничения наклона/изгиба помогают сохранить форму ткани. Как видите, при идеальном решении этой системы ткань будет двигаться слишком жёстко. Именно поэтому перед разрешением новых позиций мы добавляем к ограничениям наклона/изгиба коэффициент.
vOffset *= fStiffness;
constraint.m_vertex0.m_vCurrentPosition += vOffset / 2.0f;
constraint.m_vertex1.m_vCurrentPosition -= vOffset / 2.0f;
При коэффициенте жёсткости 1.0 ткань будет непластичной, а при 0.0 ткань будет изгибаться без всяческих ограничений.
Фиксированный шаг времени
Должно быть, вы уже заметили, что интегрирование Верле предполагает, что предыдущий шаг времени был точно таким же, как и текущий; в противном случае вычисленная скорость будет неправильной. При использовании интегрирования Верле можно обойтись переменным шагом времени, но разрешение ограничений очень чувствительно к изменениям шага времени.
Так как солвер решает задачу, итеративно обходя ограничения, их никогда невозможно будет разрешить идеально. В игре эта неточность будет проявляться в виде растяжения, и чем короче шаг времени, тем меньше растяжения увидит игрок.
В конечном итоге это будет компромиссом между точностью и количеством времени процессора, которое вы можете потратить на одежду. Если шаг времени непостоянен, то растяжение одежды будет варьироваться, и мы привнесём в систему нежелательные колебания. Ещё важнее то, что шаг времени будет влиять на показатель жёсткости и другие параметры ткани: чем короче шаг времени, тем более жёсткой будет ткань, даже при использовании одинакового коэффициента жёсткости.
На практике это означает, что прежде чем начать настраивать при помощи параметров ткани внешний вид одежды, сам придётся определиться с фиксированным шагом времени. Я знаю, что существуют игры, в которых для физики используется переменный шаг времени, но мой личный опыт говорит мне, что жизнь становится намного проще, когда шаг времени и для физики, и для игровой логики фиксирован.
Капюшон
Прежде чем мы перейдём к подробностям симуляции ткани, взглянем вкратце на то, как симулируется капюшон. Для скиннинга вершин меша капюшона мы использовали дополнительную кость. Мы создали маятник от центра кости до позиции за капюшоном. Конец маятника — это одна частица, управляемая физикой Верле. Затем при помощи look-at constraint кость направляется в сторону маятника.
Рисунок 5. Капюшон и маятник.
Создание матриц костей
Капюшон даёт нам подсказку о том, что нужно делать далее с нижней частью пиджака. Мы будем использовать позиции вершин в симулируемом меше для вычисления преобразований костей.
Первое, что мы делаем — сопоставляем кости так, чтобы шарнир каждой кости соответствовал вершине симулируемого меша. Благодаря этому задание части матрицы, относящейся к перемещению, окажется тривиальным процессом.
Затем нам нужно вычислить матрицу поворота 3x3. Каждая строка (или столбец, в зависимости от конфигурации матрицы) задаётся осями x, y и z кости.
Мы задаём ось x кости как направление от базовой вершины к следующей под ней. Затем ось y задаётся вектором от вершины слева до вершины справа.
Рисунок 6. Кости, прикреплённые к мешу ткани.
На рисунке 6 ось x показана красным, а ось y — зелёным. Затем вычисляется ось z как векторное произведение этих векторов. В конце мы также ортонормируем матрицу, чтобы избавиться от искажения в данных перемещения.
Как видите, в вертикальном направлении мы используем для настройки костей каждую строку меша ткани (за исключением последней), но в горизонтальном используется только каждый второй столбец. Кроме того, что это даёт описанные выше художественные преимущества, этот метод ещё и довольно быстр. Благодаря этому традиционные техники скиннинга можно применять на стороне GPU для рендеринга меша, потому что в противном случае нам приходилось бы обновлять огромный динамически буфер вершин.
Меш ткани может иметь довольно малое разрешение, что снижает нагрузку на ЦП. Единственные дополнительные расходы нашего решения — это преобразование симуляции низкого разрешения в меш высокого разрешения, но в нашей схеме эти расходы будут ничтожными по сравнению с остальной частью симуляции.
Коллизии
Для решения проблемы усечения ткани ногами и телом мы используем распознавание коллизий между эллипсоидом и частицей. На рисунке 7 показаны эллипсоиды, необходимые для разрешения усечения пиджака моделью персонажа.
Рисунок 7. Система эллипсоидов для модели Уэйка.
Распознавание коллизий эллипсоидов с частицами выполняется очень быстро. Коллизии можно решать преобразованием пространства, в котором существуют эллипсоид и частица, благодаря чему эллипсоид превращается в сферу. Затем можно выполнить быстрый тест коллизии сферы и частицы.
На практике это сопровождается созданием обратного преобразования на основании значений длины, ширины и высоты эллипсоида с применением его к позиции частицы. Единственная проблема заключается здесь в том, что нормаль коллизии, которую мы получаем после преобразования обратно в исходную систему координат, оказывается искажённой.
Мы решили, что можем смириться с небольшой неточностью при вычислении направления коллизии. В случаях, когда сильно растянутый эллипсоид мог бы вызвать неверные реакции, мы разделяли его на два более однородных.
Максимальное расстояние до частицы
Ещё одна проблема, которую нужно было решить — это стабильность пиджака. Ткань при быстром движении могла вызывать создание узлов или оказываться на другой стороне объёмов коллизий и проходить сквозь тело. Мы решили эту проблему, задав для каждой вершины симулируемой ткани безопасное расстояние.
Для каждой вершины исходное положение покоя скиннингом прикрепляется к ближайшей кости и мы используем её как опорную точку. Если симуляция превышает пороговое значение, то мы просто сдвигаем вершину ближе к опорной точке. В нашей схеме мы позволили вершинам внизу двигаться на большее расстояние, чем вершины ближе к надплечьям.
Максимальное расстояние, на которое мы можем позволить двигаться вершинам — около 40 см, при превышении этого значения начинают проявляться редкие случаи узлов и усечения. Также мы пробовали использовать и другие техники, например, плоскости коллизий, но метод максимальных расстояний оказался наилучшим. Он был быстрым, простым в настройке и обеспечивал наибольшую свободу перемещений до того, как в ткани начинали появляться заметные ошибки.
Больше твида, меньше резиновости
Пока нам удавалось находить хорошие способы достижения своих целей. Наш художник смоделировал пиджак так, как ему нравилось; для анимирования пиджака не нужен был аниматор, потому что всё симулировалось в игре, а процессор был доволен тем, что у нас хватало ресурсов и на другие внутриигровые вычисления. Но нас беспокоило одно — ткань выглядела как резина.
Боремся с растяжением
Во-первых, нам нужно избавиться от растяжения. Как я говорил выше, явление растяжения вызывается ошибками, появляющимся вследствие итеративной природы алгоритма. Это популярная тема для исследований и можно найти множество методов решения этой проблемы.
К сожалению, все имеющиеся решения заставили бы нас выделить на расчёты ткани намного больше дефицитных ресурсов ЦП. Поэтому мы решили проблему растяжения, добавив к симуляции ткани последний этап, на котором применяются так называемые «жёсткие ограничения» (hard constraints).
Мы сделали жёсткие ограничения ограничениями растяжения (все они направлены вертикально). Эти ограничения были отсортированы сверху вниз, чтобы ограничения рядом с плечами разрешались до ограничений возле ног.
Так как мы выполняем итерации ограничений сверху, то знаем, что верхняя вершина в паре уже решена и не вызывает никакого растяжений, поэтому нам достаточно только подвинуть нижнюю вершину в сторону верхней. Благодаря этому мы можем быть уверены, что после единственной итерации длина сверху донизу будет точно такой же, как длина в положении покоя.
Vector3 vDelta = constraint.m_vertexTop.m_vCurPos - constraint.m_vertexDown.m_vCurPos;
float fLength = vDelta.length();
vDelta.normalize();
Vector3 vOffset = vDelta * ( fLength - constraint.m_fRestLength );
constraint.m_vertexDown.m_vCurrentPosition += vOffset;
Рисунок 8. Жёсткие ограничения.
Как видите, мы не принимаем во внимание горизонтальное растяжение пиджака. Невозможно применить жёсткие ограничения к горизонтальному направлению, потому что при этом вершина будет разрешаться дважды, то есть мы потеряем результаты этапа вертикальных вычислений и длина ткани в покое не сохранится.
Однако мы заметили, что в случае с пиджаком растяжение по горизонтали на самом деле остаётся незаметным для человеческого глаза, а из-за вертикального растяжения пиджак выглядит очень плохо. Такое решение оказалось достаточно хорошим.
Края пиджака
Во-вторых, мы хотели, чтобы края пиджака двигались чуть больше, чем остальная его часть. Например, если вы побежите в распахнутом пиджаке, то заметите, что сопротивление воздуха сильнее влияет на края пиджака, чем на центральную часть. Так происходит потому, что ваше тело закрывает от ветра остальную часть пиджака.
Края можно легко найти по количеству прикреплённых к ним ограничений. Любая вершина, у которой меньше четырёх ограничений растяжения, является краем. Поэтому мы можем пометить эти вершины и симулировать их с другими параметрами.
- Сниженный показатель затухания.
- Глобальный ветер оказывает большее влияние.
- Движение в мировом пространстве оказывает большее влияние (подробнее о движении в мировом пространстве см. ниже).
- Допустимое максимальное безопасное расстояние выше.
Благодаря этому внутренняя частота краёв будет отличаться от остальной части пиджака. Теперь весь пиджак не реагирует на импульсы как большой маятник, и только края добавляют к движению красивое вспомогательное движение.
Рисунок 9. Вершины краёв.
Движение в мировом пространстве и в локальном пространстве
Затем мы заметили, что при перемещении персонажа движение в мировом пространстве оказывает довольно большое влияние на симуляцию, в то время как мелкие локальные повороты тела или движения надплечий остаются незамеченными.
В традиционной симуляции ткани позиции вершин симулируются в мировом пространстве. Кто-то может сказать, что так симулировать ткань правильно, но оно ощущается неестественно. Потому мы симулировали пиджак на персонажах в в локальном пространстве и отдельно добавляли небольшое движение в мировом пространстве. Мы заметили, что нужные нам результаты получаются при 100% локальной анимации скелета с 10-30% движения в мировом пространстве.
Трение
И, наконец, мы хотели преувеличить контраст между пиджаком в медленном и быстром движении. Мы хотели, чтобы при ходьбе пиджак был относительно неподвижным, а когда Алан прыгает или уклоняется, движение должно быть более живым.
Мы подумали, что когда пиджак касается тела, он должен двигаться меньше из-за трения между пиджаком и рубашкой, а когда пиджак поднимается, он должен двигаться сильнее, потому что его ничто не ограничивает. Мы имитировали это, применив к каждой касающейся эллипсоида вершине повышенное значение затухания. Благодаря этому вершины, касающиеся тела, будут казаться немного липкими, создавая достаточный контраст между пиджаком в обычной ситуации и в быстром движении.
Заключение и дальнейшая работа
Первое воплощение симуляции ткани реализовать было довольно просто: мы всего лишь поискали слово «ткань» в литературе по разработке игр и применили найденные алгоритмы. Второй этап, на котором мы пытались добиться убедительного ощущения твидового пиджака, потребовал изучения научных статей, множества проб и ошибок и даже удаления части кода.
Разумеется, всегда можно что-то улучшить. Например, использование симуляции низкого разрешения и привязка её к мешу высокого разрешения усложняет решение проблемы всех усечений. На другие мелкие детали нам не хватило времени: например, это карты складок на местах сгибов пиджака или реализация правильного взаимодействия пиджака с торнадо.
В конечном итоге, наши усилия оправдали себя — наша ткань сильно отличается от симуляции тканей в других играх. Она выглядит гораздо больше похожей на твид, чем на шёлк или резину. Кроме того, наша система оказалась очень гибкой и позволила симулировать другие ткани, например, пуховик Барри Уиллера и вуаль старой леди. Похоже, что благодаря настройке параметров можно достичь симуляции и других видов ткани.
Рисунок 10. Твидовый пиджак.
Автор: PatientZero