1. 从零件到场景:这条工作流到底在解决什么问题
如果你同时接触过机械设计和实时三维应用开发,大概率遇到过这种局面:在SolidWorks里把一套装配体画得清清楚楚,尺寸、公差、配合关系全都对得上,但一旦要把这些模型放进Unity里做交互展示、装配模拟或者操作培训,整个流程就变得磕磕绊绊。模型导入后要么面数爆炸导致帧率暴跌,要么材质全丢变成一片灰白,要么坐标系对不上,零件散落在场景各处。
这个问题的本质在于:SolidWorks和Unity服务于两个完全不同的目标。SolidWorks是参数化精确建模工具,它的核心是B-rep(边界表示法)实体模型,每个面、每条边都有精确的数学定义,一个圆柱面就是解析几何意义上的圆柱面。而Unity是实时渲染引擎,它只认三角网格,所有几何体最终都要被离散成三角形,材质靠的是基于物理的渲染管线。两者之间的数据转换不是简单的"导出再导入",中间涉及网格化精度控制、坐标系转换、材质重建、层级结构映射等一系列环节。
我前后做过好几个从SolidWorks到Unity的项目,有工业设备的三维操作手册,也有产线布局的交互式预览。踩过的坑从模型导入后法线翻转导致光照全黑,到装配体层级丢失导致动画无法绑定,几乎每个环节都交过学费。这篇文章就把这套工作流的完整链路拆开讲清楚,从SolidWorks端的导出准备,到Unity端的导入配置,再到材质和性能的优化处理,每一步都给出具体的操作方法和背后的原理。
适合阅读这篇文章的,包括需要把机械模型用于实时三维展示的工程师、做工业仿真或数字孪生的开发者,以及任何在这两个工具之间来回折腾的人。不需要你是SolidWorks或Unity的专家,但至少要对三维建模和游戏引擎有基本的操作认知。
2. SolidWorks端的导出准备:别急着点"另存为"
很多人拿到模型后的第一反应是直接"另存为"一个FBX或者OBJ,然后拖进Unity。这个操作本身没错,但如果不做任何预处理,后面在Unity里要花三倍的时间来收拾残局。SolidWorks端的准备工作做得好不好,直接决定了后续流程是顺畅还是痛苦。
2.1 为什么不能直接导出整棵装配体树
SolidWorks的装配体是一棵层级树,顶层是总装,下面有子装配,再下面是零件,零件里面还有特征树。导出的时候,如果直接选整个装配体导出FBX,SolidWorks会按照它的内部逻辑把层级关系翻译成FBX的节点树。问题在于,这个翻译结果往往和你在Unity里期望的层级结构不一致。
举个例子,一个包含气缸、活塞、连杆的装配体,在SolidWorks里可能是"气缸总成"下面挂着"缸体"和"活塞组件","活塞组件"下面再挂"活塞"和"连杆"。导出到Unity后,你可能发现多出了好几层空节点,或者某些零件的命名变成了"Part1_1"这种毫无意义的编号。更麻烦的是,如果后续要做动画,比如让活塞在气缸里往复运动,你需要的是一个干净的层级:活塞是独立节点,连杆是另一个独立节点,它们之间的父子关系清晰明确。
我的做法是,在SolidWorks里先对装配体做一次"扁平化"处理。具体操作是:把需要独立控制的零件从子装配中提出来,放到顶层,或者至少放到一个层级较浅的位置。对于不需要单独控制的零件,可以合并成一个子装配。判断标准很简单——在Unity里需要单独移动、旋转或者做动画的零件,就必须是独立节点;不需要单独操作的,可以合并。
注意:SolidWorks的"另存为"对话框里有一个"输出装配体结构"的选项,选择"多实体零件"和"装配体"的结果完全不同。如果选"多实体零件",所有零件会合并成一个网格,层级信息全部丢失;如果选"装配体",层级会保留但可能包含冗余节点。根据你的实际需求选择。
2.2 网格精度的取舍:面数不是越多越好
SolidWorks导出FBX时,有一个关键参数叫"自定义网格精度"或者"图像品质"。这个参数控制的是B-rep实体被离散成三角网格的精细程度。精度越高,曲面越平滑,但面数也越多。
我做过一个测试:一个直径200mm、长度500mm的圆柱体,在SolidWorks里用"草稿"精度导出,三角面数是32个;用"精细"精度导出,面数是512个;用"自定义"把偏差调到0.01mm,面数直接飙到2048个。而实际上,在Unity里如果这个圆柱体只是作为背景元素,32个面的效果和2048个面在视觉上几乎没有区别,但性能差距是64倍。
这里给一个实用的判断标准:
| 零件在场景中的角色 | 建议精度 | 典型面数范围 |
|---|---|---|
| 背景/远景元素 | 草稿或粗略 | 50-200面 |
| 中景操作对象 | 中等精度 | 500-2000面 |
| 近景核心零件 | 精细 | 2000-8000面 |
| 需要展示曲面细节 | 自定义高精度 | 8000面以上 |
具体操作路径是:在SolidWorks里打开零件或装配体,点击"另存为",选择FBX格式,在弹出的选项对话框里找到"输出"选项卡,把"自定义网格精度"勾上,然后拖动滑块或者输入具体数值。我一般会先按中等精度导出一版,进Unity看效果,如果曲面有明显的棱角感,再回去提高精度重新导出。
还有一个容易被忽略的点:圆角和小特征的处理。SolidWorks模型里经常有R2、R3的小圆角,这些圆角在工程图里很重要,但在Unity里如果面数预算有限,可以考虑在导出前压缩掉。压缩的方法是用SolidWorks的"简化"功能,把小于某个阈值的圆角识别出来并移除。不过这个操作要谨慎,因为一旦压缩,模型就和原始设计不一致了,如果后续需要做尺寸校验就会出问题。
2.3 坐标系与单位:一个容易翻车的地方
SolidWorks默认使用毫米作为单位,Y轴朝上,Z轴朝前。Unity默认使用米作为单位,Y轴朝上,Z轴朝前。看起来好像一致,但实际上FBX格式在转换过程中会做一次坐标系的转换,如果不注意,导入Unity后模型可能缩小1000倍,或者旋转了90度。
我在第一次做这类项目时,导入Unity后发现模型只有0.2米高,而实际应该是200毫米。原因就是SolidWorks导出FBX时,默认把单位从毫米转成了米,但Unity导入时又按厘米解析了一次。解决方法是:在SolidWorks的FBX导出选项里,明确设置"单位"为"米",并且在Unity的导入设置里把"Scale Factor"设为1。
另外,SolidWorks的装配体原点位置也很关键。如果装配体的原点在某个角落而不是中心,导入Unity后模型的轴心点就会偏在一边,旋转和缩放操作都会变得别扭。建议在SolidWorks里把装配体的原点调整到几何中心或者一个有意义的位置,比如设备的底面中心。
3. Unity端的导入配置:模型进来只是第一步
模型从SolidWorks导出成FBX之后,拖进Unity的Assets文件夹,看起来就完成了。但实际上,Unity的模型导入设置里有十几个参数会影响最终效果,其中大部分默认值并不适合工业模型。
3.1 Model选项卡里的关键设置
选中导入的FBX文件,Inspector面板会显示Import Settings。在Model选项卡里,有几个参数需要特别关注。
Scale Factor:这个值控制模型的缩放比例。如果SolidWorks导出时单位设置正确,这里保持1即可。如果发现模型大小不对,可以在这里调整。但更好的做法是回到SolidWorks端修正单位设置,而不是在Unity里用Scale Factor硬凑,因为后者会导致后续所有尺寸相关的逻辑都要跟着改。
Convert Units:这个选项勾上后,Unity会自动把FBX里的单位转换成米。如果SolidWorks导出时用的是毫米,勾上这个选项,Unity会自动除以1000。但前提是FBX文件里正确记录了单位信息。我遇到过一些FBX文件单位信息丢失的情况,这时候只能手动设置Scale Factor。
Import BlendShapes:如果模型需要在Unity里做形变动画,比如展示零件的弯曲或膨胀,需要勾上这个。但工业模型一般用不到,勾上反而会增加导入时间。
Generate Colliders:这个选项会自动为模型生成碰撞体。对于需要在Unity里做物理交互的场景,比如零件装配模拟,这个功能很有用。但自动生成的碰撞体往往是Mesh Collider,面数多了性能很差。我的建议是不要在这里勾选,而是导入后在Unity里手动添加合适的碰撞体,比如Box Collider或Capsule Collider。
Normals和Tangents:这两个参数控制法线和切线的导入方式。对于需要法线贴图的模型,Tangents必须设置为"Calculate MikkTSpace"或"Import"。如果模型不需要法线贴图,可以设为"None"来节省内存。Normals一般保持"Import"即可。
3.2 材质与贴图的重新映射
SolidWorks的材质系统和Unity的材质系统完全是两回事。SolidWorks里的材质主要定义外观颜色、反射率、透明度等参数,用于渲染出效果图。Unity的材质是基于Shader的,需要指定具体的Shader类型和贴图。
导出FBX时,SolidWorks会把材质的基本颜色信息带过去,但贴图、法线、粗糙度这些PBR参数通常不会保留。导入Unity后,你会看到模型上挂着一个默认的Standard材质,颜色可能是灰色或者SolidWorks里设置的颜色,但没有任何质感。
我的处理流程是这样的:先在SolidWorks里给每个零件指定一个有意义的材质名称,比如"铝合金阳极氧化"、"不锈钢拉丝"、"橡胶黑色"。导出FBX后,在Unity里根据这些名称创建对应的PBR材质。具体操作是:在Project窗口右键创建Material,选择Standard Shader或者URP的Lit Shader,然后设置Albedo颜色、Metallic(金属度)、Smoothness(光滑度)等参数。
对于需要贴图的模型,比如有Logo或者表面纹理的零件,需要在SolidWorks里先展开UV。SolidWorks的UV展开功能比较基础,对于简单零件够用,复杂曲面可能需要借助其他工具。展开UV后,导出时勾选"输出纹理坐标",Unity导入后就能在材质里指定贴图了。
提示:如果模型数量多,手动创建材质会很耗时。可以写一个简单的Editor脚本,根据FBX里的材质名称批量创建对应的Unity材质,然后自动赋值。这个脚本的核心逻辑是遍历模型的所有Renderer,读取sharedMaterial的名称,然后在指定文件夹里查找或创建同名材质。
3.3 层级结构与命名规范
导入Unity后,在Hierarchy窗口里展开模型,你会看到FBX内部的节点树。这个树结构直接反映了SolidWorks导出时的装配体层级。如果之前在SolidWorks里做了扁平化处理,这里的结构应该比较干净。
但即使如此,还是建议做一次重命名和整理。SolidWorks导出的节点名称可能包含特殊字符、空格或者中文,这些在Unity里虽然能用,但在写代码时容易出问题。我的习惯是统一改成英文加下划线的命名,比如"cylinder_body"、"piston_rod"、"connecting_rod"。
另外,如果模型包含多个零件,建议在Unity里创建一个空物体作为根节点,把所有零件拖到下面,然后重置根节点的Transform。这样做的好处是,后续移动整个设备时只需要操作根节点,不会因为某个零件的Transform有偏移而导致整体变形。
4. 从静态模型到可交互场景:性能与交互的平衡
模型导入Unity并且材质配置好之后,接下来要考虑的是怎么让它跑得流畅,以及怎么和用户交互。工业模型的面数通常比游戏模型高得多,一个中等复杂度的装配体可能有几十万甚至上百万个三角面,直接放进Unity,在普通电脑上帧率可能只有个位数。
4.1 面数优化的几个实用手段
LOD(Level of Detail)是最直接的手段。思路是根据相机距离,动态切换不同精度的模型。近处用高精度,远处用低精度。Unity内置了LOD Group组件,操作方法是:为同一个零件准备三到四个精度版本,在SolidWorks里分别以不同精度导出,然后在Unity里创建一个空物体,添加LOD Group组件,把不同精度的模型按距离阈值拖进去。
遮挡剔除(Occlusion Culling)对于设备内部结构复杂的场景特别有效。比如一个机柜,外面的门板挡住了内部的电路板,当门板关闭时,内部的电路板不需要渲染。Unity的Occlusion Culling需要先烘焙,烘焙完成后运行时会自动剔除被遮挡的物体。
合并网格是另一个常用手段。对于不需要单独控制的零件,可以把它们合并成一个网格,减少Draw Call。Unity里可以用Mesh.CombineMeshes API在运行时合并,也可以在导入前用建模工具合并。但合并后就不能单独控制每个零件了,所以只适用于静态部分。
GPU Instancing适用于大量重复的零件,比如一排相同的螺栓或者支架。在材质上勾选"Enable GPU Instancing",Unity会自动把相同的网格和材质合并渲染,大幅降低Draw Call。
4.2 交互逻辑的绑定方式
工业模型在Unity里的交互通常包括:点击选中零件、高亮显示、显示信息面板、拖拽移动、装配动画等。这些交互的实现方式取决于模型的层级结构。
如果零件是独立的GameObject,可以直接挂Collider和脚本。点击检测用Physics.Raycast,从相机发射一条射线,检测命中的Collider,然后通过GetComponent获取零件上的脚本组件,执行高亮或显示信息的逻辑。
高亮效果一般有两种实现方式:一种是修改材质的Emission属性,让零件自发光;另一种是叠加一个描边Shader。Emission方式简单直接,但会影响所有使用该材质的零件。描边方式需要额外的Shader支持,但效果更干净。我一般用Emission方式做快速原型,正式项目再换成描边。
装配动画可以用Unity的Animator或者DOTween插件来实现。Animator适合复杂的动画状态机,DOTween适合简单的位移旋转。比如活塞在气缸里往复运动,用DOTween的DOLocalMove方法,几行代码就能实现。
4.3 碰撞体的合理使用
工业模型的碰撞体设置需要根据交互需求来定。如果只是点击选中,用Box Collider或者Mesh Collider都可以,但Mesh Collider的面数不能太高,否则物理计算会很慢。Unity对Mesh Collider的面数有限制,超过一定数量会报错。
如果要做装配模拟,比如零件需要精确地插入另一个零件的孔位,碰撞体就需要更精细。这种情况下,可以用多个Box Collider组合成一个近似形状,或者用Convex Mesh Collider。Convex Mesh Collider会把网格凸包化,计算量比普通Mesh Collider小,但精度会降低。
注意:Unity的物理系统默认使用NVIDIA PhysX,对于非常小的零件(比如毫米级),物理计算可能会出现穿透或者抖动。解决方法是在Physics设置里调小Default Contact Offset和Sleep Threshold,或者把模型整体放大到米级再计算。
5. 那些文档里不会写的踩坑记录
上面讲的都是流程性的东西,按部就班操作基本不会出大问题。但实际项目中,总有一些意想不到的情况。这一节记录几个我踩过的坑,以及排查和解决的过程。
5.1 法线翻转导致模型全黑
有一次导入一个泵体模型,进Unity后发现整个模型是黑色的,无论怎么调整光照都没用。一开始以为是材质问题,换了几个Shader都不行。后来把模型放大看,发现法线方向全部朝内了。
原因是SolidWorks导出FBX时,对于某些曲面,法线的朝向和Unity的预期不一致。SolidWorks的法线定义是基于B-rep面的外法线,而Unity的三角网格法线是基于顶点顺序的。如果导出时顶点顺序反了,法线就会翻转。
解决方法有两个:一是在SolidWorks导出时勾选"翻转法线"选项(如果有的话);二是在Unity里用脚本重新计算法线,或者用建模工具翻转面。我当时的做法是在Unity里写了一个简单的Editor脚本,遍历Mesh的法线,如果发现法线朝向和面朝向不一致,就翻转过来。
5.2 材质名称冲突导致贴图错乱
另一个坑是材质名称冲突。SolidWorks里不同零件可能用了相同的材质名称,比如两个零件都叫"Steel"。导出FBX后,Unity导入时会把它们当成同一个材质,结果一个零件的贴图被另一个零件的贴图覆盖了。
解决方法是,在SolidWorks里给每个材质加上唯一的前缀或后缀,比如"Steel_PumpBody"和"Steel_Cover"。或者在Unity导入后,手动检查每个Renderer的材质,把冲突的材质复制一份重新命名。
5.3 导入后模型位置偏移
有时候导入Unity后,模型不在原点,而是偏移到了很远的地方。检查Transform发现Position是(0,0,0),但视觉上模型就是不在中心。这种情况通常是FBX文件里的轴心点(Pivot)设置有问题。
SolidWorks导出FBX时,轴心点默认在装配体的原点。如果装配体原点不在几何中心,导入Unity后模型的轴心就会偏。解决方法是在SolidWorks里调整装配体原点,或者在Unity里创建一个空物体作为父节点,把模型拖到空物体下面,然后调整空物体的Position,让模型视觉上居中。
5.4 大装配体导入卡死
最后一个坑是导入大装配体时Unity卡死。一个包含几百个零件的装配体,FBX文件可能有好几百MB,Unity导入时需要解析所有网格和材质,内存占用飙升,配置差一点的电脑直接卡死。
解决方法是分批导入。在SolidWorks里把装配体拆成几个子装配,分别导出FBX,然后在Unity里逐个导入。导入完成后,再用脚本或者手动把子装配组合到一起。虽然麻烦一点,但至少不会卡死。
另外,导入前可以把FBX文件里的网格压缩一下。SolidWorks导出时有一个"压缩"选项,可以减小文件体积。Unity导入设置里也有"Mesh Compression"选项,可以进一步压缩网格数据。但压缩会损失精度,需要根据实际情况权衡。
6. 一套可复用的检查清单
做了几个项目之后,我总结了一套检查清单,每次从SolidWorks导出模型到Unity之前,都会对照着过一遍。这套清单帮我省了不少返工的时间。
SolidWorks端:
- 装配体层级是否已经扁平化,需要独立控制的零件是否在顶层
- 网格精度是否根据零件在场景中的角色做了区分
- 单位是否设置为米,坐标系是否确认无误
- 材质名称是否唯一且有明确含义
- 装配体原点是否在合理位置
- 是否需要展开UV,是否需要输出纹理坐标
Unity端:
- Scale Factor和Convert Units是否正确
- 法线方向是否正确,是否需要翻转
- 材质是否已经重新创建并赋值
- 节点命名是否规范,是否创建了根节点
- 碰撞体是否按需添加,是否使用了合适的类型
- LOD、遮挡剔除、GPU Instancing是否按需配置
- 物理设置是否需要调整(针对小尺寸模型)
这套清单不是死的,不同项目可以根据实际情况增减。但核心思路是一样的:在导出前多想一步,在导入后少改十次。
从SolidWorks到Unity的这条工作流,说到底是一个数据转换和适配的过程。SolidWorks的精确性和Unity的实时性各有各的优势,关键是怎么在两者之间搭一座可靠的桥。桥搭好了,后面的交互开发、性能优化、场景搭建才能顺利展开。希望这篇文章能帮你少走一些弯路,把更多时间花在真正创造价值的地方。