41 KiB
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 捷径。
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. 能力探测与资源调度
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,表单请求:
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。失败批次可能已有图片,应保留结果与错误。
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/<IMAGE_JOB_ID>"
curl -fL -o shark.png "$BASE/api/local-images/jobs/<IMAGE_JOB_ID>/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,再调用:
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 示例
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 网页混元:微信会话和建模
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 会话也登录;每台设备、浏览器和无痕会话分别登录。
# 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 步:按类型绑骨
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 四足:解剖拟合 → 物种模板 → 固定骨架蒙皮
- 提取三维表面,在 DGXA 拟合 SMAL/AniMer 参考,得到头颈、躯干、四肢和尾部解剖标记。
- 将官方物种模板关节适配到标记和网格表面,保留骨名、父子关系与语义;长尾还参考真实模型尾部中心线。
- 指定适配后的骨架输入 TokenRig 计算蒙皮;内部处理共点分支与量化,导出恢复模板定义并校验绑定姿态。
- 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 套兼容模板
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 步:生成动作
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 获得相同结果:
- 内部
backend.unimate_runner.submit_unimate_job(duration_sec=...)支持长动作。超过 60 帧走官方 motion expansion、重叠 10 帧续生成,再裁至目标长度;由维护方纳入资源调度,不让外部项目直接 SSH 调 helper。 - 从稳定时段选完整步态/振翅/摆尾周期。四足按同一只脚、同相位选整周期,结合全部关节姿态和速度,避免左右脚交换的半周期。
- 检查内部续生成边界。已有实测约第 60/110/160 帧出现过跳变;具体以数据为准,首尾相同不能修复内部跳帧。
- 做局部四元数与速度连续修正、必要的周期平滑,保留身体/头/尾与竖直动态;不要冻结全身掩盖问题。变速用完整周期一致重采样。
- 比较接缝姿态、速度和加速度与内部帧,导出保留闭环关键帧;循环预览不重复计算末尾闭环帧。
- 前进位移另做 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 导入、男女、骨数
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 动作库
POST /api/skinning/animations
Content-Type: application/json
{
"gender": "male",
"bone_count": 41,
"animation_id": "male-walking",
"rig_task_id": "<SKINNING_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;示例末尾调用为注释,接入方选定参数后执行。
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 主机:
- 探测运行在启动后端的 Windows 账号下;复制工程不复制 SSH 身份。配置该账号的 DGXA 别名、授权密钥和 known_hosts,确认
ssh -o BatchMode=yes DGXA "echo READY"成功。私钥不放工程。 - 从 LLM3DGraphics 目录运行只读自检:
.venv\Scripts\python.exe deploy\check_dgx_connection.py --output outputs\dgx_connection_check.json,不推理、不打印密钥。 - 当前 Blender 5.2:
C:\Program Files\Blender Foundation\Blender 5.2\blender.exe;通过 BLENDER_PATH 或 config.json.blender_path 配置。自动化用 blender.exe,不用 blender-launcher.exe。 - 当前 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 环境。以部署配置为准,调用方无需自行安装。 - 项目内
.venv\Scripts\python.exe launcher.py启动,默认 0.0.0.0:8008,避免重复 worker;已有任务运行时不要重启刷新配置。
Hunyuan HTTP 绿灯而其他 SSH 分支红灯,优先查 Windows 账号、别名、认证、路由与主机指纹;HTTP 可达不表示 SSH 可达,绿灯也不等于执行过 GPU 推理。
11. 给其他项目 Agent 的调用规则
通过配置的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 |
| 网格与低模流水线 | pipeline.py、hunyuan_studio.py |
| 角色类型分支 | rig.py、animate.py |
| 动物模板与适配 | animal_templates.py、quadruped_rig.py、avian_rig.py、marine_rig.py |
| 动作参数 | unimate_runner.py、kimodo_runner.py |
| 导入人形与动画库 | skinning.py、manifest.json |
| 本地图像、资源与 Unity 导出 | 本地图像说明 |
| SSH 部署诊断 | check_dgx_connection.py |
| 深层实现与旧阶段说明 | DGX3D生成说明.md,如与本文冲突以当前代码/OpenAPI为准 |
变更时同步更新参数表、类型映射、下载内容和客户端,不只在文首追加覆盖条款而留下矛盾正文;带Key的运维文件不分发给调用方。