把 Claude Code 式的 coding agent 搬进 Blender:一个 VLM 写 Blender Python 脚本 → 渲染 → 另一个 VLM 拿渲染图跟参考图比(可以自己挪相机、隐藏物体、跳关键帧去看)→ 写一段文字修改意见 → 再改脚本,循环直到像为止。相比 BlenderAlchemy 这类「迭代编辑」前作,多的是只读的观察工具和只保留最近 L 轮的滑动窗口上下文。完全 training-free。
get_better_assets 调外部 3D 生成模型拿到的 mesh,agent 负责的是摆放、材质、光照和相机。| 论文用词 | 在本文里具体是什么 |
|---|---|
| inverse graphics | 输入一张图,输出能在图形引擎里重新渲染出这张图的程序(这里是 Blender Python / python-pptx 脚本)。 |
| target \(s_g^*\) | 参考图片(照片 / 草图 / 风格迁移过的图 / 带箭头提示的图)+ 可选文字指令,例如「把篮球移到 goal state 的位置」。 |
| program \(p_t\) | 第 t 轮的完整 Blender Python 脚本:建/导入物体、设位置、材质、灯、相机。每轮都重写整份代码(同时附一个 diff)。 |
| state \(s_t\) | 执行 \(p_t\) 后的 Blender 场景及其渲染图。 |
| agent \(\mathcal{A}\) | 现成的 VLM(GPT-4o / GPT-5 / Claude-Sonnet-4 / Gemini-2.5-Pro / Qwen3-VL-8B),不训练,靠 system prompt + tool calls 驱动。实际分成两个会话:Generator(写代码)和 Verifier(只读检查)。 |
| skill library | 一组 JSON-schema 工具:execute_code、set_camera、investigate、get_scene_info、set_visibility、set_keyframe、get_better_assets、undo_action、end_process 等,包装底层 bpy 调用。 |
| feedback \(f_t\) | Verifier 输出的一段自然语言,例如「篮球卡进桌子里了,应该再高一点」。 |
| memory \(M_t\) | 聊天上下文里最近 L 轮的 (plan, code diff, 完整代码, 渲染图, feedback)。更早的直接丢掉。定性实验 L = 12。 |
| best-of-N | 每轮并行生成 N 个候选修改,用渲染图与目标图的 CLIP 相似度挑最好的进入下一轮。 |
| 4D | Blender 物理模拟(刚体、碎裂)+ 关键帧。Verifier 用 set_keyframe 跳到不同时间点看动画。 |
set_camera 换视角。
flowchart LR
T["Target image"] --> G["Generator VLM"]
M["Memory last L"] --> G
G -->|"execute_code"| B["Blender render"]
B --> V["Verifier VLM"]
T --> V
V -->|"set_camera, investigate"| B
V -->|"text feedback"| M
G -->|"plan + code"| M
G -->|"end_process"| E["Final program"]
Algorithm 1 每一轮分四个阶段:
end_process 就结束。execute_code 提交(含 thought、code_diff、完整 code 三个字段)。两个角色共用同一个 VLM,只是 system prompt 和可用工具不同:Generator 拿「修改型」工具,Verifier 只拿「只读型」工具。每次回复必须恰好一个 tool call 加一段推理。
直观理解:VLM 本身做不好「看图报坐标」,但很擅长「看两张图说哪里不一样、往哪个方向改」。VIGA 把精确数值的问题转化成一串「比较 + 方向」的判断,让渲染器去负责精确的投影。
Memory 是多模态轨迹(plan、代码、渲染图、反馈交替)。保留历史的作用是让 agent 能比较自己之前的尝试(0.8 太高、0.7 太低 → 取 0.75),否则会在两个错误状态之间来回振荡。但全部保留会导致 context rot:图片越堆越多,模型推理变差。所以只保留最近 L 轮。
作者论证截断是「无损」的:因为每轮的 \(p_t\) 是完整代码,本身就编码了之前所有成功的编辑;老的推理过程是多余的。(这个论证只对「场景状态」成立,对「哪些试过不行」这种负面经验并不无损。)
直接让 VLM 写裸 bpy 动作空间太大、容易幻觉 API。VIGA 把它包装成两类工具,观察和修改严格分开:
| 类别 | Skill | API | 作用 |
|---|---|---|---|
| State Observation(只读) | Spatial Navigation | set_camera, investigate | 换视角、绕开遮挡 |
| Temporal Inspection | set_keyframe | 跳到 4D 时间轴某一帧 | |
| Structural Query | get_scene_info, set_visibility | 查物体属性、临时隐藏物体 | |
| Diagnostic Rendering | initialize_view | 渲染标准视角、算目标 bounding box | |
| State Modification(写) | Asset Instantiation | init_scene, get_better_assets | 调外部模块(SAM-3D、Meshy、Tripo 一类)拿高质量 mesh |
| State Execution | execute_code | 执行完整脚本,持久修改场景 | |
| Trajectory Control | undo_action, end_process | 回滚 / 结束 |
{
"name": "execute_code",
"description": "Execute blender python code and trigger verifier evaluation. Returns either (1) on error: detailed error information; or (2) on success: a clear render (you must add a camera in your code) and further modification suggestions from a separate verifier agent.",
"parameters": {
"thought": "step-by-step reasoning about what code to write next",
"code_diff": "minimal line-level edits: '-: [lines to remove]' then '+: [lines to add]'",
"code": "the COMPLETE updated Blender Python code after applying code_diff"
}
}
要求同时输出 diff 和完整代码:diff 让模型先想清楚改哪几行,完整代码保证每轮的状态自包含(这也是滑动窗口能「无损」截断的前提)。
换执行引擎即可:Blender 做 3D 重建和编辑;Blender 物理引擎 + 关键帧做 4D(扔球撞水果、打碎镜子、地震);python-pptx 做幻灯片。Prompt 只改几个关键词(2D → 3D)。
init_scene 调 SAM-3D 拿 mesh,镜子由基本几何体程序化生成;agent 负责设质量、碰撞体、初速度等物理参数。物理正确性由 Blender 物理引擎保证,而不是生成模型。
让 VLM 只做它擅长的「比较两张图、说往哪改」,把精确投影交给渲染器、把状态交给完整代码、把观察交给可以随意挪相机的只读工具,然后循环。
| Model | Setting | T1 PL↓ | T1 N-CLIP↓ | T1 VLM↑ | T2 PL↓ | T2 N-CLIP↓ | T2 VLM↑ | T3 PL↓ | T3 N-CLIP↓ | T3 VLM↑ | Impr.% |
|---|---|---|---|---|---|---|---|---|---|---|---|
| GPT-4o | One-Shot | 48.16 | 64.17 | 0.58 | 7.36 | 7.12 | 2.75 | 30.14 | 38.69 | 0.25 | - |
| BlenderAlchemy bo1 | 10.62 | 17.68 | 1.75 | 6.13 | 7.71 | 3.08 | 19.60 | 26.10 | 0.67 | 67.56 | |
| VIGA bo1 | 8.56 | 18.19 | 1.44 | 5.11 | 4.33 | 3.58 | 14.51 | 17.91 | 1.53 | 113.96 | |
| BlenderAlchemy bo4 | 14.50 | 19.57 | 1.75 | 1.95 | 2.47 | 3.53 | 20.62 | 25.79 | 0.56 | 77.48 | |
| VIGA bo4 | 5.47 | 6.10 | 3.25 | 2.94 | 3.50 | 3.83 | 12.62 | 22.84 | 1.61 | 159.19 | |
| Qwen3-VL-8B | One-Shot | 60.82 | 78.16 | 0.28 | 33.14 | 37.51 | 1.61 | 22.81 | 22.98 | 1.25 | - |
| BlenderAlchemy bo1 | 25.61 | 37.93 | 0.36 | 10.69 | 13.92 | 2.64 | 30.45 | 23.91 | 0.64 | 27.36 | |
| VIGA bo1 | 10.54 | 17.27 | 1.31 | 3.85 | 3.34 | 3.33 | 11.35 | 6.86 | 2.25 | 112.79 | |
| BlenderAlchemy bo4 | 11.57 | 17.58 | 1.19 | 5.38 | 4.64 | 2.94 | 23.62 | 15.67 | 0.93 | 82.24 | |
| VIGA bo4 | 8.80 | 15.67 | 1.38 | 5.02 | 3.86 | 3.08 | 9.08 | 6.94 | 2.02 | 112.87 |
注意 GPT-4o 的 Task 2 best-of-4:BlenderAlchemy 的 PL / N-CLIP(1.95 / 2.47)明显好于 VIGA(2.94 / 3.50),VIGA 只在 VLM Score 上略胜。
| Benchmark | Model | One-Shot | VIGA | 说明 |
|---|---|---|---|---|
| BlenderGym | GPT-4o bo4 | Impr. 基准 | +48.86% | vs BlenderAlchemy bo4 的 +27.63%;material N-CLIP 12.20 → 7.63 |
| BlenderGym | Qwen3-8B bo4 | Impr. 基准 | +42.88% | vs BlenderAlchemy bo4 的 +23.23%;全设置平均 +35.32% |
| SlideBench | GPT-4o | 55.8 | 58.3 | Overall +4.5% |
| SlideBench | Claude-3.5 | 60.7 | 62.2 | Overall +2.5%;执行成功率反而 0.93 → 0.90 |
| SlideBench | Gemini-2.5 | 41.4 | 48.9 | Overall +18.1% |
| SlideBench | Qwen3-8B | 6.0 | 32.8 | Overall +446.7%;执行成功率 0.10 → 0.54,本质是「报错后能改代码」 |
SlideBench 宣传的「平均 +117.17%」几乎完全来自 Qwen 那一行(+446.7%);强模型上的增益只有 2.5–18%。对强模型而言,PPT 一次就能写对,迭代只修了少量执行错误。