Subtítulos con cámara 3D
El movimiento de After Effects (cámara 3D, profundidad de campo, texto sobre trayectoria, Posterize Time 15, desenfoque de movimiento) reconstruido como un pintor determinístico por fotograma para HyperFrames. Un solo reloj pinta el plato, la máscara y cada glifo. Nada es una animación CSS.
Cuándo usar / cuándo no
- Usar en una toma de rostro parlante con cámara fija (avatar o real), horizontal, plano medio o más abierto, de 5 a 20 s por pieza. Las palabras deben actuar en profundidad alrededor del hablante.
- No usar para subtítulos planos, cámara en movimiento o cámara al hombro (la máscara y la transformación del plato asumen una toma fija), primeros planos extremos (sin espacio detrás o alrededor de la cabeza), o un sujeto que un modelo de segmentación de personas no pueda separar.
Requisitos y efectos secundarios (lista completa)
- Herramientas:
- Node 22+ y la CLI de HyperFrames anclada a la versión probada,
npx [email protected](descarga desderegistry.npmjs.org). Usa esa versión exacta para cada comando a continuación. ffmpegen PATH.- Python 3 con
fonttools,brotli,numpy,pillow.
- Node 22+ y la CLI de HyperFrames anclada a la versión probada,
- Máscara:
npx [email protected] remove-backgroundse ejecuta localmente. En la primera ejecución descarga su modelo de segmentación de personas (~170 MB) a~/.cache/hyperframes/. - Tiempo de renderizado: las composiciones cargan GSAP 3.14.2 desde
cdn.jsdelivr.neten el momento de la vista previa/renderizado. - Reloj de palabras: cualquier transcriptor a nivel de palabra que el usuario ya ejecute localmente. La precisión del inicio es importante; Whisper simple comienza tarde.
- Fuentes: archivos proporcionados por el usuario. Confirma que la licencia permita la incrustación.
- Estilo dibujado a mano (opcional): necesita la skill
p5-paint-animationinstalada y configurada (su configuración descarga puppeteer fijo, Chrome for Testing, p5 y p5.brush).build-sprites.pylo ejecuta sin interfaz gráfica; los fotogramas se escriben en el directorioout/de esa skill y se eliminan después. - Nada más: sin credenciales, sin llamadas de pago, nada subido. Los archivos se escriben solo dentro del proyecto (
assets/,renders/).
Preparación (una vez por toma)
- Porción y plato.
bash scripts/prep-take.sh <take.mp4> <start_s> <dur_s> <project>/assetsproduce:portion.mp4: la fuente de audio.plate-tall.mp4: plato relleno por reflejo de 240 px a cada lado, 480 px arriba, 240 px abajo, para que los whips, trucks y pull-backs nunca muestren un borde.person.webm: la máscara alfa, mismos fotogramas.- El script luego verifica el plato y la máscara contra la porción (tamaño, frecuencia de fotogramas, número de fotogramas) y falla si hay alguna discrepancia.
- Reloj de palabras. Temporización de palabras para la porción, en segundos desde su inicio.
- Fuentes. Elige 2–3 roles (subtítulo / héroe / anillo), luego
python3 scripts/font-metrics.py assets/metrics.js key=font.woff2[@wght=…,opsz=…] ….- Una fuente variable debe recibir sus ejes aquí Y fijarse en CSS (
font-variation-settings,font-optical-sizing: none). De lo contrario, el opsz del navegador sigue el tamaño y las palabras se superponen.
- Una fuente variable debe recibir sus ejes aquí Y fijarse en CSS (
- Silueta. Lee la caja de la cabeza y la línea del hombro de la máscara
person.webmantes de colocar cualquier cosa.
Construcción
- Copia
assets/kit/{cam3d.js,tables.js,finish.js}ymetrics.jsdentro del directorioassets/del proyecto. references/worked-film.htmles la película de referencia completa. Tiene dos tomas, un wipe de cuerpo, ambos tipos de anillo, un fly-through, un final y la gradación; su material multimedia no está incluido.references/recipes.mdcontiene los grupos de profundidad, el anillo envuelto, la escalera y las palabras dibujadas a mano.C3.init(metrics, {W, H}):- El lente mantiene el campo de visión vertical del tutorial (F = 1326.1·H/1080), por lo que
tables.jsse aplica sin escalar a cualquier ancho. - En un fotograma más ancho que 1440, añade
(W − 1440) / 2awipe.x.
- El lente mantiene el campo de visión vertical del tutorial (F = 1326.1·H/1080), por lo que
Gramática del plano
Elige 4–6 tiempos. Divide cada cláusula hablada en su propio grupo.
| Tiempo | Construcción | Regla |
|---|---|---|
| Whip-in punch-in | tabla camA (19 f desde 502 px abajo) luego un avance linear | plato S0 1.1–1.3; el fotograma nunca aterriza |
| Grupos de profundidad | 2–3 grupos de subtítulos por cláusula en D 1250 / 1500 / 2200 | cerca = más grande con una sombra más grande; el avance y el truck revelan la paralaje |
| Héroe detrás | palabra héroe en la capa detrás a la profundidad del plato, estilo masthead a través de la cabeza | la cabeza oculta las letras del medio; ≥ 60 % permanece legible; el ascendente está dentro del fotograma |
| Parallax truck | segmento swing, tx 420 | el diseño anterior se desvanece en 3 pasos; el siguiente diseño llega por paralaje |
| Wipe de cuerpo | la propia máscara del hablante, brightness(0) blur(), borde de ataque desde wipe.x | toma A recorta a la izquierda de la banda, toma B a la derecha; pull-out en ambos lados |
| Anillo envuelto | círculo horizontal DELANTE del hablante (centro Z 2000), 11–16° desde arriba, girando | las palabras entran pequeñas en el lado al ser habladas, giran alrededor del frente, se asientan; el arco inferior se voltea para leer en posición vertical |
| Fly-through | frases en D 1500 / 2000 / 2500, la cámara oscila entre ellas | el foco sigue la frase |
| Final en escalera | segmento push; palabras de subtítulos cada una más profunda y más pequeña en diagonal | el push separa las palabras cercanas |
Reglas
- Movimiento:
- El texto se posteriza a 15 fps (su propia animación y la cámara que ve). El plato y la máscara se mueven en cada fotograma.
- El desenfoque de movimiento es 7 fantasmas durante medio paso de 15 fps. Usa
shutter: 0.5en anillos giratorios. - Los subtítulos adelantan a la voz en 0.2 s, ajustados a la cuadrícula de 15 fps:
fr(t) = 2·round((t − 0.2)·15). - Las entradas suben por
ENTRY: subtítulos 30 px, héroes 110–260 px. Las palabras del anillo suavizan sus últimos 30–44 px a lo largo de la trayectoria. Usa el sobreimpulso deRING_ENTRYsolo cuando ningún vecino esté aún colocado.
- Profundidad:
- Cada grupo tiene su propia D. Plato 3000, subtítulos 1250–2400, anillo envuelto ≈ 1100–2700.
- Sombra de profundidad
[6, 16, 0.5, 1500]escala como 1500/(D − cz). - DOF σ = 6.7·|D − foco|/D px a 1080 de alto. Los subtítulos planos tienen un límite de 1.2 px; los glifos del anillo reciben el desenfoque completo.
- Tamaños:
size/riseson px en pantalla en la cámara de aterrizaje (tland). Los sprites se renderizan a su tamaño en pantalla. - Espaciado:
- El texto del anillo siempre pasa por
ringLine(avance en pantalla; tipo: track 1.10, gap 26–30; dibujado a mano: track 1.0, gap 28). - Un anillo giratorio se recoloca en cada pose.
- El texto del anillo siempre pasa por
- Oclusión:
- Los anillos se dibujan delante del hablante.
- Divide una palabra en instancias detrás/delante (
side,zSplit) solo cuando se pretende que pase detrás de la cabeza.
- Legibilidad:
- Los subtítulos van sobre el lado oscuro del fotograma.
- El texto que cruza piel o ropa clara recibe un halo oscuro.
- Sin puntos al final en pantalla.
- Gradación (opcional):
finish.jsproporciona curvas, saturación 1.12 para piel, y grano recalculado por fotograma. Coloca la escena opaca (plato + texto detrás + máscara) en un contenedor filtrado. Dale al texto frontal la misma gradación con grano compuesto encima, para que una palabra dividida detrás/delante mantenga un color (verreferences/worked-film.html).
Estilos
- Editorial: una serifa condensada de exhibición, cursiva para héroes; una serifa de texto para subtítulos; crema más un acento.
- Bold: héroes grotescos pesados, subtítulos ligeros, un acento saturado (la película de trabajo).
- Dibujado a mano: sprites write-on de p5.brush desde
scripts/hand/build-sprites.py:- Cada palabra se escribe a sí misma desde su inicio.
- Los anillos colocan palabras completas, rotadas a la tangente y escaladas por profundidad, en un anillo más grande y plano.
- Receta en
references/recipes.md.
Verificar
npx [email protected] check . --no-contrast. Las capas fantasma activan la verificación de contraste WCAG; las costuras del whip y wipe se superponen por diseño.- En la página de vista previa, pega
scripts/frame-bounds.jsy ejecuta__frameBounds(0, dur).- Debe devolver
[]: ningún grupo fuera del fotograma durante 5+ pasos mientras es visible.
- Debe devolver
- Pega
scripts/glyph-gaps.jsy ejecuta__glyphGaps(t)en reposo.- Sin toques entre palabras.
- Las palabras planas se miden como una caja (los descendentes marcan falsos positivos). El volado de una f cursiva se lee como negativo. Las palabras en lados opuestos de la banda de wipe se muestran ambas.
- Espacios entre palabras: el espacio renderizado entre palabras ≈ un avance de espacio.
- Captura instantánea de cada tiempo, renderiza con
npx [email protected] render . --crf 12, y verifica los fotogramas a resolución completa por tiempo.
Límites
- Los bordes de la máscara parpadean en cabello fino. El relleno reflejado se muestra brevemente durante un whip.
- Un hablante que llena el fotograma deja poco espacio detrás: coloca a la altura del masthead, o esconde detrás de un hombro.
snapshot --atcomparte una página, por lo que el grano no se recalcula entre capturas. Juzga el grano en un renderizado.- Renderizar con la gradación y muchas capas fantasma cuesta aproximadamente 2× un renderizado simple.