关于 blender-mcp
blender-mcp 是用于检查 Blender 场景并准备 GLTF 导出的工作流技能,指导用户进行 MCP 健康检查、场景与材质分析、无头命令行导出、优化和验证。
主要功能
- 01MCP 连接与工具健康检查
- 02场景层级和材质检查
- 03无头命令行 GLTF 导出指引
- 04纹理与网格优化流程
- 05资产及动画导出验证
适用场景
- 01诊断 Blender MCP 连接
- 02查找场景或材质导出问题
- 03准备适用于 Web 的 GLB 资产
- 04检查导出的纹理、网格和动画
MCP 相关
该技能指导使用 get_scene_info、execute_python 和 screenshot 等 Blender MCP 工具检查场景及连接状态。由于 MCP 服务导出 GLTF 会超时,导出应使用 Blender 无头命令行。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vladmdgolam/ agent-skills --skill blender-mcp安装后在 Claude Code 中输入/blender-mcp确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/asset-integrations.mderrors.mdtexture-optimization.md详细介绍
Blender MCP
工具选择
快速检查时,请使用结构化 MCP 工具(get_scene_info、screenshot)。
对于任何非简单任务,请使用 execute_python:遍历层级、提取材质、烘焙动画、批量操作。它可以完整访问 bpy API,避免工具架构限制。
导出 GLTF 时,请使用无头 CLI——MCP 服务会在导出操作期间超时。
健康检查(始终先执行)
get_scene_info— 确认连接正常(默认端口为 9876)- 使用
execute_python执行print("ok")— 确认 Python 可正常运行 screenshot— 确认视口捕获正常
如果 MCP 无响应,请检查 Blender MCP 插件是否已启用,以及套接字服务是否正在运行。
完整导出工作流
这是端到端的线性流程。请按顺序完成以下步骤,不要跳过。
步骤 1:健康检查
在执行其他操作前,确认 MCP 正常运行:
# In MCP tool call:
get_scene_info
execute_python: print("ok")
screenshot
如果任何步骤失败,请先停止并修复 MCP 连接。请参阅已知错误与解决方法。
步骤 2:检查场景
运行完整的层级提取,了解当前场景:
import bpy, json
def extract_hierarchy(obj, depth=0):
data = {
"name": obj.name,
"type": obj.type,
"location": list(obj.location),
"rotation": list(obj.rotation_euler),
"scale": list(obj.scale),
"visible": not obj.hide_viewport,
"children": [],
}
if obj.type == 'MESH' and obj.data:
data["vertices"] = len(obj.data.vertices)
data["faces"] = len(obj.data.polygons)
data["materials"] = [slot.material.name for slot in obj.material_slots if slot.material]
if obj.type == 'LIGHT':
data["light_type"] = obj.data.type
data["energy"] = obj.data.energy
data["color"] = list(obj.data.color)
for mod in obj.modifiers:
if mod.type == 'ARRAY':
data.setdefault("modifiers", []).append({
"type": "ARRAY",
"count": mod.count,
"offset_object": mod.offset_object.name if mod.offset_object else None,
})
for child in obj.children:
data["children"].append(extract_hierarchy(child, depth + 1))
return data
scene_data = {
"name": bpy.context.scene.name,
"fps": bpy.context.scene.render.fps,
"frame_start": bpy.context.scene.frame_start,
"frame_end": bpy.context.scene.frame_end,
"objects": [],
}
for obj in bpy.context.scene.objects:
if obj.parent is None:
scene_data["objects"].append(extract_hierarchy(obj))
print(json.dumps(scene_data, indent=2))
请留意:
- 阵列修改器(烘焙后会大幅增加文件体积,必须在运行时重新生成)
- 顶点数量较多的对象(可能导致导出缓慢或 GLB 文件过大)
- 可能需要或不需要导出的隐藏对象
- 缺少材质的对象(
material_slots为空)
步骤 3:验证材质
运行材质提取,在开始导出前发现可能造成信息丢失的设置:
import bpy, json
def extract_materials():
materials = []
for mat in bpy.data.materials:
if not mat.use_nodes:
continue
info = {"name": mat.name, "nodes": [], "warnings": []}
has_principled = False
for node in mat.node_tree.nodes:
node_data = {"type": node.type, "name": node.name}
if node.type == 'BSDF_PRINCIPLED':
has_principled = True
for inp in node.inputs:
if inp.is_linked:
node_data[inp.name] = "linked"
elif hasattr(inp, 'default_value'):
val = inp.default_value
try:
node_data[inp.name] = list(val)
except TypeError:
node_data[inp.name] = float(val)
if node.type == 'TEX_IMAGE' and node.image:
node_data["image"] = node.image.filepath
node_data["size"] = [node.image.size[0], node.image.size[1]]
if node.image.size[0] > 2048:
info["warnings"].append(f"Large texture: {node.image.filepath} ({node.image.size[0]}x{node.image.size[1]})")
if node.type in ('TEX_NOISE', 'TEX_VORONOI', 'TEX_WAVE', 'TEX_MUSGRAVE'):
info["warnings"].append(f"Procedural texture node '{node.name}' ({node.type}) will be LOST on GLTF export")
if node.type == 'VALTORGB': # Color Ramp
info["warnings"].append(f"Color Ramp '{node.name}' remapping will be LOST on GLTF export")
if not has_principled:
info["warnings"].append("No Principled BSDF found — export result unpredictable")
info["nodes"].append(node_data)
materials.append(info)
return materials
result = extract_materials()
for mat in result:
if mat["warnings"]:
print(f"WARN [{mat['name']}]: {'; '.join(mat['warnings'])}")
print(json.dumps(result, indent=2))
继续前请检查所有警告,并决定是现在烘焙程序纹理,还是在导出后于运行时修补材质。
步骤 4:通过无头 CLI 导出
MCP 服务无法处理 GLTF 导出(会超时)。请始终使用无头 CLI:
# Use 'blender' if it's on PATH, otherwise use the platform-specific path:
# macOS: /Applications/Blender.app/Contents/MacOS/Blender
# Windows: "C:\Program Files\Blender Foundation\Blender 4.x\blender.exe"
# Linux: /usr/bin/blender
blender \
--background "/path/to/scene.blend" \
--python-expr "
import bpy, os
export_path = '/path/to/output.glb'
os.makedirs(os.path.dirname(os.path.abspath(export_path)), exist_ok=True)
bpy.ops.export_scene.gltf(
filepath=export_path,
export_format='GLB',
export_apply=False,
export_animations=True,
export_nla_strips=True,
export_cameras=True,
export_lights=False,
export_draco_mesh_compression_enable=False,
)
size_mb = os.path.getsize(export_path) / 1024 / 1024
print(f'Export complete: {export_path} ({size_mb:.1f} MB)')
"
关键标志:
export_apply=False— 不要烘焙修改器(阵列修改器会将 1 MB 文件增大到 56 MB)export_draco_mesh_compression_enable=False— 稍后通过 gltf-transform 应用 Draco 压缩- 对可能包含空格的路径加引号
步骤 5:使用 gltf-transform 优化
成功导出后运行。请始终逐项执行,不要使用 optimize:
# 1. Inspect raw export first
npx @gltf-transform/cli inspect output.glb
# 2. Resize textures (max 1K for web/mobile)
npx @gltf-transform/cli resize output.glb resized.glb --width 1024 --height 1024
# 3. WebP compression (quality 90 preserves detail)
npx @gltf-transform/cli webp resized.glb webp.glb --quality 90
# 4. Draco mesh compression (LAST — irreversible)
npx @gltf-transform/cli draco webp.glb final.glb
# 5. Inspect final result
npx @gltf-transform/cli inspect final.glb
预期体积缩减:原始约 22 MB → WebP 约 3.7 MB → Draco 约 1 MB。详细指标请参阅 references/texture-optimization.md。
步骤 6:验证
发布前,请按照下方完整的导出后验证清单进行检查。
导出后验证清单
每次导出后,在交付 GLB 以供集成前,请验证以下内容:
- 文件大小合理 — 典型 Web 场景中,原始 GLB 小于 30 MB,优化后的 GLB 小于 5 MB。超过这些阈值的文件都应标记。
- 使用 gltf-transform CLI 检查 — 运行
npx @gltf-transform/cli inspect final.glb并检查:网格数量、纹理数量、纹理大小、动画数量、访问器大小。确认没有意外重复。 - 在 Babylon.js Sandbox 中进行视觉测试 — 将 GLB 拖放到 sandbox.babylonjs.com。验证:网格正确渲染、纹理正常显示、动画正常播放、没有黑色或粉色材质。
- 没有 Three.js 控制台错误 — 在最简 Three.js GLTFLoader 测试页面中加载,并检查浏览器控制台。常见错误:
THREE.GLTFLoader: Unknown extension、纹理文件缺失、Draco 版本不受支持。 - 抽查材质 — 选择 3–5 个材质,目视确认粗糙度、金属度和基础颜色正确。与 Blender 视口渲染进行比较。标记看起来扁平或过于闪亮的材质。
- 抽查动画 — 如果场景包含动画,请在 Babylon.js Sandbox 或 Three.js 中确认至少一个动画能正确播放。检查帧数是否符合预期。
- 已验证名称映射 — 如果运行时代码引用了网格名称,请确认经过 GLTF 导出转换后名称仍匹配(空格 → 下划线,移除点号)。参见关键规则 5。
- 没有缺失纹理 — 检查 Babylon.js Sandbox 的网络标签页。纹理文件没有 404 错误。所有纹理都应打包在 GLB 内。
示例
示例 1:导出带动画的角色绑定
**场景:**你有一个带骨架、3 个 NLA 动作(idle、walk、run)、PBR 纹理集以及通过父级关系附加武器的人形角色。你需要一个可用于 Three.js 场景的 Web 就绪 GLB。
步骤 1:健康检查和场景检查
# MCP tool calls
get_scene_info
execute_python: print("ok")
步骤 2:检查绑定
import bpy, json
# Check armature and NLA strips
for obj in bpy.data.objects:
if obj.type == 'ARMATURE':
print(f"Armature: {obj.name}")
if obj.animation_data:
print(f" Active action: {obj.animation_data.action.name if obj.animation_data.action else 'None'}")
for track in obj.animation_data.nla_tracks:
print(f" NLA track: {track.name}")
for strip in track.strips:
print(f" Strip: {strip.name}, frames {strip.frame_start}-{strip.frame_end}")
步骤 3:检查材质导出损失
运行上文的材质提取。对于角色,请留意:
- 程序化皮肤纹理节点(Noise → 颜色变化)——这些内容会丢失
- 面料粗糙度上的 Color Ramp——会丢失,粗糙度会显得平坦
- 决策:将程序化变化烘焙到图像纹理中,或在运行时修补粗糙度值
步骤 4:导出
blender \
--background "/path/to/character.blend" \
--python-expr "
import bpy, os, tempfile
export_dir = tempfile.gettempdir()
bpy.ops.export_scene.gltf(
filepath=os.path.join(export_dir, 'character.glb'),
export_format='GLB',
export_apply=False,
export_animations=True,
export_nla_strips=True,
export_cameras=False,
export_lights=False,
export_draco_mesh_compression_enable=False,
export_skins=True,
export_morph=True,
)
print('done:', os.path.getsize(os.path.join(export_dir, 'character.glb')) / 1024 / 1024, 'MB')
"
步骤 5:验证动画已导出
npx @gltf-transform/cli inspect character.glb | grep -i anim
预期输出:3 个动画(Idle、Walk、Run)。如果为 0,请检查 NLA 条带是否已静音,或轨道是否设置为独奏。
步骤 6:优化
npx @gltf-transform/cli resize character.glb char_resized.glb --width 1024 --height 1024
npx @gltf-transform/cli webp char_resized.glb char_webp.glb --quality 90
npx @gltf-transform/cli draco char_webp.glb character_final.glb
步骤 7:设置运行时动画(Three.js)
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
import * as THREE from 'three';
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/');
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load('/character_final.glb', (gltf) => {
const mixer = new THREE.AnimationMixer(gltf.scene);
const clips = gltf.animations; // [Idle, Walk, Run]
const idleAction = mixer.clipAction(clips.find(c => c.name === 'Idle'));
idleAction.play();
// Animate mixer in render loop: mixer.update(delta)
});
示例 2:调试材质导出损失(粗糙度显得平坦)
**场景:**导出后,金属面板材质在 Three.js 中显得均匀、平坦且闪亮。在 Blender 中,它通过 Noise Texture → Color Ramp → 粗糙度输入具有有趣的粗糙度变化。
步骤 1:在 Blender 中确认问题
import bpy, json
mat = bpy.data.materials.get("MetalPanel")
if mat and mat.use_nodes:
for node in mat.node_tree.nodes:
print(f"Node: {node.type} - {node.name}")
for inp in node.inputs:
if inp.is_linked:
print(f" Input '{inp.name}': linked to something")
预期输出显示:
Node: BSDF_PRINCIPLED - Principled BSDF
Input 'Roughness': linked to something
Node: VALTORGB - Color Ramp <-- this will NOT export
Node: TEX_NOISE - Noise Texture <-- this will NOT export
步骤 2:了解 GLTF 接收到的内容
导出过程会导出 Principled BSDF 的粗糙度输入。连接到 Color Ramp 时,GLTF 导出器会采用输入插槽的default_value(回退值),通常为 0.5——因此表面完全平坦。
步骤 3A:在 Blender 中通过烘焙修复(质量最佳)
import bpy
# Select the object
obj = bpy.data.objects["MetalPanelMesh"]
bpy.context.view_layer.objects.active = obj
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
# Create a new image to bake into
bake_img = bpy.data.images.new("MetalPanel_roughness_baked", width=1024, height=1024)
bake_img.colorspace_settings.name = 'Non-Color'
# Add image texture node to material
mat = obj.active_material
nodes = mat.node_tree.nodes
img_node = nodes.new('ShaderNodeTexImage')
img_node.image = bake_img
nodes.active = img_node
# Bake roughness (use ROUGHNESS pass or EMIT trick)
bpy.context.scene.cycles.bake_type = 'ROUGHNESS'
bpy.ops.object.bake(type='ROUGHNESS', save_mode='INTERNAL')
# Save baked image
import tempfile, os
bake_path = os.path.join(tempfile.gettempdir(), 'MetalPanel_roughness_baked.png')
bake_img.filepath_raw = bake_path
bake_img.file_format = 'PNG'
bake_img.save()
print(f"Baked roughness to {bake_path}")
然后将新的图像纹理节点连接到 Roughness 输入,并重新导出。
步骤 3B:在 Three.js 运行时修复(快速修补)
如果无法烘焙,请在加载后覆盖材质粗糙度:
loader.load('/metal_panel.glb', (gltf) => {
gltf.scene.traverse((child) => {
if (child.isMesh && child.material) {
const mats = Array.isArray(child.material) ? child.material : [child.material];
mats.forEach(mat => {
if (mat.name === 'MetalPanel') {
// Instead of flat 0.5, set a textured roughness or varied value
mat.roughness = 0.3; // adjust to match intended look
mat.metalness = 0.9;
mat.needsUpdate = true;
}
});
}
});
});
步骤 4:验证修复
重新导出并运行验证清单。在 Babylon.js Sandbox 中,将金属面板材质与 Blender 视口截图进行比较,确认粗糙度变化得以保留。
关键规则
1. 导出时 MCP 服务超时
Blender MCP 服务无法处理 GLTF 导出——这类操作会超出超时时间。请始终使用无头 CLI:
blender --background "scene.blend" --python-expr "
import bpy, os
export_path = 'output.glb'
os.makedirs(os.path.dirname(export_path), exist_ok=True)
bpy.ops.export_scene.gltf(
filepath=export_path,
export_format='GLB',
export_apply=False,
export_animations=True,
export_nla_strips=True,
export_cameras=True,
export_lights=False,
export_draco_mesh_compression_enable=False,
)
print(f'Size: {os.path.getsize(export_path)/1024/1024:.1f} MB')
"
2. 导出时不要应用修改器
设置 export_apply=False。数组修改器(圆形阵列、线性重复)烘焙后会使文件体积大幅膨胀。应在运行时复制它们。
示例:通过 Array 修改器创建 16 个滚轮实例,导出的 GLB 约为 1 MB;烘焙后则约为 56 MB。
3. 首次导出时不要使用 Draco
如果计划使用 gltf-transform 优化,请不要使用 Draco 压缩导出。对已有 Draco 压缩的数据重新编码会损坏网格。最后一步再应用 Draco。
4. 程序化纹理无法导出到 GLTF
这些 Blender 节点设置在导出时会丢失:
| 节点设置 | 丢失内容 | 解决方法 |
|---|---|---|
| Noise Texture → roughness | 整个程序化节点链 | 烘焙为纹理,或在运行时进行着色器补丁 |
| 粗糙度纹理上的 Color Ramp | 数值重映射范围 | 手动设置粗糙度值,或在运行时重映射 |
| 程序化凹凸(Noise → Bump) | 凹凸细节 | 在 Blender 中烘焙法线贴图 |
| 带有复杂因子的 Mix Shader | 混合逻辑 | 导出前简化为单个 BSDF |
可以导出的内容: 固定的粗糙度/金属度值、图像纹理(不包含 Color Ramp 重映射)、烘焙的法线贴图、PBR 纹理集(baseColor、metallicRoughness、normal)。
5. GLTF 名称映射
Blender 中的名称在 GLTF 中会转换:
- 空格 → 下划线
- 点号 → 删除
- 末尾空格 → 末尾下划线
| Blender | GLTF |
|---|---|
RINGS ball L | RINGS_ball_L |
Sphere.003 | Sphere003 |
RINGS L.001 | RINGS_L001 |
RINGS S (末尾有空格) | RINGS_S_ |
在代码中引用网格时,务必检查导出的 GLB 中的名称,而不是 Blender 中的名称。
6. 切勿使用 gltf-transform optimize
optimize 命令包含会破坏网格几何的 simplify。请改用单独的步骤:
# Resize textures (max 1024x1024)
npx @gltf-transform/cli resize input.glb resized.glb --width 1024 --height 1024
# WebP texture compression
npx @gltf-transform/cli webp resized.glb webp.glb --quality 90
# Draco mesh compression (LAST step)
npx @gltf-transform/cli draco webp.glb output.glb
7. 为包含空格的路径加引号
Blender 项目路径通常包含空格。务必使用双引号:
blender --background "$HOME/Downloads/blend 3/scene.blend" ...
场景提取模式
提取完整层级结构,包括材质、变换和修改器:
import bpy, json
def extract_hierarchy(obj, depth=0):
data = {
"name": obj.name,
"type": obj.type,
"location": list(obj.location),
"rotation": list(obj.rotation_euler),
"scale": list(obj.scale),
"visible": not obj.hide_viewport,
"children": [],
}
if obj.type == 'MESH' and obj.data:
data["vertices"] = len(obj.data.vertices)
data["faces"] = len(obj.data.polygons)
data["materials"] = [slot.material.name for slot in obj.material_slots if slot.material]
if obj.type == 'LIGHT':
data["light_type"] = obj.data.type
data["energy"] = obj.data.energy
data["color"] = list(obj.data.color)
if obj.data.type == 'AREA':
data["size"] = obj.data.size
data["size_y"] = obj.data.size_y
# Array modifiers (important for runtime replication)
for mod in obj.modifiers:
if mod.type == 'ARRAY':
data.setdefault("modifiers", []).append({
"type": "ARRAY",
"count": mod.count,
"offset_object": mod.offset_object.name if mod.offset_object else None,
})
for child in obj.children:
data["children"].append(extract_hierarchy(child, depth + 1))
return data
scene_data = {
"name": bpy.context.scene.name,
"fps": bpy.context.scene.render.fps,
"frame_start": bpy.context.scene.frame_start,
"frame_end": bpy.context.scene.frame_end,
"objects": [],
}
for obj in bpy.context.scene.objects:
if obj.parent is None:
scene_data["objects"].append(extract_hierarchy(obj))
print(json.dumps(scene_data, indent=2))
材质提取模式
import bpy, json
def extract_materials():
materials = []
for mat in bpy.data.materials:
if not mat.use_nodes:
continue
info = {"name": mat.name, "nodes": []}
for node in mat.node_tree.nodes:
node_data = {"type": node.type, "name": node.name}
if node.type == 'BSDF_PRINCIPLED':
for inp in node.inputs:
if inp.is_linked:
node_data[inp.name] = "linked"
elif hasattr(inp, 'default_value'):
val = inp.default_value
try:
node_data[inp.name] = list(val)
except TypeError:
node_data[inp.name] = float(val)
if node.type == 'TEX_IMAGE' and node.image:
node_data["image"] = node.image.filepath
node_data["size"] = [node.image.size[0], node.image.size[1]]
info["nodes"].append(node_data)
materials.append(info)
return materials
print(json.dumps(extract_materials(), indent=2))
动画关键帧提取
import bpy, json
def extract_animation(obj):
if not obj.animation_data or not obj.animation_data.action:
return None
tracks = []
for fc in obj.animation_data.action.fcurves:
keyframes = []
for kp in fc.keyframe_points:
keyframes.append({
"frame": int(kp.co[0]),
"value": float(kp.co[1]),
"interpolation": kp.interpolation,
})
tracks.append({
"data_path": fc.data_path,
"index": fc.array_index,
"keyframes": keyframes,
})
return {"object": obj.name, "tracks": tracks}
animations = []
for obj in bpy.data.objects:
anim = extract_animation(obj)
if anim:
animations.append(anim)
print(json.dumps(animations, indent=2))
GLTF 导出设置参考
| 设置 | 值 | 原因 |
|---|---|---|
export_format | 'GLB' | 单个二进制文件 |
export_apply | False | 不烘焙修改器(Array 等) |
export_animations | True | 包含动画数据 |
export_nla_strips | True | 将 NLA 条带烘焙到动作中 |
export_cameras | True | 包含摄像机装置 |
export_lights | False | 在运行时处理灯光(Three.js/R3F) |
export_draco_mesh_compression_enable | False | 稍后通过 gltf-transform 应用 Draco |
纹理优化流程
目标:在保持可接受视觉质量的前提下,尽可能减小 GLB 体积。
Blender export (no Draco) → resize (1K max) → WebP (q90) → Draco
~22 MB ~3.7 MB ~3.7 MB ~1 MB
要点:
- 4K 纹理(4096x4096)每张约占 89 MB GPU 内存。1K 纹理约占 5.6 MB,减少 16 倍。
- PNG 金属度粗糙度纹理使用质量为 85-90 的 WebP 压缩,效果良好。
- 纹理降采样对移动 GPU(Adreno、Mali)的提升最明显。
- 使用以下命令检查:
npx @gltf-transform/cli inspect model.glb
具体命令和质量指标请参阅 references/texture-optimization.md。
资源集成
配置后,可通过 Blender MCP 使用:
| 集成 | 功能 |
|---|---|
| PolyHaven | 搜索、下载并导入免费的 HDRI、纹理和 3D 模型,自动设置材质 |
| Sketchfab | 搜索并下载模型(需要访问令牌) |
| Hyper3D Rodin | 根据文本描述或参考图像生成 3D 模型 |
| Hunyuan3D | 根据文本提示词、图像或两者创建 3D 资源 |
用法示例和工作流模式请参阅 references/asset-integrations.md。
已知错误及解决方法
完整的错误表请参阅 references/errors.md。
数据输出
- 对于小型结果(场景信息、单个对象),使用
print()+json.dumps() - 对于大型提取结果(完整层级结构、动画数据、材质报告),使用
tempfile.gettempdir() - 始终包含元数据:场景名称、fps、帧范围、Blender 版本