materias/README.md · generada al construir
Materias
Papel, acuarela, pincel, risografía, tejido y transiciones: la convención de pasadas y la orden materia.
La biblioteca de materias de Tórculo: lo que convierte un plano limpio en papel, acuarela, pintura, risografía, punto de media o transición. Todo en WGSL, con un crate de Rust que lo compila, lo encadena y lo prueba. No depende del motor, pero sigue su convención de pasadas para que el fork de ManimGX la enganche tal cual.
[imagen: El texto del vídeo pintado a pincel sobre khadi]
Cómo se usa
cargo build --release
# una cadena sobre un PNG, un directorio de PNG o un MP4 (ffmpeg por tubería)
./target/release/materia aplica -c "pincel > difuminado" -e foto.png -s pintura.png
./target/release/materia aplica -c "risografia(tambor1=rosa, tambor2=ultramar)" -e clip.mp4 -s riso.mp4 --dura 4
# una transición entre dos planos
./target/release/materia transicion -t pasa_pagina -a A.png -b B.png -s paso.mp4 --dura 1.4
# las muestras de este README (en muestras/, a 1080p, con sus vídeos) y los tiempos
./target/release/materia muestra todas
./target/release/materia mide
# las pruebas de fotograma de referencia (MATERIAS_ACTUALIZA=1 las reescribe)
cargo test --releaseUna cadena es materia(clave=valor, …) > materia > …. Los valores pueden ser
números, nombres que la materia entiende (tipo=khadi, tambor1=rosa,
trama=grano) o colores #RRGGBB.
La convención de pasadas
Cada materia de plano es una o varias pasadas de cómputo con el mismo grupo 0:
| binding | qué | tipo |
|---|---|---|
@group(0) @binding(0) | entrada | texture_2d<f32>, rgba16float lineal premultiplicado |
@group(0) @binding(1) | muestreo | sampler lineal, borde pegado |
@group(0) @binding(2) | plano | uniform Plano |
@group(0) @binding(3) | salida | texture_storage_2d<rgba16float, write> |
@group(1) @binding(n) | lo propio de cada materia | texturas que lee, que escribe, búferes |
struct Plano {
resolucion: vec2f,
fotograma: u32,
tiempo: f32, // tiempo de escena, nunca reloj del sistema
semilla: u32,
paso: u32, // libre: el paso de simulación, el sentido de una gaussiana…
_hueco0: u32, _hueco1: u32,
params: array<vec4f, 4>,
}Las materias con estado (la acuarela) guardan sus texturas persistentes en el
grupo 1, en parejas que se leen y se escriben por turnos (Doble), y avanzan a
paso fijo de 60 Hz: nada mira el reloj, así que dos pasadas dan los mismos
píxeles (hay una prueba que lo comprueba).
#import nombre pega un módulo una sola vez (preprocesador propio,
src/preprocesa.rs). Los módulos van incrustados en el binario; canon se lee
de canon/gen/canon.wgsl, así que la paleta (la crema de Scholaris, la tinta
café, los tambores) y hash32 son los de todo Tórculo. espectral se genera
desde src/espectral.rs.
| módulo | qué trae |
|---|---|
canon | la paleta y el azar del canon |
comun | sRGB ⇄ lineal, PCG, ruido de valor, fbm, IGN, sobre |
plano | la convención de arriba |
campos_papel | los once campos de papel y color_papel |
espectral | transmitancia_rgb (16 bandas) y coeficientes_de (subida a espectro) |
Las materias
Papel
Puerto fiel de los diez generadores de papel de
watercolor-playground
(src/engine/shaders.ts), en unidades físicas (una unidad es un texel de una
hoja de 1100 de alto, unos 0,18 mm: el grano mide lo mismo a cualquier
resolución), más un undécimo, cuaderno, que es el papel de la casa de
film-look-lab/nativa/src/papel.rs (fibra anisótropa, poro, grano de
imprenta) con sus manchas, su cerco de taza y su viñeta. El relieve se
ilumina desde arriba a la izquierda con un poco de oclusión en los valles.
Los blancos llevan la crema del canon (#F5F0E8); el tintado y el kraft, la
suya.
La hoja (r = relieve iluminado, g = altura, b = moteado, a = mota) se calcula una vez por tamaño, tipo y semilla, y la leen también la acuarela, el pincel, la risografía y las transiciones.
[imagen: Los once papeles]
De izquierda a derecha y de arriba abajo: grano fino, satinado, torchon, khadi, washi, verjurado, lienzo, tintado, kraft, escolar y cuaderno.
[imagen: El cuaderno de la casa con manchas, cerco y viñeta]
Acuarela espectral
El modelo de watercolor-playground pasado a cómputo WGSL a paso fijo:
- Pigmento espectral. Cada color se sube a una reflectancia de 38 bandas
(el método de spectral.js), se pasa a absorbancia (Beer-Lambert) y se
proyecta en una base SVD de 7 coeficientes (
spectralData.ts). La absorbancia es lineal en la concentración: los coeficientes se suman, se arrastran y se difunden, y la mezcla sigue siendo sustractiva. Para ver, se reconstruye a 16 bandas. - Agua y fluido. Un campo de agua sobre el papel (en
tinta_b.a) y un fluido de Navier-Stokes en una rejilla a un cuarto (advección, vorticidad, 20 iteraciones de presión), confinado a lo mojado. - Transporte como flujo entre caras. Difusión entre vecinos mojados (sangrado) y capilaridad hacia el vecino más seco, escritas como flujos conservativos: el pigmento nunca rezuma sobre papel seco (bordes duros en seco, floraciones en mojado).
- Líneas de marea. La orilla se evapora antes que el centro y el agua de dentro sigue llegando: el pigmento se queda en el borde.
- Floraciones. El papel húmedo bebe más deprisa que el seco, y por donde corren las fibras más todavía (un campo de capilaridad fijo por hoja); un empuje capilar a gran escala (el agua promediada en la rejilla de simulación) lleva el pigmento al frente. El agua que cae en un lavado a medio secar abre una coliflor con el borde oscuro y el centro pálido.
- Granulación y oscurecimiento de borde al mostrar, como en el original.
- Relleno por rampa diferencial. Un lavado avanza desde donde se vierte; cada paso pone la diferencia de la rampa entre el frente de antes y el de ahora, así que la suma es una sola carga por texel, sin costuras. El agua corre por delante del pigmento.
La API (Rust):
let mut a = Acuarela::nueva(&gpu, 1920, 1080, "grano_fino", semilla)?;
a.pincelada(Trazo::nuevo(puntos, 40.0, caja("prusia").unwrap()).velocidad(800.0));
a.lava(Lavado::elipse([960.0, 540.0], [300.0, 200.0], caja("gualda")).desde([800.0, 500.0]));
a.espera(1.0);
a.seca(); // 1,2 s de secado de golpe: lo móvil se asienta
a.escribe("Tórculo", &Fuente::carga("hershey_script")?, [300.0, 600.0], 200.0, 14.0, caja("tinta").unwrap(), 1000.0, 0.2);
a.avanza(&gpu, 1.0 / 30.0); // dos pasos de 60 Hz
a.pinta(&gpu, &mut enc, &salida, false);escribe recorre una tipografía de un trazo (Hershey, EMS, Relief: el lector
de fuentes SVG y el StrokeWriter del repositorio original) trazo a trazo,
con temblor de mano y pausas de pluma en el aire. Las fuentes traen tildes,
eñes y signos de apertura. caja(…) es la caja de acuarelas de la casa: los
tambores del canon más un azul que verdea (prusia), una gualda, siena,
sepia y la tinta ferrogálica.
Como materia de plano (acuarela en una cadena), lo que aparece en el plano
se posa como pigmento mojado (cada color subido a espectro en el sombreador) y
se deja correr y secar fotograma a fotograma.
Azul y amarillo dan verde, mojado en mojado y en veladura:
[imagen: Azul y amarillo dan verde]
Un lavado que florece (antes y después del agua):
[imagen: Antes de la floración] [imagen: La floración]
Pincel por flujo
Puerto del PostFX de jelly-painting. Tensor de estructura de Sobel sumado por canal (a media resolución) → dos gaussianas (el tensor y su halo) → flujo: el autovector menor, con sesgo hacia un ángulo y girado por un ruido lento → semillas de pincelada en celdas (cobertura, prioridad, brillo) → recorrido LIC de ±10 pasos por el flujo, en el que gana la semilla de mayor peso y la pincelada toma su color → relieve iluminado de la pasta, motas del color vecino, un posterizado suave con ruido y la hoja de papel con el borde barbado (cerdas de pincel seco, barbas de papel hecho a mano y salpicaduras).
Una foto de archivo (un fotograma de Mujeres al borde de un ataque de nervios) vuelta pintura:
[imagen: La foto] [imagen: La foto pintada] [imagen: Otro retrato]
Difuminado IGN anclado
Ruido de gradiente entrelazado (Jiménez, 2014) en distribución triangular, sumado en el espacio en que se va a cuantizar (sRGB de 8 o 10 bits) y devuelto a lineal: va al final de la cadena. «Anclado»: el patrón no hierve; modo 0, fijo a la pantalla; modo 1, temporal; modo 2, pegado al papel (se mueve con la cámara).
La misma viñeta nocturna a 8 bits, sin difuminar y difuminada, con el contraste multiplicado por diez para que se vean los escalones:
[imagen: Sin y con IGN]
Risografía
El plano se separa en dos o tres tambores del canon por mínimos cuadrados no
negativos en absorbancia (donde las tintas se suman), cada tambor se trama a
su ángulo (puntos AM que se tocan al 50 %, grano FM o líneas), cae con su
desregistro (desplazamiento y giro por semilla; con hervor, la pose cambia
unas veces por segundo, como la línea que hierve de la casa), la tinta no cae
igual en todo el pliego (moteado y rayas del rodillo), el tambor no entinta
del todo los valles del papel, y las tintas se mezclan por multiplicación
sobre la hoja.
[imagen: Verde, rosa y humo] [imagen: Bermellón y azurita]
Tejido
El plano en punto de media. Cada celda de la rejilla es un punto en V: dos patas de hilo (cápsulas con perfil de cilindro) que suben un poco dentro de la fila de arriba, con la torsión de los cabos en diagonal, grosor y posición irregulares y una variación de tono a lo largo del ovillo. Lo que el plano tiene de color se teje con hilo de ese color; lo demás es lana cruda jaspeada. Los puntos de color se revelan uno a uno (por líneas de texto, de izquierda a derecha), llegan un poco grandes y se asientan, y la aguja arrastra el hilo suelto desde el borde; alrededor flota la pelusa del mohair; al deshacer, los puntos saltan, se encogen y se van, del último al primero.
El texto se rasteriza con Hershey de trazo grueso (fuente::rasteriza), sin
fuentes del sistema.
[imagen: Revelado punto a punto, con el hilo suelto] [imagen: Tejido] [imagen: Deshacer]
Transiciones
Las catorce que .mesa tiene reservadas, las que son de imagen (los eventos
se sostiene, retención larga y a la vez son de tiempo, no de materia;
corta en la acción es un corte). transicion::de_evento traduce el evento
del pliego al tipo.
| tipo | qué hace |
|---|---|
corta | corte seco a mitad del progreso |
encadena | fundido encadenado en luz lineal |
funde_a_negro, funde_desde_negro | a y desde la noche del canon (#1A0F0A), no a negro puro |
funde_a_papel, funde_desde_papel | a y desde la hoja (es el «funde a blanco» de la casa) |
pasa_pagina | la hoja A se enrolla en un cilindro que barre en diagonal; el dorso es papel con A transparentándose, y el rollo echa sombra |
la_mano_tapa | una mano muy cerca del objetivo, desenfocada, cruza y tapa el cuadro; a mitad, oscuridad total; la luz se cuela por el filo |
cortinilla_de_tijera | un corte a mano (tramos rectos con quiebros) barre la hoja; el canto del papel enseña su alma blanca y la hoja echa sombra sobre B |
iris | se cierra sobre A y se abre sobre B, con el filo en sombra |
quemado | el fotograma se para y se quema: calor, chamuscado, filo incandescente y B detrás del agujero |
desenfoque_de_pasada | A se desenfoca, se encadena con B desenfocado y B se enfoca |
chasquido | un golpe de luz de papel y un temblor de dos fotogramas |
entra_de_golpe | corte seco a B con un empujón que se asienta |
Cinco instantes de cada una (al 15, 35, 50, 65 y 85 %), en el orden de la tabla, de la acuarela escrita a la foto pintada:
[imagen: Las catorce transiciones]
Tiempos por fotograma a 1080p
Apple M4 Max, Metal, materia mide (media de varias repeticiones tras
calentar, sin E/S):
| materia | ms |
|---|---|
| papel (hoja en caché) | 3,1 |
| papel: hoja nueva (grano fino · khadi · washi · lienzo) | 8,1 · 15,2 · 11,1 · 5,1 |
| acuarela: un paso de 60 Hz | 7,3 |
| acuarela: pintar | 0,3 |
| acuarela de plano a 30 fps (dos pasos) | 18,6 |
| pincel por flujo | 5,5 |
| risografía, 2 tambores · 3 tambores | 3,1 · 4,0 |
| tejido (peor caso: todo el plano de color) | 15,4 |
| difuminado IGN | 0,5 |
| transiciones | 0,3 (entra de golpe) a 2,1 (desenfoque de pasada) |
| lectura a sRGB de 8 bits (E/S) | 6,6 |
Todo cabe holgado en un fotograma de 30 fps; la acuarela de plano y el tejido son los únicos que se acercan al de 60.
Lo que no está a la altura
- Acuarela: el interior de los lavados es demasiado uniforme. Hay charcos (la carga varía), marea, floración y granulación, pero un lavado de verdad tiene más vida dentro: el pigmento que se va al fondo según se inclina la hoja, los cambios de tono de un pincel que se recarga. Y el verde de mojado en mojado y el de la veladura se parecen demasiado: la frontera húmeda debería deshilacharse más.
- Acuarela: las floraciones nacen de la forma del lavado de agua (los lóbulos del relleno) y la física las termina; no salen solas de una gota. Para eso haría falta transporte de agua de mayor alcance por paso (más subpasos o una rejilla de agua más gruesa).
- Acuarela de plano pinta lo nuevo del plano como pigmento mojado; no
sabe de pinceladas: un texto que aparece se ve «empapado», no «escrito».
Para escribir, la API (
escribe) es el camino. - Pincel por flujo en vídeo: las semillas son fijas, pero el flujo cambia con la imagen, así que en planos con mucho movimiento las pinceladas nadan. Falta una versión temporal (semillas que viajen con el flujo óptico).
- Tejido: las letras de Hershey con trazo grueso cierran algunos ojos (la «s», la «e») a este tamaño de punto; una fuente de palo seco de verdad daría letras más limpias. No hay hebras flotantes entre letras (las del vídeo de referencia), solo el hilo suelto de la aguja.
- Risografía: la separación por mínimos cuadrados da mucho del tambor
oscuro en las fotos de cine (todo tiene algo de sombra); con dos tambores
claros conviene subir
gamma. - Transiciones: la mano es una silueta procedural (palma y cinco cápsulas), creíble desenfocada pero no de cerca. El pasa página dobla un solo cilindro recto: el papel de verdad se curva de forma cónica.
- Licencia de jelly-painting: el puerto viene del paquete minificado de la web; no consta su licencia. Antes de publicar, confirmarla o reescribir esa materia desde el algoritmo.
Procedencia y licencias
- Papel, acuarela, lector de fuentes y
StrokeWriter: watercolor-playground, © Maksim Surguy, MIT. - Las pasadas de fluido: WebGL-Fluid-Simulation, © 2017 Pavel Dobryakov, MIT.
- El tratamiento del agua: inkwash, © 2026 Jonathan Whitaker, MIT.
- Los datos de reflectancia: spectral.js, © Ronald van Wijnen, MIT.
- El papel de la casa:
film-look-lab/nativa/src/papel.rs. - Las tipografías de un trazo de
fuentes/: Hershey (dominio público, con la nota de uso de los datos de Hershey), EMS (SIL OFL, verfuentes/OFL.txt) y Relief Single Line. - El pincel por flujo: puerto del PostFX de jelly-painting (ver arriba).
Las imágenes de muestra parten de fotogramas de Mujeres al borde de un ataque de nervios (Pedro Almodóvar, 1988), usados como material de prueba.