Communitygithub.com

lowpoly-shorts

Créez un court-métrage 3D low-poly vertical de 30 à 60 secondes dans le style PS1 (personnages CC0 en blocs, visages plats et pixellisés, effet de tramage crunchy, sous-titres surlignant les mots, effets sonores synchronisés, voix IA) entièrement par code avec Blender, pour un coût de rendu de 0 €.

Qu'est-ce que lowpoly-shorts ?

Cette compétence permet à un agent IA (comme Claude) de générer automatiquement un court-métrage 3D animé dans le style rétro PS1, entièrement via l'écriture et l'exécution de scripts Blender et Python. L'agent rédige un script comique, génère les personnages, les décors, les animations, les sous-titres, les effets sonores et le doublage vocal (via ElevenLabs ou autre) le tout sans aucun frais de rendu cloud. Le pipeline comprend une boucle de prévisualisation interactive où l'utilisateur peut laisser des commentaires sur chaque plan, que l'agent corrige automatiquement avant le rendu final. Le résultat est une vidéo verticale 1080×1920 avec le rendu PS1 caractéristique (couleurs 15 bits, tramage Bayer, sur-échantillonnage x3).

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Courts-métrages PS1 low-poly

Tout est dans le code : Claude écrit un script Blender par court-métrage, le rend sur la machine de l'utilisateur, regarde les images, les corrige, puis construit le son et les sous-titres. Aucun générateur vidéo.

Testé sur examples/lift-rocket/ (« Et si les ascenseurs étaient aussi rapides que des fusées ? », 34 s). Lisez cet exemple avant d'écrire un nouveau court-métrage : c'est le modèle.

Prérequis (vérifier d'abord, installer ce qui manque)

  • Blender 4.2+ (testé sur 5.3). Trouvez-le : blender dans le PATH, sinon C:\Program Files\Blender Foundation\<ver>\blender.exe / /Applications/Blender.app/Contents/MacOS/Blender.
  • Python 3.10+ avec numpy pillow soundfile faster-whisper ; ffmpeg dans le PATH.
  • Personnages : Quaternius « Ultimate Modular Men » (CC0, gratuit, quaternius.com). Décompressez dans assets/quaternius-modular-men/ ou définissez LOWPOLY_PACK vers son dossier Individual Characters/Blends.
  • Une voix TTS (ElevenLabs ou tout outil que l'utilisateur possède). Optionnel : le MCP earshot pour vérifier le son.

Le pipeline (un dossier par court-métrage, copiez examples/lift-rocket/ comme point de départ)

#ÉtapeFichier / commandeTerminé quand
1Scriptscript.mdQuestion d'accroche + chaîne de conséquences, chacune pire et plus drôle + chiffres réels + chute sèche. 60-110 mots pour 30-40 s. Chaque chiffre vérifié avec une source et écrit dans la liste Faits.
2Voixvo/takeA.mp32 prises, choisir celle avec des timings de mots propres. Voix de créateur énergique, pas calme (calme = retour « ennuyeux »).
3Timings des motspython ../../scripts/words.py vo/takeA.mp3takeA_words.json n'a aucun mot de longueur nulle.
4Liste des planstableau dans script.mdUn plan par phrase VO, une nouvelle image toutes les 1 à 3 s, chaque plan lié à un temps de mot.
5Scènebuild.py (Blender)blender -b -P build.py -- frames --still 30,120,... rend des images ; regardez chaque image avant le rendu complet.
6Aperçu + notespython ../../scripts/preview.py . render puis serveL'utilisateur n'a pas de notes ouvertes (voir boucle d'aperçu).
7Rendublender -b -P build.py -- framesPNG 360×640 EEVEE, f_0001.png...
8Sonpython build_mix.pyEffets sonores placés sur les images, VO par-dessus, nappe lo-fi en dessous, ducking. Écrit mix_raw.wav + cues.json.
9Postpython post.pyTramage PS1, effets, sous-titres avec surlignage du mot, callouts statistiques -> out/ en 1080×1920.
10Multiplexagevoir ci-dessousshort.mp4, puis mesurer et vérifier la synchronisation.

Règle d'image partout : 24 ips, image 1 = t 0, F = lambda t: int(round(t * 24)) + 1.

Multiplexage :

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

Échanger la voix plus tard sans recouper l'image : python ../../scripts/words.py vo/takeB.mp3 puis python ../../scripts/align.py vo/takeA_words.json vo/takeB.mp3 --end 34 (--end = là où la nappe VO s'arrête ; la sortie dure end + 2 s. Sans elle : dernier mot + 1,5 s, ce qui donne un fichier plus court que l'image) -> takeB_aligned.wav + takeB_aligned_words.json. Pointez build_mix.py (variable d'environnement VO) et post.py vers eux.

Boucle d'aperçu (avant le rendu complet)

Aperçu rapide basse résolution dans le navigateur, l'utilisateur laisse des notes sur la timeline, Claude corrige et re-rend uniquement les plans dont le contenu a changé. Plans = les marqueurs de caméra dans build.py.

FaireCommande (depuis le dossier du court-métrage)
Rendre l'aperçupython ../../scripts/preview.py . render (première exécution : tous les plans ; ensuite : uniquement les plans modifiés)
Ouvrir la page de notespython ../../scripts/preview.py . serve -> http://127.0.0.1:8765
Lire les notespython ../../scripts/preview.py . notes (groupées par plan, avec les numéros d'images)
Après correctionéditer build.py, relancer render, puis python ../../scripts/preview.py . done 3 5 --reply "ce que vous avez changé"
Forcer des plansrender --shots c_press,c_slam ou render --all
  • build.py doit accepter --blend-only (construire la scène, sauvegarder, ne pas rendre). Voir l'exemple.
  • Détection de changement : chaque image d'aperçu reçoit une empreinte (caméra active, transformation de chaque objet visible, pose d'armature, échanges de visages, matériaux, lumières, données de maillage). Un plan se re-rend uniquement si l'une de ses images a changé. Les plans inchangés conservent leurs anciennes images.
  • Paramètres dans preview.json (optionnel) : scale 50 (%), step 2 (toutes les 2 images), samples 1, audio (par défaut mix_raw.wav, sinon la VO), final = les commandes du pipeline complet. Le bouton « Approuver & rendre final » de la page les exécute ; notes affiche APPROUVÉ lorsque l'utilisateur l'a cliqué.
  • Corrigez les notes une par une, répondez à chacune, re-rendez, et dites à l'utilisateur de ne rien rafraîchir : la page recharge la vidéo elle-même. Les notes répétées du même type deviennent une règle dans ce fichier.
  • Mesuré sur lift-rocket (RTX 3070 Ti portable, 820 images) : premier aperçu 76 s, un plan modifié 13 s, rien changé 12 s. La majeure partie du temps de rendu d'une image est une surcharge fixe, donc une résolution plus basse aide moins que de rendre moins d'images.

API du kit (scripts/lowpoly_kit.py, importer à l'intérieur de Blender)

  • load_character(scn, "Suit") -> (armature, meshes, actions). Personnages : Adventurer, Beach, Casual_2, Casual_Hoodie, Farmer, King, Punk, Spacesuit, Suit, Swat, Worker.
  • Actions (sur place, 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) avant de déplacer l'armature. Types : neutral, blink, side, shock, grimace, squint, dazed, xeyes. drop supprime les yeux/sourcils 3D du pack.
  • show_faces(faces, [(image, type), ...]) : coupures franches, chaque visage tient jusqu'au suivant.
  • nla(arm, [(action, début, fin, blend[, influence]), ...]) : les entrées suivantes sur des pistes plus hautes.
  • drive_root(arm, [(image, x, y, lacet, ease), ...], last) : lacet 0 fait face à -Y (la caméra par défaut).
  • new_cam, aim, yaw_to, ps1_render_settings(scn, outdir).
  • Décors : scripts/set_lift_lobby.py (exécutez avec BUILD_ONLY=True, il retourne box, uv_world, mat). Les nouveaux décors suivent le même modèle : boîtes avec textures pixel (interpolation = "Closest").

Règles de rendu

  • Rendu 360×640, filter_size 0, AgX Punchy, ombrage plat ; post : couleurs 15 bits + tramage Bayer, x3 nearest.
  • Les visages sont des décalcomanies plates 32×28 pixels échangées par coupures franches ; n'animez jamais un visage en douceur.
  • Écrasement sous force en animant l'échelle de l'armature (z vers le bas, xy vers le haut). Temps comiques : rapide à l'entrée (2-3 images), maintien.
  • Sous-titres : 1-3 mots, sans-serif gras, blanc avec contour noir, mot actuel en jaune, tiers inférieur (y 1300 de 1920).
  • Callouts statistiques : boîte arrondie rouge, tiers supérieur, apparition en 4 images, un par nombre réel.

Règles sonores

  • Chaque effet sonore est placé de sorte que son pic tombe sur l'image (at(name, frame, db, peak=...)).
  • La voix doit gagner : VO normalisée à 0 dBFS, bus SFX -11 dB, musique -7 dB, plus ducking sidechain pendant que la voix parle (SFX -5 dB, musique -9 dB). La v1 du court-métrage ascenseur avait SFX à -3 et musique à 0 : « Je n'entends pas la voix off ».
  • Coupez la musique net sur le temps d'arrêt important, ramenez-la après. Le silence vend la blague.
  • Master : loudnorm -15 LUFS, crête vraie -1,5 dBTP.
  • Vérifiez : earshot timeline sur le mp4 vs cues.json (objectif : chaque cue à moins de 100 ms), measure pour LUFS.

Bugs déjà rencontrés (ne pas répéter)

  • Les maillages du pack sont livrés cachés au rendu : hide_render = False (le kit le fait).
  • Les maillages de porte sont déjà à leur emplacement fermé : n'animez que le décalage, sinon ils se ferment avec un écart de 0,7 m.
  • Un plafond plus bas que ~2,75 m cache les affichages muraux de la caméra à l'intérieur d'une cabine d'ascenseur.
  • Un personnage qui ne doit pas encore être vu : mettez en clé location.z = -50 jusqu'à son plan.
  • NLA un plan unique passant à une tenue : réglez blend_out = 0 du clip ou la pose saute pendant une image.
  • Les plans inversés deviennent noirs (plafond non éclairé) : ajoutez une lumière avant et une petite émission au plafond. Rendez l'angle inversé tôt.
  • faster-whisper sur mp3 peut planter avec une incompatibilité PyAV : words.py décode d'abord avec ffmpeg.
  • Une phrase commençant à -0,04 s donne une tranche vide dans l'alignement : serrez avec max(0.0, ...) (fait).

Coût (court-métrage ascenseur, RTX 3070 Ti portable)

Rendu 820 images en 92 s, post 17 s, 0 €. Personnages, SFX, musique : 0 € (CC0 + synthétisé dans le code). Voix : ElevenLabs environ 0,10 € par prise, 2 prises par court-métrage.

Avant de remettre le travail

  • Regardez un plan contact des images finales (toutes les ~2 s), pas seulement les images fixes.
  • Mesurez le volume, vérifiez la synchro, vérifiez les sous-titres par rapport au texte VO (les corrections orthographiques vont dans post.py FIX).
  • Dites à l'utilisateur ce qui a été mesuré et ce qui n'a pas été entendu par un humain.

Skills associés