Los shaders siempre fueron el tema de Unity que más me costó entender. Llevo años usando el motor (hice un juego 3D de sigilo entero desde cero para mi tesis) pero cada vez que abría un archivo de shaders se me apagaba el cerebro. La GPU piensa distinto, y nunca me tomé el tiempo de aprender su idioma.
Lo que cambió fue un rabbit hole en YouTube. Los videos de Sebastian Lague, los boids de Richard Lord en Vimeo, el arte procedural de Arsiliath. Dos fines de semana después, tenía mis propias versiones. No son proyectos pulidos. Son pruebas de concepto, de esas donde cambias un número a las 2 de la mañana y la pantalla hace algo que no esperabas.
Boids
Craig Reynolds (1986): dale a cada pájaro tres reglas. No amontonarte con los vecinos, volar más o menos para el mismo lado que ellos, y no alejarte mucho del grupo. Sin líder, sin plan. Juntas miles y la bandada aparece sola.
Separación, alineamiento, cohesión. Lo demás (los remolinos, las divisiones, las formas orgánicas) es emergente.
Mi versión corre cientos de miles de agentes. Como el 1% son depredadores (los rojos/naranjos), el resto presas (cyan). Las presas siguen las tres reglas clásicas más una: si ves un depredador, arranca. Los depredadores no siguen reglas de bandada. Solo persiguen.
Lo interesante es el comportamiento que nadie escribió. Las presas se agrupan en clusters densos, igualito a los peces reales cuando hay un tiburón cerca. Un depredador atraviesa la bandada, se parte en dos y se reagrupa por atrás. Nunca programé "dividirse y reagruparse." Pasa solo.
Para manejar cientos de miles de agentes a 60 fps, una grilla de hash espacial divide el espacio 3D en cajas. Cada agente solo revisa su caja y las 26 vecinas (3x3x3), bajando el costo de O(n^2) a más o menos O(n). El renderizado usa instanciado en GPU (una sola draw call), con el vertex shader estirando los meshes según la velocidad para un efecto de estela bioluminiscente.
Reacción-Difusión
Alan Turing (1952) propuso que los patrones en la piel de los animales (manchas de leopardo, rayas de cebra, ramificaciones de coral) podrían salir de dos sustancias químicas esparciéndose a velocidades distintas. Una activa el crecimiento, la otra lo mata. Donde se encuentran, los patrones se forman solos.
El modelo Gray-Scott: dos químicos, A y B, en una grilla 2D. A se esparce rápido, B se esparce lento. Donde se juntan, A se consume y B crece. A se repone (tasa de alimentación f), B decae (tasa de eliminación k). Dos ecuaciones, todos los patrones salen de acá:
dA/dt = Da*nabla^2*A - AB^2 + f(1-A)
dB/dt = Db*nabla^2*B + AB^2 - (k+f)B
Cambias f y k y aparecen manchas que se dividen como células, rayas tipo huellas dactilares, espirales, manchas que pulsan, ramas de coral. Misma ecuación, distintas perillas.
Le agregué tres especies compitiendo, cada una con su propio inhibidor, todas alimentándose del mismo A, suprimiéndose entre sí al contacto. Lo que sale es una guerra territorial: tres especies delimitando dominios, peleando en las fronteras, a veces tragándose enteras.
Unos trucos lo hacen parecer vivo: difusión anisotrópica ponderada por un campo de ruido rotante para que los patrones fluyan como corrientes, ruido Perlin modulando las tasas de alimentación/eliminación por la grilla, e iluminación falsa con bump mapping que hace que la simulación 2D plana se vea tridimensional.
Lo Que Me Queda
El shader de boids tiene unas 200 líneas. El kernel de reacción-difusión no mucho más. Pura aritmética: sumas, multiplicaciones, un Laplaciano por acá, un producto cruz por allá. Nada se parece a una bandada. Nada se parece a un coral. Le das play y ahí están.
Terminé construyendo una versión para navegador de estos algoritmos para que puedan jugar con ellos.
