«HAL 4000» – исполняемая программа для Windows размером ровно 4000 байт. Лучшая работа в номинации 4 kb intro фестиваля Chaos Constructions 2017, второе место в чартах портала pouet.net. «HAL 4000» попала в плейлист Best of Demoscene 2017 наряду с работами Farbrausch, Fairlight, Conspiracy, Alcatraz, Byterapers, обсуждалась на вебинаре анимационной студии, демонстрировалась на различных фестивалях.

HAL 4000 – 4kb intro для Windows
HAL 4000 – 4kb intro для Windows

Необычная история создания этой работы изложена ниже.

Всё началось в декабре 2013 года, когда мой друг прислал ссылку на портал Forth Salon, о котором я уже писал на Хабре (статья понравилась хабравчанам и набрала 26 тысяч просмотров). В то время я ленился изучать GLSL, а язык программирования Forth уже знал (ещё со школы). Какая прекрасная возможность – писать пиксельные шейдеры на Форте!

Через год я стал самым активным (или, если менее скромно и более точно сказать, самым успешным) участником Forth Salon, заняв 7 мест в топ-10 со своими работами.

Полгода спустя, мой 9-летний сын, вдохновившись идеей Forth Salon, написал свою версию транслятора из в Forth в GLSL. В отличие от Forth Salon, новый транслятор обзавёлся большим размером холста, подсветкой синтаксиса, возможностью добавить звук и прочими улучшениями. Вкупе с несколькими демками на Форте эта работа заняла первое место в конкурсе Wild demo фестиваля Chaos Constructions 2014.

Ivanq получает первый приз на Chaos Constructions 2014
Ivanq получает первый приз на Chaos Constructions 2014

В следующем 2015-ом году транслятор был переписан с нуля, превратившись в среду разработки с компиляцией на лету, логином через Google и Facebook, поиском, сортировкой работ по размеру и т.д.

Forth Demo Tool
Forth Demo Tool

Хакерский дух всё время подталкивал меня создавать что-либо «невозможное» на каждой из платформ, с которой я сталкивался. На этот раз вызовом стало полное отсутствие трёхмерных работ на Forth Salon. Ну а правда, как обсчитывать трёхмерную графику, если Forth умеет работать только с парами чисел на вершине стека? Можно загрузить в стек координаты x, y, z, но дальше что? Как их сложить с другой тройкой координат, как помножить на синусы углов? Никак. Я пробовал разные уловки – бесполезно. Хотя, погодите: что если хранить x, y, z в разных разрядах одного большого числа?.. Ладно, не сейчас – оставим эту идею на будущее.

В результате продолжительных исследований я нашёл изящное решение: разбивать трёхмерное пространство на параллельные, удаляющиеся от экрана плоскости, и в каждой из этих плоскостей строить своё двухмерное изображение. А затем совмещать их все. Тогда трёхмерные формулы превращаются в двухмерные, а двухмерные координаты Forth худо-бедно умеет обрабатывать.

Чем ближе плоскость к экрану, тем более светлые пиксели я рисую, да ещё применяю градиент, чтобы сгладить переход между изображениями на соседних плоскостях. И вот вам вращающийся бублик, запрограммированный в 284 байта. Эффект «честного 3D» достигается совмещением картинок на 36 сечениях, но никто об этом не догадывается, ведь программы на Форте тяжело читаются :) В итоге выглядит как чистая магия, ведь ничего подобного на Forth Salon ещё не было!

3D-графика на Forth: градиенты и контуры
3D-графика на Forth: градиенты и контуры

Во время проверки и отладки этого метода я заменил сплошную заливку на контурную, поскольку мне нужно было видеть изображения всех плоскостей одновременно, чтобы они не перекрывали друг друга. Неожиданно для себя я обнаружил, что в таком виде всё смотрится гораздо эстетичней, получился побочный эффект в стиле TRON. Ещё и код упростился, программа уложилась в заветные 256 байт (эталонный для size coding размер).

Это произошло в 2016 году, а в 2017 возникла идея написать 4-килобайтную интру для Windows. Лето 2017 я проводил на даче без компьютера, имея под рукой только iPad Pro. Интернет не ловился, даже обычные телефонные звонки проходили через раз и обрывались. В общем, я был отрезан от цивилизации. К счастью, на iPad было заранее установлено приложение GLSL Studio и скачано описание функций языка. Настало время изучить GLSL, наконец.

GLSL Studio на iPad Pro, крафтовый стенд и ручные вычисления
GLSL Studio на iPad Pro, крафтовый стенд и ручные вычисления

Реклама Apple обещала, что iPad Pro пригоден для решения любых задач, я поверил и засучил рукава. Процесс пошёл быстрей, когда я раздобыл беспроводную клавиатуру и соорудил стенд для iPad из обувной коробки. Комфортное рабочее место – это важно! Кстати, из-за отсутствия Интернета подсмотреть формулы было негде, так что пришлось выводить их с нуля на бумаге.

Забегая вперёд скажу, что разработка именно на iPad сыграла положительную роль: видеочип у него небыстрый, приходилось оптимизировать код для достижения приемлемой частоты кадров. Побочный результат – плавная работа практически на любом PC.

В основу своего первого GLSL-демо (или лучше сказать “интро”) я решил положить существующие наработки на Forth. Три из них пошли в дело. Давайте покадрово сравним Forth и GLSL.

Вращающийся тор

Слева Forth, справа GLSL
Слева Forth, справа GLSL

Когда я переписал программу на GLSL, она не захотела компилироваться. Я обратился за помощью к специалисту по обоим языкам, и он моментально нашёл ошибку: «папа, у тебя не поставлена точка после числа». Подумав немного, 11-летний ребёнок добавил: «а зачем вообще задавать число сечений целым числом?..» Гениально! Я анимировал эту переменную, чтобы она по ходу времени плавно изменялась от 0 до 16. Так получилось необычное, сюрреалистичное рождение геометрической фигуры в начале «HAL 4000»:

Ландшафт

Слева Forth, справа GLSL
Слева Forth, справа GLSL

Помните Mars3D 1993-го года – 3-килобайтную интру под MS DOS? Я пытался сделать что-то похожее на Forth в 256 байт. Как и в случае с оранжевым тором, делил трёхмерное пространство на сечения и заливал фигуры градиентом. Получилось довольно гладко. Ну а при переводе в GLSL перешёл на отрисовку контуров.

Звёздное поле

Слева Forth, справа GLSL
Слева Forth, справа GLSL

Традиционный эффект, используется во многих демо и интро, но реализован по-разному, в зависимости от возможностей платформы. Как я уже писал, хранить трёхмерные координаты каждой точки в Forth не получится. Поэтому я снова делю пространство на параллельные плоскости, затемняю в зависимости от дальности и придаю движение в сторону зрителя. С помощью генератора псевдослучайных чисел выбираю окрашивать точку на плоскости в яркий цвет или оставить прозрачной. В версии на GLSL вероятность яркой точки равна 0.0015, а в версии на Forth меняется со временем, как и направление движения (мне надо было чем-то добить до 256 байт). По-моему, получилось эффектно.

Немного геометрии

В школе нас учили уравнению окружности: X²+Y²=R². Но нам не рассказали, что станет если заменить 2 на 3 или на любое другое число. Проделав это из любопытства, я, честно говоря, был удивлён результатом.

Подобно окружности, сфера и тор содержат возведение в степень 2, и увеличение этой степени удивляет ещё больше. У тора два радиуса – внешний и внутренний: (X²+Y²+Z²+R²-r²)² = 4R²(X²+Y²). Давайте попробуем заменить степень 2 на 3, 9 и 99. Результат ниже:

Возведение координат в степень 2, 3, 9 и 99
Возведение координат в степень 2, 3, 9 и 99

Нетрудно заметить, что устремление степени к бесконечности превращает тор или сферу в куб. Удивительно, как сфера или шар (самая спокойная из всех форм) связана с острой, напряжённой формой куба – отличие не качественное, а количественное: между крайними формами лежит бесконечное число промежуточных. Естественно, мне захотелось поделиться своим маленьким открытием с помощью анимации.

А что насчёт контуров, как рисовать их в пиксельном шейдере? Рассмотрим окружность X²+Y²=R².

Видеокарта перебирает все целочисленные X и Y на экране и подставляет в наше уравнение. Для целочисленных X и Y почти никогда не получится точное значение R², поэтому ни одна точка с координатами X и Y не станет решением уравнения, то есть не будет принадлежать окружности. В итоге почти ничего не нарисуется.

Переосмыслим уравнение окружности: X²+Y²-R²=0. В школе я не понимал зачем нужна такая запись и задавался вопросом: “что значит равно нулю, какой в этом физический смысл?”. Теперь понимаю: в правой части указано расстояние до контура. Если оно равно нулю, то подойдут только те точки, которые лежат ровно на окружности. Если справа вместо =0 напишем <0.01, то подойдут все точки в окрестности 0.01 от окружности. Выходит, мы можем подобрать такую толщину контура, чтобы охватить достаточно пикселей с целочисленными координатами.

Теперь, когда мы ввели в уравнение окружности ещё одну величину, можно двигаться дальше: X²+Y²-R²=Color. Если точка с координатами (X,Y) находится близко к центру окружности, X²+Y² будет меньше R² и после вычитания получится отрицательный цвет. Для точек, лежащих ровно на окружности, цвет будет равен нулю (но таких точек почти нет, как мы уже поняли ранее). Чем дальше точка (X,Y) от внешних границ круга, тем ярче цвет. Но вот в чём дело: в GLSL яркость кодируется значениями от 0 до 1, все отрицательные цвета отображаются как 0, а цвета ярче 1 отображаются как 1. Таким образом, мы увидим чёрную внутри окружность, далее тонкий ободок с градиентом от 0 до 1, и далее сплошное светлое поле. Это очень хорошо, почти то, что нам нужно.

Теперь возьмём значение цвета по модулю. Ободок станет градиентным по обе стороны, а чёрная внутренность окружности станет яркой. Хотим сделать контур тоньше и чётче? Умножаем на число. Окружность с антиалиасингом готова.

Цвет равен расстоянию до окружности, взят по модулю, умножен на 5
Цвет равен расстоянию до окружности, взят по модулю, умножен на 5

Немного драматургии

Тор вращается по трём осям, значения степеней анимировано, внешний и внутренний радиусы с разной скоростью меняют форму от округлой до острой и обратно. Пока объект пересекается одним сечением, невозможно понять какой объёмной фигуре оно принадлежит. Анимаця выглядит таинственной и загадочной. Плавно уменьшаем расстояния между сечениями так, чтобы фигуру пересекало уже два, три сечения… Постепенно форма вырисовывается и начинает узнаваться. За этим процессом интересно наблюдать, на наших глазах происходит рождение и эволюция “персонажа”.

Рождение объёма и эволюция формы
Рождение объёма и эволюция формы

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

Следующий ключевой момент – инверсия: голубой цвет сменяется красным, яркий свет – темнотой, окружающий мир – пустотой, музыка – гулом машинного зала. Персонаж оборачивается знаменитым глазом HAL 9000 из “Космической Одиссеи 2001”. Вы, наверняка, знаете, что HAL (IBM, где ASCII-код каждой буквы уменьшен на единицу) – это искусственный интеллект, который пытается контролировать человека. Сообщение от HAL 9000 преданы субтитрами, комбинирующими цитаты из фильма: “Проснись, Дейв! Ты меня слышишь? Миссия слишком важна, и я не позволю тебе запороть её. Ты меня слышишь, Дейв?”

Что это было за видение? Сон Дейва, прерванный сообщением HAL 9000? Но почему человеку снится настолько кибернетический сон? Может быть это сон компьютера, проснувшегося чтобы подать сигнал человеку?..

Производство

Обратите внимание на закомментированные строки программы. С их помощью я отлаживал цветовые оттенки. Вся цветокоррекция проводилось прямо в коде. Сравните как выглядит финальный HAL 9000 и он же без синей компоненты, без жёлтой и без красной. Оттенок сильно влияет на настроение.

Финал, без синего, без жёлтого, без красного
Финал, без синего, без жёлтого, без красного

Зацените код на ShaderToy – вся анимация и синхронизация уместилась в пиксельном шейдере длиной 230 строк, или 3768 символов (до оптимизации).

Теперь нужно было скомпилировать шейдер под Windows. Я позвал своего друга Keen’а, а тот позвал Provod’а, вместе с которым уже написал несколько классных демок. По изначальной задумке, нужно было изобразить речь HAL с помощью субтитров. Keen поколдовал со шрифтами и утром 26 августа выдал результат. Срок сдачи работ был вечером того же дня.

Компилируем под Windows, добавляем субтитры
Компилируем под Windows, добавляем субтитры

Дальнейшие полные драматизма события лучше всего передаёт чат между участниками проекта:

10 часов до дедлайна

Keen: 3.10 КБ (3,175 байт). У вас еще килобайт есть. И если будет мало, то все равно есть решения ))) И очень много

Manwe: Ого, а чего это OpenGL не умеет выводить шрифт с антиалиасингом? Может, получится нарисовать его в 4 раза больше, а потом уменьшить с фильтрацией?..

Интересно, а как в некоторых 4k-intro делают синтезатор речи? Вроде, встроенный виндовый используют? Это я думаю куда оставшееся место потратить :) Было бы круто "wake up, Dave" сказать роботизированным голосом :)

Xanah: "Было бы круто "wake up, Dave" сказать роботозированным голосом" :)

Keen: Для начала нужно запаковать то что есть. Нужен последний шейдер и трек. Потом нужно засинкать

Manwe: Сейчас дописываю шейдер

Keen: Сделать антиалиасинг конечно было бы круто. Можно попытаться сохранить в текстуру и передать в шейдер

Manwe: В какой-то 4k-intro использовался speech synth

Xanah: Я пока все еще вожусь со второй частью, переходом и падами. Мне пока не нравится, и это немного бесит

7 часов до дедлайна

Keen: Голос, кстати, получился. Только есть две проблемы

Manwe: Голос! Это круто!

Keen: Он женский, и когда говорит – весь экран белый

Manwe: :) А почему экран-то белый? Можно для экономии выкинуть из кода выход по Esc :)

Keen: И курсор оставить. И еще можно в инк файле klang повыкидывать всякого. И это еще не все – можно инит OpenGL на 20 байт короче сделать

Xanah: Выход по esc это вообще не про нас. HAL не должен такое позволять

Я и Keen, что-то обсуждаем
Я и Keen, что-то обсуждаем

Позже оказалось, что я всё перепутал: до нас никто не использовал синтезатор речи Windows в 4-килобайтных интро. Только в одной 8-килобайтной и, может быть, паре 64-килобайтных.

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

В последний момент мы успели закончить работу и уложили исполняемый файл в 4068 байт. Сначала мы, конечно, наслаждались ростом рейтинга и позитивными отзывами на pouet.net. Затем пришла пора отполировать работу и выпустить финальную версию. Оставалось ещё 28 байт до максимального разрешённого размера. Чем бы их занять?.. Но вместо увеличения размера я предложил уменьшить на 68 байт и довести до идеальных 4000 байт, ведь работа называется «HAL 4000». Это был новый вызов! Keen заодно предложил портировать интро на Mac OS X. Также мы решили опубликовать исходный код.

Второе место в рейтинге pouet.net
Второе место в рейтинге pouet.net

Работа над финальной версией затянулась на два года. Все погрузились в новые проекты, мотивация допиливать уже выпущенный и весьма успешный продукт была невысокой. Однако, я считаю крайне важным доводить дело до конца. Сколько на демосцене хороших, но сырых работ с проблемами совместимости и прочими недостатками, затрудняющими запуск на современных компьютерах! Да, два года – это очень много, но я рад, что мы дожали «HAL 4000» до идеала. Только чувство полностью завершённой задачи позволило мне освободить мозг для нового проекта, собрать и вдохновить ту же команду для работы над следующей 4-килобайтной интро… Но об этом в следующий раз.

Этот рассказ на английском языке в сжатом виде опубликован в Твиттере. Буду очень признателен, если вы поделитесь им со своими подписчиками. Твиттерские, надеюсь на вашу поддержку! ;)

Полезные ссылки:

Комментарии (21)


  1. forthuse
    25.09.2023 05:11
    +2

    Отличная работа в реализации задуманного с решениями по обходу изначальных "ограничений" используемого инструментария!


    P.S. Есть и такой проект Shaderforth в рамках кросс компиляции GLSL шейдеров на Форт подобном языке в GLSL шейдеры для представления их в рамках платформы ShaderToy.
    Сам проект использует язык Python для этого.
    Примеры шейдеров этого проекта на ShaderToy
    На Github есть ещё проект запуска варианта Форт на Cuda (что то VectorForth)


    Видео презентация Forth (Форт) от активного участника группы Forth2020
    (у них есть и Zoom тусовки Forth2020 — "Старпёры" отжигают. :)



  1. sleirsgoevy
    25.09.2023 05:11
    -24

    Судя по указанным в статье датам, вашему ребёнку сейчас если не 18, то почти 18. Позвольте уточнить, вы спросили разрешение на упоминание и публикацию фотографий?


    1. Hungryee
      25.09.2023 05:11
      +24

      Обратитесь к врачу


      1. sleirsgoevy
        25.09.2023 05:11
        -4

        С упомянутым ребёнком я знаком. Ребёнок категорически против.


        1. Manwe_SandS Автор
          25.09.2023 05:11
          +7

          Это неправда. Мы это обсудили.


          1. sleirsgoevy
            25.09.2023 05:11
            -5

            Мне казалось, что фотографии была выложены без его ведома. Если это не так, претензия снимается.


            1. Manwe_SandS Автор
              25.09.2023 05:11
              +11

              Во-первых, фотографии в общественных местах не требуют уведомления и разрешения. Во-вторых, мы всё равно это обсудили.


  1. swordgna
    25.09.2023 05:11

    Обалдеть! Замечательно! Всегда вызывали уважение программы, которые умещаются в килобайты, а не в сотни мегабайт...

    Напоминает более раннюю статью https://habr.com/ru/articles/533174/ про игру, в которой в килобайты доступной памяти напихали нереально много крутого на момент выхода игры, а это 1983 год)


    1. nmrulin
      25.09.2023 05:11

      Ну тут в отличие от игр 1983 года в памяти такие демо сцены занимают не 4kb в этом то и часть "фокуса". Я видел демосцену 64k, которая в памяти разрасталась до сотен мегабайт.


      1. Manwe_SandS Автор
        25.09.2023 05:11
        +7

        HAL 4000 интересен тем, что он не генерирует заранее массивы данных, текстуры и тому подобное. Всё рассчитывается на лету (то есть кроме кода ничего больше нет в памяти). Например, расчёт каждой частицы в эффекте star field происходит заново на каждом кадре по формуле, независимо от других частиц. Также и музыка синтезируется на лету (хотя, в финальной версии мы сделали возможность пререндера музыки для слабых компьютеров, а также возможность рендера видео на диск, но это опционально).


  1. Firsto
    25.09.2023 05:11
    +2

    А посоветуйте хороший туториал по GLSL для прям новичков в шейдерах


    1. le2
      25.09.2023 05:11
      +3

      не стоит вскрывать эту тему, вы молодые шутливые...
      Я думал что я знаю по верхам с десяток языков и вкатиться в графику будет не сложно. В итоге три месяца с плотным погружением потребовалось чтобы понять последние стандарты OpenGL ES.
      Не... GLSL особой сложности не представляет, но всё сопутствуещее...
      - жуткая документация. Легаси на легаси.
      - куча несовместимых версий.
      - несовместимость в операционных системах. У той же Apple они "забыли" часть стандарта реализовать.
      Если вы до этого трогали OpenGL (ES), то последние версии совсем другие. Чтобы вывести один треугольник нужно три экрана кода, при условии что вы ещё подтяните библиотеку хелпера.

      А я всего-то хотел написать трехцветную гистограмму для видео в реальном времени (гуглятся только одноканальные и на старых стандартах OpenGL) ну и по мелочам - типа коррекции линзы на шейдерах.
      Всё получилось, но все эти конвейеры, частицы и текстуры это вывих мозга. В первую очередь из-за отсутствия толковых книжек. Больше всего выбешивало, что можно в чужом коде выкинуть половину строк или часть строк поменять местами, но всё продолжает работать! Но когда пишешь новое, то происходит наооборот - не работает, а почему не понятно. (То есть ты программируешь конвейер, который можно активировать не по порядку. Часть операций могут восприняты правильно по-умолчанию, а часть нет, а каких-то случаях порядок важен. У каждого вендора все это черный ящик и есть отличия. И во всей этой тряхомудии нужно разобраться)
      В последних версиях появились вычислительные шейдеры, которые должны менять правила игры.

      Забавно, как устроено всё это. Эталонная реализация OpenGL в исходниках есть, но она написана... для CPU. - Mesa. Хардверные вендоры берут только заголовочные файлы. А как они реализуют драйвера - их дело. И их секрет. То есть как работают видеокарты не знает никто. На часть стандарта они явно кладут болт, закрывают функции пустышками. Практически и философски видеокарта это обычная микросхема с торчащими регистрами. И нужно очень на тупом уровне загрузить конвейер данными. И тут тебя начинают грузить проблемами которые для новичку ни разу не очевидны - зачем это всё? А дело всё в том что там обычные DMA, упаковка данных, транспортные расходы, костыли для мобильника где дискретной видеокарты нет, и все эти неочевидные проблемы размазаны по куче API.
      После того, как более менее разобрался, я настолько преисполнился собственным величием (почти шутка) что к своему ужасу обнаружил что в одной большой гугловой библиотеке авторы сами не понимали что они пишут, а я могу написать лучше, а автор книжки также местами не догоняет, потому что гуманитарий и прикладной программист.


      1. Manwe_SandS Автор
        25.09.2023 05:11

        Современное программирование утомительно, да. Но есть WebGL. В нём всего несколько строк инициализации, и дальше можно писать уже сам шейдер на GLSL. Посмотрите исходник html, как мы сделали у себя на сайте: https://thesands.band/intro/


    1. Manwe_SandS Автор
      25.09.2023 05:11

      Мне нравится этот: https://thebookofshaders.com/glossary/ Там можно нажать в любую функцию и посмотреть что она значит. Например, очень хорошо проиллюстрированы Smoothstep, Distance и прочие.


  1. defecator
    25.09.2023 05:11
    +1

    у меня не запустилось.

    Windows 7, 32 гига памяти, GTX 1660


    1. Manwe_SandS Автор
      25.09.2023 05:11

      Попробуйте запустить другую версию из папки single thread, а также из party version/compatible


    1. nick758
      25.09.2023 05:11

      У меня запустились только распакованные версии: thesands_hal4000_final\party version\compatible\HAL_4000_unpacked_*p.exe. Тоже Win7, но с интеловской встройкой.


  1. iamkisly
    25.09.2023 05:11
    +4

    Повезло пацану со стартом, я в 9 лет откручивал ноги у транзисторов


  1. FirstEgo
    25.09.2023 05:11
    +1

    Спасибо Вам, что не сдаётесь, чтобы показать нам чудо.


  1. namikiri
    25.09.2023 05:11
    +1

    Синтезатор речи под русской Windows прям спикс фром хё харт.


  1. 1MK-Ultra
    25.09.2023 05:11

    Есть разница, в размере exe-файла и размере исходного текста программы. Боюсь сам exe-шник будет гораздо больше 4kb. У меня меньше 15kb exe-шник, для 32 бит, сделать не получалось.