关于 wireframe-to-3d
wireframe-to-3d 是将正交线框图转换为 Blender 参数化模型并导出 GLB 的技能,适用于从线稿创建 3D 资产。它通过图像分析、曲线建模和导出验证构成处理流程。
主要功能
- 01线框图轮廓分析
- 02生成参数化 Bezier 曲线
- 03通过 Blender 执行建模代码
- 04转换网格并赋予 PBR 材质
- 05验证并导出 GLB 文件
适用场景
- 01将眼镜等线框图转为模型
- 02按预览或生产细节等级生成模型
- 03检查模型拓扑与导出文件大小
MCP 相关
该技能通过 Blender MCP 检查场景、执行 Blender Python 代码并获取对象信息。它使用 MCP 与 Blender 协作完成建模和验证。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ roble3/ cc-blender-skill --skill wireframe-to-3d安装后在 Claude Code 中输入/wireframe-to-3d确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/algorithms.mdbest-practices.mdblender-patterns.mdscripts/wireframe_analyzer.py详细介绍
线框图转 3D 模型
将 2D 正交线框图转换为参数化的 3D Blender 模型,并导出为 glTF 2.0 二进制文件(.glb)。
概述
此技能通过四个阶段的流水线完成转换:
- 分析:使用
scripts/wireframe_analyzer.py在本地分析线框图(OpenCV → JSON 格式的贝塞尔控制点)。 - 生成:生成 Blender Python 代码,将轮廓还原为参数化贝塞尔曲线。
- 执行:通过
mcp__blender__execute_blender_code在 Blender 中执行代码,将曲线转换为带有 PBR 材质的网格。 - 导出:导出经过优化的 GLB(≤ 15 MB),并验证文件大小和拓扑。
你(Claude)负责协调流程。scripts/ 目录包含唯一的独立代码(wireframe_analyzer.py);其他内容都是你生成并通过 MCP 运行的模式。
前置条件 — 请先检查
在处理任何线框图之前,请确认环境满足以下条件:
-
Blender MCP 可访问。调用
mcp__blender__get_scene_info。如果报错“Could not connect to Blender”,请停止并告知用户:“Blender 的 MCP 插件未运行。请启动 Blender,启用 BlenderMCP 插件(端口 9876),然后重新运行。”
-
分析器所需的 Python 依赖。运行:
python3 -c "import cv2, numpy, scipy" 2>&1如果报错,请运行
pip install opencv-python numpy scipy Pillow(或指导用户运行)。 -
图像输入。确认用户至少提供了一张 PNG。建议尺寸:≥ 400×400 px,黑底白线或白底黑线的线稿。
决策流程
Q1:有多少个视图?
- 单视图 → 仅进行平面 2D 挤出(提醒用户;必须提供或假设深度)。
- 正视图 + 侧视图 → 完整的 3D 重建(轮廓 × 深度剖面)。
- 正视图 + 侧视图 + 后视图 → 使用后视图验证对称性。
Q2:细节级别?
preview— RDP epsilon = 4.0,目标约为 1–2k 个三角形,GLB < 1 MB。production— RDP epsilon = 2.0,目标约为 5–8k 个三角形,GLB 为 2–4 MB。默认值。high— RDP epsilon = 1.0,目标约为 10–20k 个三角形,可能需要使用 Decimate 才能将文件控制在 15 MB 以下。
Q3:几何体类型?
wires— 框架、镜腿、铰链。对曲线使用bevel_depth。surfaces— 镜片、穹顶。使用放样剖面或填充端盖。hybrid— 两者兼有。类似眼镜的物体默认使用此类型。
Q4:实际尺寸?
- 如果用户提供了尺寸(例如“眼镜宽 140 mm”),请使用该尺寸。
- 否则,根据线框图的宽高比推断,并假设合理的默认值(眼镜宽 140 mm、头盔宽 180 mm 等)。如果不明显,请与用户确认。
参考图/纹理驱动主体的强制修正循环
当用户反馈模型与参考图/纹理不匹配时,请停止常规的“从基本几何体生成”循环,改用参考图锁定建模:
- 先根据提供的纹理/线框图统计可见设计部件的数量,并在建模前将数量写入 stdout/备注。不要根据对称性推断径向或重复部件的数量;源清单中的视觉设计数量才是验收依据。
- 以前视图为基准。 在用于验证的同一前视正交相机中创建一个不含图像的平面/参考图叠加层。添加深度前,先对齐比例、中心线和边界圆。
- 先在前视图的 X/Z 平面上描摹或定义二维轮廓。 根据这些轮廓构建网格表面;不要先放置通用椭圆,再尝试给它们贴图。
- 锁定前向投影。 只有当前轮廓匹配后,才能沿视轴添加深度。侧视/后视图用于调整厚度和叠放关系,但不得改变前向轮廓。
- 对于需要匹配正面图稿的纹理,请使用 Project-from-View 风格的 UV。一个朝前的表面应与对应的纹理裁剪区域按 1:1 映射。
- 渲染叠加验证图:将参考图/线框置于模型后方或叠加在模型上,并打印检查清单:预期数量、实际对象数量、面的位置、外轮廓边界,以及侧面/背面的深度。
- 如果连续两次迭代都重复同一错误,请查阅
references/best-practices.md,并在再次尝试前添加一条简短的长期备注。
对于重复图案主体,请严格按照源清单声明的数量创建主要结构组件。根据源图中可见的位置或语义角色为它们命名,并在导出前验证数量。
阶段 1 — 运行分析器
每个视图运行一次捆绑的分析器:
python3 ${CLAUDE_SKILL_DIR}/scripts/wireframe_analyzer.py <input.png> <output.json>
该脚本会输出以下结构的 JSON:
{
"metadata": {"image_size": [W, H], "num_contours": N, "parameters": {...}},
"contours": [[[x, y], ...], ...],
"bezier_curves": [[[[P0], [P1], [P2], [P3]], ...], ...]
}
调整 RDP epsilon(仅在默认值无效时):
- 输出的轮廓过少/过于锯齿 → 将 epsilon 降至 1.0–1.5。
- 输出的噪声点过多 → 将 epsilon 提高至 3.0–4.0。
- 通过
--rdp-epsilon传入(或编辑脚本中的调用)。
使用 Read 读取 JSON。不要将大量 JSON 直接传给 Blender——请先提取所需内容。
阶段 2 — 生成 Blender 代码
将代码拆分为小型、自包含的代码块(每次调用 execute_blender_code 都会获得一个全新的 Python 命名空间;只有 bpy.data 会在调用之间保留)。始终重新导入所需内容。
模式:根据控制点创建 Bezier 曲线
import bpy
# Identify by stable name; bpy.data persists between calls.
name = 'GEO-lens-right'
curve_data = bpy.data.curves.new(name=name, type='CURVE')
curve_data.dimensions = '3D'
curve_data.resolution_u = 16 # tessellation resolution
curve_data.bevel_depth = 0.001 # 1 mm wire thickness (adjust for surfaces)
curve_data.use_fill_caps = True
obj = bpy.data.objects.new(name, curve_data)
bpy.context.collection.objects.link(obj)
# Control points come from the analyzer JSON (px → mm scaling done client-side).
control_points = [(0.0, 0.0, 0.0), (0.5, 1.0, 0.0), (1.5, 1.0, 0.0), (2.0, 0.0, 0.0)]
spline = curve_data.splines.new(type='BEZIER')
spline.bezier_points.add(len(control_points) - 1)
for i, (x, y, z) in enumerate(control_points):
pt = spline.bezier_points[i]
pt.co = (x, y, z)
pt.handle_left_type = 'ALIGNED' # C¹ smooth
pt.handle_right_type = 'ALIGNED'
print(f"created:{name}") # signal back via stdout
像素 → 世界坐标转换(在生成的代码中执行,然后再发送给 Blender):
norm_x = px_x / img_width
norm_y = 1.0 - (px_y / img_height) # flip Y; image origin is top-left
x_world = (norm_x - 0.5) * world_width_mm / 1000.0 # to metres
y_world = (norm_y - 0.5) * world_height_mm / 1000.0
模式:将曲线转换为网格 + 清理
import bpy
name = 'GEO-lens-right'
obj = bpy.data.objects[name]
bpy.context.view_layer.objects.active = obj
bpy.ops.object.convert(target='MESH')
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.remove_doubles(threshold=0.0001)
bpy.ops.mesh.normals_make_consistent(inside=False)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.shade_smooth()
mesh = obj.data
print(f"mesh:{name} verts:{len(mesh.vertices)} polys:{len(mesh.polygons)}")
模式:PBR 材质(Principled BSDF — 唯一可由 glTF 正确导出的着色器)
import bpy
mat = bpy.data.materials.get('MAT-frame-metal') or bpy.data.materials.new('MAT-frame-metal')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
bsdf.inputs['Base Color'].default_value = (0.08, 0.08, 0.10, 1.0)
bsdf.inputs['Metallic'].default_value = 1.0
bsdf.inputs['Roughness'].default_value = 0.25
obj = bpy.data.objects['GEO-frame']
if obj.data.materials:
obj.data.materials[0] = mat
else:
obj.data.materials.append(mat)
print('material:assigned')
材质预设(除非用户另有指定,否则使用以下预设):
MAT-frame-metal— 基色(0.08, 0.08, 0.10),metallic=1.0,roughness=0.25(拉丝钢)MAT-lens-mirror— 基色(0.05, 0.08, 0.15),metallic=0.8,roughness=0.05,IOR=1.5(镜面玻璃)MAT-pad-silicone— 基色(0.65, 0.63, 0.60),metallic=0.0,roughness=0.7(哑光硅胶)
模式:导出为 GLB
import bpy, os
filepath = '/tmp/wireframe_output.glb'
bpy.ops.export_scene.gltf(
filepath=filepath,
export_format='GLB',
export_materials='EXPORT',
export_uv=True,
export_normals=True,
export_animations=False,
export_yup=True,
)
size_mb = os.path.getsize(filepath) / (1024 * 1024)
print(f"export:{filepath} size_mb:{size_mb:.2f}")
如果 size_mb > 15:应用 Decimate 并重新导出(参见错误恢复)。
阶段 3 — 验证
完成整个流程后,请先验证,再宣布成功:
mcp__blender__get_scene_info— 确认预期对象存在。- 对于成对部件(左/右镜片),分别调用
mcp__blender__get_object_info并比较边界框宽度。容差:1 mm。 - 三角形数量:通过
get_object_info获取。如果某个部件超出预算,请规划 Decimate。 - 文件大小:不得超过 15 MB 的硬性上限,最好不超过 8 MB。
错误恢复
| 症状 | 可能原因 | 修复方法 |
|---|---|---|
MCP 返回 Code execution error: ... | 生成的代码存在 Python 错误 | 将代码拆分为更小的片段重新输出;根据错误消息追踪出错行 |
Could not connect to Blender | 插件未运行 | 提示用户启动 Blender 和插件 |
Timeout waiting for Blender response | 代码片段过大或执行过慢 | 将代码拆分为多个较小的 execute_blender_code 调用 |
| 多次调用之间变量未定义 | 每次调用都会获得一个全新的命名空间 | 重新导入模块;通过 bpy.data.objects['name'] 引用对象 |
| 分析器输出 0 条轮廓 | 图像对比度过低 | 使用 --gaussian-kernel 7 --canny-t1 30 重新运行 |
| 镜片不对称 | 原始绘图不对称,或轮廓检测结果不一致 | 提醒用户;除非用户提出要求,否则不要自动镜像 |
| GLB 过大 | 面数过多或嵌入式纹理过多 | 使用比例为 0.6–0.8 的 DECIMATE 修改器;重新导出 |
| 网格存在孔洞 | 曲线分辨率过低 | 将 curve_data.resolution_u 提高到 24 或 32;重新转换 |
| GLB 中缺少材质 | 使用了非 Principled BSDF 节点 | 仅使用 Principled BSDF 重建材质 |
精简代码模式(GLB > 15 MB 时)
import bpy
obj = bpy.data.objects['GEO-frame']
bpy.context.view_layer.objects.active = obj
mod = obj.modifiers.new(name='Decimate', type='DECIMATE')
mod.ratio = 0.7
mod.use_collapse_degenerate = True
bpy.ops.object.modifier_apply(modifier=mod.name)
print(f"decimated:{obj.name} verts:{len(obj.data.vertices)}")
输出给用户
完成后,请报告:
- GLB 文件的输出路径
- 文件大小(MB,与 15 MB 上限对比)
- 每个部件的三角形数量(与 30 000 上限对比)
- 已分配的材质槽
- 任何警告(不对称、已应用精简、使用了回退方案)
示例:
✓ 已导出
/tmp/wireframe_output.glb(2.4 MB)
三角形:5 200(3 个部件:GEO-frame、GEO-lens-right、GEO-lens-left)
材质:MAT-frame-metal、MAT-lens-mirror
警告:无
何时加载更深入的参考资料
上文涵盖了 80% 的常见情况。对于少数特殊情况,请按需加载以下资料:
references/algorithms.md— 图像处理流程理论(Canny、RDP、最小二乘贝塞尔拟合)、二维到三维重建原理、ISO 128 正投影标准。当分析器输出看起来不正确且需要调整参数时加载。references/blender-patterns.md— 全面的 Blender Python 模式(放样、曲面旋转、自定义修改器堆栈)。当用户要求非标准几何体(曲面、复杂鼻梁、可活动部件)时加载。references/best-practices.md— 性能优化(foreach_set、批量操作、上下文缓存)、命名约定(Blender Studio 标准)、修改器堆栈排序。当构建速度较慢或输出拓扑不佳时加载。
约束
- Blender ≥ 4.0(优先使用 5.x)。Principled BSDF 节点和 glTF 导出器在这些版本中均稳定。
- 仅嵌入式 glTF(不使用
.bin和纹理旁置文件;不使用 KTX2/Draco 压缩——Three.js 需要额外加载器,而我们尚未随附这些加载器)。 - 仅使用 PNG 纹理(最大 1024×1024)。优先使用平涂 PBR 颜色;仅在必要时使用纹理。
- GLB 中不包含骨骼动画。空闲动作由使用方网站通过 JS 驱动。
提示
如果用户只说“转换此线框”,默认使用:view_type=auto-detect、detail_level=production、geometry_type=hybrid、world_width_mm=auto。只有在存在多种合理解释时才询问用户澄清。
范围界定 — wireframe-to-3d 的产出与不产出的内容
此技能会将 2D 轮廓描摹并挤出为细曲线,生成输入图形的 Y 轴扁平线框表示。它不会生成:
- 填充表面(例如镜框轮廓之间的镜片玻璃)
- 单视图下真实的 3D 深度(输出在 Y 轴上是扁平的)
- 多视图 3D 重建(将正面和侧面视图合并为体积)
- 输入线条中未包含的子特征(鼻托、可活动的铰链等)
- 可用于生产的材质(仅提供占位材质——实际材质由
blender-materials提供)
如果要制作“完整渲染并带纹理的 X”(例如,根据线框制作 Ray-Ban Aviator),请将此技能与其他技能串联使用:
| 步骤 | 技能 | 添加的内容 |
|---|---|---|
| 1 | wireframe-to-3d | 镜框轮廓作为 3D 曲线——基础,而非最终交付物 |
| 2 | blender-modeling | 填充镜片圆盘(按镜片尺寸缩放的 UV 球体);镜腿(沿 Y 轴向后延伸的 Bezier 曲线);线框中未包含的任何 3D 细节 |
| 3 | blender-materials | 金色/银色金属镜框;使用 Metallic=0.9, Roughness=0.04 的镜面镜片材质;等等 |
| 4 | blender-lighting | 为产品拍摄布光设置 subject_class='metal' |
| 5 | blender-cameras | 使用 85-100mm 焦距和浅景深拍摄主视觉产品图 |
| 6 | blender-rendering | 使用 Cycles 256+ 采样、降噪和 AgX 视图变换 |
请参阅 text-to-blender/assets/v0.9.0-validation/03_aviator_wireframe_to_3d.webp,了解 wireframe-to-3d 单独生成的效果(扁平轮廓描摹);并参阅 04_aviator_chained_upgrade.webp,了解串联编排生成的效果(Ray-Ban 风格的主视觉渲染图)。
如果用户要求根据线框制作“X 的模型”,编排器(text-to-blender)应始终规划技能串联流程。