← 返回 H3 记忆实验主页

让 GPT-6 Astra 写 Blender 脚本,把一帧画面搭成可交互的 3D 房间

写脚本 → 渲染 → 对照参考打分 → 修改,自动循环 10 轮;搭好的场景再当作“证据”喂给 SolarWM-H3

2026-10-07 · 场景 robot_bedroom(H3 生成视频的第 0 帧)· Blender 5.2.1,CPU 上跑 Cycles · H3 在 8 × AMD Instinct MI300X 上推理

后续:能改吗?能动吗?这个场景改材质、改朝向、一句话换家具,用来做视频编辑,以及把动态内容也建模 →
0.791物体轮廓重合度 IoU(10 个物体平均,对照 SAM 3 掩码)
3.8%深度误差(墙面背景 1.3%)
5 / 5自由摆放的物体通过 1 秒物理静置测试
10 轮约 $9.2 的 Astra 调用费,每轮 2–7 分钟

结论速览

参考帧 vs Blender 渲染(左右拖动对比)

H3 第 0 帧(参考) Blender 渲染
Blender(Astra 搭建)H3 第 0 帧(参考)

左:Astra 写脚本搭出的场景,用 H3 第 0 帧的相机内外参在 Blender 里渲染。右:H3 第 0 帧。两个机器人是动态角色,按要求不建模,位置留空。

参考帧、Blender 渲染与轮廓叠加
左:参考帧。中:Blender 渲染。右:轮廓叠加,绿色是 SAM 3 在参考帧里检测到的物体,红色是搭出的物体在同一相机下的轮廓。
窗 ×2:0.921、0.906被子:0.893门:0.886 床:0.876床头板:0.852枕头 ×2:0.713、0.492 床头柜 ×2:0.701、0.672

每个物体的 IoU。枕头大半被被子盖住、床头柜在阴影里,露出的部分小,轮廓最难对齐。

系统是怎么跑起来的

H3 第 0 帧→ 测量:DA3 深度、地面、SAM 3 物体 → 测量表→ GPT-6 Astra 写 Blender 脚本→ Blender 无界面搭建 + 渲染→ 打分:IoU、深度、悬空、物理→ 对比图 + 俯视图 + 指标反馈给 Astra,再改一轮 ↺
选最好的一轮→ 导出成世界资产(每个物体一个 + 房间外壳)→ 沿 H3 相机轨迹渲染→ 当证据注入 H3

模型负责什么

只返回一个完整的 Blender Python 脚本:房间外壳、窗、门、床、被子、枕头、床头柜、小物件、灯光和材质,全部程序化生成,不用外部模型和贴图。每个测得的物体按“编号_类别_部件”命名,并标注类别、能否移动和质量。模型不执行任何命令,也不碰相机和渲染设置。

驱动和评分负责什么

  • 相机:按 H3 的内参(fx = 678.8,fy = 689.6)和第 0 帧的位姿摆放,Cycles 渲染。
  • 评分:物体 IoU(对照 SAM 3 掩码);深度误差(对照 DA3 深度,尺度用机器人身高 1.75 m 校正,门高 1.98 m 交叉验证);悬空检查;PyBullet 1 秒静置测试(凸包碰撞体,被子这类软物体除外)。
  • 反馈:参考帧、物体编号图、测量表、上一轮脚本,加上报错信息,或者对比图、房间内视角、俯视图和指标。

借鉴的开源项目:OpenAI 没有开源这一整套流程,只有博客演示。循环结构参考了 Codex-and-Blender(模型写 bpy、渲染、看图再改);修改的先后顺序参考了 SEIG 的分阶段逆向图形:布局 → 比例 → 物理 → 材质 → 灯光。LL3M(单个资产的多智能体建模)留给以后重建单个资产;blender-mcp 需要让模型实时操控 Blender,没有采用。

10 轮是怎么一步步改出来的

10 轮迭代的渲染与指标
第一格是参考帧,其余是每一轮从参考相机渲染的结果。每格标了 Astra 思考用时、费用、IoU、深度误差和物理检查结果。点击看大图。
轮次IoU深度误差物理静置测试Astra 这一轮说要改什么(摘译)
首轮·1——脚本报错按测得的位置摆床、窗和床头柜;重建被掀起的条纹被子;补全被挡住的家具;机器人的位置留空。脚本读了测量表里没有的视场角字段,报错。
首轮·20.7444.2%2 个枕头会掉用参考值 89.4° 兜底,修好视场角报错;保留布局和被子褶皱。地板这时还是黑底白线。
首轮·30.7714.1%2 个枕头会掉修掉地板面重叠造成的黑色木条;抬高床架、加深软包颜色;被子褶皱更宽更软;床头柜收窄。
首轮·40.7654.0%2 个枕头会掉每个物体拆开单独标注;被子条纹随褶皱弯曲;木色和墙色加深,灯光压暗;窗框嵌进墙洞。
首轮·5——脚本报错细化枕头宽度、床单褶皱和床头柜抽屉比例。又读了视场角字段,这次没有兜底,报错。
续跑·10.7284.0%2 个枕头会掉从首轮第 4 轮接着改,改看新的房间内视角和俯视图。被子褶皱更饱满;床头柜收窄,前腿伸到参考图里的落地点。
续跑·20.7524.0%2 个枕头会掉去掉被子上的尖刺;褶皱更宽;枕头顶部压平;床头柜支撑和床单褶皱更柔和。
续跑·30.8283.8%右床头柜倾倒 81°重做床头柜轮廓、抽屉比例和落地腿;枕头按露出的小轮廓重塑;加深被子褶皱。
加物理检查·1 ✓0.7913.8%全部稳定重做床头柜腿的落地范围,防止倾倒,尤其是右边那个;细化枕头和被子中间的凹口。
加物理检查·20.8294.1%一个枕头翻倒 136°让枕头顶部露出被子;细化被子褶皱和条纹走向;门的高度对齐地面;加墙脚线。

物理一列是事后用同一套静置测试统一重算的;前 8 轮 Astra 看不到这项反馈。早期几轮的枕头压在被子上,而被子作为软物体不参与仿真,所以枕头会往下掉。最终选用“加物理检查·1”:IoU 不是最高,但所有能自由摆放的物体都站得住。选优规则是 IoU − 深度误差 − 0.05 ×(会倒或悬空的物体数)。两次报错都源于测量表不一致:给 Astra 看的表里有视场角,传进 Blender 的表里没有,已修复。

物理检查抓到了图像看不出的问题

修复前的右床头柜
续跑第 3 轮(IoU 0.828):两条腿交叉斜放,落地点偏到一侧,静置 1 秒倾斜 81°。
修复后的右床头柜
加物理检查后(IoU 0.791):四条腿回到柜体四角下方,静置 1 秒位移 0.16%,几乎不动。

这两张是用 Blender 从床头柜右前方低处拍的特写。从参考视角看,这里只是床边一小块阴影,IoU 和深度误差都发现不了,静置测试却一放就倒。收到“右床头柜 1 秒内移动 58%、倾斜 81°”的反馈后,Astra 改写了床头柜的腿,还在脚本里留了注释:

# The two front feet share a floor depth and span the full cabinet,
# rather than converging beneath its right-hand side.
foot_pixels = (left_foot_px, right_foot_px)
top_pixels = (x0+8, x1-8)
for side in range(2):
    top = from_image(top_pixels[side], bottom_py, fy)
    top.y = fy+0.028
    top.z = bottom_z+0.002
    foot = floor_from_image(foot_pixels[side], foot_py)
    leg(f"front_leg_{side}", tuple(foot), tuple(top),
        0.021, 0.032, night_wood, "nightstand", iid, 0.45)
    back_top = Vector((top.x+rear_shift, fy+depth-0.028, top.z))
    outward = -0.004 if side == 0 else 0.004
    back_bottom = Vector((back_top.x+outward, fy+depth-0.018, 0.0))
    leg(f"rear_leg_{side}", tuple(back_bottom), tuple(back_top),
        0.022, 0.032, night_wood, "nightstand", iid, 0.45)

注释意思是:“两条前腿落在同一深度,横跨整个柜体,而不是收拢到右侧下方。”整个脚本约 1300 行,都是 Astra 写的。

换个角度看这个场景

房间内视角
房间内视角:站在左墙边 2 米高处往右后方看,隐藏天花板。左边是门和左窗,床两侧是床头柜。
俯视平面图
俯视平面图(隐藏天花板,图上方是参考相机朝向):床靠后墙居中,两侧床头柜,门在左墙靠后的位置。
H3 生成视频(左)和 Blender 场景(右)沿同一条相机轨迹:镜头先左转 40°,转回正前方,再左转 55°、下俯 12°。H3 在左侧画出了参考帧里没有的敞开的门和植物(提示词里写了“白色镶板门”和“角落里的高大绿植”);Blender 场景只根据第 0 帧搭建,这一侧是 Astra 的推测(深色墙和门)。Blender 渲染每帧约 1 秒。
轨迹关键帧
关键帧:帧 0(正前方)、54(左转 40°)、120(正前方)、160(左转 13°)、198(左转 55°、下俯 12°)。点击看大图。

把场景当证据喂给 H3

做法沿用主页的资产证据:从第 68 帧起,每一段都把 Blender 在当前相机下渲染的画面用 H3 的 VAE 编码,作为干净的 K/V 追加进注意力。用种子 42 的换朝向回访轨迹,试了三种证据范围:

组别证据覆盖第二次访问(帧 198)机器人
基线无和第一次访问一致:按提示词画出的门和植物在
完整场景墙、地板、全部家具(约 100% 画面)照着 Blender 场景画成深色墙,第一次访问出现过的门和植物被抹掉第 68 帧后消失
只用家具不含墙和地板(约 50%)家具一致;空墙处画出圆镜、植物、长凳消失
静态结构不含被子和枕头,避开机器人区域(约 60–68%)墙面一致;机器人区域画出一盆植物消失
五格对比:H3 基线、Blender 场景、三种证据下的 H3。前 67 帧所有组完全相同(还没开始注入证据)。
第二次访问帧 198
第二次访问(帧 198,左转 55°、下俯 12°):完整场景证据下,H3 照着 Blender 场景画成深色墙,第一次访问时出现过的门和植物不见了;只用家具时,没有证据的空墙处又按提示词画出了镜子和植物。
帧 120 机器人消失
帧 120(正前方):三种证据下机器人都不见了,被子也变成了 Blender 里的形状。

主页的实验里,资产证据只覆盖几个小物体,机器人保留了下来;这次证据覆盖了画面的一半以上,H3 就整体切换成照着静态场景渲染。即使把机器人区域排除在证据外,机器人也没有回来,空出的区域被填上了别的东西。所以要把这样的完整场景当证据,必须先控制证据强度。

更正(2026-10-07 晚):本页最初写的是“基线凭空多画了一扇门和一盆植物,证据让回访一致”。复查 H3 的提示词后发现,提示词里写了“圆镜、木书桌和笔记本电脑、角落里的高大绿植、白色镶板门”,这些内容在第一次访问(第 54 帧)就已经出现,基线前后两次访问其实是一致的。Blender 场景只根据第 0 帧搭建,没有这些东西,所以用它当证据时,第二次访问反而和 H3 自己的第一次访问对不上。正确的做法是先把第一次访问新看到的内容补进场景(慢速环的工作,也可以让 Astra 参照第 54 帧补建),再给第二次访问当证据。

局限与下一步

  1. 场景要跟上视频。第一次访问新看到的内容(门、植物等)要先补进三维场景,再给回访当证据,否则证据会和视频自己的第一次访问冲突。
  2. 控制证据强度。只在回访的片段注入、只在前几层注入、给证据加噪声(不用完全干净的证据)、限制证据 token 的占比。目标是房间保持一致,同时机器人保留。
  3. 接入慢速环。用 Astra 搭的场景作为世界的初始资产,H3 新生成的物体再按准入规则加入。
  4. 重建被拒的资产。主页实验里 53 个候选物体只通过 5 个,主要卡在单视角网格过不了物理检查。可以用同样的“写脚本 → 渲染 → 对照”循环重建,物理检查直接复用。
  5. 泛化。目前只搭了一个场景、只试了一条纯旋转轨迹;这个场景约 10 轮、$9。还需要在更多参考图上统计平均要几轮、花多少钱。