Вот пример кода Forth Haiku и изображения, которое этот код создаёт: «Light Drop» by Brad Nelson.
: iii x y z* Sin ; x 5 * x y - iii exp y iii
Впереди нас ждут немало удивительных (в том числе и «живых») картин, но сперва — немного теории.
Основные понятия
Forth — стековый язык программирования. Чтобы совершить действия над несколькими числами, нужно сперва загрузить их в стек. Делается это простым перечислением чисел, разделённых пробелами. Затем указывают действие, которое нужно совершить над числами из стека. Команды также отделены пробелами и могут идти вперемешку с числами в любом нужном для вычисления порядке. Обычные арифмитические действия изымают с вершины стека операнды и помещают вместо них результат.
1 2 3 + /
Пример 1: загружаем в стек числа 1, 2 и 3, затем выполняем сложение двух верхних чисел (стек приобретает вид 1, 5) и далее делим одно число на другое. В итоге стек содержит ответ: число 0.2
1 2 + 3 /
Пример 2: иной порядок действий. Загружаем в стек числа 1 и 2, складываем. Результат — в стеке число 3. Загружаем в стек ещё одно число 3 и выполняем деление. Ответ: число 1 на вершине стека.
Шейдер — здесь речь пойдёт о пиксельных шейдерах, называемых иначе фрагментными. Это программа, исполняемая видеокартой для каждой точки текстуры, а в нашем случае — для каждой точки квадратного окошка размером 256×256. Таким образом, полотно, на котором мы рисуем свои картины, состоит из 65536-ти точек, и именно столько раз будет исполняться программа-шейдер. На выходе она должна выдать цвет точки. Казалось бы, если одну и ту же программу выполнить для всех точек, то и выглядеть эти точки должны одинаково — мы получим большой квадрат Малевича (чёрный, красный, любой другой). Всё так. Но к счастью, программа-шейдер может узнавать координаты x и y той точки, которую рисует в данный момент. А это всё меняет.
Система координат
По окончании работы наша программа-шейдер (Forth Haiku) должна оставить в стеке три числа: красную составляющую цвета, зелёную составляющую и синюю. Очень удобно, что яркость RGB-составляющих измеряется в том же масштабе, что и геометрические размеры окна. Просто положив в стек x или y (он ведь разный для каждой точки, да?), получим плавный переход цвета от чёрного к яркому через всё наше полотно.
Время
Кроме x и y, нам доступна ещё одна глобальная переменная t — время. Правда, в браузерах, лишённых поддержки WebGL, она всегда равна нулю. Отсчёт начат в незапамятные времена, к настоящему времени натикало уже немыслимое количество секунд, поэтому практическую пользу для нас представляет, пожалуй, только дробная часть переменной t и периодическая функция типа sin(t), так как она выдаёт значения в диапазоне от -1 до +1.
x y t sin 2 / 0.5 +
Пример самой простой анимации. Кладём в стек координату x и оставляем без изменений — она будет отражать красную составляющую цвета. На второе место в стек кладём зелёную составляющую, это будет y (стало быть, зелень заколосится снизу вверх). В качестве синей составляющей кладём время t. А теперь выполняем над последним положенным числом ряд операций. Сперва берём от него синус. Затем на 2 делим и 0.5 прибавляем. Всё. Был у нас синус в диапазоне [-1,1], а стал [0,1] — как и положено составляющей цвета. И вот пошла приятная пульсация по синей компоненте, и вот засветились пуще прежнего точки, оттенённые красной и зелёной компонентой — красота!
Для сравнения, вот тот же самый шейдер на языке GLSL:
void main(void) { vec2 uv = gl_FragCoord.xy / iResolution.xy; gl_FragColor = vec4(uv,0.5+0.5*sin(iGlobalTime),1.0); }
Кому как, а мне код Forth кажется проще и понятней, он предлагает очень низкий порог вхождения в тему. Посмотрите, какую анимацию сделал (с нуля) мой 9-летний сын, просто понаблюдав пару дней за моими экспериментами с Forth Haiku. Мне кажется, это показатель.
Время, вперёд!
Что ж, с теорией покончено, переходим к практике. В галерее Forth Programming Salon полно станковой живописи красивых неподвижных изображений. Но нас больше интересует анимация.
Warning (техника безопасности): когда Вы начнёте ковырять чужие работы («Create a derived work») или создавать новые («Create a Haiku», «Haiku Editor»), рука Ваша потянется к кнопке «Submit». Ни в коем случае не нажимайте её, поскольку она делает не то, что Вы подумали! Посмотреть свою программу Вы можете, нажав другую кнопку — «Update». А кнопка «Submit» опубликует в общую галерею Вашу забагованную недописанную программу, да ещё и с именем пользователя «Анонимус». Такой хоккей нам не нужен, база и без того заспамлена неработающими обрывками программ и клонами (Бред Нельсон обещал почистить базу и улучшить интерфейс). Так что только "Update". Вдох-выдох, 15-минутный перерыв на гимнастику, свежим взглядом оцениваем свою программу, и только если на 146% уверены в ней — тогда даём ей название, вписываем автора и — «Submit».
Нажимайте на картинки. Оно там движется! (в браузерах Chrome, Firefox, IE11)
Мои программы, конечно, ужасно нечитаемы. Во-первых, только недавно (благодаря сынишке, который ковырял JavaScript этого Forth-транслятора) я узнал о существовании недокументированной возможности вписывать в программу комментарии (в круглых скобках, которые должны быть отделены пробелом от самого текста комментария). Во-вторых — и это главное — я пытался уменьшить размер программы. Есть такой вид спорта — оптимизация, очень популярен на демосцене. Смог в 256 байт? Молодец! Теперь давай уложи в 128. Поэтому первое, что я делаю, это переопределяю команды языка Forth.
: q dup * ;
Двоеточие — это такой оператор языка Forth (вообще-то, в среде Форт-программистов принято говорить «слово» вместо «оператор» и «словарь» вместо «набор команд и функций», но не станем усложнять). Оператор «двоеточие» сродни определению функции. Следом за двоеточием (через пробел, как обычно) идёт имя нового оператора, в данном примере это «q» (от слова «квадрат»). После имени через пробел записана небольшая программка, которая и составляет тело нашего новоиспечённого оператора «q». Последний оператор — «точка с запятой», конец определения (в действительности он вынимает из специального стека возвратов адрес возврата и переставляет туда счётчик команд, но опять же — не будем усложнять).
Часто используемые последовательности команд имеет смысл выносить в отдельные новые операторы, даже если в этом нет физического смысла. Скажем, пишу я рей-трейсинг с традиционным подходом к программированию. Вектора для меня — это неразрывно связанные наборы из трёх чисел, функция нормализации для меня — отдельная формула, имеющая конкретный физический смысл, и так далее. Но стоит мне чисто механически, не задумываясь над «глубоким внутреннем смыслом», начать оптимизировать программу на Forth, как испаряются умножения на константы, одни функции рвутся посередине и слипаются хвостами с другими функциями, а в итоге… В итоге я получаю совершенно иначе структурированную программу, имеющую свою особенную красоту. Не говоря уже о том, что она стала короче. Например:
x ' r / z * lx m y ' r / z * ly m
Здесь x и y — глобальные константы, всё остальное определено программистом. Оператор «штрих» переносит нас в другую систему координат, оператор «r» вычисляет длину вектора, оператор z вычисляет нормаль, «lx» и «ly» выдают координаты источника света, «m» нормализует вектор (точнее, одну из его координат). Если нам угодно, умножение после «z» можем внести в тело самого оператора «z» (сэкономим пару байт). Да и деление перед «z» можно присовокупить к нему (или к предыдущему оператору «r»). При оптимизации все эти промежуточные действия уползают в словарь, а в теле программы остаётся только голая суть: x, y, t .
Это красиво. Но через неделю уже совершенно нечитаемо, конечно.
Хотите пример простенькой программы, в которой Вы сможете разобраться при помощи словаря Forth Haiku? Словарь этот намного беднее словаря классического Forth, и это сделано намеренно. Нажимайте на сам оператор и читайте о нём подробнее.
: r dup y 12 ** * t + sin swap x * cos + 1 mod ; 18 r 25 r dup 12 r /
В первой строке определяем оператор «r» (от слова «render»), который заглатывает число с вершины стека, тщательно пережёвывает, закусывая иксами и игреками, после чего выплёвывает в стек результат в диапазоне [0,1]. Оператору «r» три раза скармливаем разные числа, а на выходе получаем три компоненты RGB. В целях цветокоррекции дублируем зелёную компоненту и делим её на синюю. Теперь у нас новая синяя компонента, благодаря чему общая картинка получилась карамельной.
«1 mod» — это получение дробной части числа. Правда, работает оно не везде (Forth Haiku, на самом деле, рисуются и в случае отсутствия WebGL, на чистом JavaScript, но с рядом ограничений). Вместо «1 mod» лучше использовать «dup floor -». Но когда Вы гонитесь за размером меньше 128 байт, приходится жертвовать совместимостью в пользу краткости.
Для любителей хардкора — отрисовка спрайтов в условиях отсутствия бинарных операций и ещё немножко всякого:
Заключение
Forth Haiku очень прост и очень ограничен. При желании, выжать из него можно довольно много, но ни о какой конкуренции с GLSL речи не идёт. Не в том цель. Программа на Forth красива сама по себе. Ход мысли человека, программирующего на Forth, да ещё и программирующего пиксельные шейдеры — интересен сам по себе. Замечательный способ отвлечься и развлечься самому, прекрасное упражнение для детей. Я бы ввёл курс Forth Haiku в школе, честное слово. Хотя бы на одну четверть подтолкнуть детей think different — это здорово.
Ссылки
Проекты Бреда Нельсона, посвящённые языку Forth.
Haiku dump — «чёрный ход» в галерею работ.
ссылка на оригинал статьи http://habrahabr.ru/post/209730/
Добавить комментарий