Communitygithub.com

lowpoly-shorts

製作 30-60 秒垂直低多邊形 PS1 風格 3D 短片(方塊 CC0 角色、平面像素臉、顆粒抖動外觀、單字標題、同步音效、AI 語音),完全以程式碼搭配 Blender 完成,渲染成本為 0 美元。

lowpoly-shorts 是什麼?

這個技能讓 Claude 能夠編寫單一 Blender 腳本,在用戶的本地機器上渲染出低多邊形 PS1 風格的 3D 短片。它完全避免使用影片生成器,而是透過程式碼建構整個場景:撰寫 Blender 腳本、渲染幀、檢查並修復問題,最終合成聲音和字幕。該技能包含一個完整的 10 步驟流程:從腳本構思、語音錄製、鏡頭規劃,到場景建置、預覽迭代、最終渲染和後製。它支援 Blender 4.2+、Python 3.10+ 以及特定的 CC0 角色素材包。關鍵功能包括增量預覽循環(僅重新渲染變更的鏡頭)、PS1 風格後期效果(15 位元色彩、Bayer 抖動、最近鄰放大)、字級標題高亮和同步音訊。用戶只需詢問「低多邊形短片」、「PS1 風格影片」或「如果...」這類 3D 短片即可觸發。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mr-render/lowpoly-shorts/tree/HEAD

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Low-poly PS1 短片

一切都是程式碼:Claude 為每部短片編寫一個 Blender 腳本,在用戶的機器上渲染,檢視幀、修正問題,然後構建聲音和字幕。不使用影片產生器。

已在 examples/lift-rocket/(「如果電梯像火箭一樣快?」,34 秒)上驗證。在撰寫新短片前請先閱讀該範例:它是模板。

要求(先檢查,缺少則安裝)

  • Blender 4.2+(已在 5.3 上測試)。在 PATH 中尋找:blender,否則 C:\Program Files\Blender Foundation\<ver>\blender.exe / /Applications/Blender.app/Contents/MacOS/Blender。
  • Python 3.10+ 且安裝了 numpy pillow soundfile faster-whisper;ffmpeg 在 PATH 中。
  • 角色:Quaternius「Ultimate Modular Men」(CC0,免費,quaternius.com)。解壓縮到 assets/quaternius-modular-men/ 或將 LOWPOLY_PACK 設定為其 Individual Characters/Blends 資料夾。
  • TTS 語音(ElevenLabs 或使用者擁有的任何工具)。可選:earshot MCP 來檢查聲音。

流程(每個短片一個資料夾,複製 examples/lift-rocket/ 作為起點)

#步驟檔案 / 指令完成條件
1腳本script.md鉤子問題 + 連鎖後果,每個更糟更有趣 + 真實數字 + 乾冷笑點。30-40 秒時長約 60-110 字。每個數字都須查證來源並寫入事實列表。
2語音vo/takeA.mp3錄製 2 次,選取單字時間點乾淨的那一次。充滿活力的創作者聲音,而非沉穩的聲音(沉穩 =「無聊」的反饋)。
3單字時間點python ../../scripts/words.py vo/takeA.mp3takeA_words.json 中沒有零長度的單字。
4鏡頭列表script.md 中的表格每句 VO 短語一個鏡頭,每 1-3 秒一個新畫面,每個鏡頭對應一個單字時間點。
5場景build.py (Blender)blender -b -P build.py -- frames --still 30,120,... 渲染靜態幀;在完整渲染前檢查每個靜態幀。
6預覽 + 筆記python ../../scripts/preview.py . render 然後 serve使用者沒有未處理的筆記(參見預覽循環)。
7渲染blender -b -P build.py -- frames360x640 EEVEE PNG,f_0001.png...
8音效python build_mix.pySFX 放置於畫面幀上,VO 在上方,低保真背景音樂在下,側鏈壓縮。輸出 mix_raw.wav + cues.json。
9後製python post.pyPS1 抖動、效果、單字高亮字幕、統計數據標註 -> out/ 目錄下 1080x1920 格式。
10封裝見下方short.mp4,然後測量並檢查同步。

幀規則:24 fps,幀 1 = 時間 0,F = lambda t: int(round(t * 24)) + 1。

封裝:

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

稍後無需重新剪輯畫面即可更換語音: python ../../scripts/words.py vo/takeB.mp3 然後 python ../../scripts/align.py vo/takeA_words.json vo/takeB.mp3 --end 34(--end = VO 背景音樂停止處;輸出長度為 end + 2 秒。如果不加:最後一個單字 + 1.5 秒,這會產生比畫面更短的檔案) -> takeB_aligned.wav + takeB_aligned_words.json。將 build_mix.py(VO 環境變數)和 post.py 指向它們。

預覽循環(在完整渲染之前)

在瀏覽器中進行快速低解析度預覽,使用者在時間軸上留下筆記,Claude 修正並僅重新渲染內容變更的鏡頭。鏡頭 = build.py 中的攝影機標記。

動作指令(從短片的資料夾執行)
渲染預覽python ../../scripts/preview.py . render(首次執行:每個鏡頭;之後:僅變更的鏡頭)
開啟筆記頁面python ../../scripts/preview.py . serve -> http://127.0.0.1:8765
讀取筆記python ../../scripts/preview.py . notes(按鏡頭分組,帶有幀號)
修正後編輯 build.py,再次執行 render,然後 python ../../scripts/preview.py . done 3 5 --reply "你變更了什麼"
強制渲染特定鏡頭render --shots c_press,c_slam 或 render --all
  • build.py 必須接受 --blend-only(建立場景、儲存、不渲染)。請參閱範例。
  • 變更檢測:每個預覽幀都會產生一個指紋(活動攝影機、每個可見物體的 變換、骨架姿勢、面部交換、材質、燈光、網格資料)。僅當鏡頭的一個 幀發生變更時,該鏡頭才會重新渲染。未變更的鏡頭保留其舊幀。
  • preview.json 中的設定(可選):scale 50(%)、step 2(每隔一幀)、samples 1、 audio(預設 mix_raw.wav,否則為 VO)、final = 完整流程的指令。頁面上的 「核准並渲染最終版」按鈕會執行這些指令;當使用者點擊該按鈕時,notes 會列印 APPROVED。
  • 逐一修正筆記,對每個筆記回覆,重新渲染,並告訴使用者無需重新整理:頁面 會自動重新載入影片。重複的同類筆記會成為此檔案中的規則。
  • 在 lift-rocket(RTX 3070 Ti 筆記型電腦,820 幀)上測量:首次預覽 76 秒,一個鏡頭變更 13 秒, 無變更 12 秒。大部分幀的渲染時間是固定開銷,因此降低解析度的幫助 不如減少渲染幀數。

Kit API(scripts/lowpoly_kit.py,在 Blender 內部匯入)

  • load_character(scn, "Suit") -> (armature, meshes, actions)。角色:Adventurer, Beach, Casual_2, Casual_Hoodie, Farmer, King, Punk, Spacesuit, Suit, Swat, Worker。
  • 動作(原地,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) 在移動骨架之前。種類: neutral, blink, side, shock, grimace, squint, dazed, xeyes。drop 刪除角色包的 3D 眼睛/眉毛。
  • show_faces(faces, [(frame, kind), ...]):硬切,每個表情保持到下一個。
  • nla(arm, [(action, start, end, blend[, influence]), ...]):後續條目在較高軌道上。
  • drive_root(arm, [(frame, x, y, yaw, ease), ...], last):yaw 0 面向 -Y(預設攝影機)。
  • new_cam、aim、yaw_to、ps1_render_settings(scn, outdir)。
  • 場景:scripts/set_lift_lobby.py(以 BUILD_ONLY=True 執行,它回傳 box、uv_world、mat)。 新場景遵循相同模式:帶有像素紋理的方塊(interpolation = "Closest")。

外觀規則

  • 渲染 360x640,filter_size 0,AgX Punchy,平面著色;後製:15 位元色彩 + Bayer 抖動,x3 最近鄰放大。
  • 面部是 32x28 像素的平面貼圖,以硬切方式切換;永遠不要平滑動畫面部。
  • 在受力下壓扁:透過關鍵影格設定骨架縮放(Z 向下,XY 向上)。喜劇節拍:快速進入(2-3 幀)、保持。
  • 字幕:1-3 個單字,粗體無襯線字體,白色帶黑色描邊,當前單字為黃色,下方三分之一(y 約 1300/1920)。
  • 統計數字標註:紅色圓角框,上方三分之一,4 幀內彈入,每個真實數字一個。

聲音規則

  • 每個 SFX 的放置位置應使其峰值落在畫面幀上(at(name, frame, db, peak=...))。
  • 語音必須清晰:VO 歸一化至 0 dBFS,SFX 匯流排 -11 dB,音樂 -7 dB,此外在語音播放時 進行側鏈壓縮(SFX -5 dB,音樂 -9 dB)。電梯短片的 v1 版本 SFX 為 -3,音樂為 0: 「我聽不到旁白」。
  • 在大的停頓節拍處音樂驟停,然後再恢復。沉默能強化笑點。
  • 主輸出:loudnorm -15 LUFS,真實峰值 -1.5 dBTP。
  • 檢查:對 mp4 使用 earshot timeline 與 cues.json 比對(目標:每個提示點在 100 ms 內),使用 measure 檢查 LUFS。

已遇到的錯誤(請勿重複)

  • 角色包網格預設為渲染隱藏:hide_render = False(Kit 已處理)。
  • 門網格已位於關閉位置:僅動畫化偏移量,否則它們會關閉時留下 0.7 公尺的間隙。
  • 低於約 2.75 公尺的天花板會隱藏電梯車廂內牆上的顯示器,使其無法被攝影機看到。
  • 暫時不該被看到的角色:在其鏡頭開始前,設定位置關鍵影格 location.z = -50。
  • NLA 一次性動作過渡到保持狀態:將剪輯的 blend_out = 0,否則姿勢會彈回一幀。
  • 反向鏡頭變黑(天花板未照明):添加前置燈光和一點天花板自發光。提早渲染 反向鏡頭。
  • faster-whisper 在 mp3 上可能因 PyAV 不匹配而中斷:words.py 先使用 ffmpeg 解碼。
  • 從 -0.04 秒開始的短語會在校準中產生空片段:使用 max(0.0, ...) 進行鉗制(已修正)。

成本(lift 短片,RTX 3070 Ti 筆記型電腦)

渲染 820 幀需 92 秒,後製 17 秒,成本 0 美元。角色、SFX、音樂:0 美元(CC0 + 程式碼合成)。 語音:ElevenLabs 每次約 0.10 美元,每部短片 2 次。

在提交之前

  • 檢視最終幀的縮略圖(大約每 2 秒一張),而不僅僅是靜態幀。
  • 測量響度,檢查同步,檢查字幕與 VO 文字是否一致(拼寫修正請在 post.py 的 FIX 中進行)。
  • 告訴使用者哪些是經過測量的,哪些未經人工聆聽檢查。

相關技能