以程式碼建立無縫運動循環,並從 Scenario MCP 加入您的物件
概述
此處的循環是一個純時間函數。draw(ctx, w, h, t, P) 會在 t 秒時繪製影格,且其中的每個運動的週期長度都能整除循環總長度 (12 秒)。因此,第 12 秒的影格會與第 0 秒完全相同,影片循環時沒有任何接縫。一切都是程式碼生成的:不會扭曲或閃爍,更改一個顏色就能重新品牌化,且相同的程式碼能在任何尺寸下渲染。scenario.com 上的 Motion Loops 探索頁面展示了 80 個以此方式製作的循環(40 個背景、40 個形狀動畫),每個都有其對應的程式碼。scripts/loop-template.html 包含了這些循環所使用到的相同輔助函式 (M.*)、調色盤 (P) 和 tile(...) 註冊方式,因此這些循環的 tile 程式碼可以直接在其中執行,無需修改。
Scenario MCP 負責生成程式碼無法繪製的部分:使用者的主體。吉祥物、產品或角色會以去背的形式從 Scenario 輸出,而循環頁面會使用循環本身的時脈將它繪製在循環背景之上,因此它能獲得相同的動態模糊效果,且合成後的影片依然能無縫循環。對於需要真正移動的主體,則會從該去背圖像生成一個 AI 影片片段,在 Scenario 中移除其背景,然後透過 ffmpeg 將該片段疊加到已渲染的循環影片上。循環本身完全不需要模型,且渲染成本為零。
需要 Node 18 或更新版本、Playwright(執行 npm install playwright,然後執行 npx playwright install chromium;或安裝 Google Chrome 後使用 --channel chrome)、ffmpeg 和 ffprobe。僅在使用主體時才需要 Scenario MCP。關於連線、模型選擇和生成循環的詳細說明:如果已安裝 scenario 技能則使用它,否則請參閱 references/scenario-mcp.md。
快速參考
cp scripts/loop-template.html loop.html # 每個循環一個檔案:設定、draw()、疊加層
# 在瀏覽器中開啟 loop.html:即時預覽;?t=3 可凍結畫面,?tone=accent 或 ?tone=light 可切換色調
node scripts/render-loop.mjs loop.html --check # 檢查確定性與循環接縫,不輸出影片
node scripts/render-loop.mjs loop.html --stills look --at 0,3,6,9 # 輸出靜態影像供檢視
node scripts/render-loop.mjs loop.html --out loop.mp4 # 輸出 12 秒、1920x1080、24 fps 影片
node scripts/render-loop.mjs loop.html --params "overlay=subject.png" --out loop.mp4 # 使用去背圖像
node scripts/render-loop.mjs loop.html --width 1080 --height 1920 --out loop-9x16.mp4 # 直向影片
node scripts/render-loop.mjs --qa loop.mp4 composite.mp4 # 檢查任何 MP4 檔案的接縫與格式
請在已安裝 Playwright 的資料夾中執行。如果沒有使用 --force 參數,現有檔案絕不會被覆蓋。頁面檢查會渲染同一個時間點兩次(兩次結果必須相符),比較第 12 秒與第 0 秒的影格,並比較跨越循環點前後的運動狀態與其他週期邊界處的運動狀態。渲染工作會拒絕編碼,除非看到 PASS 輸出;只有當設計刻意要在循環點產生接縫時,才使用 --ignore-seam。
| 規則 | 原因 |
|---|---|
每個週期長度都能整除 12 秒 (1, 1.5, 2, 3, 4, 6, 12);使用 M.phase(t, period, offset) | 確保第 12 秒的影格與第 0 秒相同 |
| 滾動效果在每個週期內移動整數個圖案重複次數 | 移動非整數倍重複次數會在接縫處造成跳躍 |
隨機性僅來自 M.rand(i, salt);不在影格之間使用 Math.random、Date 或狀態變數 | 動態模糊和檢查功能會以非順序的方式渲染影格 |
尺寸計算使用 u = h / 100,計數使用 w | 在 16:9、9:16、1:1 以及縮圖尺寸下都能正確顯示 |
顏色僅從 P(bg、fg、fg2、mid、accent、paper、ink)中選取,每個品牌色有三種色調 | 透過更改 BRAND 來重新品牌化;透過更改色調來調整對比度 |
緩動函數來自 M.ease,可產生預期、 overshoot 和停頓效果;僅滾動效果使用恆定速度 | 呈現經過設計的動畫,而非機械式的運動 |
這 80 個循環在發布時所使用的渲染設定,也是腳本的預設值:24 fps、8 個動態模糊子影格搭配 180 度快門角、2 倍超取樣、H.264 High Profile、CRF 16、4:2:0 色彩取樣、BT.709 Limited Range、faststart。關於閉合循環、景深技巧以及審查習慣:請參閱 references/loop-craft.md。
| Scenario MCP 步驟 | 呼叫的 API |
|---|---|
| 連線、選擇團隊和專案 | teams_list(如果有多個,請詢問使用者) |
| 匯入使用者自己的素材 | upload_asset、curl PUT 每個部分、upload_asset_complete |
| 選擇模型 | recommend(txt2img、img2img、img2video、video2video),或 search |
| 讀取模型的輸入參數 | model_schema_get |
| 估算價格,使用者同意後再執行 | 先使用 dry_run: true 參數呼叫 model_run,再呼叫 model_run 和 jobs_wait |
| 取得結果 | asset_download,然後 curl -L -o |
實作範例:品牌吉祥物漂浮在程式碼循環之上
目標:製作一個 12 秒、無縫、使用品牌色、並含有品牌吉祥物漂浮其中的 1920x1080 循環影片,以及一個吉祥物會揮手的版本(可選)。
-
連線。 呼叫
teams_list。如果返回多個團隊或專案,詢問使用者要使用哪一個,並在後續所有呼叫中傳入其team_id和project_id:使用已登入的帳戶時,search、model_schema_get和model_run若缺少這些參數將拒絕執行。 -
先建立循環(本地端、免費)。 執行
cp scripts/loop-template.html mascot-loop.html,將BRAND.accent設定為品牌色,保留範例 tile(Perspective Floor,一個向攝影機延伸的網格),或撰寫你自己的draw()函式。在瀏覽器中開啟該檔案進行預覽,然後執行:node scripts/render-loop.mjs mascot-loop.html --check node scripts/render-loop.mjs mascot-loop.html --stills look --at 0,3,6,9檢視這些靜態影像。
--check的結果必須以PASS結尾。 -
匯入吉祥物。 如果使用者有吉祥物的檔案,請上傳它:
{ "file_name": "mascot.png", "content_type": "image/png", "kind": "image", "file_size": 482113 }(從
wc -c mascot.png取得file_size。)使用 curl 將檔案內容 PUT 到回傳的 URL,然後使用{"upload_id": "<upload id>"}呼叫upload_asset_complete,並記下回傳的 asset id。 -
選擇模型。 使用以下參數呼叫
recommend:{ "capability": "img2img", "prompt": "The mascot from the reference image, full body, three-quarter view, a light floating pose, cut out on a transparent background with clean edges, no shadow, no text", "limit": 5 }如果沒有檔案,請使用
"capability": "txt2img"並描述吉祥物。如果next_step.type是ask_user,則顯示選項並等待使用者選擇。優先選擇其 Schema 具有透明背景選項的模型;否則,請計畫在生成後進行去背處理。 -
讀取 Schema 並估算價格。 使用
{"model_id": "<id from recommend>"}呼叫model_schema_get。根據 Schema 建構參數:參考圖像欄位接收上傳的 asset id(如果 Schema 要求陣列則以陣列形式傳入),並設定透明背景選項。然後:{ "model_id": "<id from recommend>", "dry_run": true, "parameters": { "prompt": "<上述的提示詞>", "<參考圖像欄位>": ["<吉祥物 asset id>"], "<背景欄位>": "transparent" } }告知使用者價格,並等待其確認。
-
執行並下載。 使用相同的參數呼叫
model_run,並設定"wait": false,然後使用{"job_ids": ["<job id>"]}呼叫jobs_wait。接著使用{"asset_id": "<asset id>", "format": "png"}呼叫asset_download,並在mascot-loop.html所在的資料夾中執行curl -L -o subject.png "<url>"。在循環的背景色上以 100% 比例開啟 PNG 檔案:檢查主體輪廓在深色調上是否有光暈;如果有的話,請重新生成,不要試圖隱藏它。 -
在 Canvas 中合成並渲染。
node scripts/render-loop.mjs mascot-loop.html --params "overlay=subject.png" --stills look --at 0,0.75,1.5,2.25 --force node scripts/render-loop.mjs mascot-loop.html --params "overlay=subject.png" --out mascot-loop.mp4 --psnr 4drawOverlay()函式會讓吉祥物以 3 秒為週期上下浮動,並以 6 秒為週期左右搖擺,同時帶有接觸陰影,所有週期皆可整除 12,因此合成後的影片依然能無縫循環;請在此函式中調整吉祥物的大小和位置。腳本會檢查頁面、渲染 288 個影格、進行編碼,並檢查檔案的循環點。使用--psnr參數可以將一個編碼後的影格與其無損渲染結果進行比較。 -
(可選)製作一個吉祥物會動的片段。 使用
"capability": "img2video"呼叫recommend,將去背圖片的 asset id 作為起始影像,並使用類似「the mascot waves and turns its head, plain flat background, locked-off camera, no cuts」的提示詞。依序執行 Schema 讀取、乾執行(dry run)、使用者確認、正式執行、jobs_wait、下載。然後再使用"capability": "video2video"與「remove the background from this clip, keep the subject with an alpha channel」的提示詞呼叫recommend,選擇支援 Alpha 通道的 WebM VP9 作為輸出格式。再次依序執行上述步驟。接著,這次不使用--params overlay=...參數渲染一次循環(使用--out loop-plain.mp4),然後使用 ffmpeg 將該 AI 片段疊加到循環影片上(具體命令請參閱 references/scenario-mcp.md)。這個合成影片的長度與該 AI 片段相同;除非該片段本身就是無縫循環,否則最終影片不會是無縫的。 -
交付成品。 交付 HTML 檔案(循環仍可編輯)、MP4 影片、靜態影像,以及任何合成影片。
常見錯誤
- 在第 12 秒出現接縫跳躍。 使用了不能整除 12 的週期(例如 5 秒循環),或是滾動移動了 2.5 個重複次數。
--check會報告SEAM JUMP(第 12 秒的影格與第 0 秒不同)。修正週期長度;切勿使用淡入淡出效果來銜接頭尾。 - 在 tile 中使用狀態變數、
Math.random或Date。 這會導致相同的t值產生不同的影格,動態模糊的子影格也會不一致,循環因此無法閉合。--check會報告NOT DETERMINISTIC。所有計算都應基於t和M.rand(i, salt)。 - 只能讀取十六進位顏色的輔助函式。
P.mid和M.mixColor回傳的是rgb()字串,如果只用能解析十六進位顏色的解析器來處理,它們會直接被轉成黑色。範本中的M.rgba和M.mixColor可以同時處理兩種格式。 - 快速移動時出現閃爍(Strobing)。 對於整個畫面的平移,8 個子影格是不夠的,它會顯示出階梯狀的複製影像。在實作中,全頁滑動效果需要 40 個子影格。對於非常快速的循環,請增加
--samples參數值。 - 線框圖看起來像雜亂的線條。 一個 3D 波浪網格曾經導致這個問題;後來將其重建為隱藏線脊線(每一行將其背景填滿在其下方,從遠到近繪製),看起來就像地形了。
- 深度計算了兩次。 一個投影在根據深度縮放焦距後又再次縮小場景,導致場景集中在畫面中央。請使用
M.proj投影一次。 - 使用每隔 1.5 秒的靜態影像來審查一個 3 秒週期的循環。 這只能看到週期中的兩個姿勢。請在一個週期內以及循環點兩側都進行取樣(例如
--at 11.9,11.95,0,0.05)。 - 將所有標記都視為錯誤。 一個經過設計的瞬間變化(例如彈出、爆發)或在循環點的停頓,可能會觸發接縫或閃爍檢測器。請先檢視影格畫面。腳本會比較循環點與其周圍的運動狀態,以及其他週期邊界,這避免了大多數誤報:所有 80 個循環都能通過這個檢查。
- 標籤或計數器換行錯誤。 任何由週期索引的內容都必須使用
M.mod來處理換行;在實作中,一個頁碼計數器因為忘了使用M.mod,導致在循環點顯示成「06 / 05」。 - 在某一色調下對比度過低。 與其新增一個顏色,不如為該循環切換色調(深色、強調色、淺色)。
- 從磁碟載入疊加的 PNG 檔案。 從
file://協定開啟的頁面,如果繪製了本地圖片並嘗試讀取像素資訊,會因為圖片污染了 Canvas 而失敗。render-loop.mjs腳本會透過一個本地位址來提供檔案服務;瀏覽器預覽功能只負責顯示,因此不受影響。 - 字型尚未載入就開始繪製文字。 Canvas 的文字繪製不會等待網頁字型載入;請在第一個影格繪製前使用
await document.fonts.load(...),或者在循環中不使用文字。 - 過度追求位元率。 在純色區域上,x264 編碼器只會使用極少的位元,檔案看起來很小。在實作中,PSNR 顯示編碼器幾乎沒有引入畫質損失;唯一真正的損失來自 4:2:0 色彩取樣對細小飽和線條的影響。請使用
--psnr檢查,並透過加粗細小的彩色線條來解決,而不是提高位元率。 - 遺失了 VP9 影片的 Alpha 通道。 ffmpeg 內建的 VP9 解碼器會忽略 Alpha 通道;請確保在
-i shot.webm之前加上-c:v libvpx-vp9。 - 使用影片模型來建立循環。 一個生成式影片片段的最後一個影格極少能與第一個影格完全吻合。