recur-shadertoy-ports

Shaders adaptats a r_e_c_u_r que funcionen a la Raspberry Pi.

Shadertoy corre GLSL ES 3.00; r_e_c_u_r corre GLSL ES 1.00 sobre una GPU VideoCore IV. La Pi no avisa quan alguna cosa no hi cap: un shader mal portat compila, enllaça i pinta una pantalla verda, negra, o es penja, sense cap error ni a la màquina ni a la interfície. Que es vegi bé al portàtil, amb un GLSL més modern, no garanteix res sobre l'aparell — per això la regla d'aquesta carpeta és tan estricta.

Es va omplint a mesura que un port passa la prova a la màquina de veritat.

Els ports es fan amb agent-recur, un agent d'IA especialitzat amb la seva skill, públic i reutilitzable — encara que qui tanca un port sempre és qui el veu funcionar a la Pi.

Cada shader d'aquí és obra d'algú altre, i no tots amb les mateixes condicions: deu són CC BY-NC-SA 3.0 i un és MIT. Vol dir que has de citar l'autor original —no pas a nosaltres— i que en deu dels onze un bolo pagat queda fora. Abans de reutilitzar-ne cap, llegeix LICENCIAS.md: hi ha la procedència, l'autoria i la llicència de cadascun, una per una.

La regla d'aquesta carpeta

Aquí només hi entra el que algú ha vist funcionar a la Raspberry Pi. No el que compila, no el que es veu bé a glslViewer, no el que «hauria d'anar». Cada shader de shaders/ té darrere una sessió a l'aparell i una confirmació explícita.

El que està en dubte, a mig fer o descartat per lent es queda al repositori de treball (Medialab-Terata, a proyectos-en-curso/cacharreo/recur/), que és on viu també tota l'eina: el revisor automàtic, la guia de conversió, les notes de màquina i la instrumentació de fps. Aquest repositori és la sortida, no el taller.

Què hi ha

Shader Què és Controls
crt_margarita Patró geomètric sobre CRT simulat 3
crt_plaid Ídem, trama de quadres 3
crt_threesome Ídem, ventall de colors 3
crt_bacteria Ídem, tramat orgànic 3
crt_sunlight Ídem, calidoscopi 3
beat_ring Anell de 8 polsos amb envolupant ADSR sincronitzada a un tempo x0 velocidad, x1 radio, x2 halo
rejilla Reixa de pilars vista des de dins, amb sorra x0 zoom, x1 spine, x2 arena
rejilla_lisa La mateixa sense sorra, cantell net x0 zoom, x1 spine, x2 zoom2
panal Reixa hexagonal de dos tons polsant en anells, a 144 BPM x0 compás, x1 onda, x2 zoom
osciloscopio L'ona d'un bombo sintetitzat, traçada com en un oscil·loscopi x0 fase, x1 escala, x2 limpieza
osciloscopio_negativo El mateix, en negatiu: fons negre i traç blanc x0 fase, x1 escala, x2 limpieza
sol Un sol en remolí de pinzellades, a pantalla completa x0 altura, x1 remolino, x2 color
atardecer El mateix cel sobre un mar de pinzellades, amb camí de sol x0 altura, x1 remolino, x2 color
pad Veu de pad: ona quadrada amb FM lleu, traçada sobre un remolí de soroll x0 tempo, x1 temblor, x2 densidad
bateria Veu de bateria: bombo amb atac sec i hi-hat de soroll x0 tempo, x1 temblor, x2 densidad
arpegio Veu d'arpegi: sis notes encadenades pel seqüenciador, en diagonals x0 tempo, x1 temblor, x2 densidad
acorde Veu d'acord: tres notes per síntesi additiva, sis harmònics x0 tempo, x1 temblor, x2 densidad
acorde_ligero El mateix acord amb tres harmònics en lloc de sis, i més barat x0 tempo, x1 temblor, x2 densidad
cascada Cascada infinita de triangles en zoom, tres direccions a 120 graus, una per canal de color x0 velocitat, x1 enquadrament, x2 contorn/ple
voronoi Cel·les de voronoi animades, amb les vores taronja, isolínies a dins i un punt per cel·la x0 escala, x1 velocitat, x2 gruix
zumbido Camp de franges en blanc i negre que es retorça i titil·la com si reaccionés a un cop de greus x0 limpieza, x1 manual, x2 velocidad
alumbrado La mateixa sala del shader d'en Flyguy —reixa de punts, barres, anell amb una ranura que gira— recorreguda per les sis llums, sense ombres. Adaptació poc fidel: no s'assembla a l'original x0 parell de mà, x1 pas de la reixa, x2 intensitat
alumbrado_sombras La mateixa sala amb UNA llum i ombres de debò, marcades pas a pas. Va a 4,8 fps: base lenta, no per marcar ritme x0 mou la llum, x1 pas de la reixa, x2 intensitat
alumbrado_luces Sis llums de color movent-se sobre un fons llis: una de vermella que batega al centre, una de blava girant, dues que pugen i baixen pels costats i un parell simètric que porta el comandament x0 parell de mà, x1 velocitat, x2 intensitat

Tots són 0-input (generatius). Les captures de referència són a capturas/.

alumbrado es publica declarat com a adaptació poc fidel: qui el va provar a l'aparell diu que no s'assembla a l'original, i es publica per allò que és —una peça que va a 30 fps i es pot tocar— no com a reproducció. Qui busqui l'original, que vagi a Shadertoy.

alumbrado_sombras és l'única de les tres amb ombres reals, i va a 4,8 fps, mesurats a l'aparell. Són 208 ms entre fotogrames, així que qualsevol cop més curt que un cinquè de segon no s'hi veu. Es publica dient-ho.

alumbrado_luces és un tros, i convé saber-ho: l'original de Flyguy és de dues passades i el seu Buffer A calcula un mapa d'ombres que r_e_c_u_r no pot tenir. Aquí hi ha les sis llums amb els colors, les brillantors i els recorreguts copiats número a número d'aquell buffer; el que no hi és és la sala que il·luminaven ni les ombres que projectaven. Refer-ho sencer en una sola passada surten unes 5.800 operacions per píxel contra un sostre mesurat de 250.

panal no és una traducció: l'original fa raymarching (234 crides a map() per píxel) i en aquesta màquina això no hi cap de cap manera. Es conserva la idea —la reixa hexagonal, l'ona radial, el compàs— i es dibuixa en 2D, amb una avaluació per píxel. Està explicat a la seva capçalera.

osciloscopio sí que és una traducció, però amb un avís: l'original multiplica el temps per 5e19, que no és un càlcul sinó llençar la precisió a propòsit perquè l'ona surti com a soroll. Això és l'efecte, i per això no es veu igual que a Shadertoy: depèn de com arrodoneixi cada GPU. El control x2 baixa aquest multiplicador i porta del soroll a un traç net.

Diagnòstics

diagnostico/ no són efectes: són shaders que contesten preguntes sobre l'aparell que no es poden contestar llegint el codi.

  • test-orientation.frag — resolució i orientació de l'eix Y.
  • test-precision.frag — si mediump en aquesta màquina és fp16 o fp32. Tres franges: blanques = es comporta com a fp32.

On es va provar

Raspberry Pi 3B+, amb el sistema precompilat per cyberboy666, versió 2.0.2 (imatge recur2_0_2).

Placa Raspberry Pi 3B+
Sistema Imatge recur2_0_2 de cyberboy666, precompilada
GPU VideoCore IV, OpenGL ES 2.0 (GLSL ES 1.00)
Sortida 720x576 (sdtv_mode=02, PAL entrellaçat)
Ritme Topall de 30 fps, que el posa c_o_n_j_u_r per programari

Això no és un peu de pàgina: els límits que hi ha darrere d'aquests ports són els d'aquesta combinació concreta, no els de r_e_c_u_r en abstracte. Quanta complexitat hi cap abans que la GPU no pugui més es va mesurar allà, i en una altra placa o amb una altra imatge caldria tornar a mesurar-ho.

Sobre la norma de vídeo (corregit el 2026-09-19): l'aparell de referència és PAL, perquè tota la cadena de sortida de qui l'opera (Roland V-4, CRT) ho és. Fins aquesta data, aquí es documentava NTSC (720x480) per error. Alguns dels fps que hi ha en aquesta mateixa pàgina i a les capçaleres dels .frag es van mesurar abans que es detectés l'error, sobre un quadre un 20% més petit del que corre l'aparell de debò -- encara estan pendents de remesurar-se un per un.

Cyberboy publica també una 2.1.0 que no hem comprovat. Si fas servir la 2.1.0 i et funciona, posa't en contacte amb les companyes del Medialab Terata [www.terata.org].

Instal·lació

Copiar els .frag a la carpeta de shaders de la targeta. A la imatge 2.0.2:

rsync -av shaders/0-input/ pi@IP:/home/pi/Shaders/mis_shaders/

Compte: r_e_c_u_r busca per nom de fitxer i es queda amb la primera coincidència, començant per /media/pi. Si hi ha un llapis USB posat amb un shader del mateix nom, es carrega aquell i no el de la targeta.

Notes de cada port

Les condicions de cada shader són a LICENCIAS.md, una per una. El que segueix aquí són les notes de cada port: què es va retallar, què es va reescriure i què convé saber abans de carregar-lo.

sol i atardecer són un retall, no una traducció, i convé saber-ho. L'original de Noztol té quatre capes —cel, mar i sorra, arbres, herba de l'illa— i costa unes 2214 operacions per píxel. Aquest aparell aguanta unes 250. Mesurades les capes per separat, la del cel costa 62 i va a 13,7 fps; qualsevol versió amb el bucle de pinzellades de l'horitzó es queda entre 3,8 i 7,8.

Així que sol és aquesta capa sola, i atardecer és aquesta capa més un mar escrit a mà: en lloc de portar la capa d'aigua de l'original (725 operacions), reflecteix el punt respecte a l'horitzó, hi avalua només la paleta i ho modula amb bandes i un camí de sol. Unes 19 operacions. Va a 10,1 fps.

Els dos porten dos ajustos que no són a l'original i que són d'aquest aparell: ASPECTO_PANTALLA, perquè el quadre és 720x480 i surt per compost a una tele 4:3 que l'aixafa de costat; i TONO_BASE, perquè amb la paleta sense desplaçar el cel surt blau de dia i això és una posta de sol.

osciloscopio_negativo és el mateix shader amb una sola línia canviada: 1.0 - l en lloc de l. Surt blanc net i no un color complementari perquè la imatge ja era grisa — kick() retorna les dues components iguals, així que el vec4(l.x, l.y, l.x, ...) de l'original està preparat per a dos traços que aquí mai arriben a separar-se. Comprovat abans de donar-lo per bo: fixant el temps, els dos renders sumen 255 en els 262.144 píxels, o sigui que és una inversió exacta. Va a 28,1 fps.

zumbido és el primer port l'original del qual depenia de l'entrada d'àudio de Shadertoy, que r_e_c_u_r no té: conjur.cpp enllaça u_time, u_resolution, u_x0..u_x3, u_tex0 i u_tex1, i res més. L'original no calculava un volum — llegia la textura d'àudio amb abs(sin(t)*100.0), que gira tan de pressa que retorna soroll acotat en [0,1] — així que aquí es reconstrueix el titileig, no l'àudio, amb un hash de coma flotant sobre el temps. Es va descartar a propòsit treure'l de la luminància de u_tex0: hauria fet que un shader generatiu depengués de si hi ha vídeo carregat a la cadena en aquell moment.

Avís per a qui el carregui: de vegades salta un fotograma sencer en negre, i no és una fallada. w és un sinus, va de -1 a 1, i el framebuffer retalla a 0 tot el negatiu; quan el factor que no depèn del píxel cau pel costat negatiu s'apaguen tots alhora. Verificat amb números, no suposat: mitjana de gris exactament 0 en els 262.144 píxels amb manual=0.5 a t=5.0, i no nul·la a t=5.3 ni a t=6.0 amb el mateix control. És un apagament de l'instant, no un tram mort del control, i encaixa amb el cop de greus que imita. Costa 12 operacions per píxel: el port més barat del repositori.

Les quatre veus de "Music toolbox"

pad, bateria, arpegio i acorde (més acorde_ligero) surten d'un mateix shader de nimitz que a Shadertoy divideix la pantalla en quatre quadrants i traça a cadascun la forma d'ona d'una veu. És la pestanya d'imatge d'un shader amb so, però la part visual és autocontinguda: dibuixa les ones a partir d'un rellotge de compassos a 112 BPM, sense necessitar l'àudio.

Els quatre quadrants junts no hi caben aquí: la traducció literal dona 179 línies i unes 396 operacions per píxel, i aquesta màquina deixa de dibuixar per sobre de 146 línies i es traba per sobre de 360. Va una veu per fitxer, a pantalla completa, com els 124 shaders que porta la imatge. Es perd veure-les alhora; es guanya que es vegin.

El que a l'original era la textura de soroll de Shadertoy (iChannel0) aquí és soroll procedural, perquè no depenguin de si hi ha vídeo carregat a la cadena.

Sobre acorde i acorde_ligero: el lleuger retalla els harmònics de la síntesi additiva de sis a tres. Després d'optimitzar els dos, el lleuger costa 66 i el fidel 80, així que la diferència ja no és de "hi cap o no hi cap" sinó de timbre: el fidel sona —es veu— més dens. Hi són els dos perquè l'elecció és estètica.

Els cinc van passar per tres voltes d'optimització que no canvien un sol píxel (0 de 262.144, verificat amb el temps i els controls fixos): envolupants idèntiques que es calculaven un cop per nota en lloc de tres, pesos d'arpegiador calculats quinze cops quan en bastaven tres, i un cos que s'escrivia a les dues branques d'un if —que en aquesta GPU es paguen totes dues, perquè els salts curts s'aplanen—. El més car va baixar de 213 a 80.

cascada, i la trampa de l'alfa

De Fabrice Neyret2. Una cascada infinita de triangles que fa zoom cap al seu propi punt de fuga, amb tres direccions a 120 graus, cada una en un canal de color.

Val la pena explicar-ne el port perquè la trampa que va costar més no és de GLSL, és d'aquí: l'original acaba amb O = 1. - vec4(N,N,N, 1.), i el quart component surt 1.-1. = 0.0. O sigui que el shader pinta amb alfa zero. Shadertoy ignora l'alfa i allà es veu perfecte; en aquest aparell un fragment transparent no pinta res, i la pantalla es queda negra tot i que el shader compila i corre a 20 fps. Va costar tres viatges amb la targeta.

Dues regles que en surten, per a qui porti alguna cosa:

  • Escriu l'alfa a part i a 1.0 sempre: gl_FragColor = vec4(c, 1.0), amb el color calculat com a vec3. No el deixis sortir d'una operació.
  • Quan mesuris un render, mira els QUATRE canals. Les conversions a escala de grisos o a RGB descarten l'alfa, així que un shader invisible dona mesures de brillantor perfectes.

L'altra decisió: l'original ofereix dues sortides i en deixa una comentada — abs(d) dibuixa el contorn i d omple els triangles. El contorn és fidel però molt fosc (brillantor mitjana 3/255 contra 85/255 de l'omplert), i si aquestes línies fines es llegeixen en un tub per compost no es pot saber sense mirar-ho. Per això la tria és un commutador al comandament x2 en lloc d'una constant: es decideix davant la pantalla. fwidth(), que l'original fa servir per mantenir el gruix del traç constant, s'ha substituït per una fórmula analítica, perquè en aquest aparell no se sap si GL_OES_standard_derivatives existeix.

Els crt_* van de 2-6 fps a 30

Els cinc crt_* es van mesurar el 2026-09-09 i quatre anaven a 2,1-6,4 fps. La causa era l'antialiasing: l'únic que corria bé era l'únic amb AA 1., i els altres quatre tenien AA 2., o sigui quatre mostres i el cos del bucle desenrotllat quatre vegades. Amb AA 1. van a 30,0 / 30,0 / 30,0 / 15,4.

El detall que ho explica: la reixeta de CRT és cos(3.14*uv.x*640.), 320 cicles en 720 px = 2,25 px per franja, just al límit de Nyquist d'aquesta sortida. Es pagaven quatre mostres per píxel per sostenir una trama que la sortida no pot mostrar a cap nivell d'AA. També se'ls va treure el comandament u_x3 d'intensitat de CRT, que estava mort: amb X3_AS_SPEED actiu aquest uniform arriba valent 0.0 i el comandament no el mou.

voronoi, i el limit que no es veu al codi

D'Inigo Quilez, i l'unic d'aquest repositori amb llicencia MIT --els altres nou son CC BY-NC-SA. Aixo vol dir que aquest si es pot fer servir en un bolo pagat i si es podria aportar de tornada a r_e_c_u_r; els altres no. MIT obliga a conservar el text integre de la llicencia, i per aixo originals/voronoi-bordes-iq.frag no es opcional.

El que hi ha aqui NO es l'algorisme del shader original, i val la pena saber per que. iq va escriure aquell shader precisament per ensenyar el metode CORRECTE de dibuixar les vores: una segona passada que mesura la distancia real a la bisectriu entre la cel·la guanyadora i les veines, en lloc del F2-F1 de tota la vida. El seu article comenca ensenyant que F2-F1 dona linies de gruix desigual.

Aquella segona passada no hi cap en aquest aparell. Amb els dos bucles 3x3 el shader compila, enllaca, corre a 4,6 fps i pinta NEGRE; amb un de sol funciona. Un bucle 3x3 es desenrotlla en nou copies del cos, i dos son divuit. Es va trigar cinc carregues a esbrinar-ho, i pel cami va apareixer una cosa que no sabiem: el log SI avisa, amb OpenGL error after checkAndCreateProgram(): error 1285, que es GL_OUT_OF_MEMORY.

Aixi que aqui hi ha el F2-F1 d'una sola passada --el que el mateix iq escriu al seu article com "la implementacio que la gent fa servir"--. Es perd l'exactitud de la vora, que era la gracia de l'original, i es conserven les cel·les, l'animacio, les isolinies i els punts. Cost 66 en lloc de 154.

S
Description
Ports de shaders de Shadertoy per a fer servir amb r_e_c_u_r
Readme 8.9 MiB
Languages
GLSL 100%