# LLM3DGraphics · 3D / DGX 3D 跨项目 AI 调用指南 更新:**2026-11-03**。按当前后端代码、运行服务 OpenAPI 与能力接口核对,供其他项目、开发者和 Agent 通过 HTTP 使用。本文替换旧版“两步生成”和“混元模型不能接绑骨”的说明。 - 本机服务:`http://127.0.0.1:8008`;当前局域网部署:`http://192.168.130.16:8008`。调用方将地址配置为 `BASE`。 - 接口文档:`{BASE}/docs`;机器可读契约:`{BASE}/openapi.json`。 - 页面:普通 3D `/#workspace`、DGX 3D `/#dgx`、图片 `/#local-images`、导入人形绑骨 `/#skinning`。 - 外部项目统一访问 **Windows LLM3DGraphics 服务**。DGXA 推理服务、SSH、模型文件和密钥由本项目管理,调用方不需要直接连接。 ## 1. 流程总览 ### 1.1 两种建模引擎 | 页面 / engine | 网格生成 | 低模、UV、贴图 | 后续绑骨与动作 | |---|---|---|---| | 3D 模型生成 / `hunyuan` | 混元 3D 官方网页,当前客户端微信登录 | 官网 Studio 低模、UV,本机 Blender 烘焙;超出目标 30% 时保留 UV 减面 | **当前已支持**:先获得 `model_ex.glb`,再调用 `/rig`、`/animate`,使用与 DGX 相同的角色分支 | | DGX 3D 生成 / `dgx` | DGXA Hunyuan3D 2.1,生成带贴图高模 | 本机 Blender 减面、UV、烘焙;低模为 `model_ex.glb` | 网格完成后不自动绑骨;依次调用 `/rig`、`/animate` | 两种引擎当前都只接收图片建模:`mode=image`。只有文字时,先生成参考图再上传实际图片;`mode=text` 不是可用的文生 3D 捷径。 ```mermaid flowchart TD A[文字或参考图] --> B{已有可用图片?} B -->|否| C[本地图像 API 生成并选择参考图] B -->|是| D[上传图片 POST /api/jobs] C --> D D --> E{engine} E -->|hunyuan| F[微信会话 / 官网高模 / Studio 低模 / Blender 烘焙] E -->|dgx| G[DGXA Hunyuan3D / Blender 可选低模烘焙] F --> H{需要可驱动角色?} G --> H H -->|否| I[下载静态网格与贴图] H -->|是| J[POST /api/jobs/id/rig] J --> K[检查本阶段成功与 ready_for_motion] K --> L[POST /api/jobs/id/animate] L --> M[下载角色和动作 / 检查蒙皮预览] ``` 角色正式调用顺序是 **生成网格 → 单独绑骨 → 单独生成动作**。三步使用同一个 3D job ID,每步均需等待该阶段完成。静物在第一步结束。 ### 1.2 模型类型、骨架与动作引擎 | 类型 | character_type | `/rig` 实际流程 | `/animate` 实际流程 | |---|---|---|---| | 人形、NPC、士兵 | `humanoid` | DGXA Make-It-Animatable 预测关节和权重 → 适配 Mixamo 65 骨 → 本机映射权重、导出 | DGXA Kimodo 生成 SOMA 动作 → 本机重定向到 `mixamorig:*` | | 猫、狗、鹿、马等四足 | `quadruped` | SMAL 表面拟合取得 AniMer 解剖标记 → 适配 Truebones 物种模板 → TokenRig 按指定骨架蒙皮 | DGXA UniMate v3,输入适配骨架、官方语义、层级和朝向 → 重定向回原始绑定姿态 | | 鹰隼、鸟类等 | `non_humanoid` | 模板匹配;`Eagle` 用双翼、头颈、尾羽、腿部锚点及翼面约束适配 → TokenRig 固定骨架蒙皮 | UniMate v3 | | 鲨鱼、鱼类 | `non_humanoid` | 模板匹配;`Jaws` 用头尾方向、TokenRig 中心线及鳍部表面标记适配 → TokenRig 固定骨架蒙皮 | UniMate v3 | | 昆虫、爬虫、恐龙、其他生物 | `non_humanoid` | 已匹配模板时适配并蒙皮,多数其他模板仍用几何配准;未识别时回退原生 TokenRig | UniMate v3,陌生拓扑需检查骨数、语义和效果 | | 道具、建筑、静物 | `static` | 不绑骨 | 不生成动作 | - 人形当前是 **Make-It-Animatable(mia)**,不是 TokenRig,也不是独立的 HumanRig 推理分支。Kimodo 是人形文生动作默认引擎,Mixamo 骨名不表示自动检索 Mixamo 动作库。 - 四足最终骨数随物种模板变化,如 `Cat=43`、`Raindeer=35`、`Deer=41`。AniMer 35 关节是拟合中间参考,**不是所有动物的最终骨架**。 - 当前 Cat 43 骨、Eagle 51 骨、Jaws 16 骨已完成端到端模型、蒙皮、动作与导出检查。70 套模板均校验结构和导出兼容性,尚未逐套验收生成效果;Eagle/Jaws 专门适配也不能推广为所有鸟、鱼均已验收。 - `static` 默认拒绝后续阶段。若确实要改成角色,应在 `/rig` 显式传入三种角色类型之一。 ## 2. 能力探测与资源调度 ```http GET /api/capabilities GET /api/local-images/status GET /api/skeleton-templates GET /api/skinning/catalog ``` | 能力字段 | 对应阶段 | |---|---| | `capabilities.hunyuan_logged_in` / `hunyuan_status` | 网页混元,按当前 Cookie 会话判断 | | `capabilities.dgx.hy3d_ready` / 顶层 `dgx_ready` | DGX 网格生成 | | `capabilities.dgx.mia_ready` | 人形绑骨 | | `capabilities.dgx.kimodo_ready` | 人形文生动作 | | `capabilities.dgx.tokenrig_ready` | 非人形绑骨 | | `capabilities.dgx.animal_templates_ready` / `animal_template_count` | 动物模板库,当前 70 套 | | `capabilities.dgx.animer35_ready` | 四足适配依赖;历史字段名,不表示最终固定 35 骨 | | `capabilities.dgx.unimate_ready` / `unimate_version` | 非人形动作,当前 v3 | | `capabilities.formats` / `target_faces` / `texture_sizes` | 可用格式、面数范围和贴图档位 | | `local-images/status.connected` / `ready` / `models.turbo.installed` | 本地图像;不要用云端 `image_generate_ready` 替代 | | `skinning/catalog.mia_ready` / `blender_ready` | 导入人形蒙皮;还提供男女骨架和动作目录 | `topocap_ready`、`hymotion_ready` 是旧前端兼容别名,目前跟随 UniMate。`topocap_legacy_ready` 是旧诊断项,不能据此判断正在使用该引擎。Puppeteer、TopoCap、GIF/视频参考动作不属于当前默认调用链。 探测主要检查连接、入口和文件,不代表模型已加载或本次推理一定成功。能力缓存约 20 秒;首次 SSH 探测可能较慢,客户端超时建议至少 90 秒。2026-11-03 核对时主要 DGX 分支均就绪;调用方每次仍应探测。 - 3D 主队列最多 5 个 queued+running,串行执行;导入人形蒙皮共享执行器。 - 本地图像最多排队 4 批,每批 1–16 张、逐张生成。图片与 DGX 建模/绑骨/动作遵守本机资源调度,不直接调用 DGXA 的 8090 worker 绕过保护。 - 图像批次结束后调用 `POST /api/local-images/prepare-3d` 准备 3D;它只恢复资源,不创建模型。DGX `/api/jobs` 也会执行准备,显式调用便于阶段诊断。 - 当前 auto_switch、co_resident 已启用;共同驻留不等于并行推理。是否保留 Turbo 由服务端按内存决定,不要暂停其他项目任务或强制修改精度。 - 409 等已有阶段结束,429 退避;轮询至少 2 秒、建议 3 秒。创建请求超时后先查询任务记录,避免重复提交;当前没有公开幂等键或任务克隆 API。 ## 3. 第 0 步:文字或参考编辑生成图片 有可用用户图片时可跳过本节。3D 输入应为单个完整主体、简单背景、肢体/鳍/翼轮廓分离,避免极端透视和遮挡。人形用完整 T/A 姿势,四足站姿、四腿分开,鸟类展开双翼,鱼类身体水平。不要把拼贴三视图作为单张多个主体的建模输入。 ### 3.1 本地图像 API 当前安装 **Qwen/Qwen-Image-2.1-Turbo BF16**,表单请求: ```http POST /api/local-images/jobs Content-Type: multipart/form-data model=turbo profile=concept prompt=完整大白鲨,身体水平,尾鳍竖直,各鳍清晰分离,灰蓝色背部、白色腹部,纯白背景,无文字 width=1536 height=1024 count=1 seed=20261103 transparent=false ``` | 字段 | 默认与范围 | |---|---| | model | turbo,当前唯一支持值;采样步数和 CFG 由 worker 固定 | | prompt | 必填,去空格后 1–4000 字;限制和编辑要求也写在这里 | | profile | 默认 concept;另有 sprite、albedo、background,服务端追加用途提示 | | width / height | 默认 1024;256–2048,必须为 32 的倍数 | | count | 默认 4;1–16 | | seed | 默认 -1 随机;或 0–4294967280;第 i 张使用 seed+i | | transparent | 默认 false;实际 Alpha 仍需检查 | | negative_prompt | 留空,Turbo CFG=1 不使用独立负面提示词 | | images | 可省略;多参考编辑时重复同名文件字段,最多 10 张 PNG/JPEG/WebP,每张 ≤12 MB、≤1600 万像素 | 返回 202 和图片批次 ID。轮询 `GET /api/local-images/jobs/{id}` 至 completed/failed,读取 `images[].url`、index、seed、has_alpha,下载选中 PNG。图片批次 ID **不能代替**之后的 3D job ID。失败批次可能已有图片,应保留结果与错误。 ```bash BASE=http://192.168.130.16:8008 curl -fS "$BASE/api/local-images/status" curl -fS -X POST "$BASE/api/local-images/jobs" \ -F model=turbo -F profile=concept -F count=1 -F width=1536 -F height=1024 \ -F 'prompt=完整大白鲨,水平体态,尾鳍竖直,各鳍分离,纯白背景,无文字' # 取图片批次 ID,轮询至 completed,再下载: curl -fS "$BASE/api/local-images/jobs/" curl -fL -o shark.png "$BASE/api/local-images/jobs//images/0" curl -fS -X POST "$BASE/api/local-images/prepare-3d" ``` 以上为 Bash 语法;PowerShell 使用 curl.exe 并按其换行规则书写,或用 §9 Python 客户端。多参考增加 `-F 'images=@shape.png' -F 'images=@style.png'`,提示词按顺序说明各图作用;本接口不接收 URL/base64 作为 images 文件。 只要 2D 素材时可直接导出:`GET /api/local-images/jobs/{id}/unity?selected=0,2`。省略 selected 导出全部已生成图片;ZIP 含 PNG、参考图、manifest 和 Unity 导入脚本。sprite 按 Sprite,albedo 按 Repeat base color,其他按普通图片;不是 unitypackage 或完整 PBR 材质。转入 3D 则下载 PNG 后上传 `/api/jobs`。 ### 3.2 可选云端转接 用户选择云端生图时,检查 `capabilities.image_generate_ready`,再调用: ```http POST /api/image/generate Content-Type: application/json {"prompt":"完整角色,站姿,纯白背景,无文字","aspect_ratio":"1:1","quality":"auto","images":[]} ``` prompt 必填、≤4000 字;images 最多 4 个 URL/data URL/base64 参考,区别于本地图像文件接口。成功后从返回 url 下载缓存图片再建模。模型与上游 Key 由服务端配置,外部项目不要直连上游或索取 Key。 ## 4. 第 1 步:生成网格 ### 4.1 公共创建接口 `POST /api/jobs`,multipart/form-data,返回 202 和 job ID。 | 字段 | 默认 / 范围 | 说明 | |---|---|---| | engine | 默认 **hunyuan**;hunyuan / dgx | 外部调用务必显式指定,避免误进微信分支 | | mode | image | 两种引擎当前都只支持 image | | image | 必填文件 | PNG/JPEG/WebP,≤12 MB、≤1600 万像素;服务端归一化最长边到 1536 | | prompt | 可选,≤2000 字 | 图片模式作为描述保存并用于物种匹配,不是直接文生 3D | | character_type | 默认 non_humanoid | humanoid / quadruped / non_humanoid / static;两种引擎后续均使用此分类 | | resolution | 默认 256 | DGX 为抽取分辨率 16–512;网页仅允许 128/256,不是贴图尺寸 | | source_faces | 默认 50000 | 网页高模档:5000/20000/50000/500000/1000000;DGX 会改写保存为 50000,不据此承诺精确输出面数 | | target_faces | 默认 5000 | 低模目标 500–20000 任意整数;500000 或 1000000 表示跳过低模,建议统一用 500000 | | texture_size | 默认 1024 | 低模烘焙贴图 512/1024/2048,不控制 DGX 原始生成贴图 | | output_format | 默认 glb;glb / fbx | 始终保留 GLB;fbx 额外导出资产包,需 Blender | 保留字段 mixamo_motion 不是当前动作开关。骨架 `skeleton_template` 应在 **rig 请求**中指定,创建网格时传它不会选择骨架。 仅当原模型面数大于目标且未选跳过低模时生成低模。读取 `lowpoly_ready`、downloads 判断实际产物;原模型低于目标时不会凭空产生 model_ex。DGX 绑骨优先低模、否则高模;网页混元后续绑骨要求低模。 ### 4.2 DGX 示例 ```bash curl -fS -X POST "$BASE/api/jobs" \ -F engine=dgx -F mode=image -F character_type=non_humanoid \ -F 'prompt=great white shark' -F 'image=@shark.png' \ -F resolution=256 -F target_faces=20000 -F texture_size=1024 -F output_format=fbx ``` 轮询 `GET /api/jobs/{id}`。网格完成后 `ready_for_motion=false` 是正常状态,下一步调用 rig。 ### 4.3 网页混元:微信会话和建模 ```http GET /api/hunyuan/status POST /api/hunyuan/login GET /api/hunyuan/login/qr POST /api/hunyuan/login/cancel POST /api/hunyuan/logout ``` 在发起请求的客户端工作台点击“微信登录”,真人扫描**客户端页面展示的二维码**;后端用 Playwright/Edge 操作官网,不要求用户去服务机窗口扫码。 HTTP 接入必须在登录、二维码、状态与任务请求间保留同一服务端 Cookie,例如 requests.Session() 或 curl 的 `-c cookies.txt -b cookies.txt`。浏览器已登录不代表另一个无状态 Python/curl 会话也登录;每台设备、浏览器和无痕会话分别登录。 ```bash # COOKIE_FILE 对应已完成扫码的同一 HTTP 会话。 curl -fS -b "$COOKIE_FILE" "$BASE/api/hunyuan/status" curl -fS -b "$COOKIE_FILE" -X POST "$BASE/api/jobs" \ -F engine=hunyuan -F mode=image -F character_type=humanoid \ -F 'image=@person.png' -F source_faces=50000 \ -F target_faces=5000 -F texture_size=1024 -F output_format=fbx ``` 网格完成后可只下载高/低模;需要角色时确认 `lowpoly_ready=true`、`rig_followup_ready=true`,然后对同一 job 调 §5–6。后续依赖 DGXA,因此网页建模不免除绑骨和动作阶段的 DGX 能力要求。 ### 4.4 状态、错误与低模重做 - `GET /api/jobs` 返回最近最多 30 个任务的**数组**,不是 `{jobs:[...]}`;指定 ID 查询不受列表条数限制。 - 状态 queued → running → completed/failed;后续阶段复用同一 ID 并重走状态。stage、progress、elapsed 是当前阶段信息,不是全流程累计。 - **completed 也必须检查 error**。绑骨或动作失败可能保留旧资产并软失败为 completed,同时设置顶层 error;绑骨还会记录 result.dgx.rig_error,部分动作失败会标记 partial_motion。旧文件存在不表示本次阶段成功。 - 绑骨成功:本阶段无 error、ready_for_motion=true、result.dgx.rigged=true,且 rigged_* 可用。 - 动作成功:本阶段无 error、result.dgx.animated=true,且 animated_* 可用。 - `POST /api/jobs/{id}/resume-studio` 仅用于已有高模/Studio 记录且 can_resume_studio=true 的混元中断任务,不是通用重试 API。 - `POST /api/jobs/{id}/rebake` 表单 target_faces、texture_size:重出低模。已有蒙皮角色可能自动重绑骨并按原 motion_prompt 重做动作;先下载要保留的资产。网页混元仍检查当前会话登录。 - `GET /api/jobs/{id}/log` 返回 JSON `{text:"..."}`;必要时维护方另查人形任务目录 mia.log。 ## 5. 第 2 步:按类型绑骨 ```http POST /api/jobs/{id}/rig Content-Type: multipart/form-data character_type=quadruped skeleton_template=Cat ``` 返回 202。character_type 可省略以沿用任务,显式值仅 humanoid/quadruped/non_humanoid;skeleton_template 默认 auto,非人形可指定物种 ID。人形由此分支固定使用 Mixamo 65 骨,不通过该参数选择男女或骨数。 ### 5.1 人形 Make-It-Animatable 在 DGXA 预测关节与蒙皮权重,本机适配参考骨架到模型并映射权重。标准 jobs/rig 固定使用 `YBot65` 资源,生成 mixamorig:* 骨名和 Mixamo profile;YBot 名称不改变输入网格的性别外貌。 选择男女、65/49/41/25 骨或直接导入已有 GLB/FBX/OBJ,使用 §8 独立 skinning 接口;不要给 jobs/rig 传不存在的 gender、bone_count 参数。 ### 5.2 四足:解剖拟合 → 物种模板 → 固定骨架蒙皮 1. 提取三维表面,在 DGXA 拟合 SMAL/AniMer 参考,得到头颈、躯干、四肢和尾部解剖标记。 2. 将官方物种模板关节适配到标记和网格表面,保留骨名、父子关系与语义;长尾还参考真实模型尾部中心线。 3. 指定适配后的骨架输入 TokenRig 计算蒙皮;内部处理共点分支与量化,导出恢复模板定义并校验绑定姿态。 4. result.dgx.animal_profile 包含 template_id、bones、names、parents、joint_labels、selection、fit、validation,供下游记录与生成动作。 auto 先匹配任务名称(上传文件名)和 prompt 等描述;没有明确描述的四足按拟合后的体型比例选择。它不是基于纹理的通用视觉分类器;已知物种建议显式指定模板。 ### 5.3 鸟类、鱼类和其他非人形 | 示例 | 参数 | 检查重点 | |---|---|---| | 鹰隼 | non_humanoid + Eagle(51 骨) | 双翼、头颈、尾羽和腿部解剖锚点;保持翼展,避免翼尖收缩到躯干 | | 鲨鱼 | non_humanoid + Jaws(16 骨) | 完整头尾中心线、头尾方向、背鳍朝上、左右鳍分离 | | 其他鱼 | non_humanoid + 对应目录 ID,如 Pirrana | 当前一般配准分支,检查形态匹配和关节分布 | | 其他已收录生物 | non_humanoid + 物种 ID | 多数非四足模板仍使用 TokenRig 几何支撑配准,逐模型检查 | | 未收录生物 | non_humanoid + auto | 未识别时回退原生 TokenRig;骨数、语义和 UniMate 效果不保证 | ### 5.4 70 套兼容模板 ```http GET /api/skeleton-templates GET /api/skeleton-templates/library.zip GET /api/skeleton-templates/{template_id}/{kind} ``` kind 为 json/glb/fbx/preview。以目录 ID 为准,保留现有拼写:Raindeer(35 骨)与 Deer(41 骨)不同;Leapord、Comodoa、Pirrana 也不要自行改写。 | 模板族 | 数量 | ID(骨数) | |---|---:|---| | quadruped | 30 | BrownBear(38), Buffalo(42), Camel(46), Cat(43), Coyote(40), Deer(41), Dog(51), Dog2(50), Elephant(39), Fox(39), Gazelle(41), Goat(31), Hamster(41), Hippopotamus(40), Horse(63), Hound(44), Jaguar(45), Leapord(47), Lion(31), Lynx(38), Mammoth(41), PolarBear(40), PolarBearB(38), Puppy(39), Raindeer(35), Rat(18), Rhino(43), SabreToothTiger(64), SandMouse(37), Skunk(35) | | avian | 13 | Bat(47), Bird(61), Buzzard(61), Chicken(40), Crow(24), Eagle(51), Flamingo(40), Ostrich(53), Parrot(70), Parrot2(62), Pigeon(9), Pteranodon(43), Tukan(13) | | marine | 2 | Jaws(16), Pirrana(20) | | dinosaur | 7 | Raptor(35), Raptor2(61), Raptor3(60), Stego(40), Trex(60), Tricera(31), Tyranno(68) | | arthropod | 6 | Crab(53), HermitCrab(67), Scorpion(62), Scorpion2(56), Spider(69), SpiderG(61) | | insectoid | 6 | Ant(40), Cricket(53), FireAnt(42), Giantbee(51), Isopetra(60), Roach(43) | | reptile | 4 | Alligator(25), Comodoa(64), Crocodile(38), Turtle(47) | | serpentine | 2 | Anaconda(27), KingCobra(19) | 当前选定库最多 70 关节,不是完整 Truebones 动物库;模板数量 70 也不是每套骨数。Truebones 专用模型的 85 骨能力不适用于当前通用 v3 接口。 ## 6. 第 3 步:生成动作 ```http POST /api/jobs/{id}/animate Content-Type: multipart/form-data motion_prompt=An object walks forward. seed=42 cfg_scale=5 in_place=true ``` | 字段 | 默认 / 范围 | 生效范围 | |---|---|---| | motion_prompt | 必填,非空,≤500 字 | 两类动作引擎 | | seed | 42;0–4294967295 | UniMate,当前不传给 Kimodo | | cfg_scale | **3**;0–20 | UniMate;可从 5 附近对比,不保证越大越好 | | in_place | true | UniMate;只清除根节点水平速度,保留高度变化与旋转 | HTTP animate **不公开** duration_sec/fps/model/speed/loop、视频或骨架上传字段。FastAPI 可能忽略多余表单项,返回成功不意味着它们生效。 ### 6.1 人形:Kimodo runner 默认模型 `Kimodo-SOMA-RP-v1.1`,默认时长 1.5 秒;可由服务端 kimodo_model、kimodo_duration 配置,非 HTTP 单次参数。动作写明确单一行为,如 `A person walks forward at a steady pace.`。 Kimodo 输出 SOMA BVH,本机重定向到 Mixamo;当前脚本将 Hips 位置轨道归零,输出偏原地动作。in_place=false 不会改变该人形实现。人形 animated.glb/fbx 是重定向场景导出,可能含角色网格,不应套用非人形“纯动作”假设。 ### 6.2 非人形:UniMate v3 - 当前实际调用 `unimate_uniml3d_f60_v3` 和其全局归一化统计;不是 v2,不自动选择 unimate_truebones_f60,也不通过 API 切换 cross-attention 版本。 - 常规 HTTP 生成 **60 帧、30 FPS,名义 2 秒**;关键帧首末时间差可能为 59/30 秒。不要用重复帧冒充新生成长动作。 - 条件包含骨架位置/比例、层级、语义和朝向,不只凭 `_00` 这样的原始骨名理解物种。不同骨架即使提示词、seed 相同,也不保证逐帧相同或视觉步速相同。 - 服务端传递模板语义和朝向,不要擅自改骨名/层级,或只换语义标签而不检查绑定姿态。 - 提示词优先英文 **An object ...**,只描述动作。走/跑用 forward,原地需求由 in_place=true 处理。中文归一化只覆盖部分常见动作,飞行、游泳中文可能变成泛化 moves forward,建议直接用英文。 | 目标 | 提示词起点 | 需检查 | |---|---|---| | 四足走路 | `An object walks forward at a steady pace.` | 肢体相位、滑步、躯干起伏 | | 小跑 | `An object trots forward at a steady pace.` | 是否对角肢配对,文本不保证正确步态 | | 奔跑 | `An object runs forward at a steady gallop.` | 后腿幅度、脊柱、身体倾角 | | 扇翅 | `An object flies forward while repeatedly flapping its wings.` | 躯干俯仰、高度漂移、翼面形变 | | 快速游动 | `An object swims fast.` | 身体水平、尾鳍左右摆、实际频率 | 这些只是起点,不保证一次可用;调整 seed、提示词和 CFG 做候选对比。fast 不等于指定了体长/秒或摆尾频率。每次 animate 都更新该 job 的动作,**每段完成立即下载到不同目录**,再做下一个动作。 ### 6.3 长动作、循环与后处理:服务端扩展,非公共 HTTP 参数 项目已有脚本完成以下能力,但尚未封装成统一公共 API,外部项目不能仅传 duration_sec=8 或 loop=true 获得相同结果: 1. 内部 `backend.unimate_runner.submit_unimate_job(duration_sec=...)` 支持长动作。超过 60 帧走官方 motion expansion、重叠 10 帧续生成,再裁至目标长度;由维护方纳入资源调度,不让外部项目直接 SSH 调 helper。 2. 从稳定时段选完整步态/振翅/摆尾周期。四足按**同一只脚、同相位**选整周期,结合全部关节姿态和速度,避免左右脚交换的半周期。 3. 检查内部续生成边界。已有实测约第 60/110/160 帧出现过跳变;具体以数据为准,首尾相同不能修复内部跳帧。 4. 做局部四元数与速度连续修正、必要的周期平滑,保留身体/头/尾与竖直动态;不要冻结全身掩盖问题。变速用完整周期一致重采样。 5. 比较接缝姿态、速度和加速度与内部帧,导出保留闭环关键帧;循环预览不重复计算末尾闭环帧。 6. 前进位移另做 root-motion 版本,明确单位或体长/秒,不从原地片段推断真实速度。 已验证经验:橘猫从较长走路片段选稳定整周期;鹰隼的水平飞行成品对原始竖直姿态做过修正,**不是 animate 对所有鸟的默认规则**;鲨鱼快速循环从长片段 38–87 帧选择周期,修复内部续接、重采样成 16 独立帧加闭环帧(30 FPS、约 1.875 次摆尾/秒),增强后半身波动,另做约 2 体长/秒前进版。它们是生成后的编辑结果,不是未经处理的原始输出或通用默认速度。 维护脚本参考:`tests/make_cat_walk_loop.py`、`cat_loop_blender.py`、`analyze_cat_walk_cycles.py`、`eagle_level_flight.py`、`boost_shark_tail.py`、`shark_forward_motion.py`(均在 LLM3DGraphics 下)。这些脚本有案例目录/状态前置条件,不是外部 SDK 或任意资产通用的一键命令。 ## 7. 产物、下载与目标引擎 读取 job 返回的 downloads、preview_url、anim_preview_url,相对 URL 加 BASE。文件路由:`GET /api/jobs/{id}/files/{format}`。 | 键 / 字段 | 内容 | |---|---| | glb / fbx | 静态原始高模;fbx 通常是 FBX+贴图 ZIP | | glb_ex / fbx_ex | 低模、UV 与烘焙贴图,仅实际触发时存在 | | rigged_glb / rigged_fbx | 适配骨架与蒙皮网格,角色资产 | | mixamo_glb / mixamo_fbx | 人形 Mixamo profile 资产,实际成功整理时提供 | | animated_glb / animated_fbx | 当前动作;**非人形为骨架动画**,GLB 可能含微小代理而非完整角色;人形当前可能包含网格 | | anim_preview_url | 完整蒙皮角色播放动作,检查最终效果优先用它 | | preview_url | 默认预览,可能指向纯骨架,不保证含角色 | | ao_url | 可选烘焙 AO;其他图见 jobs/id/bake/{map_name} | animated_preview_glb 是完整蒙皮预览文件,通常从 downloads 隐藏,但可由 anim_preview_url 获取。不要将 unimate_raw.glb 当最终绑定姿态的动画;正式产物已重定向回角色原始绑定姿态。 **不能只看 fbx 键或内部扩展名判断格式**:检查响应 Content-Disposition/Content-Type,或头字节 `PK\x03\x04`。非人形 rigged_fbx 通常是 ZIP;MIA 人形可能是裸 FBX;animated_fbx 通常是裸 FBX。按真实内容命名并保留解压后的贴图结构。 Unity:人形按实际 Mixamo Avatar 设置 Humanoid;动物/非标准骨架用 Generic,角色与动作保持同一层级和绑定姿态。Jaws/Cat 动作不要导成 Humanoid。单位、朝向、根运动与循环范围在目标引擎复核。 每个动作保存 job JSON、提示词、seed、CFG、模板 ID、角色、动作、完整蒙皮预览与后处理说明。重绑骨会清旧动作,重出低模可能重绑骨/重做动作,应先归档。 ## 8. 已有模型:独立人形蒙皮与动作库 “骨骼蒙皮与动画”页使用另一条任务链,不需创建 api/jobs。它接受已有**人形** GLB/FBX/OBJ;当前没有公开的任意非人形上传 → TokenRig → UniMate 通用 import API。研究脚本不等于该接口支持动物。 ### 8.1 导入、男女、骨数 ```http GET /api/skinning/catalog POST /api/skinning/rig Content-Type: multipart/form-data model=@person.glb gender=male bone_count=41 method=mia fit_skeleton=true ``` - model ≤100 MB;贴图优先 GLB 或内嵌贴图 FBX,OBJ 外置贴图不自动上传。 - gender 为 male/female,参考骨架 YBot/XBot;bone_count 为 65/49/41/25,默认 65。 - method=mia、fit_skeleton=true 为当前默认;MIA 预测关节/权重、适配所选骨架、映射和导出,不接受关闭适配。 - API 仍有历史 accurig 兼容项,推荐流程不需要 AccuRIG,也不自动操作该软件。 返回 skinning task ID,轮询 **`GET /api/skinning/tasks/{id}`**,不是 api/jobs/id。completed 后读取 preview_url(skinned.glb)与 downloads.fbx(skinned.fbx)。日志:`/api/skinning/tasks/{id}/files/process.log`。 纯参考骨架:`GET /api/skinning/skeletons/{skeleton_id}/{format}`,例如 YBot41、XBot25;format 为 fbx/glb/json,以 catalog 为准。 ### 8.2 AnimX / AnimY 动作库 ```http POST /api/skinning/animations Content-Type: application/json { "gender": "male", "bone_count": 41, "animation_id": "male-walking", "rig_task_id": "", "generate": true } ``` 当前每个性别 **4 个动作**:Atk01、Atk02、BreathingIdle、Walking,共 8 个条目。男性 ID:male-atk01、male-atk02、male-breathingidle、male-walking,女性为 female-*;以 catalog.animations 为准。 - 使用 assets/Anim/AnimY、AnimX 原始 65 骨动画,按所选骨架和适配骨长重定向并烘焙简化骨数;**不是 Kimodo/UniMate 文生动作**。 - rig_task_id 可省略以预览参考骨架;提供时需已完成且性别/骨数一致。 - generate=false 先预览;generate=true 允许下载纯动画 FBX。相同请求复用缓存,也可将已有预览升级为可下载任务。 - 轮询新 task ID,完成读取 preview_url 与 downloads.fbx。tasks 列表只列蒙皮及标记生成的动画;未生成预览按 ID 查询。 - 该链没有转入主 jobs 的桥接,不能拿 skinning task ID 调 jobs/id/animate。 ## 9. 可复制 Python 客户端 依赖 requests,只调用正式 API。同一 Session 也可持有网页混元登录 Cookie;示例末尾调用为注释,接入方选定参数后执行。 ```python import json import mimetypes import time from pathlib import Path from urllib.parse import urljoin import requests class LLM3DClient: def __init__(self, base="http://192.168.130.16:8008"): self.base = base.rstrip("/") + "/" self.session = requests.Session() def api(self, method, path, *, timeout=120, **kwargs): response = self.session.request( method, urljoin(self.base, path), timeout=timeout, **kwargs ) response.raise_for_status() return response.json() def capabilities(self): return self.api("GET", "/api/capabilities") def wait(self, path, *, timeout=7200, interval=3): deadline = time.monotonic() + timeout while True: job = self.api("GET", path) if job["status"] in ("completed", "failed"): if job["status"] != "completed" or job.get("error"): raise RuntimeError(json.dumps(job, ensure_ascii=False)) return job if time.monotonic() >= deadline: raise TimeoutError(f"任务仍可能在运行,请保留ID并继续查询:{path}") time.sleep(interval) def download(self, url, output): response = self.session.get(urljoin(self.base, url), timeout=300) response.raise_for_status() output = Path(output) if response.content[:4] == b"PK\x03\x04": output = output.with_suffix(".zip") output.parent.mkdir(parents=True, exist_ok=True) output.write_bytes(response.content) return output def reference(self, prompt, output="reference.png", *, width=1024, height=1024): state = self.api("GET", "/api/local-images/status") if not (state.get("connected") and state.get("ready") and state.get("models", {}).get("turbo", {}).get("installed")): raise RuntimeError(state) batch = self.api("POST", "/api/local-images/jobs", data={ "model": "turbo", "profile": "concept", "prompt": prompt, "width": width, "height": height, "count": 1, "seed": 42, }) print("IMAGE_JOB_ID", batch["id"], flush=True) batch = self.wait(f"/api/local-images/jobs/{batch['id']}") return self.download(batch["images"][0]["url"], output) def mesh(self, image, *, engine="dgx", character_type="non_humanoid", description="", target_faces=5000, texture_size=1024): if engine == "dgx": self.api("POST", "/api/local-images/prepare-3d", timeout=360) caps = self.capabilities() if engine == "dgx" and not caps.get("dgx_ready"): raise RuntimeError("DGX Hunyuan3D 未就绪") if engine == "hunyuan" and not caps.get("hunyuan_logged_in"): raise RuntimeError("当前Session尚未完成混元微信登录") image = Path(image) mime = mimetypes.guess_type(image.name)[0] or "application/octet-stream" with image.open("rb") as handle: job = self.api("POST", "/api/jobs", timeout=420, data={ "engine": engine, "mode": "image", "character_type": character_type, "prompt": description, "resolution": 256, "source_faces": 50000, "target_faces": target_faces, "texture_size": texture_size, "output_format": "fbx", }, files={"image": (image.name, handle, mime)}) print("MESH_JOB_ID", job["id"], flush=True) return self.wait(f"/api/jobs/{job['id']}") def rig(self, job_id, *, character_type, template="auto"): caps = self.capabilities()["dgx"] required = ["mia_ready"] if character_type == "humanoid" else ["tokenrig_ready"] if character_type == "quadruped": required += ["animer35_ready", "animal_templates_ready"] if not all(caps.get(key) for key in required): raise RuntimeError({key: caps.get(key) for key in required}) self.api("POST", f"/api/jobs/{job_id}/rig", data={ "character_type": character_type, "skeleton_template": template, }) job = self.wait(f"/api/jobs/{job_id}") if not (job.get("ready_for_motion") and job["result"]["dgx"].get("rigged")): raise RuntimeError(job) return job def animate(self, job_id, prompt, *, seed=42, cfg_scale=5, in_place=True): current = self.api("GET", f"/api/jobs/{job_id}") key = "kimodo_ready" if current["character_type"] == "humanoid" else "unimate_ready" if not self.capabilities()["dgx"].get(key): raise RuntimeError(f"动作能力未就绪:{key}") self.api("POST", f"/api/jobs/{job_id}/animate", data={ "motion_prompt": prompt, "seed": seed, "cfg_scale": cfg_scale, "in_place": str(in_place).lower(), }) job = self.wait(f"/api/jobs/{job_id}") if not job["result"]["dgx"].get("animated"): raise RuntimeError(job) return job def save(self, job, directory): folder = Path(directory) folder.mkdir(parents=True, exist_ok=True) (folder / "job.json").write_text( json.dumps(job, ensure_ascii=False, indent=2), encoding="utf-8" ) for key, url in job.get("downloads", {}).items(): suffix = ".fbx" if "fbx" in key else ".glb" if "glb" in key else ".bin" self.download(url, folder / (key + suffix)) if job.get("anim_preview_url"): self.download(job["anim_preview_url"], folder / "skinned_animation.glb") # client = LLM3DClient() # ref = client.reference("完整橘猫,四足自然站姿,四腿分离,尾巴完整,纯白背景") # mesh = client.mesh(ref, character_type="quadruped", description="orange cat") # rig = client.rig(mesh["id"], character_type="quadruped", template="Cat") # client.save(rig, "export/cat/character") # walk = client.animate(mesh["id"], "An object walks forward at a steady pace.") # client.save(walk, "export/cat/walk") # 下一次animate前立即归档。 ``` | 目标 | mesh/rig.character_type | rig.template | motion_prompt | |---|---|---|---| | 人形 | humanoid | auto(固定人形分支) | A person walks forward at a steady pace. | | 鹿 | quadruped | Raindeer 或 Deer,按需求选 | An object runs forward at a steady gallop. | | 鹰隼 | non_humanoid | Eagle | An object flies forward while repeatedly flapping its wings. | | 鲨鱼 | non_humanoid | Jaws | An object swims fast. | | 道具 | static | 不调用 rig | 不调用 animate,直接 save(mesh, ...) | 已有用户图片直接 mesh("reference.png", ...),不生成新图;网页网格用 engine="hunyuan" 且先让同一 Session 完成扫码,后续三步相同。 ## 10. 排错与部署 | 现象 | 处理 | |---|---| | 401 混元未登录 | 当前 Session 扫码并保留 Cookie,不能依赖另一浏览器登录 | | 422 仅支持图片 | mode=image;文字先生成参考图 | | 422 静物不绑骨/不生成动作 | 静物只下网格;确为角色时 rig 显式改分类 | | 422 请先绑骨 | 先 rig;网格完成不代表已蒙皮 | | 422 请先完成混元低模 | 缺少 model_ex.glb,先重出合适目标的低模 | | 422 未知模板或类别不匹配 | 从目录取真实 ID;四足分支选择四足模板 | | 503 MIA/TokenRig/Kimodo/UniMate | 对应阶段能力未就绪,检查该字段,不盲目重复创建 | | 409 图片/DGX 冲突或任务处理中 | 等已有阶段结束,不绕过调度器或终止别的项目任务 | | 429 队列满 | 退避,继续查询已有任务 ID | | completed 但 error 非空 | 软失败、旧资产保留,本次不能算成功 | | 动画只有骨架 | 非人形角色用 rigged_*,完整效果用 anim_preview_url | | 蒙皮与骨架预览不同 | 检查绑定姿态、层级、骨长、权重,不把 raw canonical 动作当正式输出 | | 鸟竖直飞或鱼朝向异常 | 检查模板、朝向和躯干俯仰,in_place 不修复姿态 | | 动作慢/步态不对/循环跳帧 | 选择候选、长片段选周期、检查内部续接和接缝 | | duration/loop/model 不生效 | 非正式字段,需维护方扩展,不能宣称已经执行 | | 下载 404 | 阶段未完成或该格式未生成,检查 downloads 和 error | | 浏览器跨域 POST 403 | 服务有 Origin 限制;通过接入项目服务端代理调用 | 跨项目网页推荐:浏览器 → 该项目后端 → LLM3DGraphics。当前 POST Origin 校验只允许自身地址和本机 Vite 开发源;不要伪造 Origin 绕过校验。服务端/curl 通常不发送浏览器 Origin,仍受网络、Host 和队列限制,网页混元需同一服务端 Session 持有 Cookie。 迁移 Windows 主机: 1. 探测运行在**启动后端的 Windows 账号**下;复制工程不复制 SSH 身份。配置该账号的 DGXA 别名、授权密钥和 known_hosts,确认 `ssh -o BatchMode=yes DGXA "echo READY"` 成功。私钥不放工程。 2. 从 LLM3DGraphics 目录运行只读自检:`.venv\Scripts\python.exe deploy\check_dgx_connection.py --output outputs\dgx_connection_check.json`,不推理、不打印密钥。 3. 当前 Blender **5.2**:`C:\Program Files\Blender Foundation\Blender 5.2\blender.exe`;通过 BLENDER_PATH 或 config.json.blender_path 配置。自动化用 blender.exe,不用 blender-launcher.exe。 4. 当前 DGXA:Hunyuan HTTP `http://192.168.132.46:7860`,MIA `/home/spark/Make-It-Animatable`,TokenRig `/home/spark/SkinTokens`,UniMate 推理 `/home/spark/UniMate-v3`;旧 `/home/spark/UniMate` 还保留 I/O 环境。以部署配置为准,调用方无需自行安装。 5. 项目内 `.venv\Scripts\python.exe launcher.py` 启动,默认 0.0.0.0:8008,避免重复 worker;已有任务运行时不要重启刷新配置。 Hunyuan HTTP 绿灯而其他 SSH 分支红灯,优先查 Windows 账号、别名、认证、路由与主机指纹;HTTP 可达不表示 SSH 可达,绿灯也不等于执行过 GPU 推理。 ## 11. 给其他项目 Agent 的调用规则 ```text 通过配置的BASE调用LLM3DGraphics,先查capabilities;图片能力查local-images/status。 只访问本机API,不直连DGXA worker、SSH或上游,不索取或传播Key。 已有图片复用,只有外貌文字时先本地图像model=turbo/profile=concept,下载选中PNG。 两种建模engine均只支持mode=image;每次显式选dgx或hunyuan,实际默认是hunyuan。 网页混元使用同一Cookie会话扫码,完成低模后也能接rig和animate。 角色必须POST /api/jobs→等网格→POST jobs/id/rig→等绑骨成功→POST jobs/id/animate。 网格阶段ready_for_motion=false正常;completed仍检查error,旧文件不等于本次成功。 人形humanoid:MIA65骨→Kimodo;四足quadruped:SMAL标记+物种模板→TokenRig→UniMate v3。 鸟、鱼和其他生物non_humanoid;已知模板在rig显式指定,如Eagle、Jaws。 Cat43骨、Raindeer35骨、Deer41骨;不改官方ID,不统一所有动物为35骨。 static只生成网格;不调用rig和animate。 UniMate用英文An object ...,只描述动作;seed/CFG/in_place仅作用于UniMate,不作用于Kimodo。 正式animate没有时长、loop、速度或模型切换参数;长片段和闭环需维护方扩展。 每段动作完成立即归档后再生成下一段;非人形角色rigged_*,纯动作animated_*,完整预览anim_preview_url。 FBX键可能返回ZIP,按响应和文件头命名、解压并保留贴图。 已有模型的男女/65/49/41/25骨选项属于api/skinning,其task ID不能用于jobs/id/animate。 轮询至少2秒,遵守资源调度;prepare-3d不创建任务,409等待,429退避。 ``` ## 12. 实现依据与相关说明 | 内容 | 当前实现 | |---|---| | 创建、状态、绑骨、动作、下载 | [app.py](./LLM3DGraphics/backend/app.py) | | 网格与低模流水线 | [pipeline.py](./LLM3DGraphics/backend/pipeline.py)、[hunyuan_studio.py](./LLM3DGraphics/backend/hunyuan_studio.py) | | 角色类型分支 | [rig.py](./LLM3DGraphics/backend/rig.py)、[animate.py](./LLM3DGraphics/backend/animate.py) | | 动物模板与适配 | [animal_templates.py](./LLM3DGraphics/backend/animal_templates.py)、[quadruped_rig.py](./LLM3DGraphics/backend/quadruped_rig.py)、[avian_rig.py](./LLM3DGraphics/backend/avian_rig.py)、[marine_rig.py](./LLM3DGraphics/backend/marine_rig.py) | | 动作参数 | [unimate_runner.py](./LLM3DGraphics/backend/unimate_runner.py)、[kimodo_runner.py](./LLM3DGraphics/backend/kimodo_runner.py) | | 导入人形与动画库 | [skinning.py](./LLM3DGraphics/backend/skinning.py)、[manifest.json](./LLM3DGraphics/assets/Anim/manifest.json) | | 本地图像、资源与 Unity 导出 | [本地图像说明](./LLM3DGraphics/docs/local-image-generation.md) | | SSH 部署诊断 | [check_dgx_connection.py](./LLM3DGraphics/deploy/check_dgx_connection.py) | | 深层实现与旧阶段说明 | [DGX3D生成说明.md](./DGX3D生成说明.md),如与本文冲突以当前代码/OpenAPI为准 | 变更时同步更新参数表、类型映射、下载内容和客户端,不只在文首追加覆盖条款而留下矛盾正文;带Key的运维文件不分发给调用方。