Ещё слово о процедурной графике

Не так давно я публиковал здесь небольшой обзор по работам в жанре процедурной графики и, в частности, призывал поучаствовать в конкурсе. Упомянутый мной конкурс на фестивале Chaos Constructions состоялся — было представлено семь работ размером до 1кб, о которых, особенно о двух собственного изготовления, я и хочу рассказать.

Первая моя работа называется Way и написана для платформы Sony Playstation 1 (PSX) на ассемблере MIPS R3000.



Хотя RISC процессоры обычно не лучший выбор для написания компактного кода, но в случае с PSX это компенсируется способностью GPU аппаратно рисовать примитивы.
По сути, мы просто перечисляем примитивы в специальном формате и потом отправляем этот список (display list) в GPU, предварительно установив графический режим 640x480.

К примеру, вот так описывается небо — четырёхугольник с заливкой градиентом (Гуро):

sky
    db soil, soil>>8, soil>>16, $8   ; link to next list element, size of current element
    dw $38aa5b00    ; $38 - gradated 4p poly. CCBBGGRR (C - command, b,g,r - color0) $38c4deeb 
    dw $00000000    ; y0  x0 0 0
    dw $30bb5b00    ; color 1  
    dw $0000027f    ; y1  x1 639 0
    dw $30f3dec6    ; color 2    
    dw $01300000    ;  y2  x2 0 304
    dw $30f3dec6    ; color 3 
    dw $0141027f    ; y3  x3 639 321

Горизонт и другие линии намеренно идут непараллельно краям экрана, чтобы получить некий эффект мультяшности.

Вот исходник.

Совершенно аналогичным образом устроена и работа "Invitation to CC'2024" (928 байт) — с некоторым закосом под супрематизм, при этом буквы формируются «вырезанием» одних четырёхугольников из других (рисованием их в цвет фона).



Несколько сложнее обстоят дела с другой моей работой — Face (646 байт), написанной для платформы Vectrex.



Особенностью игрового компьютера Vectrex является векторный дисплей — растра нет, лучом нужно управлять вручную (с некоторыми оговорками). Я уже подробно рассказывал об этой платформе, поэтому здесь опишу суть происходящего не слишком углубляясь.

Лицо состоит из нескольких элементов — брови и границ глаза (прямые линии), зрачка глаза (кривая) и контура лица (две кривых).

С прямыми всё просто — есть подпрограмма BIOS Draw_Line_d. А вот с кривыми — сложно.
Суть подхода в том, что мы вручную запускаем интегрирование — луч начинает перемещаться сверху вниз от текущего своего положения до заданного нами значения Y. В это время мы записываем в канал X разные значения и таким образом заставляем луч смещаться влево (если значения отрицательные) и вправо (если положительные). После записи очередного значения мы или сразу записываем следующее или некоторые время ничего не делаем, предоставляя лучу продолжить движение в прежнем направлении. Это «ничегониделанье» первоначально выглядело как куча NOP'ов после каждого изменения X, причём я приклеил к экрану кусок пергамента с рисунком и подбирал значения в VIDE эмуляторе.



...
                lda     #30
                sta     <VIA_port_a        	; put X to DAC 

                nop                         ; 2 cycles for each nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop

...

В 1кб при таком подходе не уложишься, поэтому все длинные последовательности nop'ов я свернул в циклы. Причём, сами инструкции подготавливающие и реализующие цикл конечно тоже имеют некую длительность выполнения, что тоже пришлось учитывать, посчитав все такты (ну, примерно):

...
                lda     #30
                sta     <VIA_port_a        	; put X to DAC 

; 10
                ldb    #2            ; 2 cycles
d6a:            decb                   ; 2 cycles
                bpl     d6a             ; 3 cycles
...

Вообще, первая мысль которая должна возникать при взгляде на этот код — что там куча повторяющихся кусков и все задержки можно свести в таблички и доставать их оттуда. Увы, любой лишний такт приведёт к продлению рисуемого вектора, поэтому простое решение здесь не сработает. Теоретически можно написать генератор кода — т.е. чтобы программа по данным из таблички воссоздавала нужные последовательности инструкций. К сожалению, у Vectrex-а всего лишь 1кб ОЗУ — упихать наверное можно, но игра в данном случае не стоит свеч (в 1кб и так всё влезло).

Отдельная проблема — наличие в силуэте лица элементов, близких к горизонтальным (возле губ). Дело в том, что луч движется сверху вниз с постоянной скоростью — его нельзя притормозить. Крутизна изгиба определяется исключительно величиной записываемого в X значения. Но больше чем ± 127 туда, по понятным причинам, записать нельзя.
Можно было бы сделать эти два элемента просто отдельными линиями, но это привело бы либо к видимым нестыковкам концов линии и прилегающей кривой, либо к ярким точкам в местах соединения. В результате я сделал силуэт из четырёх кривых, каждая из которых начинается там, где заканчивается вторая. Тот факт, что интегрирование запускается заново, позволяет лучу с самого начала сегмента двигаться влево (и вправо), таким образом получаем близкие к горизонтальным линии.

Зрачок — просто кусочек кривой.

Летящие справа светящиеся объекты отображаются вызовом подпрограммы BIOS Dot_List, которая рисует векторы с нулевой длиной по списку координат. Подпрограмма вызывается трижды со смещением по горизонтали, при этом после каждого вызова меняется яркость (чтобы создать эффект motion blur).

Текст также выводится подпрограммой BIOS Print_List_hw, что, при таком количестве текста, является весьма неэффективным методом. Однако, в данном случае он оправдан — во-первых с целью экономии байт (вызов стандартной процедуры короче написания своей), во-вторых всё вместе (текст, прямые, кривые) укладывается в 30 тыс тактов процессора, что необходимо чтобы избежать мерцания и других нежелательных эффектов.

Хотя изначально эта работа задумывалась именно как процедурная графика, в процессе вышеописанных развлечений мне пришла в голову мысль, что можно сделать лицо слегка шевелящимся, просто изменяя в некоторых местах значения на которые отклоняется луч и задержки после записи этих значений. Так появилась ещё интро Aniface (820 байт),



Видео не передаёт теплоты и ламовости векторного CRT монитора (да и фото — лишь отдалённо). Увы, тут надо весьма серьёзную видеокамеру (нужен широкий динамический диапазон и высокая светочувствительность) и много времени на подбор параметров съёмки.

Напоследок покажу все работы (других авторов), представленные на данный конкурс фестиваля Chaos Constructions:

Дисквалифицированная работа Rebranding by artёmka // wbcbz7 — работа написана для TIC-80, но по правилам конкурса работы для виртуальных платформ не принимаются



Шестое место — Ancient Aztec Matrix 128b by g0blinish (125 байт) для Commodore 64 (процессор 6510)



Пятое место — моя, уже упомянутая выше, работа Way by Frog //ROi

Четвёртое место — Reptile 1k by g0blinish для ZX Spectrum (процессор z80)



Третье место — моя, уже упомянутая выше, работа Face by Frog //ROi

Второе место — Teonon by dart / fnm (1022 байта) — единственная из всех, написанная для современной платформы (PC/Windows)



Победитель — работа RTZX by RCL of Virtual Vision Group (983 байта).



Как пишет в пояснении к работе сам автор, им реализован честный raytracer для ZX Spectrum 48K (процессор z80). На входе положение камеры и четыре примитива. Работа была написана под впечатлением реализации raytracer'а на Бейсике, в котором рендеринг шёл 17 часов. Конкурсная работа рендерится 43 секунды.

Места определялись голосованием зрителей — интересно, что RTZX победила, т.к. люди, очевидно, смогли оценить сложность реализации raytracer'а на z80 в таком объёме кода.

Все работы можно скачать здесь или здесь.

В целом меня, как одного из организаторов Chaos Constructions, особенно топившего за этот конкурс, весьма порадовали представленные работы — все они (включая дисквалифицированную) весьма достойны.

2 комментария

avatar
Конкурс хороший, особенно как альтернатива one scene compo (когда с музыкой напряг).
Единственное, есть странный момент в правилах [СС2024]
Не совсем понятно, чем вам не угодили Фэнтази Консоли, если в 1k на GLSL шейдерах можно сделать всё тоже что и на ФК, только круче (shadertoy не даст соврать).
Получается что тут что-то лишнее: или GLSL, или приписка запрещающая ФК.
  • DArt
  • 0
avatar
Скопирую сюда мои ответы из чата...

Конечно, СУТЬ разницы между ФК и шейдерами в том, что шейдеры не были выдуманы на ровном месте — есть железки. Но с формальной точки зрения граница проходит в стандартности поддержки платформой. Ну т.е. если бы винды или линукс имели поддержку инструкций TIC-80, ситуация бы изменилась.
Аналогично, про работы на javascript — если целевая платформа браузер, вопросов нет. Если платформа PC/Windows, браузер не является частью ОС (тут конечно можно долго дискутировать, являются ли все программы в поставке ОС её частью, но моё такое мнение).
[...]
С виртуальными консолями вообще прикол — раньше, когда люди писали демку и использовали движок (скажем, werkkzeug какой-нибудь), то им надо было включать этот движок в исполняемый файл. Т.е. размер считался суммарно. И теперь вдруг (следите за руками!) мы называем движок виртуальной платформой и его размер перестаёт считаться, больше не надо включать его в состав работы! :)
----
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.