在实际 3D 场景制作流程里,最耗时的往往不是创意,而是把创意翻译成 Blender 操作。重复建模、调材质、摆灯光、设相机,这些步骤会占掉大量时间。如果能把自然语言需求交给 DeepSeek 做场景拆解,再让 Codex 这类终端编程 Agent 按照固定 skill 生成并执行 Blender Python 脚本,整个建模过程就可以变成一条可复用的自动化链路。下面以“天宫仙境”风格的悬浮宫殿场景为例,说明从环境准备、skill 设计、脚本生成到渲染验证的完整过程。
这篇文章不会停留在“AI 很厉害”这种层面,而是会给出可以直接落地的目录结构、配置文件、Python 脚本、运行命令和排错路径。你不需要一次学会所有 Blender API,只需要理解这套“自然语言 -> 结构化方案 -> Blender 脚本 -> 渲染验证”的协作方式,然后把它套用到自己的场景上。
1. 先看清 DeepSeek、Codex 与 Blender 之间的分工
1.1 为什么直接写 Blender 脚本不够
Blender 本身提供了完整的 Python API,核心模块叫bpy。通过bpy,几乎所有的建模、材质、灯光、相机、渲染设置都能用代码完成。一个最简单的例子是:
import bpy bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 1))这段代码会在世界坐标(0, 0, 1)处创建一个边长为 2 的立方体。把这类操作组合起来,就能生成复杂的场景。
但直接写脚本有两个明显问题。
第一个问题是脚本太死板。你写了一个“宫殿生成脚本”,它可能只能生成固定数量、固定位置的柱子。一旦需求变成“把宫殿抬高一点”“屋顶改金色”或“增加 12 根柱子”,你就要改代码,甚至改好几处参数。
第二个问题是模型生成的脚本不一定可靠。直接让大模型写一段长脚本,它可能给你生成一个看起来合理但无法运行的脚本。原因通常是:使用了不存在的bpy.ops参数、忘记设置渲染路径、在系统 Python 里直接import bpy、或者依赖了当前选中的对象但没有提前选中。
所以,真正要解决的并不是“能不能用 AI 生成代码”,而是“如何让 AI 生成代码时遵守足够多的工程约束”。这正是 skill 的作用。
1.2 DeepSeek 负责“想”,Codex 负责“做”
在这条链路里,DeepSeek 和 Codex 不是两个独立运行的程序,而是同一个 Agent 工作流中的不同角色。
你可以把 Codex 理解为终端里的编程代理,它负责读取任务、生成代码、执行命令、看到报错后继续修改。而 DeepSeek 是 Codex 背后的模型提供方,负责理解用户需求、把需求拆成步骤、写出可以运行的 Blender Python 脚本,并在执行失败时根据错误信息修正代码。
整体流程可以表示成下面这条路径:
用户自然语言需求 -> DeepSeek 进行场景拆解和脚本规划 -> Codex 生成 Blender Python 脚本 -> Codex 调用 blender -b -P 执行脚本 -> 根据日志和报错修正脚本 -> 最终输出 .blend 文件和渲染图片为什么需要这种分工?因为“天宫仙境”这种需求太模糊。直接让 AI 写一行bpy.ops.mesh.primitive_cube_add不难,难的是把这个需求拆成“浮岛、宫殿、柱廊、云海、主光、补光、相机、渲染参数”这些可执行模块。
DeepSeek 这类 LLM 很擅长做这种拆解。它能理解“东方奇幻风格”“傍晚光线”“悬浮宫殿”这些语义,然后输出一套结构化的建模方案。Codex 则负责把这些方案变成真实的文件、真实的命令,并在当前电脑环境里跑起来。
1.3 skill 的作用:把“经验”变成“约束”
如果你用过 Agent,肯定遇到过这种场景:同一个问题换一种问法,输出质量就完全不同。原因是 Agent 没有稳定的“工作方法”。
skill 本质上是一组结构化材料,它告诉模型:
- 当前任务属于什么领域。
- 应该遵守哪些约束。
- 推荐使用哪些函数和写法。
- 执行完成后需要检查哪些事情。
对 Blender 建模来说,skill 里可以写清楚:单位使用米、物体名必须语义化、材质使用 Principled BSDF、脚本入口放在if __name__ == "__main__"、禁止在系统 Python 里导入bpy、渲染前必须确认相机和灯光对象存在。
有了这些约束,Codex 生成脚本时就不是“从零发挥”,而是“在一个专业建模助手的经验框架里发挥”。这也是 skill 与普通提示词最大的区别:普通提示词是临时的,skill 是可复用、可版本管理、可跨项目复制的。
2. 环境准备:从 Blender 到 Codex CLI 与 DeepSeek 模型
2.1 确认 Blender 环境和 Python API
先安装 Blender。建议使用 Blender 3.6 LTS 或 4.x 版本。安装完成后,在终端确认命令可用:
blender --version如果blender不是系统命令,你需要把 Blender 的可执行文件路径加入PATH,或者在后面的命令里使用完整路径。例如 macOS 上常见路径是:
export BLENDER_PATH="/Applications/Blender.app/Contents/MacOS/Blender"接着验证 Blender 自带的 Python 环境能否正确导入bpy:
blender -b --python-expr "import bpy; print(bpy.app.version_string)"这里的关键点在于-b,它表示 background mode,也就是不启动图形界面。Blender 在后台模式下可以执行 Python 脚本,并直接输出日志到终端。这是自动化控制 Blender 的基础。
执行后如果看到类似4.1.0的版本号,说明环境正常。
注意:不要用系统 Python 安装bpy包,也不要尝试在普通的python script.py里导入bpy。bpy只有在 Blender 自带的 Python 运行时里才可用。这也是后面最常见的报错来源。
2.2 安装 Codex CLI 并确认可执行文件位置
Codex CLI 是终端里的编程 Agent。常见安装方式是通过 npm 全局安装,具体以官方文档为准:
npm install -g @openai/codex codex --version安装完成后,确认codex能被执行:
which codex这里要注意一个高频问题:搜索结果里经常看到unable to locate the codex cli binary这类报错。它通常意味着某个 IDE 插件或工具在调用 Codex 时,找不到codex可执行文件。解决方式是手动指定路径:
export CODEX_CLI_PATH="$(which codex)"如果你使用的是 Windows,可以把该环境变量写入系统环境变量,或者在 IDE 的设置面板里指定codex二进制路径。
2.3 配置 DeepSeek 作为 Codex 的模型后端
Codex CLI 默认使用官方模型。要接入 DeepSeek,需要在配置文件中指定模型提供商。下面是一份常见参考配置,字段名和写法以你当前 Codex 版本文档为准:
# ~/.codex/config.toml model = "deepseek-chat" model_provider = "deepseek" [model_providers.deepseek] name = "DeepSeek" base_url = "https://api.deepseek.com/v1" env_key = "DEEPSEEK_API_KEY" wire_api = "chat"然后设置环境变量:
export DEEPSEEK_API_KEY="你的密钥"DeepSeek API 提供 OpenAI 兼容接口,deepseek-chat和deepseek-reasoner是常见模型名称,具体以 DeepSeek 官方文档为准。配置完成后,可以先用一个简单任务验证 Codex 能正常调用模型:
codex exec "请回复:ok"codex exec是否可用取决于版本。如果不支持非交互模式,也可以直接进入 Codex 交互终端,输入一句简短任务来测试。
2.4 建立统一工作目录
自动化建模过程中会产生脚本、临时.blend文件、最终.blend文件、渲染图片、日志等多个文件。建议先建立固定目录,避免 Agent 把文件散落得到处都是:
heaven_palace/ assets/ # 存放 .blend 文件、贴图、参考图 scripts/ # 存放 Codex 生成的 Python 脚本 output/ # 存放渲染结果 skills/ # 本地 skill 备份 logs/ # 脚本执行日志这个目录结构看起来简单,但它能解决一个很实际的问题:Codex 生成的脚本经常把输出文件写到//render/xxx.png这种相对路径,而相对路径到底相对于哪个目录,取决于当前激活的.blend文件。固定工作目录后,可以把路径写成绝对路径,或者统一约定//的含义。
3. 为 Codex 设计一套可复用的 Blender 建模 skill
3.1 skill 应该包含什么
在设计 skill 时,不要把它写成长篇大论的论文。一个 skill 应有的核心内容是:
- 任务目标。
- 适用环境。
- 必须遵守的约束。
- 推荐的代码结构。
- 完成后的检查清单。
对 Blender 建模场景来说,最重要的约束是:所有脚本必须由 Blender 自带 Python 执行,不能依赖系统 Python;物体命名要有语义;材质要使用节点;输出路径要明确。
3.2 创建一个 blender-modeling skill
在用户目录下创建一个 skill 目录。如果你的 Codex 版本支持自动发现 skill,通常可以放在~/.codex/skills下。如果不支持,也可以把SKILL.md里的内容复制到项目根目录的AGENTS.md,或者作为系统提示词的一部分。
~/.codex/skills/blender-modeling/ SKILL.md checklist.md templates/ scene_builder.py examples/ create_material.py render_settings.pySKILL.md是核心文件,内容可以这样设计:
# Blender Modeling Skill ## 目标 根据用户需求生成可执行的 Blender Python 脚本,并在 Blender 命令行环境下运行验证。 ## 环境 - 使用 Blender 自带的 Python 运行脚本。 - 禁止在系统 Python 中导入 bpy。 - 模型单位使用 Metric 和 Meters。 - 脚本入口统一放在 if __name__ == "__main__" 中。 ## 建模约束 - 创建对象后必须设置语义化名称,例如 FloatingIsland、PalaceHall、Cloud_001。 - 材质使用 Principled BSDF 节点。 - 不依赖当前选中的对象,所有对象通过名称或指针引用。 - 不直接将成品覆盖到最终路径,先生成临时文件。 ## 执行方式 blender -b 目标文件.blend -P 脚本.py ## 完成标准 - 场景中必须有相机。 - 场景中必须有至少一个光源。 - 渲染输出路径必须存在。 - 脚本执行不能出现未捕获异常。checklist.md是为了让模型在最后阶段做一次自查:
- [ ] 场景单位是否为 Metric - [ ] 所有物体是否有语义化名称 - [ ] 是否设置了相机 - [ ] 是否设置了光源 - [ ] 渲染输出路径是否在项目目录内 - [ ] 材质是否使用节点 - [ ] 是否在 background mode 下验证过3.3 skill 与普通提示词的区别
普通提示词也可以让模型遵守规则,但每次都要重复写一遍,而且写得越长,模型越容易忽略中间部分。skill 把规则固化到文件里,模型在执行相关任务时可以直接读取,效果比散落在对话里的提示词更稳定。
另一个好处是 skill 可以积累。你做完一个“天宫仙境”项目,把建模规范沉淀成 skill,下次做“海底龙宫”“未来城市”时,可以复用同一套约束,只需要改场景主题和资产清单。
4. 把“天宫仙境”拆成可执行的建模任务
4.1 从自然语言到模块表
直接让 Codex 执行“创建一个天宫仙境”往往不会得到好结果,因为这句话太抽象。我们需要把它拆成场景模块。最简单的做法是给 Codex 一段需求描述,并附上输出约束。
例如:
创建一座东方奇幻风格的浮空宫殿,坐标原点作为岛屿中心。 周围分布云海,时间是傍晚。 需要主光、补光、相机。 输出 1920x1080 PNG。 所有物体使用语义化名称。基于这个需求,DeepSeek 会把场景拆成下面这张模块表:
| 模块 | 关键对象 | 建模方式 | 说明 |
|---|---|---|---|
| 浮岛 | FloatingIsland_Top/Bottom | 圆柱 + 圆锥 | 表现悬浮山体结构 |
| 宫殿主体 | PalaceHall | 立方体缩放 | 作为中心建筑 |
| 屋顶 | PalaceRoof | 圆锥体 | 表现传统建筑屋顶 |
| 柱子 | PalacePillar_01 等 | 圆柱体 | 围绕宫殿主体 |
| 云海 | Cloud_001 等 | 多球体组合 | 分布在宫殿周围 |
| 主光 | SunLight | 太阳光 | 暖色傍晚光 |
| 补光 | RimLight | 区域光 | 冷色轮廓光 |
| 相机 | MainCamera | 透视相机 | 对准宫殿主体 |
4.2 让 DeepSeek 输出结构化方案
在 Codex 交互终端里,你可以给出这样的任务:
请使用 blender-modeling skill,完成一个“天宫仙境”场景。 需求: - 在原点创建浮空岛,底部有锥形山体。 - 浮岛上方创建宫殿主体,带一个圆锥屋顶。 - 宫殿周围放 4 根柱子。 - 在宫殿四周随机生成若干云朵球体。 - 设置暖色太阳光和冷色补光。 - 设置相机,从侧前方 45 度观察宫殿。 - 渲染分辨率 1920x1080,输出到 output/heaven_palace.png。 请先生成脚本,再执行验证,最后告诉我渲染结果文件路径。模型内部会先生成结构化方案。下面是一段示意性的 JSON,不一定直接输出到终端:
{ "scene": "heaven_palace", "center": [0, 0, 0], "modules": [ { "name": "FloatingIsland", "type": "mesh", "primitive": "cylinder", "params": { "radius": 8, "depth": 2, "location": [0, 0, -1] } }, { "name": "PalaceHall", "type": "mesh", "primitive": "cube", "params": { "scale": [6, 4, 2], "location": [0, 0, 4] } } ], "render": { "engine": "CYCLES", "samples": 128, "resolution_x": 1920, "resolution_y": 1080, "filepath": "output/heaven_palace.png" } }结构化方案的价值在于:Codex 生成脚本时不再需要重新思考“这个场景有什么”,而是按照模块列表逐个创建对象。即使后续要加柱子,也只需要在 JSON 的modules数组里增加一个元素。
4.3 用 Codex 按 skill 执行
如果你使用的 Codex 版本支持codex exec非交互模式,可以在终端直接运行:
codex exec "请参考 blender-modeling skill,创建天宫仙境场景,脚本输出到 scripts/build_heaven_palace.py,并渲染预览图到 output/"如果不支持非交互模式,就打开 Codex 交互终端,输入同样的任务。Codex 会读取 skill,生成脚本,调用 Blender 后台命令,然后根据输出结果判断是否继续修改。
这里要注意权限问题。Codex 需要执行 shell 命令才能调用 Blender,但不要给它无限制的系统权限。建议让它只允许在当前项目目录内写文件,不要执行sudo、rm -rf这类危险操作。
5. 核心脚本拆解:浮岛、宫殿、云海与渲染
5.1 初始化场景与收集对象
先写一个最小可运行的 Blender Python 脚本。Codex 生成的脚本可能更复杂,但结构通常是一致的。下面代码用于初始化场景、设置单位和创建集合:
import bpy import math def clear_scene(): bpy.ops.wm.read_factory_settings(use_empty=True) def setup_units(): scene = bpy.context.scene scene.unit_settings.system = 'METRIC' scene.unit_settings.scale_length = 1.0 def setup_collection(name): collection = bpy.data.collections.new(name) bpy.context.scene.collection.children.link(collection) return collection在背景模式下,bpy.ops.wm.read_factory_settings(use_empty=True)能清理默认场景,避免脚本重复运行时出现多余的立方体、相机和灯光。
5.2 生成浮岛和宫殿
浮岛可以用圆柱体表示顶部平台,用圆锥体表示底部山体。宫殿主体用立方体缩放,屋顶用圆锥体。为了便于管理物体,把对象统一放到指定集合里。
def cleanup_to_collection(obj, collection): for c in obj.users_collection: c.objects.unlink(obj) collection.objects.link(obj) def build_floating_island(collection): bpy.ops.mesh.primitive_cylinder_add( radius=8, depth=2, vertices=12, location=(0, 0, -1) ) top = bpy.context.active_object top.name = "FloatingIsland_Top" cleanup_to_collection(top, collection) bpy.ops.mesh.primitive_cone_add( vertices=8, radius1=6, radius2=0, depth=7, location=(0, 0, -6) ) bottom = bpy.context.active_object bottom.name = "FloatingIsland_Bottom" cleanup_to_collection(bottom, collection) def build_palace(collection): bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 4)) hall = bpy.context.active_object hall.name = "PalaceHall" hall.scale = (6,