Эксперименты с AR: когда C# встречается с CSS

в 5:45, , рубрики: AR, Augmented reality, lyrics, unity, unity3d, видео, дополненная реальность, разработка игр, Разработка под AR и VR
Эксперименты с AR: когда C# встречается с CSS - 1

Часто при работе над проектом самые большие технические сложности возникают тогда, когда меньше всего этого ожидаешь. В моём случае это произошло, когда я работала с Google Creative Lab над прототипом эксперимента по переносу песни Грейс Вандервол Moonlight в дополненную реальность. Нам понравилась идея окружения зрителя красивым написанным от руки текстом песни, который будет раскрываться и парить в пространстве при движении по нему.

Наш AR-текст песни в реальном мире

Эксперименты с AR: когда C# встречается с CSS - 2

Я была кодером проекта; когда я приступила к прототипу, то мне казалось, что самой сложной частью будет AR. Расположение и сохранение стабильного положения объектов в AR — это задача для огромного объёма сложных вычислений, правда? На самом деле, благодаря ARCore, взявшему на себя основные сложности, всё оказалось достаточно тривиальным. Однако с созданием анимированного эффекта рукописного текста в 3D-пространстве всё оказалось не так просто.

В этом посте я расскажу о некоторых хитрых хаках, которые я использовала для решения задачи анимирования нашего двухмерного рукописного текста в 3D-пространстве с помощью компонента Unity LineRenderer, обеспечивающего достаточную скорость даже при наличии тысяч точек.

Концепция

Когда мы размышляли о возможных музыкальных экспериментах, которые можно провести в AR, нас заинтриговала популярность видеороликов с текстом. Эти видео с текстом, далёкие от простого караоке-стиля, выглядят потрясающе, а для их производства иногда требуется столько же усилий и эстетических деталей, как и для других музыкальных видео. Возможно, вы видели клип Кэти Перри Roar с эмодзи, или видео Тейлор Свифт в стиле дизайна Сола Басса для песни Look What You Made Me Do.

Мы наткнулись на текстовые видео Грейс Вандервол и нам понравился их стиль. В таких видео часто используется её рукописный текст, поэтому мы задались вопросом: сможем ли мы создать видео с рукописным текстом, но при этом встроить его в AR? Что, если песня будет сама писать себя параллельно с пением вокруг зрителя — но не изображениями, а изящными, парящими, похожими на трёхмерные линиями?

Мы мгновенно влюбились в эту концепцию и решили попробовать её реализовать.

Получение точек

Для начала я проанализировала данные, которые будут необходимы нам для имитации эффекта написания текста от руки.

Мы не хотели просто располагать изображения с текстом в сцене — нам нужно было, чтобы текст давал физические ощущения, когда зритель может ходить вокруг него и наблюдать за ним. Если бы мы просто разместили 2D-изображения в 3D-пространстве, то при взгляде сбоку они бы пропадали.

Эксперименты с AR: когда C# встречается с CSS - 3

Слева и справа вверху: PNG-изображение в сцене Unity. Оно выглядит нормально, пока вы не начинаете двигаться вокруг него. Если посмотреть на него сбоку, то оно практически исчезает. Кроме того, если приблизиться к нему, оно может казаться пикселизированным.
Слева и справа внизу: данные 3D-точек, используемые для отрисовки линии в пространстве. При взгляде со стороны они сохраняют ощущение объёма. Мы можем обходить вокруг и проходить сквозь них, и они по-прежнему будут казаться 3D-объектом.

Итак, никаких изображений: мне придётся разбить изображения с рукописным текстом на данные точек, которые можно будет использовать для отрисовки линий.

Чтобы всё это выглядело как рукописный текст, мне также нужно знать, в каком порядке были отрисованы точки. При письме линии возвращаются назад и пересекают друг друга, поэтому точки слева не обязательно должны появляться раньше, чем точки справа. Но как получить эти упорядоченные данные? Мы не можем извлечь их из пиксельного изображения: разумеется, можно получить значения цветов пикселей в массиве (x,y), но это не скажет нам ничего о том, какая точка должна окрашиваться первой.

Эксперименты с AR: когда C# встречается с CSS - 4

Узрите: графика программистов! Допустим, мы пытаемся отрисовать с помощью данных слово «hello», а пунктирная линия означает точку, до которой мы добрались в текущий момент. Пример слева показывает, что произойдёт, когда мы просто будем считывать данные PNG и отрисовывать цветные пиксели слева раньше, чем справа. Это выглядит очень странно! Вместо этого мы хотим имитировать пример справа, для чего нам требуется порядок, в котором отрисовывались точки.

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

Но способен ли на это Unity?

Отрисовка точек

Насколько я знаю, у Unity нет нативной поддержки извлечения данных точек из SVG. Тем, кто хочет использовать SVG в Unity, похоже, придётся полагаться на (часто дорогие) сторонние ассеты с разным уровнем поддержки. Кроме того, эти ресурсы, похоже, более направлены на отображение SVG, а не на извлечение данных точек/контуров файлов. Как сказано выше, мы не хотим отображать SVG в пространстве: нам всего лишь нужно получить данные точек в упорядоченном массиве.

Поразмышляв над этим какое-то время, я осознала, что Unity не поддерживает SVG, зато полностью поддерживает загрузку XML через несколько стандартных классов C# (например, XmlDocument). А SVG на самом деле — это всего лишь формат векторных изображений на основе XML. Могу ли я загружать данные SVG, если просто сменю расширение с .svg на .xml?

Как ни удивительно, но ответ оказался положительным!

Поэтому мы создали такой рабочий процесс: наши художники отрисовывали текст как контуры в Illustrator, я упрощала эти контуры до прямых линий, экспортировала эти данные контуров как SVG, преобразовывала их в XML (в буквальном смысле просто меняя расширение файлов с .svg на .xml) и без малейших проблем загружала их в Unity.

Эксперименты с AR: когда C# встречается с CSS - 5

Слева: один из SVG, созданных нашими художниками.
Справа: данные XML. Каждая кривая упрощена до ломаных линий. Исходя из практических и эстетических соображений, мы решили, что все слова текста будут начинать отрисовываться одновременно.

Я сделала это, и с удовольствием увидела, что мы можем запросто загружать данные в Unity и без проблем передавать их в LineRenderer. Более того, поскольку у LineRenderer по умолчанию включен эффект биллборда, он выглядел как линия с 3D-объём. Ура! Рукописный текст в AR! Задача решена, верно? Ну, как сказать…

Реализация анимации рукописного текста

Итак, мне удалось расположить парящий в воздухе рукописный текст, и теперь мне всего лишь («всего лишь») оставалось анимировать его написание.

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

Оказывается, добавление точек в LineRenderer в реальном времени — это очень вычислительно затратная операция. Я хотела избежать парсинга сложных контуров SVG, поэтому упростила данные контуров в ломаные линии, но для сохранения кривизны контуров для этого понадобилось гораздо больше точек. У меня были сотни, а иногда и тысячи точек текста, и Unity был не очень доволен тем, что я динамически изменяю данные LineRenderer. Нашей целевой платформой были мобильные устройства, поэтому торможение было ещё серьёзней.

Итак, динамическое добавление точек в LineRenderer реализовать не удастся. Но как добиться эффекта анимации без этого?

Известно, что LineRenderer Unity — неподатливый компонент, и я, разумеется, могла избавиться от всей этой работы, купив сторонний ассет. Но, как и в случае с пакетами ассетов для SVG, многие из них представляли собой сочетание дорогих, переусложнённых или неподходящих для нашей задачи решений. Кроме того, меня, как кодера, заинтриговала эта задача, и я стремилась решить её, просто чтобы получить удовольствие от решения. Мне казалось, что должно найтись простое решение с использованием компонентов, доставшихся мне бесплатно.

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

А именно в CSS.

Анимирование точек

Я вспомнила, что читала об этой проблеме несколько лет назад в блоге Криса Вонга, где он подробно рассказывал о своём решении задачи создания NYC Taxis: A Day In The Life. Он анимировал такси, движущиеся по карте Манхэттена, но не знал, как заставить такси оставлять след на карте (в формате SVG).

Однако он обнаружил, что может манипулировать параметром линии stroke-dasharray, чтобы заставить этот эффект работать. Этот параметр превращает сплошную линию в пунктирную и по сути управляет длиной пунктирных линий и соответствующих им пробелов. Значение 0 означает, что между пунктирными линиями нет пространства, то есть они выглядят как сплошная линия. Однако если увеличить значение, то линия разобьётся на точки и линии. Благодаря реализации хитрых переходов ему удалось анимировать линию без динамического добавления точек.

Эксперименты с AR: когда C# встречается с CSS - 6

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

Кроме stroke-dasharray, кодеры CSS могут также манипулировать смещением контура stroke-dashoffset. По словам Джейка Арчибальда, stroke-dashoffset управляет тем, "где вдоль контура начинается первый «пунктир» пунктирной линии, созданной stroke-dasharray".

Что это значит? Допустим, мы будем изменять stroke-dasharray так, что цветной пунктир и пустое пространство оба растянуты на всю длину линии. При stroke-dashoffset, равном 0, наша линия будет цветной. Но при увеличении смещения мы сдвигаем начало линии всё дальше и дальше по контуру, оставляя за ним пустое пространство. И у нас получается анимированная кривая линия!

Эксперименты с AR: когда C# встречается с CSS - 7

Если увеличить до максимума значение dasharray, то мы можем использовать смещение, чтобы линия выглядела анимированной. Анимация взята из отличного интерактивного демо Джейка Арчибальда.

Однако очевидно, что в C# у нас нет stroke-dasharray или stroke-dashoffset. Но мы можем манипулировать тайлингом и смещением материала, который используется нашим шейдером. Здесь мы можем применить тот же принцип: если у нас есть текстура, похожая на пунктирную кривую, одна часть которой имеет цвет, а другая прозрачная, то мы можем манипулировать тайлингом и смещением текстуры для плавного перемещения текстуры вдоль линии — то есть выполнять переход от цветной линии к прозрачной совершенно без любых манипуляций с точками!

Эксперименты с AR: когда C# встречается с CSS - 8

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

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

Скорость и частота кадров снова взлетели до небес, и нам удалось увидеть, как AR-текст сам плавно и изящно пишет себя в реальном мире.

В реальном мире! Мы начали экспериментировать с z-индексами и текстом на разных слоях, чтобы придать ему большее ощущение нахождения в пространстве

Эксперименты с AR: когда C# встречается с CSS - 9

Надеюсь, вам понравился этот небольшой экскурс в процесс обуздания мной известного своей непреклонностью компонента Unity для реализации красивого и малозатратного эффекта. Хотите посмотреть на другие примеры изучения AR в искусстве, танцах и музыке? Посмотрите видео нашего трио. Другую информацию о наших экспериментах с AR можно найти на нашей платформе Experiments. Здесь вы можете попробовать поработать с ARCore. Удачного линейного интерполирования!

Автор: PatientZero

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js