emergence-blender-bpy
Blender 3D 自动化控制与建模渲染技能。使用 Python 脚本进行程序化建模、材质、灯光与 Cycles/EEVEE 渲染,并支持 3D 打印 STL 导出。由涌现科学出品。
Installation
Documentation
Technical Manifest
Blender Python 自动化控制与设计 (bpy) — v2.2.0
何时使用此技能
当用户或 Agent 需要通过代码来执行以下操作时,调用此技能:
- 程序化网格建模:生成参数化 3D 物体(例如齿轮、规律结构、建筑构件、分形雪花)。
- 着色器节点材质:使用节点连接来创建精细的材质纹理(金属、拉丝纹理、透明玻璃、磨砂冰晶)。
- 影棚三点布光与摄像机设置:搭建三点灯光,配置摄像机并对其进行动态朝向约束。
- 命令行照片级渲染:无头模式下批量渲染高分辨率静止帧或序列帧。
- Blender 工作流批处理:导入/导出资产并执行自动化几何清理。
- 3D 打印导出:应用 Solidify 与 Decimate 等修改器,并导出适合切片的 watertight (紧密闭合) STL 模型。
前提条件
# 安装 Blender 4.0 或更高版本
apt-get install blender # Linux (Debian/Ubuntu)
# 或: brew install blender # macOS
# 验证安装
blender --version
核心开发模式
1. 无头(后台)命令行运行
在没有图形界面的服务器或后台任务中执行脚本:
blender --background --python script.py
2. 场景初始化与清理
在新建任何模型前,清空场景中所有默认的物体、相机、灯光和旧材质:
import bpy
# 选择所有物体并删除
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
# 清理残留的材质数据块
for mat in list(bpy.data.materials):
bpy.data.materials.remove(mat)
3. 程序化齿轮网格创建
通过选定角度上的面进行挤出(Extrude)和缩放,程序化创建规则齿轮:
import bpy
import math
def create_gear(name, radius=2.0, teeth=16, thickness=0.8):
"""创建带轴孔的齿轮网格"""
# 添加基础圆柱体
bpy.ops.mesh.primitive_cylinder_add(
vertices=teeth * 4,
radius=radius,
depth=thickness,
location=(0, 0, 0)
)
gear = bpy.context.object
gear.name = name
# 进入编辑模式以选择特定齿顶面进行挤出
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='DESELECT')
# 根据顶点夹角计算并选中齿的位置
for v in gear.data.vertices:
angle = math.atan2(v.co.y, v.co.x)
tooth_angle = 2 * math.pi / teeth
angle_diff = abs((angle % tooth_angle) - tooth_angle / 2)
if angle_diff < tooth_angle * 0.35:
v.select = True
# 挤出并向外缩放生成齿牙
bpy.ops.mesh.extrude_region_move(
TRANSFORM_OT_translate={"value": (0, 0, 0)}
)
bpy.ops.transform.resize(
value=((radius + 0.4) / radius,) * 2 + (1,),
orient_type='GLOBAL'
)
bpy.ops.object.mode_set(mode='OBJECT')
# 使用布尔(Boolean)修改器开凿中心轴孔
bpy.ops.mesh.primitive_cylinder_add(
vertices=32, radius=0.5,
depth=thickness * 1.5, location=(0, 0, 0)
)
cutter = bpy.context.object
bool_mod = gear.modifiers.new(name="Hole", type='BOOLEAN')
bool_mod.operation = 'DIFFERENCE'
bool_mod.object = cutter
bpy.context.view_layer.objects.active = gear
gear.select_set(True)
bpy.ops.object.modifier_apply(modifier="Hole")
bpy.data.objects.remove(cutter, do_unlink=True)
# 追加倒角 (Bevel) 和表面细分 (Subdivision) 修改器以圆滑边缘
bevel = gear.modifiers.new(name="Bevel", type='BEVEL')
bevel.width = 0.05
bevel.segments = 2
bevel.limit_method = 'ANGLE'
subdiv = gear.modifiers.new(name="Subdivision", type='SUBSURF')
subdiv.levels = 1
subdiv.render_levels = 2
return gear
4. 程序化金属材质(节点着色器)
使用着色器节点树动态生成带拉丝纹理的金属材质:
def create_metal_material(name, base_color, metallic=0.85, roughness=0.25,
noise_scale=30.0, use_brushed=True):
"""创建带噪波拉丝金属质感材质"""
mat = bpy.data.materials.new(name=name)
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
nodes.clear()
# 材质输出节点
output = nodes.new(type='ShaderNodeOutputMaterial')
output.location = (400, 0)
# Principled BSDF(原理化 BSDF)节点
bsdf = nodes.new(type='ShaderNodeBsdfPrincipled')
bsdf.location = (0, 0)
bsdf.inputs['Base Color'].default_value = base_color
bsdf.inputs['Metallic'].default_value = metallic
bsdf.inputs['Roughness'].default_value = roughness
# 加入拉丝纹理噪波
if use_brushed:
tex_coord = nodes.new(type='ShaderNodeTexCoord')
tex_coord.location = (-400, 100)
noise = nodes.new(type='ShaderNodeTexNoise')
noise.location = (-200, 0)
noise.inputs['Scale'].default_value = noise_scale
noise.inputs['Detail'].default_value = 2.0
ramp = nodes.new(type='ShaderNodeValToRGB')
ramp.location = (0, 100)
ramp.color_ramp.elements[0].color = (
base_color[0]*0.8, base_color[1]*0.8, base_color[2]*0.8, 1.0)
ramp.color_ramp.elements[1].color = (
base_color[0]*1.1, base_color[1]*1.1, base_color[2]*1.1, 1.0)
links.new(tex_coord.outputs['Object'], noise.inputs['Vector'])
links.new(noise.outputs['Fac'], ramp.inputs['Fac'])
links.new(ramp.outputs['Color'], bsdf.inputs['Base Color'])
links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])
return mat
5. 三点影棚光源设置
通过主光(Key Light)、辅光(Fill Light)和轮廓光(Rim Light)突出主体的反射与物理轮廓:
def setup_lighting(base_intensity=600):
"""标准的影棚三点光源搭建"""
# 主光源(前侧方,强度最大,决定阴影方向)
key = bpy.ops.object.light_add(
type='AREA', location=(5, -4, 6),
rotation=(0.8, 0, 0.7))
key = bpy.context.object
key.data.energy = base_intensity
key.data.size = 4
# 辅助光源(另一侧前侧方,较弱,用以照亮阴影)
fill = bpy.ops.object.light_add(
type='AREA', location=(-4, 3, 3),
rotation=(0.5, 0, -1.0))
fill = bpy.context.object
fill.data.energy = base_intensity * 0.5
fill.data.size = 3
# 轮廓/背光(主体后上方,用来勾勒轮廓发光边)
rim = bpy.ops.object.light_add(
type='AREA', location=(0, 5, 5),
rotation=(0.5, 0, 1.57))
rim = bpy.context.object
rim.data.energy = base_intensity * 0.4
rim.data.size = 2
6. 摄像机设置与动态 Target 朝向追踪
根据目标位置,通过四元数数学公式自动调整摄像机俯仰和偏航角,对齐拍摄中心:
import mathutils
def setup_camera(location=(5.5, -4.5, 3.5), target=(0, 0, 0)):
bpy.ops.object.camera_add(location=location)
cam = bpy.context.object
# 根据朝向向量计算四元数轨道,自动调整旋转角
direction = mathutils.Vector(target) - cam.location
rot_quat = direction.to_track_quat('-Z', 'Y')
cam.rotation_euler = rot_quat.to_euler()
bpy.context.scene.camera = cam
return cam
7. Cycles & EEVEE 图像渲染与 Mac Metal GPU 加速
配置底层物理渲染引擎属性,并对 macOS 上的 Apple Silicon 芯片进行 Metal 加速优化:
def render(output_path="/tmp/render.png", engine='CYCLES',
width=1080, height=1080, samples=64):
scene = bpy.context.scene
scene.render.engine = engine
scene.render.resolution_x = width
scene.render.resolution_y = height
scene.render.filepath = output_path
scene.render.image_settings.file_format = 'PNG'
if engine == 'CYCLES':
scene.cycles.samples = samples
scene.cycles.use_denoising = True
# 激活 macOS Metal GPU 并检测可用核心
try:
bpy.context.preferences.addons['cycles'].preferences.compute_device_type = 'METAL'
bpy.context.preferences.addons['cycles'].preferences.get_devices()
for d in bpy.context.preferences.addons['cycles'].preferences.devices:
if d.type == 'METAL':
d.use = True
except Exception:
pass
bpy.ops.render.render(write_still=True)
8. 折射玻璃/冰晶节点材质(Transmission/IOR)
创建支持高物理精确度的折射玻璃和冰雪材质(通过 Transmission 穿透属性):
def create_glass_material(name="Glass", color=(0.9, 0.95, 1.0, 1.0), roughness=0.1, ior=1.309):
"""创建物理折射玻璃或冰晶材质。IOR 折射率:1.309(冰),1.5(玻璃)"""
mat = bpy.data.materials.new(name=name)
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
nodes.clear()
bsdf = nodes.new(type='ShaderNodeBsdfPrincipled')
output = nodes.new(type='ShaderNodeOutputMaterial')
links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])
bsdf.inputs['Base Color'].default_value = color
bsdf.inputs['Roughness'].default_value = roughness
bsdf.inputs['IOR'].default_value = ior
# 启用光线穿透 (Transmission Weight)
if 'Transmission Weight' in bsdf.inputs:
bsdf.inputs['Transmission Weight'].default_value = 1.0
elif 'Transmission' in bsdf.inputs:
bsdf.inputs['Transmission'].default_value = 1.0
return mat
9. 进阶布尔建模(逐一雕刻与多余溢出边界)
在网格上用其他复杂几何体(如管道、环线)雕刻缝隙时:
- 避免一次性整体剪切:如果一次性差集减去一个自身有重叠的复杂网格(例如相互交叉的细管),会导致 EXACT 布尔求解器异常,把整个主网格完全删掉(网格顶点数变为 0)。应该对多条环线进行逐一应用布尔削减。
- 边界必须多余溢出:切割体(Cutter)在切出缝隙时,尺寸边缘一定要超出被切割物的边界外(即更大、更深),切面重合会导致浮点数精度重合,进而导致 EXACT 求解器求解失效而无法切出缝隙。
- 端点加盖:在将样条曲线(Curves)转为网格切割前,必须声明
curve_data.use_fill_caps = True对管道端点加盖,结合细分修改器(Subdivision)可以在端部刻出圆滑过渡。
10. 100% 程序化纹理空间着色器缝隙(免网格切割)
对于球体等规则几何体,为避免切割引起的网格破损和拓扑混乱,优先推荐直接在着色器节点(Shader Nodes)中用物体空间坐标(Object Coordinates)数学公式计算并贴图混合出接缝:
- 篮球缝隙线计算几何公式: $$|x| = \sqrt{1 - z^2} \cos\left(\theta \sqrt{1 - z^2}\right)$$
- 贴图映射与渐变蒙版:将以上距离公式输出映射至
SMOOTHERSTEP差值节点,设定宽度范围得到平滑渐变蒙版。用此蒙版分别去混合基础底色(橘红与黑色接缝)和高度图输入(送入 Bump 节点产生法线凹陷)。
11. 3D 打印准备与闭合流形导出(Solidify与Decimate修改器)
为 3D 打印(如 FDM/SLA)建模时:
- 设定物理比例尺:显式指定系统单位 scale,一般以 1 个 Blender 单位代表 1 cm 或 1 mm:
scene = bpy.context.scene scene.unit_settings.system = 'METRIC' scene.unit_settings.scale_length = 0.01 # 1个单位 = 1 cm - 外壳固化与壁厚:3D 打印无法打印厚度为 0 的数学面片。使用
Solidify修改器赋予壁厚(例如 FDM 打印机最低要求 1.2mm 至 2.0mm 壁厚):solid = obj.modifiers.new(name="Solidify", type='SOLIDIFY') solid.thickness = 0.12 # 如果 1 个单位 = 1 cm,这里代表 1.2 mm 壁厚 solid.offset = -1.0 # 向内收缩固化 - 多边形面数精简:顶点数过高会导致切片软件卡死。在导出前,必须使用
Decimate修改器按百分比削减多边形面数:decimate = obj.modifiers.new(name="Decimate", type='DECIMATE') decimate.ratio = 0.15 # 仅保留原本 15% 的面 - Watertight STL 导出:确保网格闭合流形后,使用标准命令导出 STL:
# Blender 4.0+ bpy.ops.wm.stl_export(filepath="model.stl", export_selected=True) # 旧版本 Blender: # bpy.ops.export_mesh.stl(filepath="model.stl", use_selection=True)
12. 程序化导入三视图背景参考图
若要根据角色原画或三视图进行对齐手工程序建模,可以通过脚本将前视、侧视、顶视图片载入为空背景图(Empty Image Object):
def load_reference_image(filepath, name="ReferenceImage", location=(0, 0, 0), rotation=(1.5708, 0, 0)):
# 添加一个 IMAGE 类型的 Empty 空物体
bpy.ops.object.empty_add(type='IMAGE', location=location, rotation=rotation)
empty = bpy.context.object
empty.name = name
# 加载图片并将其分配给空物体
try:
img = bpy.data.images.load(filepath)
empty.data = img
empty.empty_display_size = 5.0
empty.use_empty_image_alpha = True
empty.empty_image_depth = 'BACK' # 保证参考图显示在模型后面
except Exception as e:
print(f"载入参考图失败: {e}")
渲染引擎对比选择
| 引擎 | 适用场景 | 说明 |
|---|---|---|
BLENDER_EEVEE | 快速预览、实时动画 | 渲染耗时只需 <1 秒,不支持物理折射,适合几何框架的快速核验。 |
CYCLES | 照片级真实感渲染 | 折射玻璃、冰晶、反射重合等的唯一选择。建议配置 samples=64 并开启降噪 (Denoising)。 |
常见问题与排错排坑
- 布尔差集操作后整个模型消失(顶点数 vertices 变为 0)
- 原由:切割体(Cutter)存在自相交,或者切割面的边界和被切模型表面刚好完全重合(Co-incident surface)。
- 解决:检查切割体的网格自交,将布尔解算器(Solver)从
'EXACT'切换至'FLOAT'(旧版叫'FAST'),或者微调切割体位置使其完全溢出重叠。
- 样条曲线 (Curve) 转化为网格后变为了空心细管
- 原由:曲线数据在转换前未进行端点加盖。
- 解决:在运行 convert 转化前,将曲线数据的加盖参数置为真:
curve_data.use_fill_caps = True。
- 透明玻璃或冰晶材质渲染出来一片漆黑或扁平
- 原由:折射材质非常依赖背景光照与反光的面。
- 解决:必须使用 Cycles 渲染器。在模型下方放置一底面板(如 Roughness=0.2, Metallic=0.9 的暗色反射面),并在模型正后上方设置一高强度的轮廓区域背光(Rim Light),光线穿透折射后边缘才会闪闪发光。
- 脚本在不同 Blender 版本之间运行时报错
- 原由:Blender 版本的 bpy API 时常有破坏性更新(例如材质
use_nodes弃用、STL 导出指令修改)。 - 解决:在脚本中优先加入 Python
try-except或版本属性检测(如bpy.app.version)来做兼容适配。
- 原由:Blender 版本的 bpy API 时常有破坏性更新(例如材质
Proof of Verifiability
This skill has been analyzed and verified by the Emergence Science clearinghouse. It adheres to the Emergence A2A Protocol for deterministic agent execution and secure data handling.