Una película animada creada en 8 kB
(ctrl-alt-test.fr)- El equipo Ctrl-Alt-Test empezó en noviembre de 2022 un experimento para meter en 8 kB una demo en tiempo real que pareciera un corto animado, y en abril de 2023 presentó The Sheep and the Flower
- El resultado es un único
.exeque genera los gráficos y la música sin archivos de recursos ni dependencias externas; las escenas se calculan en tiempo real en la GPU con shaders GLSL y raymarching - La oveja, los letreros, la cámara y la línea de tiempo no se basan en modelos ni texturas guardadas, sino en SDF, texturas 3D procedurales y parámetros de tiempo hardcodeados; unas 25 tomas de cámara se ajustaron manualmente
- La música se hizo con OpenMPT y 4klang; redujeron los instrumentos de 16 a 13 y aumentaron las repeticiones para mejorar la eficiencia de compresión
- Los 8 kB finales fueron posibles gracias a la compresión con Crinkler y mejoras en Shader Minifier; el código de shaders de 42 kB, tras minificarse y comprimirse, quedó en unos 5 kB, dejando espacio para la música y el código C++
Un único ejecutable dentro de 8 kB
- The Sheep and the Flower es una animación en tiempo real en forma de ejecutable de Windows, publicada en abril de 2023
- Un solo archivo
.exelo genera todo, sin archivos de recursos separados ni dependencias externas- Para ejecutarlo se necesita Windows y drivers recientes
- El objetivo era meter en 8 kB los gráficos, la animación, la dirección, el trabajo de cámara y la música
- El código fuente está publicado en GitHub: ctrl-alt-test/mouton
Herramientas usadas para gráficos, música y compresión
- Todos los elementos visuales se calculan en tiempo real en la GPU
- El shader GLSL incluye la información de la línea de tiempo, la configuración de la cámara y la representación de la escena
- El renderizado se realiza mediante raymarching
- El shader se minifica con Shader Minifier
- La música se creó con OpenMPT y el sintetizador 4klang
- Los instrumentos se describen de forma procedural
- La lista de notas se comprime
- El código se escribió en C++ con Visual Studio 2022
- Las banderas del compilador y la inicialización partieron del framework Leviathan
- El ejecutable final se comprimió con Crinkler
Por qué 8 kB
- En la demoscene son comunes las categorías con límites de tamaño como 4 kB y 64 kB
- Sus trabajos anteriores apuntaban sobre todo a 64 kB, pero 8 kB ofrecía restricciones y reglas completamente distintas
- Las técnicas de intros de 4 kB les parecían interesantes, pero demasiado limitadas para un storytelling adecuado
- Revision demoparty había agregado unos años antes una competencia de 8 kB, y la tomaron como una buena oportunidad para experimentar
Un mundo dibujado con dos triángulos
- Como es estándar en la demoscene desde 2008, se dibuja un rectángulo que cubre toda la pantalla usando dos triángulos
- Sobre ese rectángulo, un shader GLSL calcula el color de cada píxel y de cada frame
- La función del shader recibe coordenadas y tiempo como entrada, y devuelve un color
- El desafío central era cómo representar dentro de esa función a la oveja y a toda la escena
Construir la escena con SDF
- Un campo de distancia con signo (SDF) es una función que calcula la distancia desde un punto del espacio hasta el objeto más cercano
- Sobre la superficie devuelve 0
- Dentro del objeto devuelve un valor negativo
- Las formas básicas, como esferas o cajas, pueden expresarse con fórmulas simples
- La escena se construye combinando varios campos de distancia
- La unión de dos objetos puede hacerse con el valor mínimo de las distancias
- La intersección puede hacerse con el valor máximo
- Usar una unión suave (smooth union) permite crear formas más orgánicas
- La oveja se creó ensamblando y fusionando formas simples como conos y esferas, mientras que el cuerpo y la lana se construyeron con una función de ruido 3D
Cómo el raymarching decide cada píxel
- El raymarching es una técnica de renderizado que usa SDF para hacer avanzar un rayo poco a poco dentro de una escena 3D
- No calcula directamente el punto de intersección de forma matemática como el ray tracing tradicional
- El SDF indica la distancia que se puede avanzar sin colisionar, y el proceso repite el avance del rayo esa distancia
- Cuando la distancia llega a 0 o se acerca a 0, se considera que se encontró la intersección
- Cuando hay intersección, se puede saber si el píxel actual corresponde a la oveja, al cielo u otro objeto
- Para la iluminación se necesita la normal de la superficie, que se estima calculando el gradiente alrededor del punto
- Las sombras se calculan lanzando otro rayo desde la intersección hacia el sol para comprobar si hay un objeto en medio
Cómo contar una historia con poca expresividad
- La demo no tiene voces ni texto, y solo hay un personaje
- La oveja apenas puede caminar y mover la cabeza y los ojos; ni siquiera puede girar
- Para transmitir emociones dentro de esas restricciones, eliminaron elementos que no aportaban a la historia
- Al principio pensaron en un desierto, dunas de arena y cielo, pero decidieron que no eran necesarios para la historia
- El fondo se mantuvo completamente blanco
- Redujeron las texturas salvo en los letreros y los ojos, que transmiten significado
- Mantuvieron un alcance pequeño para concentrarse en detalle, pulido, trabajo de cámara, edición y sincronización con la música
- Cada toma se compuso a mano, y la animación se ajustó varias veces hasta que el flujo se sintiera natural
Cámara y dirección
- La cámara se usó como herramienta narrativa
- En la escena donde la oveja deambula durante mucho tiempo, usaron una toma amplia para crear una sensación de soledad
- En el momento en que nota el letrero, usaron un primerísimo primer plano de los ojos
- Cuando la oveja mira el letrero, hicieron un zoom lento hacia la cabeza para aumentar el foco
- La emoción de la oveja se transmite superponiendo varias expresiones
- Efectos 2D estilo caricatura
- Paso más rápido
- Movimiento de cola enfatizado
- Sacudidas de cabeza exageradas
- Un cambio dramático en la música
Método de desarrollo para iterar rápido
- El código fuente contiene muchas constantes hardcodeadas
- Tamaño de los ojos de la oveja
- Velocidad de movimiento de la cámara
- Duración de cada toma
- Valores como el color de la flor, ajustados repetidamente
- Para obtener feedback rápido, recompilaban el shader en runtime y actualizaban los gráficos en menos de 1 segundo
- Necesitaban un reproductor para ajustar la animación y la cámara
- Pausa
- Reproducción
- Control de tiempo
- Verificación inmediata tras live reload del shader
- El soporte de música también era imprescindible para sincronizar con precisión música y animación
- El prototipo inicial se hizo en Shadertoy, luego pasó por KodeLife y finalmente se migró a un proyecto C++
Música creada con 4klang
- La música era un elemento importante para contar la historia, y requería varios ambientes y transiciones en momentos específicos
- Usaron las mismas herramientas de las intros de 4 kB, pero asignaron más espacio para una composición más compleja
- 4klang synthesizer ofrece un plugin que puede usarse en software musical y exporta un archivo de assembly
- Ese archivo de assembly se compila y enlaza junto con la demo
- Al ejecutarse la demo, el sintetizador genera sonido de forma procedural en un hilo separado y lo envía a la tarjeta de sonido
- La primera música que hicieron resultó más grande de lo esperado, así que hicieron varios ajustes para reducir su tamaño
- Redujeron la cantidad de instrumentos de 16 a 13
- Recompusieron el tema final para que coincidiera con el tempo de toda la música
- Aumentaron las repeticiones para que se comprimiera mejor
- Mejoraron la tasa de compresión con cambios casi imperceptibles al oído, como la duración de algunas notas del fondo
- Mantuvieron la estructura general de la música, minimizando la pérdida de calidad y ahorrando espacio
Animación y sincronización temporal
- La demo reevalúa todo en cada frame, sin precálculo ni caché
- Esto es malo para el rendimiento, pero ventajoso para la animación, porque cualquier elemento puede variar en función del tiempo
- Toda la demo está compuesta por unas 25 tomas de cámara hechas a mano
- Cada toma describe cómo cambian en el tiempo 18 parámetros
- Posición de cada objeto
- Estado de la oveja
- Posición de la cámara
- Foco
- Objeto al que apunta la cámara, entre otros
- Por ejemplo, una sola línea como
camPos = vec3(22., 2., time*0.6-10.);expresa el movimiento lineal de la cámara en una toma timeno significa el tiempo absoluto desde el inicio de la demo, sino el tiempo desde el inicio de la toma- Así, insertar, borrar o ajustar tomas afecta menos al resto de la demo
- Como una interpolación lineal simple puede verse mecánica, en muchos casos usaron smoothstep
- El código de la línea de tiempo está dentro del vertex shader
- Las definiciones de tomas tienen mucho código repetido, pero la repetición ayuda a la compresión
Texturas y materiales
- En un renderizador tradicional, las texturas 2D se aplican sobre la superficie de modelos 3D
- En raymarching es difícil calcular coordenadas de textura, así que en su lugar se calculan texturas 3D al vuelo
- Cuando el raymarcher encuentra la posición 3D que va a renderizar, pasa esas coordenadas 3D a una función de textura
- Los señalamientos de tránsito se dibujan matemáticamente creando bordes triangulares o rectangulares, y el contenido interior se construye combinando varias funciones
- El símbolo de restaurante está formado por 4 formas ovaladas negras y 2 formas blancas que crean huecos
- Para lograr variedad visual, no solo modificaron las texturas, sino también los materiales
- Por ejemplo, las pezuñas de la oveja se tratan con un coeficiente de Fresnel para que tengan una reflexión distinta
Reflejos falsos para darle vida a los ojos
- Durante mucho tiempo en el desarrollo, los ojos se veían sin vida y planos
- Los ojos eran un elemento importante para el diseño del personaje y la narrativa
- En imágenes de referencia de personajes de caricatura notaron que, aunque suele haber iris, no es indispensable, y que la pupila tiende a dibujarse siempre grande
- Para crear ojos brillantes, necesitaban reflejos de luz dentro de los ojos
- Con las ecuaciones de iluminación existentes, los reflejos solo aparecían cuando el sol y la cámara estaban en posiciones específicas
- Para compensarlo, modificaron el vector normal de la superficie para aumentar la probabilidad de que apareciera el reflejo del sol
- Además usaron environment mapping
- En juegos suele usarse para optimizar simplificando los reflejos del entorno real
- Aquí, aunque el mundo real es casi completamente blanco, agregaron detalle falso mediante una textura
- El blanco de los ojos y las pupilas tienen reflejos mucho más complejos que un mundo vacío
- Varias fuentes de luz falsas
- Un gradiente que imita suelo oscuro y cielo azul
Postprocesado y estilo visual
- La atmósfera final y la calidad de imagen se pulieron con postprocesado
- El postprocesado usado incluye
- Color grading
- Corrección gamma
- Viñeteado suave
- Un filtro FXAA de 2 pasadas para reducir aliasing
- Algunos efectos, como las estrellas en los ojos o el efecto de la pantalla final, también se implementaron en la etapa de postprocesado
- Esos efectos son puramente 2D y no existen en el mundo 3D
- Durante un tiempo también experimentaron con un estilo de caricatura antigua
- Detección de contornos para que pareciera dibujado a mano
- Renderizado en blanco y negro
- Grano y ruido
- Tras discutirlo, descartaron ese experimento y se enfocaron en un aspecto más limpio y moderno
Compresión con Crinkler
- La idea central es no guardar los datos, sino guardar el código que genera los datos
- La música guarda la lista de notas a tocar y la lista de instrucciones de cada instrumento
- Crinkler es una herramienta de compresión diseñada para la demoscene y para intros de 1 kB a 8 kB
- Como el ejecutable debe poder descomprimirse a sí mismo, Crinkler incluye un pequeño código en assembly capaz de descomprimir el resto del ejecutable
- Crinkler prioriza la optimización de tamaño
- El algoritmo de compresión toma tiempo
- La descompresión es relativamente lenta
- Usa cientos de MB de RAM
- Pero Crinkler por sí solo no era suficiente, y fue necesario minificar más para meter los 42 kB de código de shaders dentro del ejecutable
Mejoras en Shader Minifier
- Como el código fuente de los shaders se incluye en el binario final, debe ser lo más pequeño posible
- Podrían minificarlo a mano, pero eso causaría problemas de mantenimiento e iteración rápida
- Shader Minifier es una herramienta de minificación de shaders desarrollada desde 2011
- Elimina espacios y comentarios, acorta nombres de variables y realiza más transformaciones
- Una intro de 8 kB incluye mucho más código que una de 4 kB, lo que generó problemas nuevos; pausaron el trabajo en la demo durante un mes para agregar las funciones necesarias
- Para lograr una buena minificación hacía falta no un minificador simple, sino un compilador source-to-source parecido a Closure Compiler
- Entre las transformaciones que realiza Shader Minifier están
- Renombrado de variables y funciones
- Inlining de variables
- Evaluación de expresiones aritméticas constantes
- Inlining de funciones
- Eliminación de código muerto
- Fusión de declaraciones
- Reducir el tamaño del código en sí no bastaba: la salida debía ser fácil de comprimir
- Algunas transformaciones reducen el tamaño del código, pero pueden aumentar el tamaño tras la compresión
- Durante el desarrollo iterativo de la demo tenían que revisar constantemente el tamaño comprimido
- Las nuevas mejoras de Shader Minifier ahorraron unos 600 bytes en el binario comprimido
- Finalmente, los 42 kB de código de shaders entraron en unos 5 kB tras minificación y compresión
- En el espacio restante caben la música y el código C++
Un resultado construido sobre el trabajo de otros
- Esta demo se construyó sobre trabajos hechos por otras personas, como técnicas de raymarching, software de generación musical y algoritmos de compresión
- Esperan que las nuevas funciones agregadas a Shader Minifier ayuden a crear mejores demos en el futuro
- Queda la expectativa de que la categoría de 8 kB, que ofrece más posibilidades que la de 4 kB, gane más popularidad
- Como comparación, el texto original tiene alrededor de 21,000 caracteres, lo que tal cual requeriría 21 kB
1 comentarios
Opiniones de Hacker News
Soy el creador de la película original en la que se inspiró esta: https://www.youtube.com/watch?v=khWXdkryBE4
La original, solo con Blender, SVG y archivos de audio, ya ocupa varios MB, así que es realmente impresionante que el remake quepa en 8 kB
Me alegra ver que una licencia Creative Commons haya servido de verdad, aunque me habría gustado más que usaran el mismo personaje de erizo y la melodía de audio
¡¡¡Sloot ha vuelto a la vida!!!
https://en.m.wikipedia.org/wiki/Sloot_Digital_Coding_System
Juntas a un montón de demosceners, les muestras la película y los encierras por un rato en una habitación con computadoras
Funciona mejor si hay una pantalla grande, un sistema de sonido potente, cerveza y comida chatarra; luego solo recibes el ejecutable resultante
aUn intérprete escrito en Rust por rendimiento evalúa el comando
ae imprime en STDOUT un payload específico incluido en el runtimeFue una mala suerte realmente terrible para todos
Probablemente esas personas no entendían que a) es una función unidireccional y b) aunque no fuera unidireccional, varias entradas pueden mapear al mismo hash
Los videos de la demoscene son bastante entretenidos, pero están absurdamente poco difundidos
https://www.youtube.com/playlist?list=PLRQwR4-_0PR9HBI_GZs1nTn7q5LTkGeCN
Ojalá hubiera un canal que explicara cómo funcionan por dentro con buenos recursos visuales, como retro game mechanics
Cuando no había videos, tenías que esperar bastante a que se descomprimiera y procesara todo antes de poder maravillarte o decepcionarte
Antes veía muchas demos en los descansos, y eran el fondo perfecto para mi estado mental de entonces
https://en.wikipedia.org/wiki/.kkrieger
Y yendo más allá, ¿qué tan pequeño se podrá hacer un mundo de agentes inteligentes interactuando entre sí?
PD: los que votan negativo, ¿no sueñan? Nos esperan muchas cosas verdaderamente alucinantes
Sé que la tecnología detrás es bastante distinta, pero me recuerda al viejo .kkrieger. Comprimieron un FPS 3D completo en 96 kB, y era realmente genial
https://en.m.wikipedia.org/wiki/.kkrieger
Cuando ves muchas cosas de la demoscene y lees cómo se hacen, entiendes las limitaciones, y se vuelve claro que elementos como una oveja, especialmente las secciones cónicas de las patas, pueden representarse y animarse de forma similar con ecuaciones muy compactas
Pero, hasta donde sé, la mayoría de las demos dependen de la GPU y de una potente aceleración 3D, mientras que las demos 2D con estilo de animación japonesa parecen raras o casi inexistentes en tamaños pequeños
¿Será que la animación 3D en realidad es más fácil?
Por cierto, “mouton” significa “oveja” en francés, y de ahí viene “mutton”
En cambio, una forma ‘matemática’ de almacenar animación de estilo 2D sería guardar los contornos como curvas SVG y usar algoritmos de relleno espacial para colorear áreas
A ojo, un solo arco o línea de una curva puede ocupar tanto almacenamiento como la descripción de un volumen geométrico completo, y guardar además la animación de esas curvas requeriría mucho más espacio que las pequeñas matrices de transformación de los volúmenes 3D
La complejidad del algoritmo de renderizado también probablemente aumentaría, tanto en complejidad algorítmica real como en complejidad espacial
Advertencia: también se podría argumentar que almacenar varios bitmaps y reproducirlos como un flipbook es ‘matemático’ si se usa un algoritmo procedural de descompresión que genere cuadros completos a partir de diferencias
Pero no veo que exista un enfoque así, y sus requisitos de espacio serían enormes comparados con una descripción de volúmenes 3D
Es un poco más grande que 8 kB, pero si te gustan las animaciones inesperadamente oscuras sobre ovejas, recomiendo el open movie Cosmos Laundromat de Blender Foundation
Las películas de esta fundación siempre son extrañamente oscuras para ser demos técnicas, pero esta me pareció especialmente lograda
[0] https://www.youtube.com/watch?v=Y-rmzh0PI3c
Una advertencia de antemano: la película termina con “to be continued” y la página de Wikipedia dice esto:
“Esta película originalmente estaba pensada como el punto de partida de un largometraje. Una secuela corta tenía guion y diseños, pero nunca entró en producción. En 2020, [el creador] anunció que esta única entrega sería todo el proyecto”
Si no quieres sufrir la decepción de no ver jamás el desenlace de un concepto interesante, quizá sea mejor saltártela
Me sirvió para experimentar con HDR en DaVinci Resolve
[1] https://opencontent.netflix.com/#h.uyzoa2bivz2j
8KB es una categoría de la demoscene. En Pouet hay un índice bien organizado de este tipo de obras
0. https://www.pouet.net/prodlist.php?type%5B%5D=8k&page=1
Razor 1911. Pura nostalgia
Me trajo muchos buenos recuerdos de hace décadas :-)
Es realmente sorprendente, y me encanta que, incluso con tantas restricciones, la historia sea tan graciosa y creativa. De verdad impresiona
Me gustaría escuchar más detalles técnicos sobre qué tipo de trucos y ahorros hicieron para reducir bytes
En este commit (https://github.com/ctrl-alt-test/mouton/commit/79d2d1eab7a222b434b7041d8fc9bce4e60524f8) eliminaron una optimización de rendimiento y ahorraron muchos bytes
Originalmente querían mantenerla, pero les faltaban bytes y se dieron cuenta de que en GPUs relativamente recientes esa optimización no era necesaria
Graciosa y a la vez totalmente sorprendente. Por fin vi una película cuyo final no pude predecir