Генерируем пиксельные изображения с помощью PHP (генератор аватарок и пиксельных городов)

Генерируем пиксельные изображения с помощью PHP (генератор аватарок и пиксельных городов)

Последние несколько недель я экспериментировал с генеративным искусством, используя PHP. Генеративное искусство - это создание произведений искусства с помощью программирования. У генеративного искусства есть разные названия, такие как процедурное искусство или креативное кодирование.

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

Я выбрал PHP, по нескольким причинам. Во-первых, потому что знаю его довольно хорошо и могу быстро писать код на нём, а во-вторых (и это самое главное), потому что с его помощью я сразу же могу сохранять изображения на жестком диске. Ещё, ввиду того, что все мои проекты написаны на PHP, то это так же позволяет легко интегрировать подобные скрипты в уже существующие мои проекты.

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

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

Таким образом, работы, которые я создал, - это пиксель-арт, вдохновленный ретро-играми. На данный момент я создал 2 работы - скрипт генерации пиксельных аватаров и генерация целых изометрических пиксельных городов, и это всё на PHP.

article-board

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

Дисклеймер

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

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

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

Генерация пиксельных аватаров

Скрипт генерации аватарок работает максимально примитивно. Я просто создал различные заготовки слоёв для всех частей аватара (фоновый цвет, глаза, рот), а затем использовал PHP для наложения слоёв различных частей, выбирая случайный слой для каждой из группы.

avatars

При работе с PHP я использую библиотеку GD, которая часто идет по дефолту при установке PHP. Так же, для разработки я использовал Docker, благодаря которому вы сможете запустить этот проект у себя локально в пару команд и проверить работу этого скрипта.

Если вы ещё не знакомы с Докером, то советую прочитать отличную статью для новичков, и уже наконец-то пользоваться такой мощной штукой.

Я никак не взаимодействую с базой данных, в этом скрипте всё абсолютно случайно. Красота генеративного искусства в том, что вы можете мгновенно создавать тысячи различных изображений, и нет необходимости хранить что-то долгое время. Мне очень нравится, когда я дохожу до стадии, когда мне нравится (почти) всё, что создает алгоритм.

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

В псевдокоде процесс создания случайной аватарки выглядит так:

  • Создаём изображение (imagecreatetruecolor).
  • Получаем массив всех возможных частей для первого слоя (используя функцию glob для поиска файлов в указанной папке определённой группы).
  • Перемешиваем все найденные картинки, чтобы выбрать случайный элемент.
  • Накладываем слой на результирующее изображение (imagecreatefrompng > imagecopy).
  • Повторяем для каждого слоя.
  • Сохраняем изображение (imagepng).

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

В моём примере набор изображений составляет: 9 изображений глаз, 5 изображений рта и 7 изображений кожи - что даёт 315 уникальных комбинаций (9 * 5 * 7). Если добавить волосы, очки (и всё остальное), то можно быстро получить десятки тысяч возможных вариантов.

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

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

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

Создание заготовок для генерации изображений

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

Для создания пиксельных изображений я использую Aseprite. Aseprite - это потрясающий инструмент!

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

Я включил файлы .aseprite в репозиторий, чтобы вы могли использовать их в качестве отправной точки, если захотите создать свои собственные вариации.

Генеративное искусство на основе тайлов

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

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

buildings

Основной принцип заключается в том, что у вас есть многомерный массив (массив массивов), в котором хранится информация о различных блоках. Затем вы перебираете массивы и рендерите указанные блоки на картинке.

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

Для изометрического города код выглядел примерно так:

  • Создаём вложенные массивы города.
  • Решаем, где что находится, используя некоторые простые алгоритмы, и обновляем массивы города соответствующим образом (нужно поместить дороги, парки, реки).
  • Нужно пройтись по массивам и отрисовать соответствующие тайлы. Если на карте есть место для здания, создаём случайное здание и рисуем его поверх тайла.
  • Рендерим всё на изображение.

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

Помимо расположения тайлов, при изометрическом рисовании необходимо следить за тем, чтобы всё рисовалось задом наперёд, чтобы передние объекты закрывали задние. Из-за этого я рисую всё, что связано с каждым тайлом, за один раз. Я рисую тайл, затем рисую декорацию (деревья, людей и т.д.), затем рисую здание. Если бы я нарисовал их по отдельности, они могли бы наложиться не так, как предполагалось.

Создание зданий

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

Пример сетки

Код примера для демонстрации сетки немного длиннее, чем для демонстрации пака изображений. Большая часть дополнительного кода используется для прокладки дорог и парков.

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

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

Процесс создания тайлов города выглядит примерно так:

Создаём базовое изображение и подгружаем объекты.

  • Инициализируем массив мира.
  • Добавляем дороги.
  • Добавляем парки.
  • Рисуем подготовленные данные.
  • Сохраняем в изображение.

Изучить исходный код вы можете на Github.

Дороги и парки

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

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

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

Что еще?

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

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

В этой статье были приведены примеры работы с GD на PHP и реальный код по работе с графикой на примере генеративного искусства.

Перевод статьи: Creating Generative Art with PHP