Цифровая кисть # Art4GlobalGoals: технический анализ

В моем последнем проекте с denkwerk я помог создать интерактивный веб-сайт для информационной кампании # Art4GlobalGoals и передвижной выставки с участием известного немецкого художника Леон Лёвентраут. Посетители сайта могут не только узнать о 17 глобальных целях и изучить художественную интерпретацию их Леоном, но они также могут физически пройти через каждую проблему цифровой кистью, тем самым подписав онлайн-петицию и присоединяясь к глобальному движению по повышению осведомленности о наиболее насущных. глобальные вопросы.

Великое дело, но как перевести это символическое движение в цифровое пространство? Вызов принят! При поддержке дизайнеров denkwerk я решил расширить границы творческого кодирования и создать максимально реалистичный и естественный мазок кисти, достойный такого мощного заявления. С технической точки зрения это означало сосредоточение внимания на трех основных факторах: производительности, вариациях и зачеркивании.

Производительность и разнообразие интерактивных возможностей

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

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

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

Позвольте мне представить вам техническую разбивку того, как я построил все взаимодействие за несколько дней.

Распылять и молиться?

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

Изначально я думал, что смогу использовать то, что уже сделал, для создания мазка, перенеся код из Processing (Java) в Javascript. Но немного пошалив и изменив программный код, я понял, что мне нужно создать более сложное решение для этого проекта. Видите ли, наш главный художник Леон Левентраут в своих прекрасных произведениях искусства использует сочетание мазков и тюбиков с краской, а не баллончиков с распылителем. Итак, вернемся к исходной точке.

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

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

Это был первый прорыв, который немного приблизил меня к тому, чтобы картина выглядела и казалась более реалистичной.

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

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

WebGL спешит на помощь!

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

Работайте умнее, а не усерднее, правда? Вместо того, чтобы писать необработанный код API WebGL, который является супер-скучным, низкоуровневым и очень сложным для быстрого понимания, я предпочитаю использовать хороший фреймворк, который делает все сложные вещи за меня в фоновом режиме. Это позволяет мне сосредоточиться на творческом процессе чистки зубов вместо мельчайших деталей сложного API WebGL.

Я большой поклонник выбора подходящего инструмента для работы, поэтому зашел в Интернет и начал исследовать различные фреймворки WebGL. Лучшим кандидатом оказался Pixi.js. Он предлагает доступ к API WebGL с высокоуровневой точки зрения, но сохраняет фокус на отображении 2D-графики, приложений и игр. И после небольшой неудачи ранее я был счастлив узнать, что настроить проект Pixi.js было очень легко и просто. Я мог просто скопировать / вставить свой исходный код, адаптировать его под фреймворк и наслаждаться результатом: гладкой и шелковистой кистью!

Вот фрагмент кода, который выполняется 60 раз в секунду в методе рисования:

Приправы: добавление текстуры

Довольный своим прорывом, пришло время добавить немного «остроты» к общему впечатлению, например больше настроек и более реалистичный мазок. Здесь мне пришла в голову идея добавить уровень подачи к движению мазка кисти, который позволил бы пользователям видеть, сколько краски осталось на кисти. Когда пользователь начинает чистить щеткой, уровень подачи составляет 100% (мокрая кисть), а затем в течение заранее определенного расстояния кисть линейно высыхает, пока не достигнет 0% (высохшая кисть).

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

Вы спросите, а где пряность? Хорошо, поэтому индикатор уровня предложения на самом деле ничего не меняет визуально, но у него есть важное функциональное применение: мы можем использовать его в качестве ловушки для динамического изменения других параметров с течением времени, потому что он постепенно изменяется от 100% до 0%.

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

Еще одна небольшая настройка - масштабирование кисти вниз по пройденному пути. Когда настоящая кисть касается «холста», оно становится довольно широким, потому что все щетинки борются за место на поверхности, выталкивая друг друга наружу. Затем, когда уровень подачи истощается и маляр начинает снимать кисть с поверхности, масштаб все больше нормализуется, пока, наконец, не достигнет окончательного размера, который меньше, чем когда он впервые попал на поверхность.

Чтобы добавить немного больше текстуры и неравномерности, я реализовал небольшую проверку, которая случайным образом выбирает процентное значение от 0% до 100%, а затем полностью пропускает рендеринг в 25% случаев. Это нерегулярное тиснение создает красивый прерывистый рисунок несовершенства.

Последний шаг: обнаружение зачеркивания

После создания цифровой кисти, которая выглядела и ощущалась как настоящая кисть, оставалась еще одна задача для завершения опыта: обнаружение, когда пользователь очищает проблему. Теперь есть множество способов сделать это. Для некоторых из них могут потребоваться часы и часы программирования, но зачем тратить время, если мы можем найти решение, применимое к 95% всех случаев?

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

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

Этот прорыв сразу привел к отличным результатам. После настройки нескольких отдельных параметров я выбрал 20% в качестве минимального порога количества точек, которые должны были попадать в ограничивающую рамку. Используя уровень слива воды, я решил проверить, прошел ли пользователь установленное минимальное расстояние, чтобы отфильтровать некоторые крайние случаи. Пользователь должен был очистить щеткой не менее 25% уровня подачи, чтобы сработала логика обнаружения.

Заключение

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

Посетите сайт проекта по адресу https://art4globalgoals.com.