BT – тулчейн баннерной разработки

Ранее я обещал рассказать о своем новом проекте, не связанном с D – выполняю обещание. Волею судеб несколько лет назад я стал профессиональным баннермейкером. Это разновидность фронтенд-разработки на стыке с анимационным дизайном – чаще всего я делаю HTML-баннеры и виджеты с разнообразной анимацией, эффектами и интерактивными механиками. Поскольку почти все рекламные сети имеют ограничение для баннеров по весу (и довольно серьезное – обычно 150 кб), эта профессия еще и пересекается в какой-то степени с демосценой, где на первом месте – искусство минимизировать информацию и генерировать ее процедурно.

Баннеры – это обычные HTML-странички, которые показывают пользователю рекламный сюжет и содержат ссылку на сайт рекламодателя. Они могут быть изготовлены при помощи самых разных инструментов, в том числе визуальных (Adobe Animate, Google Web Designer), но самое гибкое решение – писать непосредственно на HTML и JavaScript, рисуя графику либо обычными элементами DOM, либо через canvas. Благодаря отказу от Animate, вы не привязаны к его JS-библиотеке, которая сама по себе сжирает много веса, если ее нужно приложить к баннеру локально. Однако для отрисовки мало-мальски сложной анимации одним CSS вы не обойдетесь, и вам нужны такие библиотеки, как GSAP, Anime.js и др. – благо, весят они совсем немного. Основная сложность – уместить все ресурсы баннера в те самые 150 кб, что порой представляет нетривиальную задачу.

Для решения этой и многих других задач, которые возникают при разработке HTML-баннеров, я написал на Node.js комплект инструментов BT (Banner Toolchain). Его идея уходит корнями во внутренний инструментарий компании SmartHead, которым я пользовался три года и решил переделать полностью с нуля, уже в качестве независимого проекта с более эффективной реализацией большинства фич.

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

(далее…)

OpenSource и выгорание. Что будет, если все махнут рукой?

В последнее время часто говорят о выгорании мейнтейнеров открытых проектов. Люди теряют интерес к обновлению и поддержке своих старых проектов, в результате чего многие (если не большинство) из библиотек в публичных репозиториях превращаются в abandonware – необновляемое заброшенное ПО. Это становится серьезной проблемой для проектов, зависящих от подобных библиотек, так как в заброшенном коде могут быть уязвимости. Например, при работе с NPM частенько можно видеть критические уязвимости в некоторых пакетах, причем они висят годами, так как никто не может создать новый релиз, кроме мейнтейнера, даже если ошибку легко исправить. Решения звучат разные: ввести некую форму демократии в управление проектами, создать автоматизированную систему финансирования для мотивации мейнтейнеров и пр. На мой взгляд, тут имеет место быть классический человеческий фактор, и потому проблему нельзя исправить чисто техническими и формальными методами – нужна смена парадигмы в головах, оздоровление мышления. Проблема не в технологии, а в психологии.

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

  • Переход в другую профессиональную сферу, на другую платформу, другой стек технологий. Поддерживать проект на языке, которым вы уже активно не пользуетесь и который вам не нужен в резюме, становится довольно напряжно и выглядит как путь в никуда;
  • Смена приоритетов в личной жизни. Не все могут позволить себе выкраивать львиную долю свободного времени на OpenSource, жертвуя отношениями, семьей, друзьями, другими увлечениями помимо программирования;
  • Сообщество вокруг проекта становится токсичным. Неадекватные требования пользователей, наплыв “доброхотов” с советами, как надо все переделать, всевозможные тролли, хамы, а также люди, пытающиеся применить ваш проект не по назначению и регулярно изливающие на вас фрустрацию по поводу своих неудач;
  • Автор в принципе не видит смысла продолжать работу над проектом, так как это по совокупности причин не делает его счастливее. Играет роль социальная атмосфера и функция, которую человек выполняет в обществе. Для счастья человеку нужен смысл – и смысл жизни в целом, и смысл любой конкретной деятельности. Потеря смысла – катастрофа.

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

  1. Вы вправе самостоятельно распоряжаться своей жизнью, временем и усилиями
    Цель не оправдывает средства, когда речь идет о жизни и здоровье. Вы можете строить планы и формулировать цели, но вы не обязаны жертвовать чем-либо – и никто не вправе требовать от вас жертв. Вы не обязаны делать то, что вам неинтересно или трудно.
  2. Ваш вклад в свободное ПО не определяет вас как личность
    Управление свободными проектами – лишь один из множества видов полезной деятельности, которые делают человека ценным для общества. Не надо думать, что если вы устали, утратили мотивацию писать и сопровождать свободный код, вы стали хуже как человек. Нет ничего плохого в том, чтобы переключаться, уделять больше внимания своему здоровью и благополучию, учиться чему-то новому, пробовать новые для себя занятия и виды творчества. Вы не раб сообщества СПО и не прикованы к своим репозиториям цепями.
  3. Свободное ПО – это подарок от разработчиков, а не их долг перед кем-то
    Скачав из Интернета архив со свободным кодом, вы получаете в свое распоряжение только этот код и ничего более! Вы не получаете право на время и усилия автора этого кода. Вы входите с автором в отношения лицензиара и лицензиата, которые прописаны в лицензии (прошу прощения за тавтологию). А в любой свободной лицензии заглавными буквами написано – никаких гарантий.
  4. Написав и выложив код, вы не становитесь автоматически ответственным за судьбу этого кода
    Ответственность возникает только по закону и добровольно заключенному договору. Вы не обязаны исправлять баги и вообще хоть как-то реагировать на багрепорты. Вы не обязаны что-то обновлять, “своевременно” выпускать релизы, поддерживать “в актуальном состоянии” пакеты в реестрах. Нигде такая ответственность не прописана, ни по какому закону она не возникает – просто нет ее от слова совсем.
  5. Все эти репозитории, VCS, менеджеры пакетов – один большой общественный эксперимент
    Никто не может гарантировать, что экосистема, основанная на чистом энтузиазме, будет стабильно работать фоном. Тот факт, что кто-то построил на основе СПО свой бизнес, начал зависеть от мейнтейнеров, которые чинят баги, реагируют на просьбы и выпускают обновления – это исключительно его проблема. Верить, что автор библиотеки никогда ее не забросит – убийственно наивное допущение. Нужно перестраховываться от этого риска, а не взваливать моральную ответственность на автора.

И напоследок, на правах имхо. Студенты-миллениалы, с небывалым интересом подхватившие идеи Ричарда Столлмана и Эрика Реймонда для того, чтобы изменить мир, сделали слишком много и получили взамен слишком мало – а то и в чем-то потеряли. Они положили молодость на то, чтобы приложения могли создаваться, как конструктор, из готовых блоков, доступных бесплатно – и это породило многомиллиардную индустрию, изменившую всю нашу жизнь. Но что в итоге? Да, мы теперь можем заказать еду в кафе при помощи мобильного приложения. Да, у нас есть соцсети и мессенджеры, электронный банкинг и кассы самообслуживания, голосовые помощники и системы умного дома. Но разбогатели на этом единицы, все миллиарды ушли в карманы Джеффов Безосов и Марков Цукербергов, а остальные – в том числе и разработчики свободного кода – вынуждены работать “на дядю” как и раньше, десятилетиями выплачивать ипотеку и лишь мечтать об образе жизни, который мог себе позволить рядовой обыватель XX века безо всяких компьютеров и приложений. А государство, тем временем, благодаря свободным технологиям получило небывалую власть: оно следит за людьми через камеры с распознаванием лиц, требует QR-код при входе в магазин во время пандемии, ловит инакомыслящих при помощи геолокации, соцсетей и все тех же “умных” систем обработки данных, созданных на основе бескорыстного труда идеалистов – на фундаменте для глобального цифрового рая строится глобальный цифровой концлагерь. Совсем не такой новый мир мы себе представляли, когда создавали что-то новое!

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

Передаем данные из GIMP в Blender

Не так давно столкнулся с интересной задачей при создании 2D-анимации в Blender: мне нужно было сделать плоскую сетку по форме объекта из PNG-изображения с прозрачным фоном. На обычную плоскость ее натянуть нельзя, так как предполагалось, что объект будет деформироваться при помощи скелета и shape keys. И таких сеток нужно было создать довольно много. Создавать их вручную, расставляя вершины по контуру картинки, как-то очень уж трудоемко – захотелось этот процесс как-то оптимизировать. И тут я вспомнил, что GIMP умеет преобразовывать маски в кривые, которые затем можно сохранить как SVG и импортировать в Blender. Осталось лишь заскриптовать эту последовательность действий!

Я решил, что переносить SVG вручную из одной программы в другую я тоже не хочу – пусть будет условно одна-единственная кнопка, по нажатию на которую слой из GIMP переносится в текущий открытый проект Blender. Подобное взаимодействие двух приложений можно реализовать при помощи технологий RPC (remote procedure call) – в частности XML-RPC, который позволяет через HTTP на клиенте вызвать серверную функцию, передав ей параметры, и затем получить результат. Преимущество XML-RPC в том, что он полностью скрывает транспортный механизм такого вызова – в скриптовых языках он выглядит просто как обычный вызов функции. Сервером я решил сделать плагин для Blender, клиентом – плагин для GIMP. Оба плагина я написал на Python, где протокол XML-RPC реализован в стандартной библиотеке. В GIMP и Blender используются разные версии Python, поэтому код работы с XML-RPC немного отличается.

Серверная часть выглядит достаточно тривиально: нужна лишь функция, которая принимает на вход строку, содержащую SVG – эта функция регистрируется как серверная функция в объекте SimpleXMLRPCServer:

import os
import bpy

import threading
import tempfile
from xmlrpc.server import SimpleXMLRPCServer

HOST = "127.0.0.1"
PORT = 8000

def svg_to_curve(svg:str):
    tmp = tempfile.NamedTemporaryFile(delete=False, mode="w")
    tmp.write(svg)
    tmp.close()
    bpy.ops.import_curve.svg(filepath=tmp.name, filter_glob='*')
    os.unlink(tmp.name)
    return {}

def launch_server():
    server = SimpleXMLRPCServer((HOST, PORT))
    server.register_function(svg_to_curve)
    server.serve_forever()

(для краткости я опустил служебный код для регистрации плагина)

Проблема возникает лишь в момент импорта SVG – Blender умеет импортировать только по файловому имени, поэтому пришлось сохранить строку во временный файл. Выглядит не очень элегантно, но работает.

На стороне GIMP делается следующее: текущему слою создается маска из альфа-канала, из маски создается выделение (gimp_image_select_item), из выделения, в свою очередь – кривая (plug_in_sel2path). Кривая экспортируется в SVG (gimp_vectors_export_to_string), а затем мы просто вызываем удаленную функцию svg_to_curve, после чего удаляем все служебные объекты.

import xmlrpclib
from gimpfu import *

def export_svg(svg):
    proxy = xmlrpclib.ServerProxy("http://localhost:8000/")
    try:
        proxy.svg_to_curve(svg)
    except xmlrpclib.Fault as err:
        pdb.gimp_message(err.faultString)

def layer_to_blender_curve(image, layer):
    if not pdb.gimp_item_is_group(layer):
        mask = layer.mask
        if not mask:
            mask = layer.create_mask(ADD_ALPHA_TRANSFER_MASK)
            layer.add_mask(mask)
        pdb.gimp_image_select_item(image, CHANNEL_OP_REPLACE, mask)
        path = pdb.plug_in_sel2path(image, None)
        pdb.gimp_selection_none(image)
        
        vector_name = pdb.gimp_path_list(image)[1][0]
        vec = pdb.gimp_image_get_vectors_by_name(image, vector_name)
        vec.name = "mask_path"
        
        svg = pdb.gimp_vectors_export_to_string(image, path)
        export_svg(svg)
        
        pdb.gimp_image_remove_vectors(image, vec)
        pdb.gimp_layer_remove_mask(layer, 0)

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

Исходники плагинов вы можете найти в репозитории https://github.com/gecko0307/image2curve.

Недостатком данного решения является то, что на стороне Blender будет постоянно работать HTTP-сервер на localhost:8000, так что вы в это время не сможете привязать к этому порту ничего другого. В Python есть способы получить случайный незанятый номер порта, чтобы не конфликтовать с другими серверами, однако в этом случае придется как-то передать порт в GIMP, что, как мне кажется, несколько усложняет весь процесс и добавляет лишнюю точку отказа.

Ссылки

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

Общее
https://timurgafarov.ru – портфолио с проектами и примерами дизайнерских работ (в будущем планирую сделать на этом домене сайт моего бизнеса);

Геймдев и разработка
https://github.com/gecko0307 – профиль на GitHub;
https://www.patreon.com/gecko0307 – страница на Patreon;
https://gamedev.timurgafarov.ru – блог о разработке игр на языке D;
https://timurgafarov.medium.com – блог о языке D на Medium;
https://www.youtube.com/channel/UCVaRZr3TpAWVP_kaqBfmoew – YouTube-канал;
https://xtreme3d.ru – портал, посвященный Xtreme3D, 3D-движку для Game Maker и Game Maker: Studio, а также, в целом, созданию 3D-игр на GM;
https://psxdev.xtreme3d.ru – страничка о разработке под первую PlayStation;
https://fps.xtreme3d.ru – журнал о цифровом творчестве;

Моделирование
https://www.artstation.com/gecko0307 – портфолио на ArtStation;
https://dobrofile.ru/gecko0307 – 3D-модели в продаже;
Также у меня до недавнего времени был профиль с моделями на CGTrader, но меня лишили статуса продавца в связи со всем известными событиями;

Искусство
https://art.timurgafarov.ru – основное художественное портфолио и краткая творческая биография;
https://www.deviantart.com/timurgafarov – профиль на DeviantArt;
https://www.behance.net/timurgafarov – профиль на Behance;
https://500px.com/p/gecko0307 – фототворчество;

Соцсети
https://twitter.com/gecko0307 – Twitter;
https://www.linkedin.com/in/timurgafarov – профиль на LinkedIn;
https://vk.com/tgafarov – страница ВКонтакте;

Разное
https://kadath.fandom.com – википроект по вселенной Страны Снов Лавкрафта;
https://timurgafarov.ru/pixeltavern – фанатский микроблог, посвященный игре Margonem.

Deferred Texturing для ландшафта

Еще одной новинкой в Dagon 0.14 станет довольно интересная и нетипичная техника, которую я еще не встречал в готовых реализациях – Deferred Texturing (отложенное текстурирование) для ландшафта.

Когда у меня в движке появилась поддержка ландшафтов, я не стал особо мудрить и сделал для них простейший шейдер, к которому подключаются до четырех текстур, причем для них пришлось сделать специальные свойства у материала, что выглядело не очень эстетично с точки зрения дизайна API. Также этот шейдер не поддерживал текстуры шероховатости и металличности, что делало его неполноценным по сравнению с остальными компонентами рендера. Техника Deferred Texturing, описанная Натаном Ридом, практически идеально вписалась в мой пайплайн и полностью решает эти две старые проблемы.

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

Полученные буферы вместе с PBR-текстурами (карта нормалей, base color, roughness, metallic) скармливаем текстурирующему проходу, который рисует экранный квад и записывает значения уже в обычный G-буфер. Для вычисления финальных нормалей используются значения из буфера нормалей ландшафта (плюс глубина, которую я сохраняю как альфа-канал буфера нормалей – она нужна для реконструирования eyespace-позиции пикселя). PBR-текстуры сэмплируются UV-координатами из буфера. Чтобы экранный квад не перезаписывал значения, уже сохраненные в G-буфере, нужно отсекать пиксель по маске, которую я храню в Z-канале буфера текстурных координат – 0 означает, что в данной точке нет ландшафта, 1 – есть.

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