从 $6.22 到 $113.96:拆解 AI 用纯 Python 在 Blender 5.2 中“手搓”百万面 3D 场景的工程真相

7 阅读10分钟

零外部模型、零外部图片贴图,同一模型(Claude Fable 5.2)用纯 Python 脚本在 Blender 5.2 中从零生成 3 个 3D 场景。
当投入成本从 $6.22 拉满到 $113.96(18 倍),代码从 1,439 行膨胀到 8,740 行,总三角面数从 12.6 万跃升至 102.8 万——这背后到底发生了什么?

最近 GitHub 上的开源项目 realcodebear/blender-fable-5.2 做了一组非常有意思的对照实验:让同一个 AI 模型在 Medium Effort(v1 快速初稿) 和 Max Effort(v2 极限投入) 两种模式下,纯粹通过编写 Python 脚本驱动 Blender 5.2 构建三个复杂度递增的 3D 场景(台灯静物、低多边形浮空岛、雨夜霓虹街道),并将最终成果烘焙导出到 Three.js 网页端进行实时 3D 交互对比。

我把这个仓库拉到本地完整拆解了一遍它的 Python 建场景脚本、GLB 烘焙管线、Three.js 预览器以及 Token 账单日志。这篇文章不聊虚的,直接从代码架构、视觉反馈闭环、WebGL 导出管线和 Token 成本结构四个维度,还原 AI 是如何“手搓”复杂 3D 工程的。


一、实验全貌:v1 与 v2 的硬核数据对比

项目规定了一个极其苛刻的前提:不允许下载任何外部 .glb/.fbx/.obj 模型,也不允许加载任何外部 PNG/JPG 贴图。所有的几何体、木纹/布料/水洼材质、灯光、相机甚至霓虹灯牌和书脊上的文字,都必须由 Python 代码在 Blender 里现场生成。

两个版本的执行方式完全不同:

  • v1(快速初稿): 单次会话、通过 Blender MCP(mcp__blender__*)连接本地 Blender GUI,一次性生成全部 3 个场景并简单微调 3 个小瑕疵。总计 29 次响应、53 次工具调用,耗时约 21 分钟。
  • v2(极限投入): 主会话拆出 3 个独立子 Agent,每个 Agent 死磕一个场景,禁止使用共享 GUI MCP,全部改用无头命令行渲染并反复读图自检。总计 284 次响应、344 次工具调用,累计工作约 219 分钟。

最终产出的量化对比如下:

场景版本脚本行数.blend 体积网页 GLB三角面数材质数烘焙图集
01 台灯静物v1153 (+332 公共库)0.23 MB0.44 MB19,076172048 px
01 台灯静物v22,0412.38 MB2.06 MB182,492244096 px
02 浮空岛v13860.36 MB0.52 MB13,020232048 px
02 浮空岛v22,7431.34 MB3.16 MB50,312114096 px
03 霓虹城市v15680.95 MB7.04 MB94,350854096 px
03 霓虹城市v23,9567.50 MB11.07 MB795,3101914096 px

从总三角面数来看,v2(1,028,114 面)是 v1(126,446 面)的 8.1 倍;而从代码量来看,v2(8,740 行)是 v1(1,439 行)的 6.07 倍。

下面直观对比三个场景在 v1(快速初稿)与 v2(极限投入)下的 Cycles 最终渲染效果:

01 台灯静物(v1 vs v2):

Scene 1 v1:台灯静物(快速初稿)

Scene 1 v2:台灯静物(极限投入)

02 低多边形浮空岛(v1 vs v2):

Scene 2 v1:低多边形浮空岛(快速初稿)

Scene 2 v2:低多边形浮空岛(极限投入)

03 雨夜霓虹街道(v1 vs v2):

Scene 3 v1:雨夜霓虹街道(快速初稿)

Scene 3 v2:雨夜霓虹街道(极限投入)


二、AI 为什么能用纯代码“造”出复杂 3D 世界?

很多人好奇:大模型没有鼠标也没有视口交互,怎么保证几十万个三角面不穿模、不浮空?拆开 scripts/v2/ 的源码后,会发现 AI 并没有去硬调用模仿人类点鼠标的 bpy.ops,而是走了一条高度工程化的程序化建模路线。

1. 放弃共享 GUI MCP,转向无头 CLI 确定性重建

在 v2 的系统提示词中,主会话明确禁止三个 Agent 调用 mcp__blender__*,因为三个 Agent 并发操作同一个 Blender GUI 会互相覆盖状态。取而代之的是无头命令行:

/Applications/Blender.app/Contents/MacOS/Blender -b --factory-startup -P your_script.py -- [options]

每次运行都从 --factory-startup 空白文件起步。由于出厂默认设置不会开启 GPU 加速,v2 脚本都在头部显式配置了 Apple Metal:

bpy.context.preferences.addons['cycles'].preferences.compute_device_type = 'METAL'
# 遍历启用所有设备后设置:
scene.cycles.device = 'GPU'

同时,为了防止三个并行 Agent 同时跑 Cycles 渲染把本机显存打爆,它们在命令行里约定了一个极其朴素但好用的原子目录锁:

while ! mkdir blender-scenes/v2/.render.lock 2>/dev/null; do /bin/sleep 3; done
<blender render command>
rmdir blender-scenes/v2/.render.lock

2. 自建 Shader Node DSL 与多材质网格构建器

在最复杂的 scripts/v2/scene3_neon_city_v2.py(3,956 行)中,模型没有满足于基础 API,而是直接在脚本顶部给自己封装了一套生产力基础设施:

  • NT(Shader Node DSL): 把 Blender 繁琐的节点创建与连线封装成链式调用,每个方法直接返回输出 NodeSocket,用几十行 Python 就能写出带噪波扰动、凹凸和粗糙度变化的复杂程序化材质(比如雨夜柏油路面的水洼反光、霓虹玻璃反射)。
  • MB(Multi-material Mesh Builder): 彻底绕开依赖上下文的 bpy.ops.mesh.*,直接用顶点坐标、面索引与 bmesh 批量组装建筑立面、门窗框、自动贩卖机、带雨伞的行人和车辆,并精确分配多材质槽位。
  • stroke font(三维笔画字体): 霓虹灯牌上的文字不是贴图,而是直接用空间折线坐标生成实体灯管曲线并赋予高强度自发光,再转为 Mesh。

3. 细节密度决定真实感

以 Scene 1(台灯静物) 为例,v1 的台灯就是“圆柱+圆锥”,书本是三个长方体;而 v2(2,041 行)补充了大量物理与叙事细节:

  • 台灯增加了关节铰链、开关、电源线与灯头内壁反射;
  • 书本做出了书脊凸起、书页边缘厚度,并调用系统字体生成立体书名网格;
  • 桌面木纹做了方向旋转,并在桌沿单独处理了**端面木纹(end grain)**与一个旧杯印磨损痕迹;
  • 盆栽叶片与橙子开启了次表面散射(SSS),马克杯上方甚至加了体积蒸汽,配合浅景深(DOF)和 1024 采样 Cycles 渲染。

三、真正的质变密码:AI 给自己写了“质检工具”

如果让大模型盲写 3,956 行 3D 坐标代码,哪怕它数学再好,一次性跑出来的画面也大概率惨不忍睹。

v2 之所以质量突飞猛进,最核心的秘密在于**“低成本渲染 → 多模态读图 → 代码修 Bug”的闭环**。查看三个 v2 脚本的命令行参数,你会发现 AI 给自己造了一整套 QA 调试工具:

  • --preview / --res 50 --samples 48:半分辨率、低采样快速出图,几秒钟看一眼效果,避免每次等 1080p 高采样浪费时间;
  • --cam NAME:内置十几个质检机位(比如 Scene 1 里有 top / lamp / head / books / mug / plant / fruit / front / edge / side / low,Scene 3 里有 main / left / right / car / people / rail / tower / top),专门用来凑近检查有没有穿模、浮空;
  • --crop x0,y0,x1,y1:只渲染画面中某一个局部方块,用来反复打磨单个道具的材质或光影;
  • --debuglight(Scene 2):切到平灰灯光模式,剥离颜色干扰,专门检查低多边形山体和瀑布的剪影结构。

统计数据印证了这一点:v1 每个场景只看图检查了 2~3 次;而 v2 的三个 Agent 各自执行了 107~127 次工具调用,绝大多数都是在重复“局部渲染 → Read PNG 读图批判 → 改脚本参数”。


四、从 Cycles 离线渲染到 Three.js 网页 3D 预览

.blend 文件里的程序化节点树、体积雾和路径追踪光照在浏览器 WebGL 里是跑不起来的。为了让访客能在网页上直接 3D 旋转查看 6 个版本,项目中的 tools/export_glb.py(500 行)和 viewer/viewer.js(260 行)实现了一套非常干净的转换管线:

1. 离线烘焙与极限压缩(tools/export_glb.py)

  1. 坐标系与元数据同步: 将 Blender 的 Z-up 转换为 glTF 的 Y-up,把渲染相机机位、视场角(vfov)、焦点距离以及所有光源参数写入 <key>.json。
  2. 天空全景烘焙: 临时新建 sky_tmp 场景,用等距柱状全景相机(EQUIRECTANGULAR)以 8 采样渲染 2048×1024 的天空贴图 <key>_sky.jpg。
  3. 程序化材质烘焙为共享图集(Texture Atlas): 保留纯色简单材质;把所有带程序化节点树的材质重新打包 UV,在 Cycles 下分别烘焙 DIFFUSE、EMISSION(按 99.5% 分位数动态计算 HDR 缩放系数 K)和 ROUGHNESS,合成一张 2048px 或 4096px 纹理图集。
  4. 剔除体积 & 合并 Draco 导出: 丢弃雾效和蒸汽体积对象,文字/曲线转 Mesh 后合并为单个对象减少 Draw Call,最后以 WebP(质量 86)+ Draco(位置 16-bit、法线 10-bit、UV 12-bit) 导出。接近 80 万面的 S3_NeonCity_v2 最终只有 11.07 MB。

2. Three.js 实时近似与性能优化(viewer/viewer.js)

  • 按需懒加载与视口休眠: 只有点击按钮时才动态加载 GLB;结合 IntersectionObserver 和 OrbitControls 的 change 事件控制渲染标志位,画面静止时不空转 GPU。
  • 竖屏视场角自适应(fitFov): Blender 的视角是按 16:9 横屏设计的,在手机竖屏上会自动换算 FOV 保持水平视野不被裁切。
  • 效果近似补偿:
    • 用 EffectComposer + UnrealBloomPass 模拟霓虹灯牌泛光,并通过 LOOK 配置表按场景微调曝光与 AgX / Filmic 色调映射;
    • 过滤掉 energy > 1e5 的探照灯(原本只靠体积雾显现),移动端自动把最大实时光源数限制在 6 盏以内;
    • 针对雨夜街道,在路面高度 y = 0.02 处叠加一个 Reflector 平面配合自定义 WetShader(利用菲涅尔项 pow(1.0 - view.y, 3.0) 与 AdditiveBlending)来近似湿滑路面的倒影。

五、账单解剖:1.29 亿 Token 到底烧在了哪里?

这是整个项目最反直觉、也最具工程参考价值的部分。按 Claude Fable 5.2 API 牌价(输出 $40/M、输入 $8/M、缓存写入 $10/M、缓存重读 $0.20/M)估算,v1 与 v2 的消耗明细如下:

运行任务响应次数工具调用输出 Tokens新输入(写缓存)缓存重读 Tokens工作耗时估算成本
v1 · 全部三个场景295371,461156,7904,273,744~21 min$6.22
v2 · 台灯静物76107303,7341,254,11028,469,106~53 min$30.38
v2 · 浮空岛96110401,1601,524,38444,557,879~66 min$40.20
v2 · 霓虹城市112127468,4701,337,44456,347,897~100 min$43.38
v2 三 Agent 合计2843441,173,3644,115,938129,374,882~219 min$113.96

算上主会话派发任务和验收的 $4.88,v2 总计 $118.84。

从这组数字里可以提炼出三个非常现实的结论:

  1. 模型“写代码”只占 40% 的钱,60% 的钱花在“读历史”:
    v2 三个 Agent 输出了 117 万 Token(约 $46.93),但重读了高达 1.29 亿 Token 的历史上下文(占总 Token 量的 96% 以上)。在 76~112 轮的漫长迭代中,每走一步都要把几千行单文件代码、终端日志和之前看过的渲染图重新过一遍,上下文呈 O(N2)O(N^2) 膨胀。
  2. 越复杂的场景,视觉闭环收益越大:
    三个场景中,霓虹城市(Scene 3) 的迭代轮次最多(112 轮)、成本最高($43.38),但也是 v1 到 v2 跨度最震撼的一个——它靠代码生生搭出了一整条包含拉面馆、24 小时便利店、街机厅、咖啡馆、行人与车流的赛博雨夜街区。
  3. 单文件过大是双刃剑:
    v2 选择了“单文件自包含”,虽然极大降低了跨文件依赖出错率,但也导致每次读写局部代码时,上下文负担越来越重。

六、总结:给 AI 图形 / 3D 工程的三条实战启示

blender-fable-5.2 证明了一件事:AI 做复杂 3D 场景已经跨过了“能不能做”的门槛,现在的核心命题是“如何设计反馈闭环与控制上下文成本”。

  1. 优先用“无头 CLI + 确定性脚本”替代 GUI 交互: 可重复运行、可局部裁剪预览(--crop)、可切换调试相机(--cam),是 AI 稳定产出的前提。
  2. 让 AI 自己造 QA 工具: 与其指望模型一次算对空间坐标,不如让它先写好低采样预览、灰模灯光模式和自动化几何检查,通过多模态视觉闭环自我纠偏。
  3. 警惕长程 Agent 的上下文膨胀: 当迭代超过 50 轮时,缓存写入与重读成本会迅速反超输出成本;合理拆分模块化文件、及时清理过期预览图上下文,是把 $40/场景压低到 $10 以内的关键优化方向。

项目地址: github.com/realcodebea…
在线 3D 对比页: realcodebear.github.io/blender-fab…