← 返回文章
开发与编程

Opus 5.5 按错版本写 Blender Python 脚本,渲染图看不出问题

Codio 于 2026 年 10 月 11 日发布。文章介绍如何为 Opus 5.5 生成的 Blender 脚本锁定目标版本、在后台运行脚本并检查网格几何。

文章发布于 2026-11-15更新于 2026-11-15浏览 1

文章信息

公开署名

Codio(@0xCodio)

原文

阅读原文 ↗

文章正文

原文封面插图

让模型用 Blender 做 3D 时,有两个问题出在几何生成之外。训练数据跟不上 API 的变动,真正要紧的缺陷又会藏在渲染图里。

Change constants in the parameter block only. Do not restructure, do not
rename, do not add geometry.

Apply exactly: <discrepancy list>
Return a diff.

下文按这个顺序说明怎么解决。


01 · 两类故障

原文图 1

有一项基准测试覆盖了 30 项脚本任务、4 个 Blender 版本和 10 个模型。每个脚本都在无界面模式下运行,并通过对应任务的断言来判定结果。总共评估了 1,190 份答案。

脚本能跑起来的比例,在 Blender 3.6 上是 91%,在 5.0 上是 70%。脚本能运行且模型能正确指出 API 变动的比例,则从 81% 降到了 35%。

合成器、布尔求解器和序列编辑器这三类任务,所有受测模型都在 Blender 5.0 上得了零分。

最明显的例子是 EEVEE 的标识符。它最初叫 BLENDER_EEVEE,在 4.2 中改为 BLENDER_EEVEE_NEXT,到 5.0 又改回 BLENDER_EEVEE。学过那次改名的模型反而会失败,没学过的碰巧能跑通。

问题的根源是脚本瞄准了错误的 Blender 版本。

02 · 先锁定 Blender 版本

原文图 2

原文发表时,最新版本为 Blender 5.2.2 LTS,4.5 也仍在维护。Blender 5.1 将 Python 从 3.11 升到 3.13。每个 bpy wheel 包只对应一个 Python 版本,必须精确匹配。

先把版本写进提示词,再用实际运行验证。

blender --background --factory-startup \
        --python-exit-code 1 \
        --python build.py -- --out ./dist --mm 20

有两个细节,各自都能让人白费一天。省略 --python-exit-code 时,脚本抛出异常并打印回溯,Blender 的退出状态也不会改变,CI 就会误判通过。命令行参数的顺序也很重要。把 -o 放在 .blend 文件之后,否则加载文件时会覆盖输出路径。-- 之后的所有参数都会通过 sys.argv 传给 Python。

03 · 操作器面向界面,脚本直接操作数据

原文图 3

Blender 官方文档说明了这些限制。操作器要处理的数据由上下文提供,返回值仅表示操作是否完成。poll() 检查失败时会抛出 RuntimeError: ... context is incorrect。报错里没有具体原因,因为用来补充说明的机制目前很少使用。

在没有界面的后台运行中,每次调用 bpy.ops.mesh.* 都会检查网格编辑上下文,而此时这个上下文还没建立。

# what the model writes
bpy.ops.object.modifier_apply(modifier="Boolean")

# what survives a headless run
dg   = bpy.context.evaluated_depsgraph_get()
mesh = obj.evaluated_get(dg).to_mesh()

布尔求解器值得单独说,因为容易在默认设置上踩坑。文档说明,FLOAT 不支持重叠几何,MANIFOLD 只适用于流形网格。EXACT 可以应付不规整的输入。默认求解器处理生成的几何时出现这些情况,符合文档描述。

04 · 容易躲过审查的比例错误

大多数难以察觉的 100 倍尺寸偏差,都来自下面三处细节。

常见理解实际行为
primitive_cube_add() 会生成边长为 1 个单位的立方体默认边长为 2 个单位。size= 指的是整条边的长度
设置 unit_settings.scale_length = 0.001 就能按毫米建模此设置只改变显示,对几何、物理模拟和导出均无影响
导出器会保留单位STL 和 OBJ 文件没有单位信息。需传入 use_scene_unit 或 global_scale

glTF 是个例外,也是制定这套单位约定的原因。规范规定以米为单位,以 Y 轴为上方向,导出器也没有缩放选项。Blender 的单位会按 1:1 导出,一个 Blender 单位就成了 1 米。

所以,在脚本开头明确写下单位约定。

# 1 Blender unit = 1 metre. A 20 mm part is 0.02.
MM = 0.001

05 · 仅凭渲染图答不了的 11 个网格问题

原文图 4

非流形边在渲染图里可以毫无异样,零面积面、自相交、内部空腔和薄于喷嘴直径的壁也是如此。Blender 官方的 MCP 页面就记录过一个例子。一个实体化(Solidify)修改器让多边形数量翻倍,模型却没发现,因为修改器没有改变视口显示。

先测量,再看图。

import bmesh
bm = bmesh.new(); bm.from_mesh(obj.data)

nonmanifold = sum(1 for e in bm.edges if not e.is_manifold)
flipped     = sum(1 for e in bm.edges if e.is_manifold and not e.is_contiguous)
loose       = sum(1 for v in bm.verts if not v.link_faces)
degenerate  = sum(1 for f in bm.faces if f.calc_area() <= 1e-4)

from mathutils.bvhtree import BVHTree
tree = BVHTree.FromBMesh(bm, epsilon=1e-5)
selfhit = len(tree.overlap(tree))

assert (nonmanifold, flipped, loose, degenerate, selfhit) == (0, 0, 0, 0, 0)
assert abs(obj.dimensions.x - 0.020) < 1e-5     # it is the size you asked for

在 Blender 之外,可以用 trimesh 的 is_volume 判断网格是否构成实体。网格必须封闭,面片顶点的绕序必须一致,法线也必须朝外。单独检查 is_watertight 只能确认网格是否封闭。对于开放网格,算出来的体积没有意义。

3D-Print Toolbox 现在以社区扩展的方式提供,无界面流程需要单独安装。在 1 个 Blender 单位等于 1 米的前提下,它的默认阈值正好与常见的打印建议相符,最小壁厚为 1 毫米,悬垂角为 45°。

06 · 迭代流程及配套提示词

原文图 5

渲染负责生成多视角拼图,验收结论由检查结果给出。实用的做法是用 GPU 离屏绘制,把六张标明视角的图拼成一张,省去完整渲染。Cycles 在离屏视图中没有输出,因此用 EEVEE 或 Workbench。Blender 5.2 新增了 gpu.init(),用于在 --background 模式下初始化 GPU 后端。

系统提示词(SYSTEM)

Target Blender 5.2 LTS, Python 3.13. Do not use any API removed before it.

Rules:
- bpy.data and bmesh only; no bpy.ops.mesh.* and no bpy.ops.object.*
  except where I have named one
- one Blender unit is one metre; declare MM = 0.001 and use it
- every dimension, clearance and wall thickness is a named module-level
  constant, never inline
- boolean solver is EXACT unless I state the inputs are manifold
- end the script with the census asserts, not with a render
- after the code, list every API you used that changed in 4.0 or later
  and state which release you are targeting for each

生成(GENERATE)

Build the part described below as a single Python file.

<spec: outer dimensions, wall, clearances, holes, tolerance, export path>

Output the parameter block first as a table. I will assert against it.

评审(CRITIQUE)接收一张包含六个视角的拼图。

Six orthographic views of one part: front, back, left, right, top,
three-quarter. The census already passed, so do not comment on topology.

Judge only what geometry cannot: proportion against the stated dimensions,
whether features sit where the spec says, parts that read as floating or
fused, and anything present that the spec did not ask for.

List discrepancies against: <parameter table>

修补(PATCH)

Change constants in the parameter block only. Do not restructure, do not
rename, do not add geometry.

Apply exactly: <discrepancy list>
Return a diff.

MCP 方面,Blender 已推出官方的 Blender Lab MCP Server(插件 v1.0.3,支持 Blender 5.1 及以上)。官方页面说明,它会在没有保护机制的情况下在 Blender 中执行大型语言模型生成的代码,因此建议在虚拟机里运行。社区版服务器约有 2.87 万个星标,通过 localhost:9876 以明文 TCP 通信,没有身份认证或加密。它曾有一个路径遍历漏洞,已在 1.6.5 修复。两种服务器都应只在开发环境中使用。

07 · 生成网格、3D 打印参数与单轮成本

原文图 6

导入 3D 生成模型的输出时,应更严格地检查网格。到 2026 年,前沿模型已放宽对网格封闭性的要求。TRELLIS.2 就将非流形几何和开放曲面作为特性。这些特性适合服装和植被这类资产。用于 3D 打印的实体则需要先修复封闭性。主要的托管模型均未承诺输出封闭网格。

导入后,脚本先进行校验和网格检查,然后用 bmesh.ops(remove_doubles、recalc_face_normals、holes_fill、dissolve_degenerate)修复网格。接着校正比例和朝向,再执行布尔运算。需要可用的拓扑时,最后运行 quadriflow_remesh 或 voxel_remesh。

四份公开的打印指南,在几乎所有项目上给出的数值都相差两到三倍。四份指南共同给出的四项数值是 45° 悬垂角、最小孔径 Ø2.0 毫米、0.5 毫米装配间隙,以及约 1.0–1.2 毫米的最小壁厚。把这四项作为命名参数传入。其余有争议的数值也设为可传入的参数,并给出默认值。同一个写死的常量可能适合一台打印机,换一台就不合适。

Opus 5.5(每 100 万 token)
输入$4
输出$20
缓存命中$0.20
批处理输入 $2 · 输出 $10

一轮包含一个使用缓存的系统提示词块、一份规格说明、一次生成、一次后台运行、一次网格检查,以及一次按测量值修补。每轮花费几美分,Blender 运行只需几秒。

锁定版本与这 11 项断言,决定了看似完成的脚本能否真正生成所需零件。


感谢阅读。