0,0
рейтинг
21 января 2014 в 02:00

Разработка → Forth и шейдеры из песочницы tutorial

Речь у нас пойдёт о поэзии. Минимализм языка программирования Forth и красота образов демосцены подтолкнули программиста Брэда Нельсона к идее Forth Haiku. Подражая японским хайку, Брэд писал свои первые программы из трёх строк, состояли они из пяти, семи, и снова пяти слов. Но в отличии от традиционного японского жанра, поэзия на языке Forth порождала картины не в воображении читателя, а зримо, на экране компьютера. Эта затея могла бы остаться причудой одинокого фаната компьютерного ретро (Forth прочно ассоциируется со старыми добрыми семидесятыми), если бы Брэд не воплотил её на самой что ни на есть современной платформе (WebGL) и не сделал бы онлайн-редактор общедоступным.

Вот пример кода Forth Haiku и изображение, которое этот код создаёт: «Light Drop» by Brad Nelson.

: iii x y z*
Sin ; x 5 * x y
- iii exp y iii

Light Drop by BradN

Впереди нас ждут немало удивительных (в том числе и «живых») картин, но сперва — немного теории.

Основные понятия


Техника безопасности. Судя по всему, до абзаца «Warning» никто не успевает дочитать, вместо этого сразу идут в онлайн-редактор и пытаются что-то делать. Последствия печальны: сервер завалили, базу работ забили неработающими хайку. Пожалуйста, пролистайте немного вниз и прочитайте правила поведения на воде, прежде чем пускаться в самостоятельное плавание. Спасибо!

Forth — стековый язык программирования. Чтобы совершить действия над несколькими числами, нужно сперва загрузить их в стек. Делается это простым перечислением чисел, разделённых пробелами. Затем указывают действие, которое нужно совершить над числами из стека. Команды также отделены пробелами и могут идти вперемешку с числами в любом нужном для вычисления порядке. Обычные арифмитические действия изымают с вершины стека операнды и помещают вместо них результат.
1 2 3 + /
Пример 1: загружаем в стек числа 1, 2 и 3, затем выполняем сложение двух верхних чисел (стек приобретает вид 1, 5) и далее делим одно число на другое. В итоге стек содержит ответ: число 0.2
1 2 + 3 /
Пример 2: иной порядок действий. Загружаем в стек числа 1 и 2, складываем. Результат — в стеке число 3. Загружаем в стек ещё одно число 3 и выполняем деление. Ответ: число 1 на вершине стека.

Шейдер — здесь речь пойдёт о пиксельных шейдерах, называемых иначе фрагментными. Это программа, исполняемая видеокартой для каждой точки текстуры, а в нашем случае — для каждой точки квадратного окошка размером 256x256. Таким образом, полотно, на котором мы рисуем свои картины, состоит из 65536-ти точек, и именно столько раз будет исполняться программа-шейдер. На выходе она должна выдать цвет точки. Казалось бы, если одну и ту же программу выполнить для всех точек, то и выглядеть эти точки должны одинаково — мы получим большой квадрат Малевича (чёрный, красный, любой другой). Всё так. Но к счастью, программа-шейдер может узнавать координаты x и y той точки, которую рисует в данный момент. А это всё меняет.

Система координат
система координат Forth Haiku
По окончании работы наша программа-шейдер (Forth Haiku) должна оставить в стеке три числа: красную составляющую цвета, зелёную составляющую и синюю. Очень удобно, что яркость RGB-составляющих измеряется в том же масштабе, что и геометрические размеры окна. Просто положив в стек x или y (он ведь разный для каждой точки, да?), получим плавный переход цвета от чёрного к яркому через всё наше полотно.

Время
Кроме x и y, нам доступна ещё одна глобальная переменная t — время. Правда, в браузерах, лишённых поддержки WebGL, она всегда равна нулю. Отсчёт начат в незапамятные времена, к настоящему времени натикало уже немыслимое количество секунд, поэтому практическую пользу для нас представляет, пожалуй, только дробная часть переменной t и периодическая функция типа sin(t), так как она выдаёт значения в диапазоне от -1 до +1.

Minimal animation
x
y
t
sin 1 + 2 /

Пример самой простой анимации. Кладём в стек координату x и оставляем без изменений — она будет отражать красную составляющую цвета. На второе место в стек кладём зелёную составляющую, это будет y (стало быть, зелень заколосится снизу вверх). В качестве синей составляющей кладём время t. А теперь выполняем над последним положенным числом ряд операций. Сперва берём от него синус. Затем 1 прибавляем и на 2 делим. Всё. Был у нас синус в диапазоне [-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».

Golden Bump by Manwe Coffee by Manwe Pacman by Manwe
Нажимайте на картинки. Оно там движется! (в браузерах 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, и это сделано намеренно. Нажимайте на сам оператор и читайте о нём подробнее.
Candy Waterfall by Manwe
: 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 байт, приходится жертвовать совместимостью в пользу краткости.

Для любителей хардкора — отрисовка спрайтов в условиях отсутствия бинарных операций и ещё немножко всякого:
PONG by Manwe Invaders by Manwe Lava Lamp by Trisomie21 and Manwe

Заключение


Forth Haiku очень прост и очень ограничен. При желании, выжать из него можно довольно много, но ни о какой конкуренции с GLSL речи не идёт. Не в том цель. Программа на Forth красива сама по себе. Ход мысли человека, программирующего на Forth, да ещё и программирующего пиксельные шейдеры — интересен сам по себе. Замечательный способ отвлечься и развлечься самому, прекрасное упражнение для детей. Я бы ввёл курс Forth Haiku в школе, честное слово. Хотя бы на одну четверть подтолкнуть детей think different — это здорово.

Ссылки


Проекты Брэда Нельсона, посвящённые языку Forth.
Haiku dump — «чёрный ход» в галерею работ.
Александр Мачуговский @Manwe_SandS
карма
17,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

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

  • –18
    Какой Бред!
    • +10
      Я вообще-то про Нельсона…
  • +4
    Форт… классная штука, мозги прочищает похлеще Пролога :) И при этому еще и вполне прикладная.
  • 0
    Не форт, но еще одна, более хардкорная стековая виртуальная машина. Если пойти по ссылкам можно узнать про механику происходящего. Особенно поражает генерация достаточно сложной музыки в одну строчку на Си. Вроде даже на хабре была статья.
    http://countercomplex.blogspot.com/2011/12/ibniz-hardcore-audiovisual-virtual.html
  • +1
    вторая часть картинок не отображается ни в хроме, ни в сафари
  • +2
    Увы, forthsalon.appspot.com/ не пашет, видно, Хабраэффект. А вот редактор пашет. Вбиваем код, жмём Update. Проба пера:
    (Хабр не знает сорцов Форта?)

    : sq dup * ;
    x 0.5 - sq
    y 0.5 - sq
    +
    sqrt t sin *
    : norm * sin 1 + 2 / ;
    dup 40 norm
    over 60 norm
    rot 80 norm
    
    • +4
      Люблю Форт за DSL'и.
      На нём даже хокку — прграмма:

      : sq dup * ;
      : norm * sin 1 + 2 / ;
      : на x 0.5 - sq ;
      : мёртвой y 0.5 - sq ;
      : ветке + ;
      : чернеет sqrt t sin * ;
      : ворон. dup 40 norm ;
      : осенний over 60 norm ;
      : вечер. rot 80 norm ;
      
      на мёртвой ветке
      чернеет ворон.
      осенний вечер.
      • +2
        Чуть улучшенный пример:

        : sq dup * ;
        : norm * sin 1 + 2 / ;
        : tt t 10 mod ;
        : xc tt 10 / ;
        : в 0 drop ;
        : вечерним x xc 0.25 + 0.75 > 1 * xc - abs 0.25 + ;
        : вьюнком - sq ;
        : я y 0.5 - sq ;
        : плен + ;
        : захвачен… sqrt t 60 mod sin * ;
        : недвижно dup tt 10 * norm ;
        : стою over 70 t 50 mod + norm ;
        : забытьи. rot 80 norm ;
        
        вечерним вьюнком
        я в плен захвачен… недвижно 
        стою в забытьи.
        
        • 0
          Поэтично!
    • +1
      Хабр не знает сорцов Форта?

      В общем случае все сводится вот к этому:
      Скрытый текст
      : INTERPRET ( -> ) \ интерпретировать входной поток
        BEGIN
          PARSE-NAME DUP   \ PARSE-NAME тупо перебирает символы из входного потока в поиске разделителя (пробел и меньше) и возвращает найденное слово
        WHILE
          SFIND   \ Ищем слово в словаре/словарях
          IF
               STATE @   \ Проверка режима работы системы: компиляция или исполнение
               IF COMPILE, ELSE EXECUTE THEN   \ Компилируем слово или исполняем его
          ELSE
               NOTFOUND   \ Зависит от реализации, но в общем случае тут обрабатываем исключение "слово не найдено", обычно тут еще бывает проверка "число?" -> компилируем числовой литерал или кладем число на стек
          THEN
        REPEAT
      2DROP  \ Выкидываем уже не нужную строку со стека
      ;


      Если же интересуют подробности, то можно заглянуть сюда (SP-Forth) или сюда (gForth), или даже вот сюда (форт форум).
      • 0
        Я имел в виду синтаксическую подсветку исходников, конечно.
        • 0
          К сожалению, редактор Habrahabr'а с синтаксисом Forth не знаком.
        • 0
          По сути вся поддержка синтаксиса заключается в разделении слов пробелами и прочими непечатными знаками :)
          • 0
            ну там ещё как минимум комментарии и определение функций, плюс можно подсвечивать числа и строки
            • 0
              Емнип, даже комментарии можно переопределить. Числа точно можно. Собственно во многих реализациях 0, 1, 2 и ещё некоторые константы определены как слова с кодом на ассемблере и не проходят обычный разбор числовых литералов.
              • +1
                Да, все верно. Логика обычного литерала в общем случае сводится к следующему: поместить в стек число из области памяти для статических данных. Обычно это следующий адрес для исполнения (да, прямо там же где и код) и пропуск данного адреса (прибавление размера ячейки) или смещение в области пользовательских данных. Говорю так размыто по причине того, что реализаций очень много и у каждого автора на все есть своё мнение =)
                А положить ноль или единицу на вершину стека — это по сути одна-две процессорных команды.
              • 0
                oh shi… а можно ли переопределить переопределялку?..
                • +1
                  Да. Можно переопределить даже компилятор и вообще практически все что угодно, а при использовании хаков вообще можно сделать все захочется.
                  Сидишь пишешь что-то на форте — вдруг надоело или еще чего: взял написал пару символов и прям тут же пишешь на си.
                  В качестве наглядного примера приведу реальный кусок кода из gForth:
                  Скрытый текст
                  c-library socket
                  \c #include <netdb.h>
                  c-function gethostbyname gethostbyname a -- a ( name -- hostent )
                  \c #include <unistd.h>
                  c-function gethostname gethostname a n -- n ( c-addr u -- ior )
                  \c #include <errno.h>
                  \c #define get_errno() errno
                  c-function errno get_errno -- n ( -- value )
                  \c #include <sys/types.h>
                  \c #include <sys/socket.h>
                  c-function socket socket n n n -- n ( class type proto -- fd )
                  c-function closesocket close n -- n ( fd -- ior )
                  c-function connect connect n a n -- n ( fd sock size -- err )
                  c-function send send n a n n -- n ( socket buffer count flags -- size )
                  c-function recv recv n a n n -- n ( socket buffer count flags -- size )
                  c-function listen() listen n n -- n ( socket backlog -- err )
                  c-function bind bind n a n -- n ( socket sockaddr socklen --- err )
                  c-function accept() accept n a a -- n ( socket sockaddr addrlen -- fd )
                  \c #include <stdio.h>
                  c-function fdopen fdopen n a -- a ( fd fileattr -- file )
                  \c #include <fcntl.h>
                  c-function fcntl fcntl n n n -- n ( fd n1 n2 -- ior )
                  \c #include <arpa/inet.h>
                  c-function htonl htonl n -- n ( x -- x' )
                  c-function htons htons n -- n ( x -- x' )
                  c-function ntohl ntohl n -- n ( x -- x' )
                  \c #define fileno1(file) fileno((FILE*)(file))
                  c-function fileno fileno1 a -- n ( file* -- fd )
                  end-c-library
                  
                  4 4 2Constant int%
                  2 2 2Constant short%
                  
                  struct
                      cell% field h_name
                      cell% field h_aliases
                      int% field h_addrtype
                      int% field h_length
                      cell% field h_addr_list
                  end-struct hostent
                  
                  struct
                      short% field family
                      short% field port
                      int% field sin_addr
                      cell% 2* field padding
                  end-struct sockaddr_in
                  

                  В данном случае, си код сохраняется в отдельный файл, к нему дописывается еще один инклюд с частью кода самого gForth, он скармливается gcc и на выходе получается so библиотека, которая подключается и используется уже в форт-коде.
                  А вот пример хака:
                  : IFNOT   \ ( flag -- ) 
                    ?COMP  \ Проверка на режим компиляции
                    [COMPILE] IF
                    0x85 HERE 5 - C!   \ Хак: подменяем скомпилированную ранее инструкцию JE(0x84) на JNE(0x85)
                    \ Высокоуровневый вариант:
                    \ POSTPONE 0= [COMPILE] IF
                  ; IMMEDIATE
                  
                • 0
                  Главное, что есть оператор forget, который возвращает указанному слову его прошлое значение.
                  • +1
                    Не так. Слово forget переносит текущий указатель области кода на адрес, соответствующий указанному слову. Т.е. затираются все слова, определенные после указанного слова. Т.о. система возвращается в состояние, предшествующее определению данному слову и следующее слово будет записано по адресу «забываемого» слова. Это если в общих чертах. А если чуть углубиться: существуют форт системы с раздельным хранением кода и данных (код и строки, структуры, константы и отеделены друг от друга) и совместным (код и данные перемешаны), есть еще системы с поддержкой многопоточности и т.н. «пользовательских переменных » — переменных, которые для каждого потока разные, плюс существуют разные варианты структуры словарей. Т.о. в какой-то простой системе forget сводится к записи адреса слова в переменную указатель текущего адреса для компиляции, а в какой-то более сложной системе forget настолько сложно сделать, что лучше заняться чем-нибудь полезным и никогда об этом думать.
                    • 0
                      Да, я просто описал простейший случай.
                      Интересно, а есть версии Forth с несколькими дополнительными стеками? Было бы здорово иметь в арсенале операторы типа >r1, >r2, >r3 и, соответственно, r1>, r2>, r3>.
                      • +1
                        Под R обычно обозначает стек возвратов. И он всегда один, т.к. на этот стек сохраняется следующий адрес для перехода перед тем, как происходит вызов слова. Использовать его для временного хранения данных можно, но не желательно, т.к. неосторожное обращение с ним может все поломать. С другой стороны, его часто используют для временных данных с целью ускорения работы программы, т.к. его вершина тоже обычно представлена отдельным регистром или несколькими процессора или он реализован аппаратно (в форт процессорах, например). Кроме того, существуют еще и другие виды стеков: например стек структур управления, стеки циклов, стек для флоат чисел(в х86 он аппаратный), локальные стеки, даже бывают отдельные стеки для адресов, строк и вообще любых других типов данных.
                        А так, стеков можно наделать сколько душе угодно:
                        1024 CONSTANT NewStackSize
                        CREATE NewStack /NewStack NewStackSize ALLOT 0 ,
                        0 VALUE NewStackDepth
                        : -TH   \ ( addr n -- addr+n*cell )
                            CELLS +
                        ;
                        
                        : NewStack!   \ ( n -- )
                            NewStackDepth 1024 < IF
                                NewStack NewStackDepth -TH !
                                NewStackDepth 1+ TO NewStackDepth
                            ELSE
                                ABORT" Переполнение стека NewStack"
                            THEN
                        ;
                        
                        : ClearNewStack
                          0 TO NewStackDepth
                        ;
                        
                        : NewStack@   \ ( -- n )
                            NewStackDepth IF
                                NewStackDepth 1- TO NewStackDepth
                                NewStack NewStackDepth -TH @
                            ELSE
                                ABORT" Опустошение стека NewStack"
                            THEN
                        ;
                        
                        : NewStackGet   \ ( -- n )
                            NewStackDepth IF
                                NewStack NewStackDepth 1- -TH @
                            ELSE
                                0
                            THEN
                        ;
                        
                      • +1
                        Под DOS были популярны варианты с дополнительным FPU-стеком, чтобы напрямую сопроцессором управлять.

                        Было немало версий с отдельным строковым стеком.

                        Наконец, на Форте часто реализуют просто универсальные слова для работы с любым стеком с указанием его в качестве аргумента перед соответствующими словами. Например:
                        : new-stack ( -- stack-id ) ... ; \ Тут реализуем нужные слова
                        new-stack value s0
                        5 s0 >s
                        10 s0 >s
                        s0 sswap
                        s0 s> s0 s>
                        

            • 0
              Плюс стандартные слова. При чём разная подсветка в зависимости от типа слов.
              • 0
                А если слова переопределены? Включая тип.
                • 0
                  Тогда сам редактор должен быть написан на форте =) Гибкость синтаксиса форта — головная боль для редакторов его исходного кода.
                  • 0
                    Ну, синтаксический анализ может провести и не форт-программа, но ей нужны полные исходники используемых (явно и не очень) словарей :)
      • 0
        Хитрость в том, что этот цикл обычно присутствует в коде FORTH-машины в виде захардкоженного вручную шитого кода, так как иначе его некому будет интерпретировать :)
  • +5
    По всем ссылкам вижу только пустые страницы. Что я делаю не так?
    Браузер Chromium 31.0.1650.63 (238485)
    • –2
      Все делаете так, я смотрел сразу после публикации новости, все было на месте, а сейчас и у меня белое. Так что проблемы на их стороне.
  • 0
    Задумка крутая, но Форт там слишком уж урезанный. Нет IF, да и неплохо бы оставлять в стеке лишние значения, а не сбрасывать.
    • +1
      Автор убрал IF и FOR, на примерах объясняет, что без них даже короче программы получаются. В самом деле, в шейдере не будешь писать цикл от 0 до 100500, а 10 повторов вполне можно записать десятью вызовами одного и того же оператора. В рейтрейсинге у меня 20 вызовов, и для окошка 256x256 этого вполне достаточно. А отсутствие IF обходится через операторы > и <. Да, приходится менять привычный образ мышления. Но ради этого всё и затевалось.
      • 0
        Циклы или рекурсия были бы весьма полезны для генерации фракталов:
        : xx x .8 - ;
        : yy y .5 - ;
        : tt t .4 * ;
        : a .25 * ;
        : mx xx 2 a tt cos * 2 tt * cos a - + ;
        : my yy 2 a tt sin * 2 tt * sin a - + ;
        : mm 2dup Z* mx my z+ ;
        mx my 
        mm mm mm mm mm mm
        mm mm mm mm mm mm
        mm mm mm mm mm mm
        mm mm mm mm mm mm
        dup * swap dup * +
        0 > negate 1 +
        dup dup
        
        • 0
          Да, но Вы же справились и без них!
          P.S. а почему от имени Anonymous размещаете?
          • 0
            Хотелось бы прерывать итерации при превышении некоторого порога — в текущем виде оно за границей фрактала, похоже, в бесконечность или NaN уходит и потом вообще сравнение не проходит. И разукрасить опять же можно было бы «классическим» образом. И глубину обсчета менять.

            А какая разница? Если пускают и так. Лишние действия.
            • 0
              Когда я делал рейтрейсинг, смекнул, что отслеживать порог не имеет смысла — при малом количестве итераций он всё равно редко достигается, и это лишнее сравнение только мешает. В окошке 256x256 и так всё шустро получается.
              Но с фракталами да, посложнее. Хотя, там в галерее есть «разукрашенный» фрактал.
              • 0
                Разукрасить несложно — хотя бы оставить после вычисления комплексное значение вместо модуля. «Классическая» раскраска зависит от количества итераций, по прошествии которых произошел выход значения за заданную границу. Хотя можно при желании и в текущей реализации попытаться прикрутить счетчик. Только удлинится программа, а тут есть какие-то ограничения то ли на длину кода, то ли на сами вычисления — при добавлении еще одной строки mm mm mm mm mm mm вместо фрактала получается однотонный розовый квадрат.
            • 0
              Автор обещал сделать проверку полей на null, чтобы случайно не спамили (сейчас половина размещённых хайку — результат случайного нажатия кнопки submit). Станет чистить базу — поудаляет всех анонимусов. Поэтому имя лучше всё же вписать.
  • 0
    Мои программы, конечно, ужасно нечитаемы.

    А не поэтому ли:
    первое, что я делаю, это переопределяю команды языка Forth.

    И конечно еще очень важны комментарии на уровне хотя бы «оно делает то-то и то-то».
    • +2
      Да, я об этом и говорю. Заменяю «dup» на «d» ради экономии нескольких байт. Цель игры — уложить программу в 128 или в 256 байт. Мы вообще в хабе «демосцена» и «ненормальное программирование», если что. В изначальном-то виде программа занимала в три раза больше места, имена были длинные и понятные. Но после нескольких дней практики читать чужие программы (даже сильно сокращённые) не составляет труда.
      Официально транслятор Forth Haiku не понимает комментарии, лишь потом был сделан хак.
      • 0
        Эх, помню, в студенческие годы одна из окончательно добивших (в хорошем смысле) вещей Форта для меня был тот факт, что комментарий (например, "--") — это тоже слово. :)
        • 0
          Вообще, обычно комментарий в форте это обратный слеш \, а два минуса -- это обычно определение нового поля в текущей структуре данных.
          0
          <size> -- field1_name
          <size> -- field2_name
          <size> -- field3_name
          CONSTANT /structure_name
          

          Впрочем, слово «обычно» зачастую сложно применимо относительно того или иного аспекта в форте.
  • 0
    Саша, привет!
    С удовольствием смотрю все созданные тобой эффекты, вот бы еще собрать их в коллекцию и сделать экзешник:)
    Space Invaders — огонь! :)

    P.S. Brad обычно все-таки на русском пишется Брэд.
    • 0
      Объединять разрозненные эффекты в exe, думаю, нет смысла. Думаю, мы лучше сразу что-то более цельное сделаем.
      А пока ещё свежие эффектики
      image

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.