先用微调过的 Qwen-Image-Layered 把一张立绘拆成按 Live2D 部件分类的 RGBA 图层(和 See-through 同一路线);再对每层 alpha 做 Delaunay 三角化;最后用一个 5M 参数的小 Transformer,把整个角色所有图层的所有顶点当成一个点云序列,对每个「参数 × 端点值」一次前向回归出每个顶点的 2D 位移,存成 Live2D 的关键帧表。novelty 在于跨图层联合 attention(而非逐层独立预测),让虹膜不滑出眼白、刘海不脱离额头。
| 术语 | 在本文中具体指什么 |
|---|---|
| Layer (\(L_i\)) | 一张和画布等大的 RGBA 图,例如「后发:金色长双马尾」,被遮挡的区域已补画。 |
| Mesh (\(M_i=(V_i,F_i)\)) | 覆盖该层不透明像素的 2D 三角网格;顶点一动,贴图跟着扭曲。新图从 alpha 自动三角化,训练时用画师原网格。 |
| Parameter \(p\) | Live2D 的具名滑杆,如 ParamAngleX(左右转头,范围如 [-30, 30])、ParamEyeLOpen、ParamBreath。主模型只建 8 个,扩展版 24 个。 |
| Keypose \(q\) | 画师在某参数上存位移的几个取值点,通常「一端 / 静止 / 另一端」三个,如 AngleX = -30, 0, +30。 |
| Keypose offset \(k^{p,c}_i\) | 在参数 \(p\) 的端点 \(c\) 上,第 \(i\) 层每个顶点相对静止位置的 2D 位移。这张表就是 Live2D 资产的全部动画内容,也就是 Stage 2 要预测的东西。 |
| Joint / per-layer | joint:全角色所有顶点拼成一个序列做 self-attention;per-layer:同一个模型但 attention 加块对角约束,每层只能看见自己。 |
| True generation | 一次前向得到结果,不喂任何 ground truth。与之对立的是 teacher forcing(自回归时每步喂真值前缀),作者早期 AR 版本 cos>0.99 就是这样「刷」出来的,已撤回。 |
| Direction cosine | 主指标:预测位移和画师位移的逐顶点余弦相似度,只算画师真正移动的顶点(‖Δv‖ > 画布 0.5%),先层内平均、再角色内平均,报告角色的均值和中位数。 |
| Magnitude ratio | 预测位移平均长度 / 真值平均长度,理想为 1;<1 动作太小,>1 动作太大。 |
| FFD oracle | 把画师真值位移拟合到一个 2×2~4×4 的 FFD 格子上,用来测「这种表示能否表达动作」,不是能预测的对手方法。 |
flowchart LR
I["Illustration"] --> S1["Stage 1 layered diffusion"]
S1 --> L["Ordered RGBA layers"]
L --> M["Alpha to mesh"]
L --> D["DINOv2 pooled"]
M --> T["Vertex tokens"]
D --> T
P["Param id + keypose"] --> T
T --> J["Joint Transformer 5.1M"]
J --> O["Offsets per vertex"]
O --> R["Live2D style rig"]
L -->|"texture edit"| R
目标资产写成 \(\mathcal{A}=(\{L_i\},\{M_i\},\{k^{p,0}_i,k^{p,1}_i\})\):图层、每层网格、每层每个参数两端的顶点位移。插值类型固定为线性,不预测。运行时滑杆只用于在相邻两个 keypose 之间线性插值:
\[ V_i(q) = (1-t)\,\hat V^{(p,k)}_i + t\,\hat V^{(p,k+1)}_i,\quad t=\frac{q-q^{(p,k)}}{q^{(p,k+1)}-q^{(p,k)}} \]
多个参数同时驱动时,运行时直接把各参数的位移场相加(和 Cubism runtime 一样),因此转头 + 歪身 + 张嘴同时出现不需要新的预测。一个完整 rig = 13 次前向(8 个参数;5 个角度参数各 3 个 keypose、3 个 [0,1] 参数各 2 个,静止态不用算)。
「只预测两帧,其余全靠插值」吗?基本是,但这里的「帧」是滑杆上的位置,不是时间帧。静止位置位移为 0 不用预测;角度参数只预测两端(如 AngleX = -30 / +30,共 5×2 = 10 次前向),[0,1] 参数一端就是静止(3×1 = 3 次)。中间值线性插值,多根滑杆同时拖则位移相加。随时间的动作(摇摆、转头过程)由运行时谁来拖滑杆决定(面捕或预设曲线),模型完全不预测时间节奏。代价:(1) 每个顶点从静止到端点走直线,真实转头应走弧线,画师常加中间 keypose 修正,本文没有;(2) 位移相加意味着同时做的两个动作互不影响;(3) 没有物理,头发不会滞后或回弹。所以效果是「两端方向大致对,中间走直线」,更像初稿 rig。
从网上收集公开 Live2D 模型,去重后约 1 万个可用(报告口径 8,884:7,773 人形 + 1,111 非人)。由于运行时二进制(.moc3)是加密的,作者在自写的 HTML 渲染器里跑官方 Web SDK,逐层录下 RGBA、art-mesh 顶点/面和参数关键帧曲线。所有顶点统一放进一个 \([-1,1]^2\) 的共享角色画布坐标系,而不是每层裁剪自己的坐标,这样「这层在角色哪里」本身就是输入。图层统一成 8 大类 / 32 子类的 taxonomy(头发、脸、眼、嘴、头饰、躯干、手臂、下半身),并用 Qwen-VL 给每层写 ≤16 token 的描述,共约 50 万个 (layer, mesh, caption) 三元组。数据增强:换动作(把别的参数曲线套到同一图层栈上)和换颜色/纹理(保持 alpha、网格、位移不变)。
从 Qwen-Image-Layered 初始化,把它通用的「前景 / 背景 / 装饰」层 token 换成 8×32 的 Live2D taxonomy token,在约 1 万个 Live2D 模型上 SFT(8×A100,6K step)。条件有四路:输入图的 VAE latent、层类别 token、可选的逐层 caption(训练时 50% dropout)、逐层遮挡 mask。损失 = 标准扩散噪声 MSE + 被遮挡区域的 L1 补画损失(\(\lambda_{occ}=0.1\))。输出分辨率按层数动态分配像素预算:\(P_{target}=\max(196608,\min(1048576,\,6.4\text{M}/(L+2)))\),层越多每层越小。
一次出所有层,不是每个部件跑一次:Qwen-Image-Layered 本身就在一次扩散采样中联合生成整组 RGBA 图层;输入图只作为条件,每个输出层带类别 token(和可选 caption)告诉模型「这一层是什么」,损失对所有层求和。不是 image-to-image 逐个抠部件。
所以层数有上限(论文没写死最大值,但实际受限):
直观理解:这一步要的不只是「干净」,而是完整且顺序正确。后面联合推理要依赖层之间的关系,少一只袖子就是 Stage 2 无论如何也修不回来的层间不一致;作者实验证明这是端到端误差的主要来源。
作者诚实地测了「贴合轮廓的三角网格 vs 包围盒均匀网格」:顶点落在内容上的比例几乎一样(0.368 vs 0.376),真正的好处是同等覆盖下少 26% 顶点(83 vs 112),而顶点就是 token,attention 是 \(O(T^2)\)。在画师角色上训练时,保留画师原顶点并在膨胀轮廓上补点用于渲染,补点的位移取最近画师顶点,指标只在画师顶点上算。
\[ h^{(0)}_{i,j}=W_v\big[\gamma_6(v_{i,j});\,e^{par}_p;\,\gamma_3(\bar q);\,e^{lay}_i\big] + W_a\,\phi(L_i) \]
| 信号 | 维度 | 变化范围 | 含义 |
|---|---|---|---|
| \(\gamma_6(v_{i,j})\) | 26 | 逐顶点 | 共享画布坐标的 Fourier 编码,也是唯一的「位置编码」 |
| \(e^{lay}_i\) | 64 | 逐层 | 按图层在栈中深度位置索引的可学习嵌入 |
| \(\phi(L_i)\) | 384 | 逐层 | 冻结 DINOv2-small 对该层 crop 的 256 个 patch token 做 mean-pool |
| \(e^{par}_p\) | 64 | 逐前向 | 正在驱动哪个 Live2D 参数 |
| \(\gamma_3(\bar q)\) | 7 | 逐前向 | 归一化到 [-1,1] 的 keypose 值 |
刻意做弱的图像条件:像素进入 Stage 2 的唯一通路就是每层一个池化后的 DINOv2 向量;顶点和图像 patch 之间没有 cross-attention,也不编码合成后的整图。作者试过「在每个顶点位置双线性采样 DINOv2 patch 特征」的富版本,结果更差(cos 0.7269 vs 0.7676,幅度比 1.735 vs 1.234)。解读:逐顶点特征让模型去拟合局部纹理,跨角色不泛化;池化向量逼模型依靠「这是什么部件 + 在哪里」,这才是能迁移的。
全角色 \(T=\sum_i n_i\) 个顶点 token 拼成一个序列,送入 6 个 pre-norm block、4 头、宽 256、无因果 mask 的 Transformer(共 5.1M 参数)。40 层 × 每层约 80 顶点 ≈ 3200 token,任意两两之间都能 attend。把 attention 矩阵按层分块:对角块 = 层内 attention(per-layer 模型能用的全部);非对角块 = 层间 attention(只有 joint 能用),让虹膜顶点读到它必须待在里面的眼白顶点,让刘海顶点读到它必须贴着的额头。
两个关键性质:(1) 对 token 置换等变,序列没有位置编码,所以同一套权重能处理 8 层和 125 层的角色,训练时每层最多采 28 个顶点、推理时用全量顶点也不用重训;(2) 所有层共享一个画布坐标,跨层的几何邻近直接可见,模型从没被告知哪些层相关,只知道它们在同一坐标系里。per-layer 基线严格等于「同一模型 + 块对角 attention 约束」,因此消融只隔离了跨层信息这一个变量。
实测 attention:在 15 层、1435 token 的角色上,对角块占矩阵面积 6.7%,拿到的 attention 质量从第 1 个 block 的 4.4% 单调升到第 6 个 block 的 12.2%,平均 92% 的 attention 质量跨层;早期 block 全局扫视、后期逐渐局部化。作者也承认:均匀 attention 也会按面积分配质量,所以这只证明跨层通路「被用了」,「有用」靠的是消融。
它不做的事:不跨参数 / keypose 推理,每个 \((p,\bar q)\) 是独立前向,真正耦合的多参数形变不在表达范围内。
Live2D 位移幅度是重尾分布:大多数顶点移动不到画布 1%,大转头时轮廓顶点能动几十个百分点。直接回归会让少数大位移顶点主导损失。做法:以该层在当前 \((p,\bar q)\) 下的峰值位移 \(a_i\) 归一化目标,两个小 MLP 头分别输出有界方向和对数尺度:
\[ u_{i,j}=\tanh f_{dir}(H_{i,j})\in[-1,1]^2,\quad s_{i,j}=f_{mag}(H_{i,j}),\quad \widehat{\Delta v}_{i,j}=u_{i,j}\cdot\exp(s_{i,j}) \]
两项都用 L1 损失(\(\lambda_a=1\),默认层权重均匀)。log 空间让「幅度错 2 倍」无论真值是 0.002 还是 0.2 代价都一样。训练:AdamW,lr 2e-4,30 epoch,单卡 H800,1,443 个角色、357,381 条 (角色, 参数, keypose, 层) 记录。
早期 Stage 2 仿照 EdgeRunner 把网格连接关系和量化位移串成一条自回归 token 流,报告 cos > 0.99;但那是 teacher forcing 下的下一 token 精度,不是生成出来的 rig 的质量。换成连续值一次前向回归后,作者把结论写成:对「给定网格的 keypose 形变」,把位移离散成 token 词表没有任何好处。
把整个角色所有图层的所有网格顶点放在同一张画布里当一个点云序列,用不带 mask 的 self-attention 一次性回归每个顶点在每个参数端点处的位移,让各层「一起动」而不是「各自动」。
| 指标 | Depth seg. | SAM seg. | Qwen-Image-Layered | See-through | Ours |
|---|---|---|---|---|---|
| 整图 PSNR ↑ | 28.24 | 25.58 | 16.58 | 19.88 | 24.28 |
| 整图 LPIPS ↓ | 0.061 | 0.082 | 0.241 | 0.198 | 0.149 |
| 匹配层 LPIPS ↓ | 0.210 | 0.066 | 0.143 | 0.064 | 0.055 |
| 罚分 α-IoU ↑ | 0.081 | 0.137 | 0.175 | 0.265 | 0.298 |
| 罚分 cost ↓ | 0.546 | 0.616 | 0.573 | 0.640 | 0.491 |
| 层序正确率 ↑ | 0.391 | 0.302 | 0.431 | 0.266 | 0.674 |
| Cov-MAE ↓ | 0.166 | 0.197 | 0.445 | 0.167 | 0.090 |
读法:整图指标会误导,深度切层只出几张「整体像原图」的层,合成后 PSNR 最高,但罚分 α-IoU 只有 0.081。真正有意义的是逐层指标:Ours 罚分 α-IoU 0.298、层序 0.674(比最好基线高 56%)、匹配层 LPIPS 比 See-through 好 14%。注意 Ours 整图 PSNR(24.28)比深度切层和 SAM 都低,说明重新合成的忠实度并不突出。
| 方法 | dir-cos mean ↑ | dir-cos median ↑ | mag median →1 | cos ≥ 0.8 的角色 |
|---|---|---|---|---|
| Ours (joint, 5.1M) | 0.7676 | 0.8278 | 1.234 | 31/50 |
| FFD 2×2 oracle(拟合真值) | 0.9909 | 0.9955 | 0.989 | 46/46 |
| FFD 4×4 oracle(拟合真值) | 0.9993 | 0.9997 | 1.000 | 46/46 |
FFD 行不是竞争方法:连 2×2 的格子都能把画师位移场表达到 cos 0.99,说明表示能力从来不是瓶颈,和 1.0 的差距全部是预测误差。均值 0.77、中位数 0.83 的差距说明分布左偏:多数角色动得不错,少数角色彻底失败(最差 cos 达 -0.97,方向几乎完全相反)。幅度上,小动作偏大、大转头偏小,典型的点估计回归在多解问题上取「中心趋势」的症状。
| 变体(46 角色集,同协议) | dir-cos ↑ | mag →1 |
|---|---|---|
| 3 seed 集成 + 幅度重缩放(3 倍推理成本) | 0.743 | 1.00 |
| Joint 多层(单模型) | 0.736 | 0.99 |
| Per-layer 独立回归(同模型 + 块对角 attention) | 0.693 | 0.86 |
| 逐顶点坐标 MLP | 0.582 | n/a |
| 0.8B Qwen-VL 回归器 | 动作被压平,约慢 10 倍 | |
噪声地板:同配置只换随机种子两次得 0.7289 和 0.7533,相差 0.024。所以 joint 对 per-layer 的 +0.043 约为噪声地板的 1.8 倍,且只是一对单次运行;但幅度从 0.86 修正到 0.99、以及定性上虹膜/刘海不再撕裂,让这个结论比数字本身更可信。
作者把天花板归因于三点:(1) 任务本身有歧义,一张静态图 + 一个参数值对应许多种合理的画师动作,L1 回归只能给出条件中位数;(2) Stage 1 分层质量封顶;(3) 1,443 个 rig 对这个领域已不少但绝对量很小。出路是分布式输出(采样 / flow matching / 扩散头)、更好的分层、更多样的 rig,而不是更大的网络。
其它没起作用的条件(都在 0.024 噪声地板内或更差):显式逐顶点绘制顺序 0.7148;零初始化的 2.5D 视差残差 0.7225 / 0.7170;视差 + 顺序 40 epoch 均值 0.7153 但中位数最好(0.8255)、幅度最好(1.054),是唯一指向幅度压缩问题的「差一点」结果;逐顶点图像特征 0.6918;按幅度加权损失(ρ=0.25/0.5)无效甚至更差;稀有参数重复采样也无效。
Warm-start:从 8 参数模型按名字拷贝 8 行参数嵌入,新增 16 行随机初始化,再训全部 24 个;原 8 个参数持平(0.6983 vs 0.7009),新增 16 个从 0.4792 升到 0.5588(+17%),提升集中在最稀有的参数上(EyeRSmile 17 条样本:0.372 → 0.724)。解读:稀有参数没法从 17 个样本学出通用位移场,但可以学会「重新索引」主干已有的位移场。
指标陷阱:逐顶点余弦随网格密度而变。8 参数模型报 0.7676、24 参数模型报 0.689,看起来扩词表掉了 0.079;逐项锁定变量后:
| 改变的设置 | 模型 | 角色数 | dir-cos | Δ |
|---|---|---|---|---|
| 主表报告值 | 8p | 50 | 0.7676 | – |
| 去掉 24p 数据包里缺失的 4 个角色 | 8p | 46 | 0.7543 | −0.013 |
| + 24% 的层被重新加密网格(平均 47.5 → 71.9 顶点) | 8p | 46 | 0.6913 | −0.063 |
| 只换模型 | 24p cold | 46 | 0.6608 | −0.031 |
| 同上,warm-start(发布版) | 24p warm | 46 | 0.6866 | +0.026 |
网格密度一项就是被研究效应的两倍。机制:加密后新增的内部顶点真值位移很小、方向接近退化,却仍越过 0.005 的门槛进入平均。任何跨不同网格的逐顶点余弦对比都被混淆,包括作者自己的网格消融(三角 vs 四边形 0.7676 vs 0.7542 本身也在噪声地板内)。
| 图层来源(3 个带画师 rig 的 OOD 角色 × 13 keypose) | 静止 PSNR | 动画 PSNR ↑ | SSIM ↑ | LPIPS ↓ |
|---|---|---|---|---|
| 画师原图层(上界) | ∞ | 41.8 | 0.955 | 0.028 |
| Ours (Stage 1) | 27.2 | 23.4 | 0.883 | 0.088 |
| See-through | 22.6 | 21.1 | 0.865 | 0.120 |
静止态(无动作)已经从 ∞ 掉到 27.2 / 22.6,加上动画只再掉约 3.7 / 1.5 dB,所以差距主要来自分层而非动作。但同时注意:在画师图层上「预测零动作」也能拿到 PSNR 41.6,几乎等于模型的 41.8,像素指标基本不反映动作质量,只能用于比较分层来源。
| 方法(120 样本池) | L1 ↓ | PSNR ↑ | SSIM ↑ | LPIPS ↓ | 可编辑 |
|---|---|---|---|---|---|
| ToonCrafter | 0.438 | 3.94 | 0.387 | 0.591 | no |
| Wan2.2-I2V | 0.064 | 17.06 | 0.767 | 0.224 | no |
| CogVideoX-I2V | 0.044 | 19.13 | 0.806 | 0.193 | no |
| AniDoc(额外给 GT 草图) | 0.027 | 23.13 | 0.898 | 0.059 | no |
| Ours,已弃用的 AR Stage 2 | 0.015 | 37.63 | 0.965 | 0.049 | yes |
| Ours joint(不同协议,不可比) | n/a | 41.8 | 0.955 | 0.028 | yes |
这张表作者自己也说「只能看量级」:四个基线和 AR 行来自旧协议,joint 行来自另一个渲染协议,而且像素 PSNR 被「几乎不动」的地板主导。在当前协议上重跑这些基线是论文自认「还欠着」的实验。视频模型的分数低也部分因为它们要从静态图重新生成全部像素,而本方法直接复用原图贴图。
因为每层网格的 UV 由该层 alpha 包围盒导出,任何保持 alpha 和像素尺寸不变的编辑都不会让网格失效,因此也不会让已预测的动画失效。作者用 Qwen-Image-Edit 按自然语言指令重绘某层衣服的 RGB,再把原 alpha 和尺寸贴回,重新导出 rig,不重新分层、不重新预测动作。16 次编辑、5 个角色、10 类图层(头发、兽耳、发饰、衣服、鞋袜、头饰、盔甲、配饰、手持道具、虹膜),共比对 323 层、31,508 顶点、50,762 三角形、21,318 帧位移,字节级零差异,文件中唯一变化的是贴图路径。
两个限制:(1) 若某层同时包含衣服和裸露皮肤(短袖层里带手臂),编辑器可能连皮肤一起改色,需要在指令里显式保护肤色;(2) 只能做不改轮廓的编辑,裙子改裤子会改变 alpha,必须整条流程重跑。说白了,这是「表示自带的性质」而不是被训练出的能力:任何按 UV 贴图的 2D 网格动画都天然支持换贴图。
动漫角色生成正从「生成像素」走向「生成可驱动的结构化资产」:See-through 解决了分层与补画,Bunraku 在其之上补上网格与参数到形变的映射,第一次把单图 → 完整 Live2D rig 跑通。它和图生视频是互补关系:视频模型提供运动先验但不可驱动,Bunraku 输出可驱动但运动先验弱(只从 1.4K 个 rig 学)。下一步很可能是两者结合:用视频 / 3D 先验蒸馏出更多「参数 → 形变」监督来补长尾,并把 Stage 2 换成扩散 / flow-matching 头以解决多解导致的幅度压缩;同时需要解决可变绘制顺序、物理摆动和与 Cubism 格式的互操作。作者论文末尾提出的「密度归一化 / 面积加权的逐顶点指标」也是 benchmark 层面一个现成的开放问题。