← На главную

Фото в живопись: интерактивный генератор на OpenGL выложили на GitHub

05.08.2026 13:34 · hackernews

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

Мазок он решил представить как 2D Gaussian splat: эллипс с центром цвета, затухающий к краям, повёрнутый и вытянутый вдоль направления кисти. Уже есть похожие проекты DrawingWithGaussians и 2d-gaussian-splatting-Art, но они работают через градиентный спуск: разбрасывают случайные пятна и подгоняют их под картинку. Это медленно, а результат выглядит просто как искажённая реконструкция исходника, а не как живопись. Автор пошёл другим путём: извлекает из изображения края и текстуры и по ним расставляет мазки.

Сначала он использовал аддитивное смешивание, но при тысячах пятен цвета переполнялись и давали белые пятна. Пришлось перейти на стандартный over-operator из альфа-композитинга.

Чтобы мазки ложились вдоль контуров, он использует структурный тензор. Собственные векторы показывают направление градиента и направление вдоль края, а когерентность определяет, насколько мазок вытянут. Для цветных краёв той же яркости, например красных губ, одного яркостного градиента мало, поэтому он применяет Di Zenzo color tensor — складывает градиенты по каждому RGB-каналу.

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

Регулярная сетка всё равно выдаёт артефакты. Автор переходит на случайные позиции из Wang avalanche hash — без периодичности, но с правильным перемешиванием, чтобы не было диагональных полос.

Чтобы убрать слишком ровную механистичность, он добавляет Perlin noise. Два канала шума образуют плавное векторное поле, и направление мазка смешивается с ним в зависимости от когерентности: сильные края остаются точными, плоские области получают органичные изгибы. Правда, общий шум создаёт регулярный волнообразный узор — нужно было сдвигать фазу для каждого мазка.

Дальше автор выстраивает многослойную живопись: сначала крупные непрозрачные мазки, затем более мелкие и прозрачные, и наконец тонкие цепочки гауссовых сегментов, которые тянутся вдоль краёв. Размер и прозрачность к концу цепочки уменьшаются, чтобы имитировать подъём кисти. Цвета берутся с одной стороны границы, чтобы края оставались чистыми.

На CPU рендер занимал 55–112 мс, но для детализированной картины нужны сотни тысяч мазков, и Clojure-аллокации не справляются. Автор перенёс генерацию в OpenGL: geometry shader проверяет кандидатов по карте деталей, делает расчёт и передаёт выжившие мазки через transform feedback. Так всё работает интерактивно.

В итоге эксперимент удался. Программа доказывает, что по краям и текстурам можно восстановить структуру изображения так же, как художник решает, куда класть мазки. Заодно проект стал испытанием для библиотек Jolt и Glimmer. Код открыт на GitHub.

Читать оригинал →