# Blender 建模与可视反馈技能

<!-- autos.skill.namespace: autos.skills.blender -->
<!-- autos.skill.description: 在 Blender 中建模、检查场景、截图和渲染，并依据图像局部修正。 -->
<!-- autos.skill.version: 0.9.1 -->
<!-- autos.skill.minAutos: 4.13 -->

## 定位

复用已有的 bpy、bmesh、mathutils、修改器与节点知识。技能只提供连接、有限观察/差异、截图、固定构图预览、后台渲染和本机 API 检索，不封装整套 bpy，不引入 MCP、独立 Python、pip 或 RAG。已用两个真实产品（OAK/01 椅子、TERRA/02 陶瓷台灯）走通建模、材质灯光、保存母版、出图与 GLB 交付；不是所有资产类型、设备和 Web/AR 查看器的全面认证。

## 日常路径：只验证本次变化

1. **定向观察**：明确用途、尺寸和交付格式；inspect/eval 看目标，不默认清场。已知正常的环境不重复安装、GPU 诊断或三路截图；合格几何不为材质任务重做灰模。
2. **编辑**：部件命名清楚。产品脚本存成文件（`scripts/01_geometry.py`…），用 `c.exec(string.load(path),args)` 执行，可复现，也避开 aardio 字符串转义。改用户已有场景时只 snapshot 目标与需保护的周边，关键断言与修改放在同一次 c.exec，发现用户插入编辑就停；破坏性操作先另存。
3. **小图审查**：几何变化用 preview 并复用 framing；材质/灯光固定真实相机、曝光与色彩管理，用 renderBackground 出小图。产图后实际调用 analyze_image，只复审变化部分，合格就停。“发灰、不亮、改了没变化”先量化（亮度分位数、投影点采样、开/关对照）找根因再改参数，见 delivery.md。
4. **保存与正式出图**：保存可编辑母版（资源打包）。正式图用 `process.blender.execfile(path,{blendFile=母版;args})` 在独立后台加载母版，一次完成关键状态回读与渲染；renderBackground 的快照不等于已保存的文件。
5. **导出与交付**：产品与棚拍分离；GLB 导出、Validator、空场景独立导入对比可在同一个后台进程完成。交付模型、图像、简短检查摘要与剩余限制，不复制工具研发的全套报告。

工具回归与作品验收分开：改桥接、截图或索引实现才跑对应回归。OCR 回退、文件存在、像素采样都不算视觉审查；没有视觉接口时保持 visualVerified=false，交图给人工复核。

## 连接与执行

```aardio
import autos.skills.blender;
var b = autos.skills.blender;
var c,err,detail = b.connect("myproduct"); // 只连已有会话；新会话用 b.open({session="myproduct";factoryStartup=true})
if(!c) return {error=err;detail=detail};
var r,err,detail = c.exec(string.load("/myproduct/scripts/01_geometry.py"),{size=0.3});
c.close();
return r ? r.result : {error=err;detail=detail};
```

- open/connect 返回 process.blender 原生客户端（默认 session="autos"）。已有会话描述只重连，失败不自动重启；新场景显式用新 session，不注入用户的任意窗口。factoryStartup 不载入用户 startup 与欢迎页。
- `c.exec(code,args)` 在 Blender 主线程运行 Python：用 `args` 取参数，给 `result` 赋值返回 JSON；成功为 `{result,stdout,stderr,logs}`，失败为 `null,err,detail`（含 traceback）。`c.eval(expr)` 直接返回值。路径和数据只经 args 传，不拼进 Python 字面量。
- 日志每流默认只保留前 1 MiB（看 `logs.stdout/stderr.truncated`，第三参数可设 `{logLimitBytes}`），完整日志自己写文件；业务 result 不截断。
- **超时不是取消，出错不等于回滚；delivery="unknown" 时绝不重放**，先重连回读真实状态再决定。
- c.close 只断开；c.quit 不保存，只在用户要求或专用测试会话时退出。每次工具调用重新 connect，不跨调用传客户端或后台 job。

## 查 API：先查再写，报错先查再改

写陌生或版本敏感的 bpy 代码（节点插槽名、枚举值、算子参数、5.x 改名/弃用）前先查；遇到 AttributeError/KeyError/TypeError，先 apiDescribe 出错的路径再改，不凭记忆猜新名。熟悉且稳定的 API 不必逐个查。

```aardio
var hits = b.apiSearch("light linking receiver",{limit=10}); // results:[{path,kind,sig,summary}]
var r,err,detail = b.apiDescribe("ShaderNodeBsdfPrincipled"); // 插槽、enabledWhen、模式分组、manual 链接
if(!r) return {err=err;candidates=detail.candidates;typeMembers=detail.typeMembers};
```

- 首次查询自动在后台工厂进程构建本机索引（约 5–12 秒），之后离线查询；Blender 升级或索引格式变化时自动重建。
- 路径写法：`Object.location`、`bpy.data.objects.new`、`bpy.ops.mesh.primitive_cube_add.size`、`bmesh.ops.bevel`、`Principled BSDF`、`ShaderNodeMix.inputs['A']`、`RenderSettings.engine['CYCLES']`。sig 中的长枚举会截断（`... +29`），全部取值请 describe 该属性。
- 节点属性 sig 末尾的 `(nodes.new: "3D")` 是新建节点的实际初值，此时类级 default 不可信（如 Noise 维度、Compare 运算）。
- 传 `client=c` 只读查询当前会话：实际值、动态枚举、节点实际插槽、几何节点修改器输入（Socket_N 附接口名与值）、插件注册的类型/算子；不调用函数、不改场景。
- 插槽字符串键按名称查找（同名取当前启用者），identifier 不能当键。结果字段、局限与已实测易错点见 `.res/knowledge/api-docs.md`。

## 入口选择

| 目标 | 入口 / 要点 |
|---|---|
| 定向场景摘要 | inspect(c,{names/collection;limit})；默认 50、上限 200，看 truncated |
| 有限误改检查 | snapshot（显式 names/collection）+ 本地 diff；changed 为空不等于没误改，看 coverage 与 equalWithinCoverage |
| 隔离几何/固定前后图 | preview：临时正交相机 + Workbench 多视图拼图；返回 framing，下次原样传回即锁定构图 |
| 实际相机材质灯光（草图） | renderBackground + 同一调用内 finishRender：场景快照 + 独立后台进程 |
| 当前视口截图 | captureViewport method=offscreen/screen/opengl：需有效 UI，三选一，不自动回退 |
| 环境疑点 | diagnostics；refreshDevices=true 才枚举 Cycles 设备 |

- preview 返回 images/sheet，finishRender 返回 path，交给 analyze_image。checkImage 只是 16×16 采样提醒，不是视觉验收。
- 指定 outputDir 时结果写在其下的唯一子目录（如 `geometry-<uuid>\preview-<hash>\`），要复用的 framing 存到自己选定的固定路径；未指定时写入 `%LOCALAPPDATA%\aardio\autos\blender\images\`，不会自动清理。
- framing 锁定世界归一化、相机与分辨率，不锁模型、材质与引擎；越界照常出图并在 framingCheck 中报告，不自动重新取景。
- renderBackground 须有场景相机；engine/device 省略即继承，显式 GPU 后端只影响该后台进程；快照使用绝对资源路径、静音 File Output 节点，不打包、不烘焙。job 不跨调用传递，取消用 `job.process.terminate()`。
- 截图和 preview 占用 Blender 主线程，后台渲染也会争用 GPU，不承诺完全不卡界面。

## 后台脚本与纯 Python

```aardio
import process.blender;
var p,err = process.blender.execfile("/myproduct/scripts/05_final_render.py",{
    blendFile=io.fullpath("/myproduct/deliverables/model.blend"); args={outDir=io.fullpath("/myproduct/deliverables")}
});
if(!p) return err;
var out,stderr,exitCode = p.readAll(); // out 已含 stderr；脚本异常时 exitCode=1
```

- exec/execfile 默认工厂配置、禁止 .blend 内嵌自动脚本；`args` 在脚本里是全局变量（省略时为 None）。需要 GPU 时在脚本中设置 Cycles 偏好（只影响该进程）。
- `process.blender.python(arguments)` 调内置 python.exe 做 AST/JSON/文件处理（如解析 GLB），没有 bpy。

## 避坑

- 5.x 改名不靠记忆：EEVEE 为 `BLENDER_EEVEE`，`scene.node_tree` 改为 `scene.compositing_node_group`，`Material.use_nodes` 已弃用、新材质自带 Principled；其余先 apiDescribe。
- 灯具点亮：灯泡玻璃挡住内部点光的阴影射线时，灯罩只剩被 Cycles 默认 clamp 截断的间接光，加功率无效；用该灯的 blocker_collection 排除玻璃，见 delivery.md。
- 显卡不稳（preview/EEVEE 全黑、截图全透明）时显式降级到 Cycles CPU：`b.preview(c,{collection="X";engine="CYCLES";device="CPU";samples=24})`，`b.renderBackground(c,{engine="CYCLES";device="CPU";samples=32})`。
- 需要公开 HDRI、模型或材质时读 `.res/knowledge/polyhaven.md`：先选定资产 ID，以固定分辨率和 maxBytes 下载，路径经 args 交给 Blender，展示时标注来源 Poly Haven。

## 按需知识路由

以下路径相对 `~/lib/autos/skills/blender/`。通常只读当前任务需要的一篇。

| 任务 | 入口 |
|---|---|
| 材质、棚拍、灯具点亮与曝光诊断、母版保存、GLB 导出与分层验收 | `.res/knowledge/delivery.md` |
| API 检索结果字段、实时查询、局限、5.2 已实测易错点、索引答不了时的做法 | `.res/knowledge/api-docs.md` |
| snapshot 覆盖、前置条件、允许改动 | `.res/knowledge/state-and-diff.md`；只读范例 `.res/examples/SnapshotDiff.aardio` |
| framing token 与越界的详细契约 | `.res/knowledge/fixed-framing.md`；范例 `.res/examples/FixedFraming.aardio` |
| 截图机制、GPU 前提、官方依据 | `.res/knowledge/capture-and-hardware.md` |
| Poly Haven 资产下载与集成 | `.res/knowledge/polyhaven.md` |
| 函数全部参数与代码提示 | `lookup_library_reference("autos.skills.blender")`、`lookup_library_reference("process.blender")` |
| 纯 Python 与原生会话范例 | `~/examples/Process/Blender/Python.aardio`、`RPC.aardio` |
