Communitygithub.com

lowpoly-shorts

Crea cortometrajes 3D verticales de estilo low-poly PS1 (personajes CC0, caras planas con píxeles, aspecto dentado, subtítulos con palabra resaltada, SFX sincronizados, voz IA) totalmente con código mediante Blender, con costo de renderizado $0. Úsalo cuando el usuario solicite un "cortometraje low-poly", "video estilo PS1", un corto 3D "qué pasaría si...", o mencione esta habilidad.

¿Qué es lowpoly-shorts?

Este skill permite generar cortometrajes 3D verticales (30-60 segundos) con estética low-poly al estilo PlayStation 1, utilizando únicamente código y Blender, sin necesidad de generadores de video externos. El flujo de trabajo es totalmente programático: Claude escribe un script de Blender por corto, lo renderiza en la máquina del usuario, revisa los fotogramas, los corrige y luego construye el sonido y los subtítulos. Está diseñado para producir videos con un aspecto distintivo: personajes bloqueados con texturas de píxeles planos, caras intercambiables mediante decals, efecto de dithering Bayer, colores de 15 bits, y subtítulos con resaltado de palabra actual. Soporta personajes CC0 de Quaternius, voces TTS (ElevenLabs), y efectos de sonido sintetizados, todo con costo cero de render (solo se incurre en costo de la voz, aproximadamente $0.10 por toma). Incluye un sistema de previsualización rápida en el navegador que permite al usuario dejar notas en la línea de tiempo, y Claude corrige solo las tomas modificadas, ahorrando tiempo de render. El skill está probado con el ejemplo `examples/lift-rocket/` y proporciona una API de kit Blender (`scripts/lowpoly_kit.py`) para cargar personajes, acciones, caras y configuraciones de cámara. Es ideal para creadores de contenido que quieren producir animaciones 3D cortas con un estilo retro distintivo de manera eficiente y reproducible.

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mr-render/lowpoly-shorts/tree/HEAD

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Cortometrajes low-poly PS1

Todo es código: Claude escribe un script de Blender por corto, lo renderiza en la máquina del usuario, revisa los fotogramas, los corrige, y luego construye el sonido y los subtítulos. Sin generador de video.

Probado en examples/lift-rocket/ ("¿Qué pasaría si los ascensores fueran tan rápidos como cohetes?", 34 s). Lee ese ejemplo antes de escribir un nuevo corto: es la plantilla.

Requisitos (verifica primero, instala lo que falte)

  • Blender 4.2+ (probado en 5.3). Búscalo: blender en PATH, si no, C:\Program Files\Blender Foundation\<ver>\blender.exe / /Applications/Blender.app/Contents/MacOS/Blender.
  • Python 3.10+ con numpy pillow soundfile faster-whisper; ffmpeg en PATH.
  • Personajes: Quaternius "Ultimate Modular Men" (CC0, gratis, quaternius.com). Descomprime en assets/quaternius-modular-men/ o establece LOWPOLY_PACK en su carpeta Individual Characters/Blends.
  • Una voz TTS (ElevenLabs o cualquier herramienta que tenga el usuario). Opcional: el MCP earshot para verificar el sonido.

El flujo de trabajo (una carpeta por corto, copia examples/lift-rocket/ como inicio)

#PasoArchivo / comandoHecho cuando
1Guiónscript.mdPregunta gancho + cadena de consecuencias, cada una peor y más divertida + números reales + remate seco. 60-110 palabras para 30-40 s. Cada número verificado con una fuente y escrito en la lista de Hechos.
2Vozvo/takeA.mp32 tomas, elige la que tenga tiempos de palabra limpios. Voz enérgica de creador, no calmada (calmado = retroalimentación "aburrido").
3Tiempos de palabraspython ../../scripts/words.py vo/takeA.mp3takeA_words.json no tiene palabras de longitud cero.
4Lista de tomastabla en script.mdUna toma por frase de VO, una imagen nueva cada 1-3 s, cada toma vinculada a un tiempo de palabra.
5Escenabuild.py (Blender)blender -b -P build.py -- frames --still 30,120,... renderiza imágenes fijas; mira cada imagen fija antes del render completo.
6Vista previa + notaspython ../../scripts/preview.py . render luego serveEl usuario no tiene notas abiertas (ver bucle de Vista previa).
7Renderizarblender -b -P build.py -- framesPNGs EEVEE 360x640, f_0001.png...
8Sonidopython build_mix.pySFX colocados en fotogramas de imagen, VO encima, cama lo-fi debajo, ducking. Escribe mix_raw.wav + cues.json.
9Postproducciónpython post.pyDither PS1, efectos, subtítulos con palabra resaltada, llamadas de estadísticas -> out/ a 1080x1920.
10Mezclaver abajoshort.mp4, luego medir y verificar sincronización.

Regla de fotogramas en todas partes: 24 fps, fotograma 1 = t 0, F = lambda t: int(round(t * 24)) + 1.

Mezcla:

ffmpeg -y -framerate 24 -i out/f_%04d.png -i mix_raw.wav -c:v libx264 -crf 16 -pix_fmt yuv420p \
  -af "loudnorm=I=-15:TP=-1.5:LRA=11,aresample=48000" -c:a aac -b:a 192k -shortest short.mp4

Cambia la voz más tarde sin volver a cortar la imagen: python ../../scripts/words.py vo/takeB.mp3 luego python ../../scripts/align.py vo/takeA_words.json vo/takeB.mp3 --end 34 (--end = donde termina la cama de VO; la salida tiene una duración de end + 2 s. Sin él: última palabra + 1.5 s, lo que da un archivo más corto que la imagen) -> takeB_aligned.wav + takeB_aligned_words.json. Apunta build_mix.py (variable de entorno VO) y post.py hacia ellos.

Bucle de vista previa (antes del render completo)

Vista previa rápida de baja resolución en el navegador, el usuario deja notas en la línea de tiempo, Claude corrige y vuelve a renderizar solo las tomas cuyo contenido cambió. Tomas = los marcadores de cámara en build.py.

HazComando (desde la carpeta del corto)
Renderizar la vista previapython ../../scripts/preview.py . render (primera ejecución: cada toma; luego: solo tomas cambiadas)
Abrir la página de notaspython ../../scripts/preview.py . serve -> http://127.0.0.1:8765
Leer las notaspython ../../scripts/preview.py . notes (agrupadas por toma, con números de fotograma)
Después de corregiredita build.py, ejecuta render de nuevo, luego python ../../scripts/preview.py . done 3 5 --reply "lo que cambiaste"
Forzar tomasrender --shots c_press,c_slam o render --all
  • build.py debe aceptar --blend-only (construir la escena, guardar, no renderizar). Ver el ejemplo.
  • Detección de cambios: cada fotograma de vista previa obtiene una huella digital (cámara activa, transformación de cada objeto visible, pose de armadura, intercambios de caras, materiales, luces, datos de malla). Una toma se vuelve a renderizar solo si uno de sus fotogramas cambió. Las tomas sin cambios mantienen sus fotogramas antiguos.
  • Configuración en preview.json (opcional): scale 50 (%), step 2 (cada 2º fotograma), samples 1, audio (por defecto mix_raw.wav, si no la VO), final = comandos del flujo completo. El botón "Aprobar y renderizar final" de la página los ejecuta; notes imprime APROBADO cuando el usuario lo ha pulsado.
  • Corrige las notas una por una, responde a cada una, vuelve a renderizar y dile al usuario que no refresque nada: la página recarga el video por sí sola. Las notas repetidas del mismo tipo se convierten en una regla en este archivo.
  • Medido en lift-rocket (RTX 3070 Ti laptop, 820 fotogramas): primera vista previa 76 s, una toma cambiada 13 s, nada cambiado 12 s. La mayor parte del tiempo de render de un fotograma es sobrecarga fija, por lo que una resolución más baja ayuda menos que renderizar menos fotogramas.

API del kit (scripts/lowpoly_kit.py, importar dentro de Blender)

  • load_character(scn, "Suit") -> (armature, meshes, actions). Personajes: Adventurer, Beach, Casual_2, Casual_Hoodie, Farmer, King, Punk, Spacesuit, Suit, Swat, Worker.
  • Acciones (en el lugar, 24): Death, HitRecieve, HitRecieve_2, Idle, Idle_Neutral, Interact, Kick_Left/Right, Punch_Left/Right, Roll, Run, Run_Back/Left/Right, Walk, Wave, Gun_Shoot, Idle_Gun*, Idle_Sword, Sword_Slash, Run_Shoot.
  • add_faces(scn, arm, head, tag, kinds, spec, drop) antes de mover la armadura. Tipos: neutral, blink, side, shock, grimace, squint, dazed, xeyes. drop elimina los ojos/cejas 3D del paquete.
  • show_faces(faces, [(frame, kind), ...]): cortes duros, cada cara se mantiene hasta la siguiente.
  • nla(arm, [(action, start, end, blend[, influence]), ...]): entradas posteriores en pistas más altas.
  • drive_root(arm, [(frame, x, y, yaw, ease), ...], last): yaw 0 mira hacia -Y (la cámara por defecto).
  • new_cam, aim, yaw_to, ps1_render_settings(scn, outdir).
  • Sets: scripts/set_lift_lobby.py (ejecutar con BUILD_ONLY=True, devuelve box, uv_world, mat). Los nuevos sets siguen el mismo patrón: cajas con texturas de píxeles (interpolation = "Closest").

Reglas de aspecto visual

  • Renderizar 360x640, filter_size 0, AgX Punchy, sombreado plano; postproducción: color de 15 bits + dither Bayer, x3 nearest.
  • Las caras son decals planos de 32x28 píxeles intercambiados con cortes duros; nunca animar una cara suavemente.
  • Aplastar bajo fuerza keyframing la escala de la armadura (z hacia abajo, xy hacia arriba). Golpes de comedia: rápido de entrada (2-3 fotogramas), mantener.
  • Subtítulos: 1-3 palabras, sans serif gruesa, blanco con borde negro, palabra actual en amarillo, tercio inferior (y 1300 de 1920).
  • Llamadas de estadísticas: cuadro rojo redondeado, tercio superior, aparición en 4 fotogramas, una por número real.

Reglas de sonido

  • Cada SFX se coloca de modo que su pico caiga en el fotograma de imagen (at(name, frame, db, peak=...)).
  • La voz debe ganar: VO normalizada a 0 dBFS, bus SFX -11 dB, música -7 dB, más ducking sidechain mientras la voz habla (SFX -5 dB, música -9 dB). La v1 del corto lift tenía SFX a -3 y música a 0: "No puedo oír la voz en off".
  • Cortar la música justo en el golpe de parada grande, traerla de vuelta después. El silencio vende el gag.
  • Master: loudnorm -15 LUFS, pico verdadero -1.5 dBTP.
  • Verificar: earshot timeline en el mp4 vs cues.json (objetivo: cada cue dentro de 100 ms), measure para LUFS.

Errores ya encontrados (no repetir)

  • Las mallas del paquete vienen ocultas de render: hide_render = False (el kit lo hace).
  • Las mallas de puerta ya están en su lugar cerrado: animar solo el desplazamiento, o se cierran con un espacio de 0.7 m.
  • Un techo más bajo de ~2.75 m oculta las pantallas de pared de la cámara dentro de un coche tamaño ascensor.
  • Un personaje que no debe ser visto todavía: key location.z = -50 hasta su toma.
  • NLA de una sola toma pasando a una mantenida: establecer blend_out = 0 en el clip o la pose aparece un fotograma.
  • Las tomas inversas salen negras (techo sin iluminar): añadir una luz frontal y un poco de emisión en el techo. Renderizar el ángulo inverso temprano.
  • faster-whisper en mp3 puede romperse con un desajuste de PyAV: words.py decodifica con ffmpeg primero.
  • Una frase que comienza en -0.04 s da un segmento vacío en la alineación: sujetar con max(0.0, ...) (hecho).

Costo (corto lift, RTX 3070 Ti laptop)

Renderizar 820 fotogramas en 92 s, postproducción 17 s, $0. Personajes, SFX, música: $0 (CC0 + sintetizados en código). Voz: ElevenLabs aproximadamente $0.10 por toma, 2 tomas por corto.

Antes de entregarlo

  • Mira una hoja de contactos de los fotogramas finales (cada ~2 s), no solo las imágenes fijas.
  • Mide la sonoridad, verifica la sincronización, revisa los subtítulos contra el texto de VO (las correcciones ortográficas van en post.py FIX).
  • Dile al usuario qué se midió y qué no fue escuchado por un humano.

Skills relacionados