
1. 当3295 个零件这个数字摆在面前时真正难的是什么第一次看到3295 个零件造车这个说法我的反应不是AI 真牛而是这活儿要是让我手动在 Blender 里摆我得摆到明年。3295 个零件意味着什么意味着一辆普通燃油车大概有 3 万个零件一辆结构相对简化的卡丁车或小型电动车把标准件、紧固件、结构件、覆盖件全算上落在 3000 到 5000 这个量级是完全合理的。所以这个数字本身不是噱头它对应的是一个真实的工程复杂度。但真正值得聊的不是AI 能不能生成 3295 个零件而是这 3295 个零件是怎么被组织起来的。零件数量从来不是难点难点在于零件之间的层级关系、装配约束、命名规范、坐标系对齐、材质分组、导出格式兼容性。你让一个模型直接吐 3295 个 mesh 出来那叫灾难不叫建模。真正有价值的做法是让模型生成一套可执行的建模脚本由脚本在 Blender 里按规则批量构建、命名、分组、导出最后落到 FBX 或 USD 这类通用格式再进 Unreal 做实时渲染或装配验证。这条链路里OpenAI 的模型扮演的角色是写脚本的人Blender 是执行车间Python 是传送带FBX 是出厂包装Unreal 是展厅兼测试场。五个关键词串起来其实是一条完整的程序化建模流水线。我下面就把这条流水线拆开讲清楚每个环节为什么这么设计、坑在哪里、怎么落地。适合读这篇的人有三类一是想用 AI 辅助做程序化建模的 Blender 用户二是需要批量生成零件级资产、再导入 Unreal 做场景或仿真的人三是单纯好奇AI 建模到底能到什么程度、想自己动手试一把的开发者。不管你是哪类核心思路是一致的——别让模型直接给你模型让它给你代码。2. 为什么让模型写 Blender Python 脚本比让模型直接出模型靠谱得多2.1 直接生成网格数据的三个死穴很多人对 AI 建模的想象是输入造一辆车输出一个 .blend 文件。这条路在技术上不是完全走不通但实际用起来有三个绕不过去的死穴。第一个死穴是精度不可控。模型生成的网格顶点坐标是看起来差不多而不是工程上准确。一个法兰盘的孔位偏差 0.5 毫米在渲染图里根本看不出来但一旦进入装配验证或者 3D 打印直接报废。而脚本生成不一样坐标是你写死的参数for i in range(6): angle i * math.pi / 3六个孔均布数学上精确。第二个死穴是不可复现、不可修改。模型给你一个网格你想把轮距从 1200 改成 1300只能重新生成而且大概率生成出来跟上一版完全不是一个东西。脚本就不一样了wheelbase 1300改一个数重跑一遍其他所有关联尺寸自动跟着变。这就是参数化建模的威力也是工业界几十年来的共识。第三个死穴是命名和层级一塌糊涂。3295 个零件如果全叫Cube.001到Cube.3295你在 Outliner 里找一颗螺栓得找到崩溃导入 Unreal 之后更是灾难。脚本可以按规则命名BRK_CALIPER_FL、SUS_ARM_RL、BODY_PANEL_DOOR_L前缀表系统、中缀表部件、后缀表位置一目了然。2.2 脚本路线的本质把建模变成写生成器我自己的经验是用 AI 做程序化建模正确的心理模型是你不是在让 AI 建模你是在让 AI 帮你写一个建模生成器。这个生成器一旦写好你想生成 1 个零件还是 3295 个零件只是循环次数的问题。举个具体的例子。假设你要生成一批标准紧固件螺栓、螺母、垫圈规格从 M3 到 M12。手写脚本大概长这样import bpy import math def create_bolt(diameter, length, name): # 螺栓头 bpy.ops.mesh.primitive_cylinder_add( radiusdiameter * 0.9, depthdiameter * 0.7, location(0, 0, length diameter * 0.35) ) head bpy.context.object head.name f{name}_HEAD # 螺杆 bpy.ops.mesh.primitive_cylinder_add( radiusdiameter * 0.5, depthlength, location(0, 0, length / 2) ) shaft bpy.context.object shaft.name f{name}_SHAFT return head, shaft specs [(3, 12), (4, 16), (5, 20), (6, 25), (8, 30), (10, 40), (12, 50)] for d, l in specs: create_bolt(d, l, fBOLT_M{d})这段代码你让模型生成它几秒钟就给你而且逻辑清晰、参数明确。你拿到之后改改就能用。这就是让模型写脚本的价值——它产出的是可读、可改、可复用的工程资产而不是一坨黑盒网格。2.3 3295 这个量级脚本是唯一现实解我算过一笔账。假设一个熟练的 Blender 建模师手动建一个中等复杂度的零件比如一个带倒角的支架平均需要 8 分钟包括建模、命名、材质、分组。3295 个零件就是 26360 分钟约 440 小时按每天 8 小时算是 55 个工作日。这还没算装配和调试的时间。而脚本路线呢写生成器可能花 2 到 3 天调试参数花 1 天跑批生成 3295 个零件Blender 后台模式跑可能就十几分钟。效率差距是几十倍。而且脚本生成的零件命名规范统一、坐标系统一、材质分组统一后续导入 Unreal 几乎不用返工。所以3295 个零件这个数字本质上是在说这个量级已经超出了手动建模的经济性边界必须走程序化路线。而 OpenAI 的模型在这里的价值是帮你把从需求到脚本这一步的脑力成本降下来。3. 从一句需求到可跑脚本模型到底帮你做了什么3.1 需求拆解模型最擅长的是把模糊变具体你给模型的原始需求可能就一句话帮我生成一辆小型电动车的 3295 个零件模型用 Blender Python。这句话对人来说信息量严重不足但模型能把它拆成可执行的子任务。我实测下来模型通常会这样拆确定车辆总体架构车身、底盘、动力系统、悬挂、转向、制动、电气、内饰、外饰九大系统。每个系统拆子系统比如悬挂拆成前悬、后悬每个再拆控制臂、减震器、弹簧、转向节、轮毂、轮胎。每个子系统拆零件控制臂拆成臂体、衬套、球头、螺栓、螺母、垫圈。给每个零件定参数尺寸、位置、材质、命名规则。生成构建脚本按系统分组每个系统一个函数最后统一调用。这个拆解过程模型做得比我快而且它不会漏掉垫圈这种小件。我自己的习惯是先让模型出一版拆解清单我审一遍把明显不合理的比如把电池包拆成 200 个电芯合并或简化然后再让它生成脚本。3.2 参数化设计模型帮你把硬编码变成可调参数模型生成的脚本如果直接写死坐标那价值就大打折扣。好的做法是让它把所有关键尺寸抽成参数放在脚本开头。# 全局参数 WHEELBASE 2400 # 轴距 mm TRACK_WIDTH 1400 # 轮距 mm WHEEL_DIAMETER 560 # 轮径 mm WHEEL_WIDTH 180 # 轮宽 mm BODY_LENGTH 3200 # 车身长 BODY_WIDTH 1600 # 车身宽 BODY_HEIGHT 1200 # 车身高 GROUND_CLEARANCE 150 # 离地间隙 # 有了这组参数你想把车从两座改成四座改WHEELBASE和BODY_LENGTH就行其他零件的位置如果写的是相对坐标会自动跟着变。这就是参数化驱动的核心思想。我踩过的一个坑是模型一开始生成的脚本零件位置全是绝对坐标改一个轴距所有零件位置全乱。后来我要求它所有零件位置必须基于参考点用相对坐标表达情况就好多了。这个要求你在提示词里明确写出来模型是能理解的。3.3 命名规范3295 个零件不乱的关键命名这件事手动建模时大家都不太在意但到了 3295 这个量级命名就是生死线。我推荐的命名规则是三段式[系统前缀]_[部件名]_[位置/序号]比如命名含义SUS_ARM_FL悬挂控制臂前左SUS_ARM_FR悬挂控制臂前右BRK_CALIPER_RL制动卡钳后左BAT_MODULE_03电池模组第 3 号BODY_PANEL_DOOR_L车身覆盖件车门左位置后缀统一用FL/FR/RL/RR前左/前右/后左/后右序号统一用两位数字。这样在 Blender 的 Outliner 里按名称排序同系统的零件自动聚在一起导入 Unreal 之后按名称筛选也极其方便。模型生成脚本时如果你在提示词里把命名规则写清楚它会老老实实按规则来。我试过不写规则结果它给我生成了part_1到part_3295那真是没法用。4. Blender 侧的实操批量生成、分组、导出的完整链路4.1 后台模式跑批别在 GUI 里点3295 个零件如果你在 Blender GUI 里跑脚本界面会卡到怀疑人生。正确做法是用后台模式background modeblender --background --python generate_car.py这条命令的意思是不启动图形界面直接跑generate_car.py脚本。跑完之后脚本里自己调用保存和导出。我实测下来3295 个零件的生成加导出后台模式大概 8 到 15 分钟取决于零件复杂度。GUI 模式可能跑一小时还卡死。脚本末尾记得加上保存和导出# 保存 blend 文件 bpy.ops.wm.save_as_mainfile(filepath/path/to/car.blend) # 导出 FBX bpy.ops.export_scene.fbx( filepath/path/to/car.fbx, use_selectionFalse, apply_scale_optionsFBX_SCALE_ALL, axis_forward-Z, axis_upY )4.2 集合分组用 Collection 代替全堆在一个场景里Blender 的 Collection集合是管理大量对象的核心工具。3295 个零件如果全堆在 Scene Collection 下Outliner 会卡选择也困难。正确做法是按系统建 Collectiondef ensure_collection(name): if name in bpy.data.collections: return bpy.data.collections[name] col bpy.data.collections.new(name) bpy.context.scene.collection.children.link(col) return col systems [BODY, CHASSIS, SUSPENSION, STEERING, BRAKE, POWERTRAIN, ELECTRICAL, INTERIOR, EXTERIOR] collections {s: ensure_collection(s) for s in systems}建好 Collection 之后每个零件生成时直接 link 到对应的 Collection而不是默认的 Scene Collection。这样在 Outliner 里就是九个折叠组清爽很多。4.3 材质与 LOD别让 3295 个零件拖垮渲染3295 个零件如果每个都独立材质渲染时 draw call 会爆炸。我的做法是按系统共用材质车身一套漆面材质底盘一套金属材质轮胎一套橡胶材质玻璃一套透明材质。总共不超过 10 套材质。def get_or_create_material(name, base_color, metallic, roughness): if name in bpy.data.materials: return bpy.data.materials[name] mat bpy.data.materials.new(name) mat.use_nodes True bsdf mat.node_tree.nodes[Principled BSDF] bsdf.inputs[Base Color].default_value base_color bsdf.inputs[Metallic].default_value metallic bsdf.inputs[Roughness].default_value roughness return mat另外如果这批资产是要进 Unreal 做实时渲染的建议在 Blender 侧就做好LODLevel of Detail或者至少把不重要的内部零件比如电池模组内部的连接片标记为不导出。3295 个零件全量进 Unreal即使都是低模场景也会很重。4.4 FBX 导出的参数陷阱FBX 导出是坑最多的一步。我列几个必须注意的参数参数推荐值原因apply_scale_optionsFBX_SCALE_ALL避免缩放不一致导致零件错位axis_forward-Z与 Unreal 坐标系对齐axis_upY与 Unreal 坐标系对齐use_selectionFalse导出全场景除非你明确只导部分mesh_smooth_typeFACE避免法线错误use_mesh_modifiersTrue应用修改器否则倒角等效果丢失我踩过最深的坑是axis_forward和axis_up设错结果导入 Unreal 之后整辆车是躺着的所有零件相对位置全乱。后来固定用-Zforward、Yup就再没出过问题。5. 进 Unreal 之后3295 个零件怎么用才不崩5.1 导入设置Datasmith 还是直接 FBX如果你的目标是做实时可视化或者装配演示Unreal 这边有两条路直接导 FBX或者走 Datasmith。直接导 FBX 简单直接但材质和层级需要手动整理Datasmith 对复杂层级的支持更好但需要额外的导出插件。我的建议是零件级资产、需要保留命名和层级的走 FBX 直接导入然后在 Unreal 里用Static Mesh批量处理。Datasmith 更适合建筑和工业场景的整体导入对于 3295 个零件的车辆反而可能引入不必要的复杂度。导入时注意勾选Import Uniform Scale保持 1.0除非你的 Blender 单位不是米。Combine Meshes不要勾勾了就把所有零件合并成一个 mesh命名和层级全丢。Generate Lightmap UVs如果要做静态光照勾上纯实时渲染可以不勾。5.2 用蓝图或 Python 做批量后处理3295 个零件导入 Unreal 之后如果一个个手动设材质、设碰撞那又回到手动时代了。Unreal 的Editor Python可以帮你批量处理import unreal def batch_set_material(mesh_path, material_path): mesh unreal.EditorAssetLibrary.load_asset(mesh_path) mat unreal.EditorAssetLibrary.load_asset(material_path) mesh.set_material(0, mat) # 批量给所有 SUS_ 开头的零件设金属材质 assets unreal.EditorAssetLibrary.list_assets(/Game/Car/Suspension) for a in assets: batch_set_material(a, /Game/Materials/M_Metal)这段逻辑跟 Blender 侧是一样的能批量的绝不手动。5.3 性能优化3295 个零件的场景怎么跑得动3295 个独立 Static Mesh即使每个只有几百个三角面总面数也可能到百万级draw call 更是灾难。几个实用的优化手段合并同材质零件把同一材质的多个零件用Merge Actors合并成一个 meshdraw call 直接降一个数量级。用 Instanced Static Mesh螺栓、螺母、垫圈这类重复零件用 ISM 渲染性能提升巨大。设置合理的 LOD远处用低模近处用高模。关闭不必要的阴影投射内部零件、小零件关掉Cast Shadow。我实测过一辆 3295 零件的车不做优化直接进 Unreal帧率可能只有个位数做完合并和 ISM 之后能跑到 60 帧以上。差距就是这么夸张。6. 这套流程里我踩过的坑和总结出的经验6.1 模型生成的脚本第一遍永远不能直接用这是我最想强调的一点。OpenAI 的模型生成的 Blender Python 脚本第一遍跑几乎一定会报错。常见错误包括API 名称记错Blender 不同版本 API 有差异、上下文对象获取方式不对、bpy.ops调用时缺少必要的 context、循环里对象引用失效等。我的做法是把报错信息原样贴回给模型让它修。通常两到三轮就能跑通。不要指望一次成功也不要自己硬改让模型改效率更高。6.2 Blender 版本差异是个大坑Blender 2.8、2.9、3.x、4.x 的 Python API 有不少变化。比如bpy.ops.mesh.primitive_cylinder_add的参数在 4.x 里有调整材质节点的输入名称也变过。你在提示词里一定要写明目标 Blender 版本比如请生成兼容 Blender 4.2 的脚本。不写的话模型可能给你生成 2.8 时代的代码跑起来一堆警告。6.3 3295 个零件不是越多越好我一开始也追求零件数量要多显得真实。后来发现零件数量应该服务于用途。如果只是做视觉展示很多内部零件根本看不到生成了也是浪费。如果是做装配仿真那关键运动件必须独立但固定件可以合并。我的建议是先明确用途再定零件粒度。视觉展示500 到 1000 个零件足够装配仿真关键运动件独立其他可以合并3D 打印那得按实际打印件拆分。3295 这个数字更像是一个展示程序化建模能力的规模实际项目里要按需裁剪。6.4 提示词里必须写清楚的几件事根据我的经验让模型生成建模脚本时提示词里这几件事必须写清楚否则生成质量会差很多目标软件和版本Blender 4.2Python 3.11。单位系统毫米还是米这直接影响坐标数值。命名规则三段式前缀表系统。坐标约定Z 轴向上还是 Y 轴向上原点在哪。参数化要求关键尺寸抽成全局变量。分组要求按系统建 Collection。导出格式FBX坐标系与 Unreal 对齐。这七条写清楚模型生成的脚本可用性能提升一大截。我对比过写清楚和没写清楚返工次数能差三倍。6.5 一个容易被忽略的点脚本的可读性模型生成的脚本有时候为了炫技会写得很紧凑一行套好几个函数调用。这种脚本跑是能跑但你后面想改就痛苦了。我的做法是在提示词里加一句代码要可读关键步骤加注释函数职责单一。这样生成的脚本你自己维护起来轻松很多。程序化建模这条链路说到底就是把手工劳动转化成代码资产。OpenAI 的模型在这个链路里的定位是帮你快速产出第一版可运行的代码省掉你查 API、搭框架的时间。但参数怎么定、零件怎么拆、性能怎么优化这些还是得靠你自己的工程判断。工具再好也只是工具。