iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0
Vibe Coding

使用 Vibe Coding 開發一套 Tutting 模擬器吧!系列 第 17

📐DAY 17 | 在 IK 基礎上實踐 Tracing 軌跡功能

  • 分享至 

  • xImage
  •  

在 Day 13 中我們討論了 Tutting 的四大中階技術之一——Tracing(軌道)。

在本文開始前觀賞一部影片:The Art of Tracing | Dance Freestyle by Kevin Paradox

影片中的舞者名為 Kevin Paradox,是來自荷蘭的頂尖舞者、編舞家與舞蹈導師,在全球 Hip-Hop 與 Experimental(實驗舞蹈)領域享有極高聲譽。他在工作坊(Workshop)中示範了 Tracing 的技巧,在音樂的搭配下流暢的將身體作為軌道,毫無死角的保持滑動!!!

在真實舞蹈中,Tutting 舞者會讓手掌或指尖貼著另一條手臂或空間軸線滑動,產生像「貼在想像軌道上」的幾何錯覺,這個技巧任何舞風(例如:tutting、popping、hiphop...)都適用。

今天 DAY 18,我們要探討如何在手腳 Two-Bone IK 的基礎上,正式實作 Tracing 軌跡輔助系統!

Tracing 軌跡輔助系統概念與操作說明

1. 概念與操作流程

  • 切換獨立分頁:點選 UI 面板獨立的 「軌跡 」 分頁,進入軌跡專用編輯介面。
  • 控制點拉設:將場景中的橘色 IK Target 球拖曳至目標位置,點擊「+ 新增控制點」生成對齊當下座標的紫色控制點,重複擺位拉出空間路徑。
  • 軌跡類型設定:選擇 line(折線模式)或 curve(Catmull-Rom 曲線模式,大於等於 3 點時自動內插連續平滑弧線)。
  • 進度預覽與拍點生成:拉動進度滑桿實時預覽手掌軌跡,確認無誤後點擊「沿軌跡生成拍點」,系統將自動將軌跡數據寫入時間軸。

2. 三大核心原理重點

  • 相對 Root 座標綁定 (Relative Offset):控制點上記錄的是「相對 Root 骨骼(肩膀/髖部)的世界座標偏移量」而非絕對世界座標。當舞者做軀幹扭腰、深蹲或平移時,描繪軌跡會自動黏著身體跟著位移。
  • 播放期即時 IK 求解 (Real-time IK Sampling):若前後拍點屬於同條軌跡,播放時會跳過關節角度的 Slerp 插值,改為依時間進度 即時向 Catmull-Rom 幾何曲線取樣,並重新求解 Two-Bone IK。末端手掌/腳掌旋轉則保留 FK Slerp,實現毫秒級無限平滑。
  • 世界矩陣強制刷新 (Matrix Sync):在即時取樣重算前,強制呼叫 model.updateMatrixWorld(true),解決 Three.js 預設延遲更新世界矩陣的問題,徹底消滅手掌跟隨軀幹位移時「滯後 1 幀」的震盪抖動感。

https://ithelp.ithome.com.tw/upload/images/20260824/20144288eXF4DlY4fF.png

Tracing 系統前後機制對照

加入Tracing 軌跡輔助系統後在能夠模擬真實舞者進行 Tracing 的技巧,以下為前後機制對照:

比較維度 傳統關節角度 Slerp 插值 Tracing 軌跡即時求解
路徑平滑度 需高密度拍點逼近,否則有明顯稜角感 毫秒級無限平滑(Catmull-Rom 即時取樣)
軀幹連動性 絕對座標固定,軀幹一動手就漂浮 相對 Root 綁定,軀幹位移軌跡自動跟走
時間軸維護 塞滿數十個拍點,極難微調修改 少量拍點 即可完整保留幾何曲線資料
UI 動線規劃 擠在手腳 IK 分頁,操作混亂 獨立至 「軌跡」分頁,動線清晰

結語

實現了 Tracing 軌跡功能後,XBOT 終於能做出順暢貼合手臂、繪製空間圓弧的流暢動態!透過將「編輯期生成」與「播放期即時 IK 取樣」相結合,我們在不破壞既有時間軸架構的前提下,完美解決了幾何曲線的平滑度痛點。


軌跡輔助功能Prompt

# 本次新增:軌跡輔助功能

## 目的

在 IK 模式下,讓末端骨骼(手掌/腳掌)可以沿著使用者畫的路徑(直線或曲線)移動,而不只是手動單點擺位。

---

## 階段一:編輯輔助工具(把軌跡取樣成一串拍點)

### 操作邏輯

- 場景中新增**紫色控制點球**(`trajPointMeshes[limb]`,陣列,順序=路徑順序)。
- 操作流程:
  1. 先拖橘色 IK target 球到想要的位置
  2. 按「+ 新增控制點」
  3. 新控制點永遠對齊 target 球目前座標

> ⚠️ 這是使用者明確要求的行為,**不要改回自動偏移/延伸算法**,之前試過會造成疊點難選。

### 軌跡模式

- `TRAJ_MODE[limb]`:
  - `'line'`:含 ≥3 點但仍逐點連折線
  - `'curve'`:≥3 點時用 `THREE.CatmullRomCurve3`

### 核心函式

| 函式 | 說明 |
|---|---|
| `sampleTrajectoryFromPoints(mode, relPoints, t)` | **純數學函式**,不依賴場景 mesh。輸入相對座標陣列,回傳 0~1 進度對應的座標。**這個純函式版本是階段二能運作的關鍵**,播放時不會依賴場景中是否還留著控制點球。 |
| `sampleTrajectory(limb, t)` | 上述函式的即時預覽包裝(讀 `trajPointMeshes` 的即時 world 座標)。 |
| `generateKeyframesFromTrajectory()` | 沿路徑等間隔取樣 N 點(UI 可調,2~20),每點依序執行:套用進度 → `solveRootFollowForLimb` → `solveTwoBoneIK` → `syncTargetFromBone` → `addKeyframe()`,等同使用者手動逐點擺拍點。 |

---

## 階段二:曲線播放時即時平滑

### 使用者需求

曲線軌跡在播放時要真的平滑沿著曲線走,不能因為取樣點數不夠就有稜角。

### 做法

`generateKeyframesFromTrajectory()` 額外把軌跡資料烘焙進每個拍點:

``js
kf.traj[limb] = {
  id: trajId,        // 同一次生成共用一個id,用來判斷播放時兩個相鄰拍點是否屬於同一段連續軌跡
  mode,               // 'line' | 'curve'
  points: relPoints,  // 控制點,相對 root 骨骼「生成當下」世界座標的偏移量(不是絕對世界座標)
  pole: poleSnapshot, // pole球,同樣相對root的偏移
  t                   // 這個拍點在整條軌跡上的參數位置 0~1
}
``

控制點/pole 存**相對 root 骨骼偏移**而非絕對世界座標,這樣角色因為軀幹 FK 動畫而移動時,軌跡會自動跟著走。

### `updateKeyframePlayback(now)` 播放邏輯改動

1. **`collectTrajOverrideKeys(frameA, frameB)`**
   檢查前後兩個拍點是否對同一個 limb 記錄了相同 `trajId` 的 `traj`,若是,回傳該 limb 的 `{chain.root, chain.mid}` 這兩個骨骼 key。

2. **跳過覆蓋 key**
   一般的 `ALL_JOINT_KEYS` 逐骨骼角度 slerp 迴圈**跳過**這些 key。

3. **⚠️ 關鍵坑:world matrix 更新時機**
   改完其他骨骼(例如軀幹)的本地四元數後,`bone.getWorldPosition()` 讀到的還是**上一幀**的 `matrixWorld`(three.js 只在 `renderer.render()` 才會重算)。
   因此在算 rootBone 世界座標之前**必須**先手動呼叫一次:
   ```js
   model.updateMatrixWorld(true)

否則肩膀/髖部世界座標會跟這一幀剛套用的軀幹動作對不上,軌跡跟身體會有一幀的滯後感。
(這跟原架構文件記錄的「IK 節流忘記比對 root 座標」是同一種坑的不同變形,都是「world 座標什麼時候才是最新的」沒搞清楚。)

  1. applyTrajOverridesDuringPlayback(frameA, frameB, et)
    對每個觸發覆蓋的 limb:

    • et(緩動後的轉場進度)在 frameA.traj[limb].tframeB.traj[limb].t 之間內插出 curT
    • 餵給 sampleTrajectoryFromPoints 取樣出當下曲線位置
    • 加上剛才刷新過的 root 世界座標組成 target,pole 同理 lerp
    • 重新呼叫一次 solveTwoBoneIK

    末端骨骼(手掌/腳掌)本身旋轉仍照舊 slerp,不受影響(呼應「IK 只驅動 root+mid,末端保留 FK」的既有設計哲學)。

結果

  • 播放時末端是連續即時取樣曲線本身,平滑度不受生成點數影響
  • 不同批次生成的軌跡片段、或手動擺的拍點之間,trajId 不匹配,維持原本的角度 slerp 行為,不受影響

上一篇
📐DAY 16 | 挑戰更具高難度的編舞實驗!3D Tutting 模擬器 IK 進階架構解析
下一篇
📐DAY 18 | 讓編舞效率翻倍!輔助設計功能:Undo/Redo、鏡像、幾何鏡頭、洋蔥皮與自動存檔
系列文
使用 Vibe Coding 開發一套 Tutting 模擬器吧!25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言