首页
/ OpenMontage 如何用 Ink Puppet 的 mocap 动作库驱动手绘角色动作动画

OpenMontage 如何用 Ink Puppet 的 mocap 动作库驱动手绘角色动作动画

2026-09-08 19:09:21作者:魏献源Searcher

如果你的目标是让一个白纸上"铅笔线"风格的手绘角色做出走、跑、跳舞、踢腿、挥手这类动作,OpenMontage 的 Ink Theater 提供的做法不是手写关键帧,而是把真实动作捕捉(mocap)数据重定向到简笔画火柴人上。你只需要按名字从 mocap/catalog.json 动作库里挑选动作,拼进一条 GSAP 时间线,再通过 HyperFrames 渲染成 MP4。整条路径运行在 HyperFrames(atelier)之上,环境要求是 Node.js ≥ 22、PATH 中可找到 ffmpegnpx,且 npx hyperframes doctor 退出码为 0(见 skills/core/hyperframes.md)。

Ink Puppet 的分工:谁写运动,谁挑动作

官方技能文档 skills/creative/ink-theater.md 把角色动画的规则定得很死:走 / 舞 / 挥手这类动作不允许手写运动(正弦曲线、手工摆帧都不行),正确姿势是"puppet + mocap 动作库"。角色部分由两个文件承载:

编排 API 只有三步,来自 ink-theater/README.md

var p = InkPuppet.create(mount, { cx: 960, ground: 902, boil: "boil" });
p.drawIn(tl, { start: 0.4 });                         // 铅笔逐肢把人物画出来
InkPuppet.choreograph(tl, p, [                          // 然后按名字播放 mocap clip,零手调
  { clip: "walk" }, { clip: "dance_spin" }, { clip: "kick" }, { clip: "wave" }
], { start: 3.7 });

两个行为细节来自 ink-puppet.js 源码,排错时有用:每个片段以 clip 的原生 fps 循环播放来填满 dur(给片段加 loop: false 则播到最后一帧为止);clip 名在 window.INK_CLIPS 中不存在时不会报错,而是打印 [InkPuppet] unknown clip "..." 的 console 警告并列出所有已知 clip,该时长的姿态保持不动。

动作库现有 12 个动作,按名字挑选

完整清单就是 ink-theater/mocap/catalog.json 的正文,全部 CMU 来源(mocap/NOTE.md 说明这些 clip 免费用于研究与商用,BVH 取自 una-dinosauria/cmu-mocap 镜像):

name 类别 说明 帧数 来源
climb locomotion clambers up 180 CMU 01_02
dance_glide dance gliding ballet dance 180 CMU 05_10
dance_spin dance expressive dance w/ a pirouette 180 CMU 05_02
jump action a forward jump 104 CMU 13_11
kick action kicks 180 CMU 10_01
march locomotion marches on the spot 180 CMU 15_01
run locomotion runs / jogs 38 CMU 09_01
shuffle locomotion a slow sneaking creep 180 CMU 77_29
sit posture sits then stands 180 CMU 13_01
twist dance a twisting dance 143 CMU 141_12
walk locomotion walks 86 CMU 02_01
wave gesture waves hello 75 CMU 141_16

挑选规则在 README 和 skill 文档中重复强调:先读 catalog,为每个叙事节拍挑匹配的动作,并且永远不要循环同一条 clip(循环播放是画面显得重复的根源)。

一条最短可跑的组合

仓库自带可直接 lint 的参考组合 ink-theater/examples/mocap-figure/index.html:铅笔线自己画出火柴人,然后依次 walk / run / dance_spin / kick / sit / wave,全程真实 CMU mocap。在自己的项目里复用引擎,按 ink-theater/examples/README.md 的说法是把 ink-theater.jsink-puppet.jsmocap/clips.jsassets/patrickhand.ttf 拷到项目根目录,然后按下面的顺序组织 composition。

1. 脚本加载顺序index.html 中依次引入 gsap,再按 ink-theater.jsink-puppet.jsclips.js 的顺序加载(顺序照抄参考 example 的 <script> 标签序列)。clips.js 必须在 choreograph 执行前加载,因为后者依赖 window.INK_CLIPS

2. 手写字体必须内嵌完整字体文件。这是文档里标注的"真正的根因":从 Google Fonts css2 API 只取一个 woff2(grep … | head -1)拿到的是单个 unicode-range 子集,常常缺 basic-latin,导致所有英文静默回退成衬线体,而渲染器日志仍显示 Fonts: 1 loaded。正确做法是内嵌仓库自带的完整 TTF,并把字幕、台词气泡放在 HTML 覆盖层 <div> 上:

@font-face { font-family: "InkHand"; src: url("assets/patrickhand.ttf") format("truetype"); font-display: block; }

不要用 Google Fonts 热链——渲染时的网络请求会破坏 HyperFrames 的确定性契约。

3. 时间线注册与编排。一个 paused: true 的时间线必须注册到 window.__timelines["<id>"],id 与 composition 元素上的 data-composition-id 一致,参考 example 的完整写法:

window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });

var pup = window.InkPuppet.create(document.getElementById("mount"),
  { cx: 960, ground: 902, boil: "boil" });   // boil 指向 SVG 里 feTurbulence 滤镜的 id

// 1) 铅笔逐肢画出人物
pup.drawIn(tl, { start: 0.4, each: 0.5 });

// 2) 按名字编排动作:每条 clip 以原生 fps 循环填满 dur
window.InkPuppet.choreograph(tl, pup, [
  { clip: "walk", dur: 2.6 },
  { clip: "dance_spin", dur: 3.4 },
  { clip: "kick", dur: 2.6 },
  { clip: "wave", dur: 2.6 }
], { start: 3.7 });

// 可选:台词气泡,文字走 HTML 覆盖层所以手写字体生效
// opts: into, overlay, at, dur, text, mouth:[x,y], center:[x,y], w, size, boil
// window.InkTheater.balloon(tl, { into: fxGroup, overlay: htmlOverlay, at: 5, dur: 2, text: "hello!", boil: "boil" });

// 3) 注册时间线(id 与 data-composition-id 对应)
window.__timelines["main"] = tl;

SVG 场景里需要一个 id="mount"<g> 作为人物挂载点,以及一个带 feTurbulenceboil 滤镜供 InkTheater.boil(...) 驱动(参考 example 中的 <filter id="boil"> 定义)。

4. 确定性约束。这个引擎遵守 HyperFrames 渲染契约:闭式弹簧缓动、boil 由时间线上的分步 seed 驱动(而非 SMIL)、"随机"摆动全部走带种子的 PRNG、禁止 repeat:-1。写自己的编排时不要引入运行时随机数或渲染时钟,否则 seek 重放会不一致。

可选:给动作库加一条新动作

catalog 里没有想要的动作时,用自带转换命令扩展,不需要改代码:

node ink-theater/mocap/add-motion.mjs backflip 90_01 dance "a backflip"

第二个参数可以是 CMU id(如 05_02005/05_02,脚本会从 una-dinosauria/cmu-mocap 镜像拉取 BVH)、一个 BVH 的 URL,或本地 .bvh 路径。要提前知道这个命令的副作用:它会发起网络下载,并在 ink-theater/mocap/ 目录内写入新 clip 的 JSON、重写 clips.js更新 catalog.json(见 ink-theater/mocap/NOTE.md),所以请在你自己的仓库副本里运行。成功时打印类似 ✓ added "backflip" (dance) · <帧数>f · library now N actions 的确认行;之后把新的 clips.js 拷进你的项目。

两个已知的适配边界:新骨架若不被自动映射(脚本自动支持 fair1 / CMU / Mixamo),需要在 bvh2clip.mjsALIAS 表里补一条别名;投影平面不合适时可用 --axis xy|zy 指定。

验证与渲染

skills/core/hyperframes.md 的验证协议,渲染前要跑完整三步,lint 或 validate 失败就不要渲染:

# 内置示例可以直接这样 lint(注意指向含 index.html 的 composition 目录,
# 不要指向 examples/ 本身——linter 在找 index.html)
npx hyperframes lint ink-theater/examples/mocap-figure

# 自己的项目:lint(静态契约检查)→ validate(浏览器运行时检查)→ render
npx hyperframes lint
npx hyperframes validate
npx hyperframes render --quality standard
  • lint 检查重复 id、轨道重叠、缺 data-composition-id、时间线未注册等静态契约,必须先过。
  • validate 会真正 seek 进暂停的组合、截屏采样、核对 window.__timelines 注册;迭代期对比度检查可用 --no-contrast 延后,最终渲染前必须完整通过。
  • render 产出 MP4;npm 包名是 hyperframes(不要使用 @hyperframes/cli,该名称不在公开 registry 上)。

如果运行期看到 console 里 [InkPuppet] unknown clip 警告,对照 catalog.jsonclips.js 里的名字修正——注意 clips.js 是动作库的打包产物,改了 catalog 后必须把新的 clips.js 同步进项目。

限制

  • 手绘角色动画是 animation / character-animation 管线上的"风格 + 引擎",不是独立管线,仓库里找不到对应的 .yaml manifest 是正常的(skill 文档明确标注 pipeline-exempt)。
  • 动作库当前只有上面 12 条 CMU 动作,新动作必须走 add-motion.mjsbvh2clip.mjs 转换,没有"直接拖个视频进来"的路径。
  • 台词气泡的文字依赖 HTML 覆盖层 + 内嵌完整字体,SVG 内直接写文字不会获得手写效果。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
34
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.16 K
2.78 K
docsdocs
暂无描述
Markdown
904
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
934
1.86 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
862
1.36 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
535
606
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.38 K
1.47 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.97 K
1.03 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
549
399
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.06 K
536