Jianghu · 本地 H3 动作精灵生成方案
Jianghu · 本地 H3 动作精灵生成方案
ComfyUI + MiniMax H3(图生视频)→ 抽帧抠绿 → 逐帧对齐 → 选窗拼表 —— 全流程实测数据与坑点
2026-10-10 整理 · 依据 tools/ 实际实现 + _gen_actions 产物 + 2026-10-06~10-10 实测记录
一、这条线是什么
挂机条带里所有会动的角色(主角 + 十八职业门人 × 男女 + 46 个关卡敌人)的动画,都不是手画的,也不是云端生成的 —— 是本机 3070 Ti 8GB 跑 MiniMax H3 图生视频、再逐帧抠出来拼成精灵表。整条链全离线、零 API 成本。
1 立绘透明底 WebP →
2 绿幕首帧alpha 合成 →
3 H3 出片448×448 / 24fps →
4 抽帧抠绿去绿溢 + 去白边 →
5 逐帧对齐统一缩放 + 脚底/居中 →
6 选窗拼表webp + meta →
⑦ 装配+ actions.json
| 关键指标 | 实测值 | 备注 |
|---|---|---|
| 单条出片(热启动) | 25.1 s | 448×448 / 39 帧 / Turbo 8 步;冷启动 45.2 s |
| 单条出片(含后段全链) | ≈ 48 s | 门人批跑实测(10 套 8 分 10 秒) |
| 显存峰值 | 7505 / 8192 MiB | 92%,贴着 8GB 卡线跑 |
| 已产出动作套数 | ≈ 400 套 | 门人 180 + 敌人 216 + 主角/怪物 13(含补跑) |
| 渲染方式 | 纯本地 | 零云端调用;显存与 Z-Image 生图互斥排班 |
为什么值得单独成篇:这条线的坑绝大多数不在代码里,而在模型行为与几何算法里(拖尾、描边、静止插画、裁切、漂移、朝向)。 下面把「参数怎么定 / 数据是多少 / 每个坑怎么踩出来的」按可复现的粒度写全,避免重蹈。
二、环境与模型
2.1 ComfyUI 侧
| 项 | 值 |
|---|---|
| 安装位置 / 版本 | D:/ComfyUI(git clone 全新装)· v0.39.0 |
| Python 环境 | D:/ComfyUI/.venv · Python 3.13.14 · torch 2.9.1+cu130 |
| 启动参数 | python main.py --lowvram --disable-mmap --port 8188 --disable-auto-launch |
| 自定义节点 | ComfyUI-GGUF(city96)、ComfyUI-KJNodes(kijai);另需 gguf + protobuf |
| 原生支持 | H3 由 ComfyUI 自带(comfy_extras/nodes_minimax_h3.py + comfy/ldm/minimax/) |
| 启动耗时 | 冷启动 22.2 s / 热启动 15.4~16.2 s 才监听 8188 |
--disable-mmap绕开 Windows 上 >20GB safetensors 崩溃,需 ComfyUI ≥ 0.31。
--lowvram把权重激进 offload 到内存 —— 本机 64GB 内存正是这条的用武之地。
- KJNodes 的
PatchTritonVAE因缺 triton 不可用(本管线用不到,无需装 triton)。
- 已纳入系统守护(
service_host.mjs的 comfy 条目,readyMs: 120000):杀掉后 18.2 s 自动恢复。
2.2 实际在用的模型文件
| 角色 | 文件名 | 大小 | 加载器 |
|---|---|---|---|
| 去噪器 UNET | MiniMax_H3_FL2VA_pruned_mixed_int4_int8_convrot.safetensors | — | UNETLoader |
| 文本编码器 CLIP | qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors | 15.7 GB | CLIPLoader(type=minimax,device 默认 cpu) |
| 视频 VAE | minimax_h3_video_vae_fp16.safetensors | 5.21 GB | VAELoader |
| 音频 VAE | minimax_h3_audio_vae_fp32.safetensors | 0.61 GB | VAELoader |
| Turbo LoRA | minimax_h3_fl2v_turbo_8step_v1.0_comfyui_bf16.safetensors | 1.96 GB | LoraLoaderModelOnly(lightx2v/Minimax-h3-Turbo) |
- 文本编码器放 CPU 内存(15.7GB,不占显存);每条提示词只跑一次 ⇒ 这不是瓶颈。
- 下载走
hf-mirror.com(HF 直连不通)。单连接被限速到 2.4 MB/s,8 连接峰值 40 MB/s,
中途还可能被限到 459 B/s ⇒ 大文件必须分片多连接;实在不行换 ModelScope(实测 8 连接 60 MB/s)。
2.3 🔴 H3 的 GGUF 一律别用(已实测否决)
调研阶段曾按「8GB 档用 Q2/Q3 量化 GGUF」选型并下载了 19.7GB 的两个 GGUF,实跑时两个都被拒:
- 去噪器:city96 的 ComfyUI-GGUF 对着
IMG_ARCH_LIST白名单校验(flux/sd1/sdxl/sd3/aura/hidream/cosmos/ltxv/hyvid/wan/lumina2/qwen_image)——里面没有 H3(upstream issue #471 未支持),UnetLoaderGGUF必然报
Unknown model architecture!。
- 文本编码器:unsloth 的
qwen3vl_32b_minimax_h3-Q2_K_M.gguf缺general.architecture元数据(它是给 stablediffusion.cpp 的)——同样被拒。
正解:convrot 量化 safetensors + 原生 UNETLoader/CLIPLoader,一次通过。那两个 GGUF 已删除腾出 19.7GB。
2.4 显存策略与「互斥排班」
- 低显存三件套:
--lowvram+ KJNodes 两个无损节点(MiniMaxLowVRAMAttentionhead_chunks=4、MiniMaxChunkFeedForwardchunks=2/seq_threshold=4096)+ Turbo LoRA 8 步。
先用它们,再考虑降量化。
- 别装 SageAttention:30 系上失败,且低显存下反而慢 2.5–3×(占显存 → offload 更多)。
- nvfp4 只在 Blackwell 原生执行;本机是 Ampere,模拟执行零提速 —— 但官方给的
nvfp4_awq文本编码器是特例(放 CPU 内存,不参与去噪),可以用。
🔴 H3 与 Z-Image 生图抢显存,必须手动排班。H3 staged 约 15GB,8GB 卡上不卸必 OOM; comfy_img.ComfyImgClient.switch_to_image() 会自动 POST /free 卸掉 H3, 但 ComfyH3Client 没有反向的 switch_to_video() —— 生图权重还占着显存就跑动作会失败。 跑动作前一律 python tools/comfy_service.py restart(重启最干净)。
三、H3 硬约束与本次参数
3.1 模型硬约束(全部已在本机核对)
| 约束 | 值 |
|---|---|
| 画布尺寸 | 必须是 32 的倍数;短边 768px 封顶(本地任何显卡都出不了 2K) |
| 时长 / 帧数 | 按 17k+5 帧网格 @24fps 吸附:1s → 39 帧、2s → 56 帧、5s → 124 帧 |
| 音视频 | 联合生成,音频无法单独关闭(我们只抽画面帧,忽略音轨) |
| 采样器 | res_multistep + simple 调度(官方模板统一选择);基座 20 步,挂 Turbo 后 8 步(4 步也可用) |
| 许可证 | 开放权重但非 OSI 开源;商用需在产品界面显著标注 "MiniMax H3"(上线前单独过条款) |
帧数吸附公式(与官方工作流 Math Expression 同一公式)
`def snap_length(seconds): n = max(5, round(seconds * 24)) return n + (5 - n % 17) % 17 # 1s→39 2s→56 5s→124
def snap_size(megapixels, aspect="1:1"):
1:1 @0.2MP → 448x448(宽高各自对齐到 32 的倍数)
...`
3.2 本场景取用的参数(比默认轻很多)
| 项 | 取值 | 理由 |
|---|---|---|
| 画布 | 1:1 · 0.2 MP → 448×448 | 精灵帧不需要 16:9,1:1 省一半像素;输入参考图也用 1:1(用 2:1 模型会按输入宽高比输出,角色只占中间一小块) |
| 时长 | 0.7 ~ 2.0 s(按动作定) | 动作循环本来就 1~2 秒;对比默认 5 秒省 2~3 倍算力 |
| 步数 | 8 步(Turbo LoRA) | 最大提速杠杆(实测 4 步 = 3.44×,峰值显存不变) |
| 模式 | FL2VA 首尾帧(本管线只给首帧) | 正是动作管线的需求 |
| 精灵表帧高 | 300 px | 作品按 sheet.width / 帧数 等比绘制,尺寸自由;总宽须 ≤ 8000(GPU 纹理上限 8192) |
首日勘察时判断「8GB 跑通不确定,即便跑通也是几十分钟量级」——被实测推翻:热启动 25.1 s。根因是本机 63.8GB 内存填上了 8–12GB 档真正的瓶颈(社区 3060 12GB 因为只有 16GB 内存,每步都要从 NVMe 重读 21GB 权重 → 单条 5 秒 25 分钟;同卡换 32GB 内存降到 6–10 分钟)。
四、生产链(脚本 + 目录 + 产物)
4.1 五段脚本
| 脚本 | 职责 |
|---|---|
| tools/gen_action_sheet.py | 主脚本。立绘 → 绿幕首帧 → H3 出片 → 抽帧 → 抠绿 → 剔光弧帧 → 对齐 → 选窗 → 拼表 → 落 _gen_actions/。内含 18 职业兵器措辞表(JOB_WEAPONS)与动作模板(ACTIONS) |
| tools/comfy_h3_client.py | ComfyUI HTTP 客户端:build(组 FL2VA 工作流)→ upload_image → submit → wait → download |
| tools/frames_from_video.py | 抽帧 + 抠绿(严判据)+ 去绿溢 + 去白边 + 空洞体检;也可直接拼表(含 --mirror 逐帧镜像、--desword) |
| tools/loop_check.py | 循环质量量化:最优循环窗(seam/baseline)+ 尺寸漂移 + 静止闸门 + GIF 预览。产线里被 gen_action_sheet.pick_window() 复用 |
| tools/gen_fx_sheet.py | 攻击/技能独立特效精灵(黑底 + alpha=亮度,7 条:slash/burst/thrust/projectile/lash/heal/palm) |
| tools/install_action_sheets.py | 装配:把 _gen_actions/ 里跑完的表拷进 public/assets/idle/(覆盖前强制备份)+ 生成 actions.json |
批量入口
| 批次 | 入口 | 做法 |
|---|---|---|
| 主角 / 怪物 | gen_action_sheet.py --all | 按 MATRIX 表跑(hero 5 套、boar 4 套、bandit 4 套) |
| 36 门人(男女 × 18 职业) | bash tools/run_job_actions.sh | 自枚举 36×5=180 套;断点续跑 + 单条失败拉起 ComfyUI 重试最多 3 次;FORCE=1 强制重做 |
| 46 关卡敌人 | gen_enemy_actions.py --all --resume | 运行时注入 CHARACTERS/MATRIX/CHAR_PROMPTS 复用主脚本的 produce(),H3 出片到拼表一个字都不重写 |
为什么不直接用 --all 跑门人:--all 会连 hero/boar/bandit/12 区域敌人一起跑(CHARACTERS 里的全部),而它的 --only 只能按动作过滤、不能按角色过滤 ⇒ 只能另写枚举脚本。批量脚本里的角色清单必须与 gen_action_sheet.JOB_WEAPONS 的键逐字一致(tools/check_joblist.py 就是为这类漂移加的校验)。
4.2 目录与产物
| 路径 | 内容 |
|---|---|
| _gen_actions/_.webp | 精灵表(暂存;人工/截图验收后再装配) |
| _gen_actions/_.json | meta:帧数 / cell / fps / loop / draw / content_h_ratio / baseline_ratio / 选窗质量 / h3 参数 |
| _gen_actions/_work/_/ | 中间产物:first_frame.png · clip.mp4(只要它还在就不用再花 GPU 时间)· frames// |
| _gen_actions/fx/ | 特效精灵 .webp + .json |
| _gen_actions/_backup_public/__ | 覆盖前强制备份(本项目无 git,覆盖不可逆;同时也是「从多次生成里挑最好一版」的依据,别清理) |
| public/assets/idle/ | 运行时素材:idle_hero_.webp / idle_job___.webp / idle_mob__.webp / idle_fx_.webp / actions.json |
4.3 actions.json 与渲染接入
- 帧数 / 帧率一律由
actions.json提供,作品代码里不手写。(历史事故:RUN_FRAMES = 23是给旧素材写的,新表只有 10 帧 ⇒ 人物被压成一条窄缝)
- 每个动作登记
content_h_ratio(人物高 ÷ 单元格高)与baseline_ratio(脚底线 ÷ 单元格高);
渲染层用 frameH = charH / content_h_ratio 定尺寸、dy = roadY - frameH × baseline_ratio 把脚钉在路面线上。
- 这两个比例在装配时从最终 webp 重新量(
install_action_sheets.measure_sheet()),不信生成时的 meta —— 素材可能被替换/回退,两者一旦不一致就是「切动作时人物突然变大变小 / 脚悬空」。
- 特效走
charFx(按兵器 kind 分配)与fxMap(全局兜底);位置/缩放/延迟(offset_x/offset_y/scale/delay_ratio)写在 meta,代码不写死。
- 门人 key 三级回退:
job_<职业>_<性别>→job_<职业>→hero(注意:职业名与真源server/jobs.mjs不一致时会静默命中 hero,表现为「挂机条里画的是主角」)。
五、实测数据
5.1 单条出片性能
| 项 | 值 |
|---|---|
| 输出规格 | h264 · 448×448 · 24fps · 39 帧(1 秒)+ aac 32kHz 立体声 |
| 热启动耗时 | 25.1 s(448×448 / 39 帧 / 8 步) |
| 冷启动耗时 | 45.2 s(含首次加载) |
| 显存峰值 | 7505 / 8192 MiB(92%) |
5.2 各批规模与耗时
| 批次 | 规模 | 耗时 | 结果 |
|---|---|---|---|
| 主角 + 2 怪物 | 13 套 | — | 首轮跑通,精灵表 3010×516 / 10 帧 |
| 十八职业首批 | 90 套(84 新跑) | 71.6 分钟 | 全部完成 |
| 36 门人全套 | 180 套 | 2 小时 28 分(单套 ≈48 s) | done=180 skip=0 fail=0 |
| 关卡敌人批跑 | 216 套(46 角色) | 批跑 126.8 分钟(0.70 分/套)· 含 rebuild 全链 198.8 分钟 | 216/216,缺文件 0、失败 0;整晚守护重启 0 次 |
| 弩手补跑 | 10 套 | 7 分 50 秒 | 补齐挂机缺失 |
5.3 动作配方表(gen_action_sheet.ACTIONS)
| 动作 | 时长 | 帧数 @fps | 类型 | 对齐 | 选窗 | 特效 |
|---|---|---|---|---|---|---|
| idle | 2.0 s | 8 @6 | 循环 | 逐帧居中 | 中段循环窗 | — |
| walk | 2.0 s | 10 @10 | 循环 | 逐帧居中 | 中段循环窗 | — |
| run | 2.0 s | 10 @12 | 循环 | 逐帧居中 | 中段循环窗 | — |
| attack | 1.0 s | 8 @12 | 一次性 | 不居中 | 活动窗 | 独立(按兵器 kind) |
| skill | 1.5 s | 10 @10 | 一次性 | 不居中 | 尾段(避拖尾) | 独立(burst) |
| hurt | 0.7 s | 6 @12 | 一次性 | 不居中 | 活动窗 | — |
🔴 循环动作必须给够 2 秒(56 帧)才有稳定的中段循环窗:实测 1 秒(39 帧)同一动作 score 1.16(WEAK),给到 2 秒后 0.32(CLEAN)。
5.4 质量判据与实测分布
| 判据 | 口径 | 阈值 / 实测 |
|---|---|---|
| 循环质量 score | 接缝差 ÷ 窗内帧差均值(loop_check.py) | 0.8 步幅超出窗(加长 seconds 或扩大Search范围) |
| ⚠️ idle 例外 | score 对 idle 不适用(呼吸幅度天然小 ⇒ 比值天然大) | 看 baseline 绝对值,25% |
| 纯白占比 white | 实心像素里 R/G/B 均 >215 的比例 | 干净帧 15% |
| 抠图空洞率 | 「实心轮廓内部还剩多少透明像素」(不是整幅透明比例) | >2% 报警;门人素材最终定稿 0.20% |
| 立绘白底漏白 | 不透明区里残留白像素占比 | >0.5% 肉眼可见 —— 46 张敌人立绘 30 张中招,最狠 4.78% |
| 精灵表总宽 | 每格宽 × 帧数 | ≤ 8000(GPU 纹理上限 8192;实测 10 帧 × 920px = 9200 已超) |
敌人 216 套的实测分布:几乎静止(MAD<1.0)0 套;循环偏弱(score>0.8)42 套,集中在 idle/walk。
六、提示词工程要点
完整措辞(主体段 / 六套动作段 / 收尾锁 / 负向词 / 十八职业兵器替换表 / 特效词)另见 docs/人物动作提示词-H3.md(H3 提示词完整版)。这里只列必须记住的行为规律:
| # | 要点 | 依据 |
|---|---|---|
| 1 | 措辞必须点名兵器(en / hold / attack / skill 全带) | ACTIONS 通用 attack 写的是 "quick sword strike",不覆写就会把拿斧头的人画成在挥剑 |
| 2 | 18×5=90 段提示词由代码生成,不手写 | 骨架完全一样、只有兵器一处变量;手写 90 段既写不对也改不动 |
| 3 | 人称一律中性(不写 he/she) | 每个职业都有男女两版立绘 |
| 4 | 怪物动词要落到身体部位(胸腹起伏 / 耳朵抖 / 尾巴摆 / 四条腿轮流抬 / 獠牙前顶) | 纸雕风 + 静物描述 = 模型当成一张图,出「静止插画」 |
| 5 | 跑步别写 "treadmill",改成「原地」正向描述 + 逐条点名禁器材/地面/道具 | H3 把 treadmill 当画面内容照画了:18 个职业跑步脚下全有跑步机 |
| 6 | 写 NO white outline,纸质感用「层间投影」表达 | 写 "visible paper thickness" 会让模型给整个人物画一圈灰白描边(绿幕原始帧上就有) |
| 7 | 技能要「举剑/举械蓄力」,不写大幅挥砍 | 大幅挥砍必然烘实心运动拖尾;力量感交给独立特效 |
| 8 | 朝向写进主体段并在收尾再强化一次:ALWAYS FACING TO THE RIGHT | 门人全套动作朝右(NPC/Boss 朝左,方向相对才成对战) |
| 9 | Python 里写中文提示词,内层别用 ASCII 双引号 | "右肩在前" 会把字符串截断 → SyntaxError;用「」或加粗 |
| 10 | 要精确控制动作,给参考图不给文字 | 实测:纯文字 ❌ / 文字+量化角度 ❌ / 动作框架图并排拼图(1:1)✅ |
七、坑点清单
按「现象 → 根因 → 解法」记。凡标注 模型层硬限制 的,改提示词压不住,只能靠后处理或改设计绕开。
7.1 模型行为(H3 的脾气)
| 现象 | 根因 | 解法 |
|---|---|---|
| 攻击/技能帧被撑得很宽,混着一大块月牙光弧 | 模型层硬限制:任何攻击类动作都会烘一条实心拖尾(实测 alpha ≥ 250、占画面 19%),靠 alpha 阈值分不开;NO_FX 里十几条禁令一条都压不住(药师扎针都能甩出整条月牙,单格 133px → 266px) | 三重保险:1 动作改小幅(技能=举械蓄力);2 skill 用 tail 选窗取尾段(拖尾只在运动段);3 后处理 drop_fx_frames() 剔帧(基准宽取10 分位× tol 1.60 + haze 判据,剔不够就分级降级);力量感交给独立特效精灵 |
| 纸雕怪物动作「看着像静止插画」 | 通用措辞「站着呼吸、微微起伏」用在纸雕四足怪上,模型照抄成静态图(对齐散布 0.0px) | 动词具体到身体部位;判据用 相邻帧 MAD(散布 0.0px 分不出「稳定」与「静止」) |
| 跑步帧里脚下有台机器 | 提示词写了 like a treadmill(本意是「原地跑、别位移」),H3 当成画面内容照画 | 删词 ⇒ 正向写「on the spot / staying in exactly the same place」+ 逐条点名 no treadmill/machine/platform/floor/props |
| 人物轮廓外面一圈灰白描边 | 提示词里的 "visible paper thickness" 被理解成「给整个人物加一圈纸的厚度边缘」;取证看未经处理的原始绿幕帧就能看到,不是抠图残留 | 改「层间投影」+ 明写 NO white outline / sticker border / rim / drop shadow |
| 技能「杵着不动」 | 措辞写 "holding upright and planted"(竖插在地上) | 改 raising / lifting / overhead(戟将就是这么修的) |
| 脚边粘白土 / 碎屑 | 动作词写了「刨地 / 踏地」,模型顺手画出扬起的土(通用禁令挡不住动作词) | 改不接触地面的描述;或重生成 |
| 弩手 attack/skill 的兵器漂成「弓」 | 已知残留:三版措辞都没治好。v1 平铺直叙→中间帧像弓;v2 加 "like a rifle" + "NOT a tall vertical bow" 更糟(负面词在扩散模型里会反向拉高该概念);v3 纯机械结构描述→仍是弓 | 停在这里,别再试。远程武器这层是对的,且 idle/run(占挂机 95% 时间)画的是真弩 |
7.2 几何:对齐、裁切、漂移
| 现象 | 根因 | 解法 |
|---|---|---|
| 人物只显示一半(缺右/缺左半边) | 旧实现把「内容中心」对齐到画布坐标 cx,而画布宽度按 cx ± half_w 算、原点却在 0 ⇒ 超出画布的部分被直接裁掉(hero_idle 6/6 帧右边缘被切;怪物系全部左边被切) | 画布尺寸由内容实际伸展范围反推,不裁是算出来的: W = span_x / (1-2×pad_frac) · H = span_y / content_ratio · anchor_x = (W-span_x)/2 + L |
| 帧底边全贴边(= 被裁) | 写成 baseline_y = H×U/span_y:当 D=0(脚底从不低于中位数)时基线正好落在画布最底边(实测 23 帧全贴边) | baseline_y = (H-span_y)/2 + U(上下对称留白);对齐函数末尾自动复核「有内容贴边」并告警 |
| 跳跃/腾空被压平 | 逐帧对齐脚底(每帧各自拽回地面) | 脚底对齐用整段同一个 dy(只统一基准,保留帧间相对高度) |
| 攻击/技能被「居中」后动作废掉 | 前冲/踉跄本来就该位移,逐帧居中把位移压平了 | center 必须按动作区分:循环类逐帧居中(消漂移);一次性类整段平移(保位移) |
| 切动作时人物突然变大变小 | 拿「帧高」当「人物高」——各动作垂直跨度不同,「人物高 ÷ 单格高」实测在 0.60 ~ 0.78 之间漂 | meta 输出 content_h_ratio / baseline_ratio,渲染层按它们反推帧高 |
| 脚悬空 / 脚陷进地面 | 用的是生成时的旧 meta,素材已被替换/回退 | 装配时从最终 webp 重新量(measure_sheet()),不信 meta |
| 冲锋时人物会缩小 | const acTweak = (anim.act==='idle') ? 1.09 : 1 —— 只有 idle 放大 9%,一出手变回 1.00 | 删掉 acTweak,补偿量并进统一的 HERO_HEIGHT_RATIO。凡「只对某个状态生效的缩放」,都要先问「切状态会不会跳」 |
| 逐帧缩放导致人物忽大忽小 | 按「本帧高度」各自缩放 —— 腿一张开就变小、一并拢又变大 | 所有帧用同一个缩放系数(以最高帧为准)+ 底边对齐 |
7.3 抠图:绿幕、白边、漏白
| 现象 | 根因 | 解法 |
|---|---|---|
| 身上被抠出洞(透绿) | 抠绿判据太宽,把角色自带的墨绿/青绿当幕布 | 判据严、宁漏勿误:(g - max(r,b) > 46) 且 (g > 110);体检用 hole_ratio()(对不透明区填洞,填不到的透明区才是「身上透绿」)。别用「画面中间一块」或 bbox 矩形当判据(必然虚高) |
| 边缘一圈绿光 | 没做 despill(绿幕反光染到角色边缘) | 把边缘像素 g 通道压到与 max(r,b) 同水平(强度 0.75) |
| 轮廓外套一圈白 | 两个来源:1 抗锯齿让边缘像素 = 前景×a + 白底×(1-a),绿幕那层只管绿不管白;2 模型自己画的描边 | 1 despill_white():alpha 腐蚀 1px + 解 前 = (c − 255×(1−a)) / a,只动半透明像素;2 只能改提示词。教训:白边要去原始帧上找证据,别在抠图算法里绕 |
| 脚底/腿间白块 | 立绘白底 + 四角 flood fill 到不了「被主体围住的白」(四足怪腿间、叉腿之间、软体盘绕缝隙),到抠绿阶段已和白衣/白毛/白獠牙混在一起 | 语义抠图(BiRefNet)1.6 s/张:同一张立绘纯白像素 4118 → 54(1.58% → 0.03%),腿间全透明、白胸毛保留 |
| 换了抠图算法但整套没变化,且不报错 | 管线断链:build_green_frame 还在吃原始白底图(静静走了白底分支),抠好的透明立绘没被喂进下一环 | 顺序必须是「立绘 → 语义抠图 → 透明立绘 → alpha 合成绿幕 → 出片」;跑完第一次看日志有没有 立绘自带透明底(自动)→ alpha 合成,没看到就是没生效 |
✅ 一句话规则:主体含白色 ⇒ 无脑「白底生图 + BiRefNet + alpha 合成绿幕」,别自创色键方案。(品红底色键泛洪在场景素材上试过,模型把底画成粉色系、白盘包住的投影够不着,最后还是回到这条路)
7.4 朝向
| 现象 / 事实 | 说明 |
|---|---|
| 同一角色 idle 朝左、attack 朝右 | 每个动作是 H3 单独出的片,模型有时把整个人翻过来(180 套里 8 个反了) |
| 判据全废,朝向只能人工判 | 试过:头肩重心偏移(挥击时乱跑)· 帧宽/宽高比(长兵器横伸)· 左右配色分布(178/180 判不出)· 肤色重心(持械也露肤色)。流程:出核对页 → 人工逐个看 → 按名单翻(tools/facing_manual.txt + fix_action_facing.py --manual --apply) |
| 判「动作帧」漏了一整批 | 立绘本身朝左的话,5 个动作会全跟着朝左 ⇒ 先判 36 张立绘,立绘朝左的整个角色一起翻(共修 32 张) |
| 名单重复翻转 = 翻回去了 | 名单语义是「当前状态需要翻一次」;执行前先看 _facing_bak/ 有没有同名文件 |
| 镜像不能整表翻转 | 整张精灵表水平翻转会让帧序倒过来(动作倒放);必须逐帧翻转(--mirror)。镜像只翻像素、不动 json,之后必须重跑 install_action_sheets.py |
| 别用 --only 重生成 | 它男女一起重做,会把本来没问题的那一半也覆盖掉 |
7.5 帧数、常量与纹理上限
- 帧数写在代码里 = 素材替换后的定时炸弹(
RUN_FRAMES=23vs 实际 10 帧 → 2770/23 = 120px、宽高比 0.40,人物被压成窄缝)。一律从actions.json/ meta 读。
- 帧数不够时先降 frame-h,别急着减帧(430 → 200:每格宽 586 → 265,23 帧只要 6118px)。
- 抽帧一律原生 24fps 抽满,之后由选窗决定采样;抽帧常是 0 基(
loop_check.py为此有--start)。
- 动画 webp 的
durationPIL 读不出来(im.info只有 loop),别据此判断「没写进去」——直接解析ANMFchunk。
7.6 批跑与运维
🔴 「产物存在」≠「产物是新的」。断点续跑第一次跑就 skip 了 170 套(_gen_actions/ 里上一版 180 套动作还在,用旧立绘生成的),而且 fail=0、输出看起来一切正常、不报错。 ⇒ 换了上游素材必须带版本/强制开关(FORCE=1,旧产物先备份到 _out/_actions_old/)。
| 坑 | 现象 | 解法 |
|---|---|---|
| ComfyUI 静默掉线 | 日志停在一次成功执行之后(Prompt executed in 10.34 seconds),无崩溃信息,端口直接拒连(WinError 10061)。一天崩 6 次以上 | 长跑加守护循环(探活 + 死了重启 + --resume 续跑)+ python -u(stdout 默认块缓冲,被强杀会丢日志)+ 每任务重试 3 次 + 逐张自愈 |
| nohup … & 起 ComfyUI | 活不过几分钟就被静默清掉(批量跑几十分钟后突然 ConnectionRefused) | 用 DETACHED_PROCESS 启动(tools/comfy_service.py)或纳入系统守护。也不能用 wscript/cscript 起 VBS(LOLBin,本机安全策略直接拦) |
| safe-delete 钩子 | 拦「单轮删 50+ 文件」,而抽一次帧正好 56 张 ⇒ 固定目录 + 每次清空的老做法,跑第二个动作必被拦(动作静默跳过) | 抽帧落到带时间戳的子目录(extract(out_dir=…)),永远只新建、不删除 |
| ComfyUI 与 Z-Image 抢显存 | 生图权重还占着显存就跑动作 → 失败(ComfyH3Client 没有反向 switch) | 跑动作前 python tools/comfy_service.py restart |
| 立绘损坏引发连锁失败 | 一个 webp 损坏 → 页面读不出;作为 ref 上传后 ComfyUI 报 av.error.InvalidDataError(错误信息里只有 ComfyUI 路径,看着完全不像源文件坏了) | 批量产完后扫一遍 Image.open(p).load()。立绘既是产物又是 ref |
| TaskStop 不杀Admin子进程 | 派生的 curl/python 变僵尸、文件被占用(可读可写但不可改名/删除,os.replace 报 WinError 32) | tasklist + taskkill //F //PID 手动清 |
| 首屏加载爆量 | 原实现遍历清单里全部 85 个角色 × 全部动作,动作表 401 张 / 33.8MB 一次性预载(首屏 35.7MB) | 改「按需 + 分层」(L0 首屏主角+当前区域怪的 idle/run;L1 Admin补 attack/skill/hurt;L2 按需)⇒ 动作表 5.51MB / 57 张,首屏 19.28MB。⚠️ hasChar 必须查清单不能查 charAnims(否则「还没轮到」会被当成「缺这一档」,误退成主角形象) |
八、已作废 / 放弃的方案
| 方案 | 为什么放弃 |
|---|---|
| H3 走 GGUF 量化 | ComfyUI-GGUF 白名单无 H3(issue #471);unsloth GGUF 缺 general.architecture ⇒ 两个都被拒。改用 safetensors 量化 + 原生加载器 |
| SageAttention | 30 系失败,且低显存下反而慢 2.5–3×(占更多显存 → offload 更多) |
| nvfp4 做去噪器 | nvfp4 只在 Blackwell 原生执行,Ampere 模拟执行零提速(但官方 nvfp4_awq 文本编码器是特例,放内存可用) |
| 混元 / Seedance 云端出视频 | 2026-10-07 拍板全部弃用(Seedance 四模型在账号下全返 ModelNotOpen)。从此动作与特效一律本地 |
| 视频原生 alpha(Wan-Alpha / TransPixar / LTX-2.5) | 1 它们是 T2V,我们是 I2V(保证不了还是那只怪);2 都是 14B 级,8GB 跑不动;3 换掉 H3 = 放弃整套已调好的管线 |
| 四角 flood fill 抠白底 | 到不了「被主体围住的白」⇒ 换语义抠图(BiRefNet) |
| 形态学开运算「把剑从帧里去掉」 | 腐蚀 4px/7px 剑都还在,腐蚀到 10px(宽 624)剑仍在,再狠就把发包、斗篷边缘啃烂 |
| 「下半身绕躯干中轴镜像」补跑步交替 | 剑横跨腰线被切成两半、腿与躯干错位 —— 任何「按高度切两段」都会撕裂跨线的长道具/衣物 |
| 程序化腰佩剑(渲染层 canvas 画) | 位置可控、绝不跟手,但形状与配色打不过原画 —— 最终回退成 AI 画的剑(函数保留、调用注释掉)。教训:程序化补件在风格一致性上赢不了原画 |
| 等运动量重采样(time remap 到 12 帧) | CV 反而升到 37.4%(最近邻产生重复帧,重复帧处运动量为 0) |
| 把动作「量化」(大腿角度 ±35°/0°/−30°/−55°) | 模型对角度指令遵循度不够,画出来照样乱 |
| 「先杀占端口的进程再起服务」 | 多会话环境下等于踩掉别人正在跑的服务 ⇒ 改「有则复用、无则补起」 |
九、验收与排查速查
| 要看什么 | 怎么验 |
|---|---|
| H3 服务与节点就绪 | python tools/comfy_h3_client.py --health(检查 5 个节点是否齐全 + 显存/内存) |
| 单条动作是否可用 | verify_actions.mjs;加载失败分支 verify_fallback.mjs(清空 window.__idle.charAnims 模拟) |
| 循环质量 | loop_check.py --cycle run --gif out.gif;判读 score / 尺寸漂移 / 静止闸门 |
| 整套动作体检 | docs/敌人动作检查.html(MAD 相邻帧差自动体检 + 窗口分)· docs/门人动作展示.html |
| 动作表与兵器是否对版 | audit_actions.py(以 idle 单格宽为基准,>1.5× 挑出混进的东西)· action_motion_check.py |
| 改了素材没生效 | actions.json 走 Cache-Control: no-cache;已打开的页面不会自己重拉(vite HMR 不管 public/ 下素材)⇒ 刷页面。看 generated 字段(比 mtime 可信) |
重跑某个动作(最省 GPU 的姿势)
`# 视频还在(_gen_actions/_work/<char>_<action>/clip.mp4)→ 只重跑后段,几秒钟,不花 GPU python tools/gen_action_sheet.py --char swordsman_m --action run --reuse-video
换提示词重生成一条
python tools/gen_action_sheet.py --char swordsman_m --action attack python tools/gen_fx_sheet.py --fx burst
装配(自动备份被覆盖的原文件 + 生成 actions.json)
python tools/install_action_sheets.py # --dry-run 先看会做什么`
十、相关文档
docs/人物动作提示词-H3.md—— H3 提示词完整版(主体段 / 六套动作 / 收尾锁 / 十八职业兵器替换表 / 特效词 / 七条避坑)
docs/本地生图方案.html—— 本地生图(Z-Image Turbo + BiRefNet)选型与显存账
docs/技术架构总览.html§4.2 —— 本管线在整体架构中的位置
docs/敌人图鉴与关卡生态设计.html·docs/敌人动作检查.html—— 敌人素材链与验收页
- 技能
~/.workbuddy/skills/sprite-action-forge/(已开源:github.com/aowind/sprite-action-forge,MIT)—— 本方法论的通用化版本
数据来源:tools/ 实现与注释 · _gen_actions 产物与日志 · .workbuddy/memory/2026-10-06 ~ 10-10 · .workbuddy/refs/minimax-h3-8gb-优化方案.md