{ "id": "5f8c3d21-7b94-4e06-a1d8-9c2e4b7f350a", "revision": 0, "last_node_id": 20, "last_link_id": 19, "nodes": [ { "id": 1, "type": "VHS_LoadVideo", "pos": [ -2680, 340 ], "size": [ 340, 480 ], "flags": {}, "order": 0, "mode": 0, "inputs": [], "outputs": [ { "name": "IMAGE", "type": "IMAGE", "links": [ 1, 2 ] }, { "name": "frame_count", "type": "INT", "links": [] }, { "name": "audio", "type": "AUDIO", "links": [] }, { "name": "video_info", "type": "VHS_VIDEOINFO", "links": [] } ], "properties": { "Node name for S&R": "VHS_LoadVideo" }, "widgets_values": { "video": "八方向行走.mp4", "force_rate": 24, "custom_width": 0, "custom_height": 0, "frame_load_cap": 0, "skip_first_frames": 0, "select_every_nth": 1 }, "color": "#232", "bgcolor": "#353", "title": "① 加载视频(force_rate 设帧率)" }, { "id": 2, "type": "RuiLucida", "pos": [ -2270, 340 ], "size": [ 340, 250 ], "flags": {}, "order": 1, "mode": 0, "inputs": [ { "name": "image", "type": "IMAGE", "link": 1 } ], "outputs": [ { "name": "alpha", "type": "MASK", "links": [ 3 ] }, { "name": "cutout", "type": "IMAGE", "links": [] } ], "properties": { "aux_id": "rui40000/Ruinode", "Node name for S&R": "RuiLucida" }, "widgets_values": [ "lucida.safetensors", "fp16", "auto", 0.5, 1.0, false, false ], "color": "#232", "bgcolor": "#353", "title": "② Lucida 抠图(一次抠整张,8 个角色都能识别)" }, { "id": 3, "type": "RuiEightDirSplit", "pos": [ -1860, 340 ], "size": [ 380, 400 ], "flags": {}, "order": 2, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 2 }, { "name": "masks", "type": "MASK", "link": 3 } ], "outputs": [ { "name": "dir_1", "type": "IMAGE", "links": [ 4, 5 ] }, { "name": "dir_2", "type": "IMAGE", "links": [ 6, 7 ] }, { "name": "dir_3", "type": "IMAGE", "links": [ 8, 9 ] }, { "name": "dir_4", "type": "IMAGE", "links": [ 10, 11 ] }, { "name": "dir_5", "type": "IMAGE", "links": [ 12, 13 ] }, { "name": "dir_6", "type": "IMAGE", "links": [ 14, 15 ] }, { "name": "dir_7", "type": "IMAGE", "links": [ 16, 17 ] }, { "name": "dir_8", "type": "IMAGE", "links": [ 18, 19 ] }, { "name": "info", "type": "STRING", "links": [] } ], "properties": { "aux_id": "rui40000/Ruinode", "Node name for S&R": "RuiEightDirSplit" }, "widgets_values": [ 3, 3, "4", "SW,S,SE,W,E,NW,N,NE", "已带透明通道(推荐·上游接抠图节点)", 0.92, 0.2, 1.0, 1.0, 0.05, true, "锚点对齐·统一画布(做游戏必选)", "脚底中心(推荐)", "逐帧对齐·脚底钉死(推荐)", true, 16 ], "color": "#232", "bgcolor": "#353", "title": "③ 八方向拆分(拆分+编号+分组)" }, { "id": 4, "type": "VHS_VideoCombine", "pos": [ -1400, -160 ], "size": [ 320, 460 ], "flags": {}, "order": 3, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 4 }, { "name": "audio", "type": "AUDIO", "link": null }, { "name": "meta_batch", "type": "VHS_BatchManager", "link": null }, { "name": "vae", "type": "VAE", "link": null } ], "outputs": [ { "name": "Filenames", "type": "VHS_FILENAMES", "links": [] } ], "properties": { "Node name for S&R": "VHS_VideoCombine" }, "widgets_values": { "frame_rate": 24, "loop_count": 0, "filename_prefix": "8dir/1_SW", "format": "video/webm", "pix_fmt": "yuva420p", "crf": 20, "save_metadata": true, "trim_to_audio": false, "pingpong": false, "save_output": true }, "color": "#232", "bgcolor": "#353", "title": "1. SW → 透明 webm" }, { "id": 5, "type": "SaveImage", "pos": [ -1040, -160 ], "size": [ 300, 300 ], "flags": {}, "order": 4, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 5 } ], "outputs": [], "properties": { "cnr_id": "comfy-core", "ver": "0.3.49", "Node name for S&R": "SaveImage" }, "widgets_values": [ "8dir_frames/1_SW/SW" ], "title": "1. SW → 透明 PNG 序列帧" }, { "id": 6, "type": "VHS_VideoCombine", "pos": [ -1400, 170 ], "size": [ 320, 460 ], "flags": {}, "order": 5, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 6 }, { "name": "audio", "type": "AUDIO", "link": null }, { "name": "meta_batch", "type": "VHS_BatchManager", "link": null }, { "name": "vae", "type": "VAE", "link": null } ], "outputs": [ { "name": "Filenames", "type": "VHS_FILENAMES", "links": [] } ], "properties": { "Node name for S&R": "VHS_VideoCombine" }, "widgets_values": { "frame_rate": 24, "loop_count": 0, "filename_prefix": "8dir/2_S", "format": "video/webm", "pix_fmt": "yuva420p", "crf": 20, "save_metadata": true, "trim_to_audio": false, "pingpong": false, "save_output": true }, "title": "2. S → 透明 webm" }, { "id": 7, "type": "SaveImage", "pos": [ -1040, 170 ], "size": [ 300, 300 ], "flags": {}, "order": 6, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 7 } ], "outputs": [], "properties": { "cnr_id": "comfy-core", "ver": "0.3.49", "Node name for S&R": "SaveImage" }, "widgets_values": [ "8dir_frames/2_S/S" ], "title": "2. S → 透明 PNG 序列帧" }, { "id": 8, "type": "VHS_VideoCombine", "pos": [ -1400, 500 ], "size": [ 320, 460 ], "flags": {}, "order": 7, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 8 }, { "name": "audio", "type": "AUDIO", "link": null }, { "name": "meta_batch", "type": "VHS_BatchManager", "link": null }, { "name": "vae", "type": "VAE", "link": null } ], "outputs": [ { "name": "Filenames", "type": "VHS_FILENAMES", "links": [] } ], "properties": { "Node name for S&R": "VHS_VideoCombine" }, "widgets_values": { "frame_rate": 24, "loop_count": 0, "filename_prefix": "8dir/3_SE", "format": "video/webm", "pix_fmt": "yuva420p", "crf": 20, "save_metadata": true, "trim_to_audio": false, "pingpong": false, "save_output": true }, "title": "3. SE → 透明 webm" }, { "id": 9, "type": "SaveImage", "pos": [ -1040, 500 ], "size": [ 300, 300 ], "flags": {}, "order": 8, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 9 } ], "outputs": [], "properties": { "cnr_id": "comfy-core", "ver": "0.3.49", "Node name for S&R": "SaveImage" }, "widgets_values": [ "8dir_frames/3_SE/SE" ], "title": "3. SE → 透明 PNG 序列帧" }, { "id": 10, "type": "VHS_VideoCombine", "pos": [ -1400, 830 ], "size": [ 320, 460 ], "flags": {}, "order": 9, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 10 }, { "name": "audio", "type": "AUDIO", "link": null }, { "name": "meta_batch", "type": "VHS_BatchManager", "link": null }, { "name": "vae", "type": "VAE", "link": null } ], "outputs": [ { "name": "Filenames", "type": "VHS_FILENAMES", "links": [] } ], "properties": { "Node name for S&R": "VHS_VideoCombine" }, "widgets_values": { "frame_rate": 24, "loop_count": 0, "filename_prefix": "8dir/4_W", "format": "video/webm", "pix_fmt": "yuva420p", "crf": 20, "save_metadata": true, "trim_to_audio": false, "pingpong": false, "save_output": true }, "title": "4. W → 透明 webm" }, { "id": 11, "type": "SaveImage", "pos": [ -1040, 830 ], "size": [ 300, 300 ], "flags": {}, "order": 10, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 11 } ], "outputs": [], "properties": { "cnr_id": "comfy-core", "ver": "0.3.49", "Node name for S&R": "SaveImage" }, "widgets_values": [ "8dir_frames/4_W/W" ], "title": "4. W → 透明 PNG 序列帧" }, { "id": 12, "type": "VHS_VideoCombine", "pos": [ -1400, 1160 ], "size": [ 320, 460 ], "flags": {}, "order": 11, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 12 }, { "name": "audio", "type": "AUDIO", "link": null }, { "name": "meta_batch", "type": "VHS_BatchManager", "link": null }, { "name": "vae", "type": "VAE", "link": null } ], "outputs": [ { "name": "Filenames", "type": "VHS_FILENAMES", "links": [] } ], "properties": { "Node name for S&R": "VHS_VideoCombine" }, "widgets_values": { "frame_rate": 24, "loop_count": 0, "filename_prefix": "8dir/5_E", "format": "video/webm", "pix_fmt": "yuva420p", "crf": 20, "save_metadata": true, "trim_to_audio": false, "pingpong": false, "save_output": true }, "title": "5. E → 透明 webm" }, { "id": 13, "type": "SaveImage", "pos": [ -1040, 1160 ], "size": [ 300, 300 ], "flags": {}, "order": 12, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 13 } ], "outputs": [], "properties": { "cnr_id": "comfy-core", "ver": "0.3.49", "Node name for S&R": "SaveImage" }, "widgets_values": [ "8dir_frames/5_E/E" ], "title": "5. E → 透明 PNG 序列帧" }, { "id": 14, "type": "VHS_VideoCombine", "pos": [ -1400, 1490 ], "size": [ 320, 460 ], "flags": {}, "order": 13, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 14 }, { "name": "audio", "type": "AUDIO", "link": null }, { "name": "meta_batch", "type": "VHS_BatchManager", "link": null }, { "name": "vae", "type": "VAE", "link": null } ], "outputs": [ { "name": "Filenames", "type": "VHS_FILENAMES", "links": [] } ], "properties": { "Node name for S&R": "VHS_VideoCombine" }, "widgets_values": { "frame_rate": 24, "loop_count": 0, "filename_prefix": "8dir/6_NW", "format": "video/webm", "pix_fmt": "yuva420p", "crf": 20, "save_metadata": true, "trim_to_audio": false, "pingpong": false, "save_output": true }, "title": "6. NW → 透明 webm" }, { "id": 15, "type": "SaveImage", "pos": [ -1040, 1490 ], "size": [ 300, 300 ], "flags": {}, "order": 14, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 15 } ], "outputs": [], "properties": { "cnr_id": "comfy-core", "ver": "0.3.49", "Node name for S&R": "SaveImage" }, "widgets_values": [ "8dir_frames/6_NW/NW" ], "title": "6. NW → 透明 PNG 序列帧" }, { "id": 16, "type": "VHS_VideoCombine", "pos": [ -1400, 1820 ], "size": [ 320, 460 ], "flags": {}, "order": 15, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 16 }, { "name": "audio", "type": "AUDIO", "link": null }, { "name": "meta_batch", "type": "VHS_BatchManager", "link": null }, { "name": "vae", "type": "VAE", "link": null } ], "outputs": [ { "name": "Filenames", "type": "VHS_FILENAMES", "links": [] } ], "properties": { "Node name for S&R": "VHS_VideoCombine" }, "widgets_values": { "frame_rate": 24, "loop_count": 0, "filename_prefix": "8dir/7_N", "format": "video/webm", "pix_fmt": "yuva420p", "crf": 20, "save_metadata": true, "trim_to_audio": false, "pingpong": false, "save_output": true }, "title": "7. N → 透明 webm" }, { "id": 17, "type": "SaveImage", "pos": [ -1040, 1820 ], "size": [ 300, 300 ], "flags": {}, "order": 16, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 17 } ], "outputs": [], "properties": { "cnr_id": "comfy-core", "ver": "0.3.49", "Node name for S&R": "SaveImage" }, "widgets_values": [ "8dir_frames/7_N/N" ], "title": "7. N → 透明 PNG 序列帧" }, { "id": 18, "type": "VHS_VideoCombine", "pos": [ -1400, 2150 ], "size": [ 320, 460 ], "flags": {}, "order": 17, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 18 }, { "name": "audio", "type": "AUDIO", "link": null }, { "name": "meta_batch", "type": "VHS_BatchManager", "link": null }, { "name": "vae", "type": "VAE", "link": null } ], "outputs": [ { "name": "Filenames", "type": "VHS_FILENAMES", "links": [] } ], "properties": { "Node name for S&R": "VHS_VideoCombine" }, "widgets_values": { "frame_rate": 24, "loop_count": 0, "filename_prefix": "8dir/8_NE", "format": "video/webm", "pix_fmt": "yuva420p", "crf": 20, "save_metadata": true, "trim_to_audio": false, "pingpong": false, "save_output": true }, "title": "8. NE → 透明 webm" }, { "id": 19, "type": "SaveImage", "pos": [ -1040, 2150 ], "size": [ 300, 300 ], "flags": {}, "order": 18, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", "link": 19 } ], "outputs": [], "properties": { "cnr_id": "comfy-core", "ver": "0.3.49", "Node name for S&R": "SaveImage" }, "widgets_values": [ "8dir_frames/8_NE/NE" ], "title": "8. NE → 透明 PNG 序列帧" }, { "id": 20, "type": "Note", "pos": [ -2680, 880 ], "size": [ 740, 1120 ], "flags": {}, "order": 19, "mode": 0, "inputs": [], "outputs": [], "properties": {}, "widgets_values": [ "八方向行走动画 —— 完整管线\n\n【前置步骤(在本工作流之外完成)】\n 角色图 →(GPTimage2)→ 八方向静态图 →(Seedance 首尾帧)→ 循环行走视频\n 视频每一帧上都排布着 8 个朝向的角色。\n\n【本工作流做的事】\n ① VHS「Load Video」把视频转成序列帧\n · force_rate 就是帧率选择。设 0 用源视频帧率;\n 设 12 可减半出帧、素材更小,走路动画通常 8~12 帧一循环就够\n · frame_load_cap 可限制总帧数,先小批量试参数很有用\n ② Lucida 抠图,为拆分提供语义级 alpha(见下方「为什么要接抠图节点」)\n ③ 八方向拆分:切格 + 编号 + 按方向分组,一次给出 8 条序列\n ④ 每个方向各接一个 VHS「Video Combine」出透明 webm\n ⑤ 每个方向各接一个 SaveImage 出透明 PNG 序列帧\n\n■ 为什么要接抠图节点,而不是让拆分节点自己扣白底 ————\n\n拆分节点自带的「白底转透明」是纯颜色阈值法:按「离白色多远」估 alpha。\n它的死穴是角色身上的白色衣物 —— 白衬衫本身就接近白,alpha 天生偏低,\n一旦收边压白边,衬衫就会被啃出破洞(实测会看到背景色透过衣服)。\n\nLucida 是按语义判断的,知道那是衣服而不是背景。实测(同一素材、\n同等白边水平):主体被啃掉的面积比阈值法少 35%,白衬衫完好。\nLucida 一次就能把整张雪碧图的 8 个角色全部识别出来\n(各格前景占比 0.18~0.24,中间空格 0.002)。\n\n素材里确实没有白色元素时,可以把 bg_mode 改成「白底转透明」省掉模型开销;\n此时 edge_shrink 要调到 0.35 以上才压得住白边。\n\n■ 治白边的两个参数 ————————————————————\n\n · edge_shrink(主力)把边缘那圈几乎全是背景的半透明像素收掉。\n 配模型 alpha 用 0.15~0.25;配阈值法要 0.35+。\n 实测:0 → 白边强度 0.048;0.2 → 0.026;0.5 → 0.016。\n · decontaminate(辅助)颜色反溢出,按\n 观察色 = 前景×a + 白×(1-a) 反解真正的前景色。\n 单独用改善很小(约 3%),要和收边配合。\n\n\n■ 尺寸统一与锚点对齐(做游戏的关键)——————————\n\n 不开对齐时,8 个方向各按自己的内容裁剪,尺寸互不相同\n (实测 8 个方向出了 8 种尺寸),角色在各自画面里的位置也不一致\n ——游戏里切换朝向时角色会跳一下。\n\n · align_mode = 锚点对齐·统一画布\n 所有方向输出同一尺寸,锚点落在画布同一位置。\n 实测各方向锚点散布:x 48.9px → 0.76px,y 27px → 0px。\n · anchor_type = 脚底中心\n y 取最低的不透明行,x 取底部窄带的水平质心。\n x 不用包围盒中心,是因为斗篷、披风、手杖会把包围盒拽向一侧,\n 而它们基本不会垂到脚底。走路时两脚一前一后,窄带质心正好\n 落在两脚之间,也就是人真正站立的点。\n · align_scope = 逐帧对齐·脚底钉死\n 等价于人工「一帧一帧对位置」。行走循环本就该原地播放、\n 位移交给游戏代码,sprite 内部不该有整体漂移;AI 生成的视频\n 往往有(实测同方向跨帧漂移 22px),此模式能压到 1px。\n 若角色本就该有前后摆动(挥剑、跳跃),改用「按方向统一平移」。\n\n ⚠ info 输出里会给出统一画布尺寸、锚点坐标,以及 Unity/Godot 的\n 归一化 pivot(左下为原点)。把那个 pivot 填进引擎的 Sprite 设置,\n 8 个方向就能共用同一套坐标,切换朝向纹丝不动。\n\n■ 拆分节点的其他关键参数 ————————————————\n\n · grid_cols / grid_rows / empty_cells\n 3×3 中间留空即 empty_cells=4。序号行优先、从 0 开始。\n · direction_names\n 按「跳过空格后的先后顺序」命名,默认对应:\n SW S SE ← 行1:面向观众\n W E ← 行2:侧面\n NW N NE ← 行3:背对观众\n 与素材不符就直接改这行,名字只影响标注不影响画面。\n · expand_beyond_cell(务必开启)\n 允许角色超出格子边界。关闭时按格线严格切分,角色只要探出格线\n 就会被切断(最典型的是脚被削掉)。开启后格子只用来判定方向,\n 实际范围由角色自身的连通区域决定。三种 bg_mode 都有效。\n · auto_crop\n 裁剪框取该方向所有帧的并集,因此整条序列尺寸一致(能合成视频),\n 角色也不会在帧间跳动。\n\n■ webm 透明的关键 ————————————————————\n\n Video Combine 的 format 必须选 video/webm,pix_fmt 必须选 yuva420p。\n 注意:alpha 存放在 WebM 的独立边带里,用 ffprobe 看主流会显示 yuv420p,\n 这是正常的。播放器/引擎(走 libvpx 解码)能正确读到透明通道;\n 若要用 ffmpeg 自行验证,需显式加 -c:v libvpx-vp9 解码,\n 否则内置 vp9 解码器不输出 alpha,会让人误以为透明丢了。" ], "color": "#432", "bgcolor": "#653" } ], "links": [ [ 1, 1, 0, 2, 0, "IMAGE" ], [ 2, 1, 0, 3, 0, "IMAGE" ], [ 3, 2, 0, 3, 1, "MASK" ], [ 4, 3, 0, 4, 0, "IMAGE" ], [ 5, 3, 0, 5, 0, "IMAGE" ], [ 6, 3, 1, 6, 0, "IMAGE" ], [ 7, 3, 1, 7, 0, "IMAGE" ], [ 8, 3, 2, 8, 0, "IMAGE" ], [ 9, 3, 2, 9, 0, "IMAGE" ], [ 10, 3, 3, 10, 0, "IMAGE" ], [ 11, 3, 3, 11, 0, "IMAGE" ], [ 12, 3, 4, 12, 0, "IMAGE" ], [ 13, 3, 4, 13, 0, "IMAGE" ], [ 14, 3, 5, 14, 0, "IMAGE" ], [ 15, 3, 5, 15, 0, "IMAGE" ], [ 16, 3, 6, 16, 0, "IMAGE" ], [ 17, 3, 6, 17, 0, "IMAGE" ], [ 18, 3, 7, 18, 0, "IMAGE" ], [ 19, 3, 7, 19, 0, "IMAGE" ] ], "groups": [], "config": {}, "extra": { "ds": { "scale": 0.5, "offset": [ 2820, 100 ] } }, "version": 0.4 }