[Перевод] Как создать процедуральный арт менее чем за 100 строк кода
Generative art (генеративное или процедуральное искусство) может отпугнуть, если вы никогда с ним раньше не сталкивались. Если коротко, то это концепция искусства, которое буквально создает само себя и не требует хардкорных знаний программирования для первого раза. Поэтому я решил немного разбавить нашу ленту, погнали.
Что такое генеративное искусство?
Это результат системы, которая принимает свои собственные решения о предмете вместо человека. Система может быть такой же простой, как одна программа на Python, если у нее есть правила и момент случайности.
С программированием довольно просто придумать правила и ограничения. Для этого есть условные операторы. Но найти способы заставить эти правила создавать что-то интересное может быть не так просто.
Conway«s Game of Life
Игра «Жизнь» (Conway«s Game of Life) — это известный набор из четырех простых правил, определяющих «рождение» и «смерть» каждой клетки в системе. Каждое правило играет определенную роль в продвижении системы через каждое поколение. Хотя правила просты и легки для понимания, быстро появляются сложные шаблоны, которые в конечном итоге формируют захватывающие результаты.
Правила могут быть ответственны за создание основы чего-то интересного, но даже нечто такое же захватывающее, как Conway«s Game of Life, предсказуемо. Четыре правила — это определяющие факторы для каждого поколения. Поэтому, чтобы получить непредвиденные результаты, нужно ввести рандомизацию в начальном состоянии ячеек. Начиная со случайной матрицы, каждое выполнение будет уникальным без необходимости изменения правил.
Лучшие примеры генеративного искусства — те, которые находят сочетание предсказуемости и случайности для создания чего-то интересного, что статистически невозможно повторить.
Почему вы должны это попробовать?
Генеративное искусство не всегда будет тем, на что вы захотите тратить время. Но если вы решите поработать над ним, то можете рассчитывать на следующие преимущества:
- Опыт. Генеративное искусство — это еще одна возможность отточить новые и старые навыки. Оно может служить входом для отработки таких понятий, как алгоритмы, структуры данных и даже новых языков.
- Ощутимые результаты. В программировании мы редко видим физические результаты наших усилий. Ну или, по крайней мере, я этого не вижу. Прямо сейчас у меня в гостиной висит несколько постеров с принтами моего процедурального арта. И мне нравится, что это сделано кодом.
- Привлекательные проекты. У всех был опыт объяснения личного проекта кому-то, возможно, даже во время собеседования. Генеративный арт говорит сам за себя. Большинство людей оценят результаты, даже если они не смогут полностью понять методы.
С чего начать?
Начало работы с генеративным искусством — такой же процесс, как начало работы с любым другим проектом. Самый важный шаг — придумать идею или найти ее для дальнейшего развития. Как только у вас есть цель, то можно начинать работать над ее достижением.
Большинство моих творческих проектов выполнены на Python. Это довольно простой язык с множество полезных пакетов, которые помогают в обработке изображений. Например, Pillow.
К счастью, вам не нужно долго искать с чего начать — ниже я поделюсь своим кодом.
Генератор спрайтов
Этот проект начался, когда я увидел пост с генератором спрайтов, написанным на JavaScript. Программа создавала 5×5 пиксель-арт спрайты со случайными вариантами цветов, а ее результат напоминал разноцветных космических захватчиков.
Я хотел попрактиковаться в обработке изображений на Python, поэтому решил воссоздать эту концепцию самостоятельно. Кроме того, я подумал, что могу расширить ее, так как исходный проект был сильно ограничен в размере спрайтов. А я хочу указывать не только размер спрайтов, но и их количество и даже размер изображения.
Вот два разных результата моей программы:
7×7–30–1900
43×43–6–1900
Эти два изображения совсем не похожи друг на друга, но они оба являются результатами одной и той же системы. Не говоря уже о том, что из-за сложности и случайной генерации спрайтов существует высокая вероятность, что даже при одинаковых аргументах эти изображения навсегда останутся единственными в своем роде. Обожаю это.
Окружающая среда
Перед знакомством с генератором спрайтов стоит подготовить небольшой фундамент для работы.
Если вы раньше не работали с Python, то скачайте Python 2.7.10. Сначала у меня были проблемы с настройкой среды, если вы тоже с ними столкнетесь — посмотрите в виртуальные среды. И убедитесь, что Pillow тоже установлен.
После настройки среды можете скопировать мой код в файл с расширением .py и выполнить следующую команду:
python spritething.py [SPRITE_DIMENSIONS] [NUMBER] [IMAGE_SIZE]
Например, команда для создания первой матрицы спрайтов будет:
python spritething.py 7 30 1900
Код
import PIL, random, sys
from PIL import Image, ImageDraw
origDimension = 1500
r = lambda: random.randint(50,215)
rc = lambda: (r(), r(), r())
listSym = []
def create_square(border, draw, randColor, element, size):
if (element == int(size/2)):
draw.rectangle(border, randColor)
elif (len(listSym) == element+1):
draw.rectangle(border,listSym.pop())
else:
listSym.append(randColor)
draw.rectangle(border, randColor)
def create_invader(border, draw, size):
x0, y0, x1, y1 = border
squareSize = (x1-x0)/size
randColors = [rc(), rc(), rc(), (0,0,0), (0,0,0), (0,0,0)]
i = 1
for y in range(0, size):
I *= -1
element = 0
for x in range(0, size):
topLeftX = x*squareSize + x0
topLeftY = y*squareSize + y0
botRightX = topLeftX + squareSize
botRightY = topLeftY + squareSize
create_square((topLeftX, topLeftY, botRightX, botRightY), draw, random.choice(randColors), element, size)
if (element == int(size/2) or element == 0):
I *= -1;
element += I
def main(size, invaders, imgSize):
origDimension = imgSize
origImage = Image.new(‘RGB’, (origDimension, origDimension))
draw = ImageDraw.Draw(origImage)
invaderSize = origDimension/invaders
padding = invaderSize/size
for x in range(0, invaders):
for y in range(0, invaders):
topLeftX = x*invaderSize + padding/2
topLeftY = y*invaderSize + padding/2
botRightX = topLeftX + invaderSize - padding
botRightY = topLeftY + invaderSize - padding
create_invader((topLeftX, topLeftY, botRightX, botRightY), draw, size)
origImage.save(«Examples/Example-«+str(size)+»x»+str(size)+»-«+str(invaders)+»-«+str(imgSize)+».jpg»)
if __name__ == «__main__»:
main(int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3]))
Это решение еще далеко от совершенства, но оно показывает, что создание генеративного искусства не требует тонны кода. Поясню ключевые моменты.
Основная функция начинается с создания исходного изображения и определения размера спрайтов. Два цикла for отвечают за определение границы каждого спрайта, в основном разделяя размеры изображения на количество запрошенных спрайтов. Эти значения используются для определения координат для каждого из них.
Посмотрите на изображение ниже. Представьте, что каждый из четырех квадратов представляет собой спрайт с размером 1. Граница, которая передается следующей функции, относится к координатам верхнего левого и нижнего правого углов. Так кортеж в верхнем левом спрайте будет (0,0,1,1), а кортеж в верхнем правом будет (1,0,2,1). Они будут использоваться в качестве размеров и базовых координат для квадратов каждого спрайта.
Пример определения границ спрайта
Функция create_invader определяет границу для каждого квадрата внутри спрайта. Тот же процесс определения границы применяется здесь и представлен ниже, только вместо полного изображения мы используем предварительно определенную границу для работы внутри. Эти конечные координаты для каждого квадрата будут использоваться в следующей функции для рисования спрайта.
Пример разбивки спрайта 3×3
Для определения цвета используется простой массив из трех случайных RGB-кортежей и трех черных для имитации 50% вероятности быть нарисованным. Лямбда-функции в верхней части кода отвечают за генерацию значений RGB.
Настоящая хитрость этой функции — создание симметрии. Каждый квадрат сопряжен со значением элемента. На рисунке ниже показано, как значения элементов увеличиваются по мере достижения центра, а затем уменьшаются. Квадраты с совпадающими значениями элементов отображаются одним цветом.
Значения элементов и симметричные цвета для строки в спрайте 7×7
Поскольку create_square получает свои параметры от create_invader, он использует очередь и предыдущие значения элементов для обеспечения симметрии. При первом появлении значений их цвета помещаются в очередь, а зеркальные квадраты удаляют цвета.
Полный процесс создания
Я понимаю, как трудно читать чужое решение проблемы и кривой код, но надеюсь, что вы найдете этому применение. Будет круто, если вы совсем откажетесь от моего кода и найдете совершенно другое решение.
Заключение
Генеративное искусство требует времени, чтобы полностью раскрыть свой потенциал. В целом, вокруг могут быть более полезные проекты, чем генеративное искусство, на которое не всегда стоит тратить время. Но это очень весело и никогда не знаешь, где может пригодиться.