☰
Blender模型拆分与合并:材质UV顶点组不丢失的流程与排错
2026/11/2 3:24:58 网站建设 项目流程

1. 拆分与合并这道坎,卡住的到底是什么

模型拆分与合并这两个操作,命令本身简单到不能再简单——一个 P 键,一个 Ctrl+J。但我在带新人的时候发现,真正让大家反复返工的从来不是“不会按”,而是按完之后模型炸了、材质乱了、UV 丢了、骨骼不认了。拆分(Separate)和合并(Join)本质上是在物体层级和网格数据层级之间来回搬运信息,而这个搬运过程里有一堆默认行为是你没被告知的,它们不会报错,只会安静地把你的工作成果啃掉一块。

这篇内容想解决的就是这件事:把拆分与合并从“两个快捷键”变成一套可复现、可回滚、出错能定位的流程。里面会涉及编辑模式的分离菜单、松散部件拆分、材质槽的重映射、UV 与顶点组的存活规则、布尔与重网格这类“另一种合并”,以及一批我用血泪换来的排查顺序。适合已经能建出完整模型、但一进到资产整理和交接环节就开始头疼的人;纯新手也能看懂,因为我会先把每个操作在干什么讲清楚,再谈坑。

我个人的判断标准很直接:看一个人是不是真的过了 Blender 进阶线,不看他会不会做拓扑,看他拆完再合回去,模型还是不是原来那个模型。这句话听着夸张,你做一遍就知道了——一个带四层材质、两套 UV、三个顶点组、一个形态键的角色部件,拆开再合回来,能原样不动的人并不多。

1.1 为什么“能拆能合”是建模效率的分水岭

拆分与合并真正的价值不在建模阶段,而在资产流转阶段。你做出一个整体模型,接下来必然要面对这些事:某个部件要单独交给别人改、某个零件要单独做 LOD、某个结构要在引擎里做成可替换插槽、某块面板要单独展 UV 做贴图、某个外壳要抽出来做 3D 打印的独立实体。这些需求没有一个能靠“整体模型”满足,全部要求你把模型切成合理的块。

而合并的价值在另一个方向:当你从资源库里拼装场景、从扫描数据里清理碎片、从布尔运算的残骸里收拾出一个干净外壳时,你需要把一堆散件焊成一个语义完整的整体,否则后面展 UV、做碰撞、加修改器都无从下手。拆分是“把整体变成可管理的部件”,合并是“把碎片变成可管理的整体”,两个方向服务于同一个目标——让模型的粒度和你的工作粒度对齐。

提示:判断该不该拆,问自己一句“这块东西未来会不会被单独替换或单独贴图”。会,就拆;不会,就别拆。过度拆分带来的管理成本,往往比它省下的时间更高。

1.2 三种典型场景与对应选型

同样是“拆”,不同场景该走的路径完全不同,选错了后面全是补锅。我把它归成三类,你对号入座即可。

场景典型诉求推荐路径不推荐的做法
硬表面产品/机械件每个零件独立,方便单独倒角、单独赋材质编辑模式按选中元素分离(P → 选中项)用松散部件拆分(装配体互相穿插时会炸成上百块)
角色/生物保留整体,只把服装、配件分出来按材质分离 + 手动框选按松散部件拆(拓扑连在一起,拆不开)
扫描件/导入模型清理成少数几个可用整体先合并,再按松散部件反向拆分筛选直接对原始数据做布尔

这张表背后是一条朴素的原则:拆分方式要匹配模型的“语义边界”,而不是几何边界。机械件的语义边界就是零件边界,所以按零件选。角色的语义边界是材质和服装分组,所以按材质走。扫描件根本没有语义,只有几何连通性,所以只能靠松散部件去逼近。

2. 动手之前:把可逆性做进流程

我见过太多人拆到一半发现拆错了,然后疯狂 Ctrl+Z,撤销栈一爆就只能重开。拆分与合并这类“结构性操作”的第一原则不是快,是可逆。可逆性不是靠撤销,是靠流程设计:动手前存版本、动手时留原件、动手后能验证。

2.1 五项开工前体检

在按 P 之前,花两分钟把这五项过一遍,能省掉后面两小时。

  1. 应用缩放。物体缩放不是 1 的时候,分离出来的新物体继承的是同一个缩放,但一旦你后面做布尔、做重网格、做物理,非均匀缩放会让结果完全不可预测。选中所有物体,Ctrl+A 选“缩放”。
  2. 检查数据块用户数。在属性编辑器切到物体数据标签,看名字旁边有没有数字。有数字说明这个网格数据被多个物体共用(通常是 Alt+D 关联复制出来的),这时候分离、合并、改材质都会互相影响。需要独立就在物体菜单里做“使其独立化 → 物体与数据”。
  3. 确认活动物体。合并的方向完全由活动物体决定,动手前先在 Outliner 里点准它。
  4. 记录材质槽顺序。打开材质属性面板,把槽位顺序截图或者记下来。合并后槽位会重排,这个记录是你后面核对错位的唯一依据。
  5. 另存一个版本。文件名后缀加_pre_split,别嫌土。

注意:第 2 项是重灾区。关联复制的模型,你在一份上改材质,另一份跟着变,很多人以为是软件出 bug,其实是数据共用。Outliner 切到“Blender 文件”视图能一眼看到每个网格被几个物体引用。

2.2 原点、变换、命名的统一规范

原点是 Blender 里最容易被忽略、又最容易在合并时造成灾难的东西。每个物体都有自己的原点,合并之后只保留活动物体的原点,其他物体的原点信息全部消失——这本来没问题,因为顶点的世界坐标被正确保留了。但如果你后面要用“原点”做旋转轴、做阵列、做镜像,原点位置错了就是全盘错。

我自己的习惯是:拆分前把所有部件的原点统一下来。选中物体,物体菜单 → 设置原点 → 原点至几何中心,或者原点至 3D 游标(先把游标 Shift+S 吸附到世界原点)。批量处理用脚本更快:

import bpy # 把所有选中网格物体的原点移到各自几何中心 bpy.ops.object.select_all(action='DESELECT') for obj in bpy.context.scene.objects: if obj.type == 'MESH': obj.select_set(True) bpy.context.view_layer.objects.active = bpy.context.selected_objects[0] bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')

命名规范看着无聊,但在拆分场景里是刚需。松散部件拆分一次能给你产出几十个物体.001、物体.002,过一个小时你根本分不清谁是谁。我用的格式是部件名_类型_序号,比如外壳_panel_003、螺丝_screw_012。批量改名可以在 Outliner 里 F2 逐个改,也可以走脚本,见 3.4 节。

2.3 备份策略:版本化保存与临时集合

除了存文件,我还建议在场景里建一个叫_backup的集合,把原始物体复制一份扔进去,然后在大纲视图里点掉它的渲染可见性和视图可见性。这样你在拆分过程中随时能对着原始形态比对,也不怕不小心把原件改坏。工作完再把这个集合整个删掉,不污染文件。

这个习惯在材质修复环节价值特别大:合并完发现材质错位,你可以直接把备份的原件拖进来,用“从物体复制材质”这类思路手动比对,比凭记忆猜槽位顺序靠谱得多。

3. 拆分:四条路径的操作细节与取舍

拆分的实现路径有四条,从最粗糙到最精确依次是:按松散部件、按材质、按选中元素、按脚本批量。它们的适用边界很清晰,混用就会出事。

3.1 按选中元素分离(P → 选中项)

这是最常用也最可控的一条。进入编辑模式,切到面选择模式(快捷键 3),选中你要独立出去的几何,按 P,选“选中项”。原物体保留剩下的部分,新物体带着选中的面独立出来,名字自动加后缀。

这条路径有三个必须知道的细节。

第一,分离出来的新物体继承原物体的修改器和父级关系。这意味着如果你原物体上挂着镜像修改器、细分修改器,新物体上也有一份,而且镜像的轴心可能已经不对了。我的处理习惯是先在一个“干净”的状态下分离——把修改器临时关掉(点掉修改器面板上那个显示器图标),分离完再按需恢复。

第二,选中项分离是按“面”搬运的,边界上的顶点会被复制。也就是说分离面之后,原物体和新物体的接缝处会各有一套重合顶点。这在视觉上完全看不出来,但会让你的顶点数莫名其妙变多,做焊接、做对称、算法线的时候出现黑边。要避免就切到边模式,用“边分离”而不是“面分离”,或者在分离前把接缝边标记清楚。

第三,顶点点组和 UV 会跟着走。这是好事,分离出来的部件还认原来的骨骼权重,UV 也不会错。但要注意顶点组里那些“只属于别的部件”的组会被保留成空组,几十个空顶点组挂在物体上,做权重归一化的时候会很烦,需要清理。

实操心得:分离前先在 Outliner 里给原物体改个能看懂的名字,因为新物体叫“原名.001”,你不改原名,一天之后就是一堆.001 .002 .003,谁也救不了你。

3.2 按松散部件分离(Separate by Loose Parts)

编辑模式下全选,按 P,选“按松散部件”。Blender 会按几何连通性把网格切碎——只要两组面之间没有共享顶点或边,就会被判为两个部件。

这个功能是把双刃剑。用对了它是救星:导入的扫描数据、从别的软件导出的模型、做完布尔之后的一堆碎片,用它一次性拆干净,然后人工筛选保留需要的部分,效率极高。用错了它是灾难:一个硬表面装配体,零件之间靠穿插摆在一起而不是焊在一起,全选按松散部件,瞬间炸出一百多个物体,Outliner 直接爆炸。

判断该不该用的方法很简单:进入编辑模式,全选,看状态栏显示的“孤立”数量(或者看选中后断开的部分有多少个视觉上独立的面片)。如果明显是一体的拓扑,松散部件拆不开;如果是拼接的碎片,一拆就散。

遇到“本来该是一个整体,结果被拆成一堆”的情况,正确做法不是硬拆,而是先焊接再拆:全选,M 键 → 按距离,把距离阈值调到刚好能焊住接缝(一般 0.0001 到 0.001 米之间,具体看模型尺度),焊完再决定要不要拆。这个顺序我踩过坑:先拆后焊,工作量翻十倍。

3.3 按材质分离与按边切断

按材质分离(P → 按材质)的逻辑是:每个材质槽对应生成一个物体。它的最大价值在于语义清晰——玻璃、金属、塑料、贴图面各成一个物体,后面单独调材质、单独做透明、单独烘焙都方便。角色模型把皮肤、衣服、头发、眼睛按材质拆开,是常规操作。

但它有个隐蔽行为:没有赋予材质的面(材质索引指向空槽)也会被单独分出去。所以拆完发现多出来一个空材质的物体,别慌,那是你漏赋材质的面。这也算是个免费的检查工具。

再说按边切断。编辑模式下按 Y(分离面)或者 V(断开顶点),这些不是在物体层级拆分,而是在网格内部把面拆开。用途很具体:给一块平板做厚度、做一个需要独立运动的布片、把某个面单独拎出来做贴图。这类操作之后一定要全选按 M 焊接一次,把该连的地方连回去,否则你得到一个看着完整、实际四处漏风的模型,法线计算会异常。

拆分方式快捷键入口保留修改器保留顶点组主要风险
选中项P → 选中项是是接缝处重复顶点
按材质P → 按材质是是空材质面单独成体
按松散部件P → 按松散部件是是穿插结构炸成碎片
内部断开Y / V不适用是法线异常、漏面

3.4 用 Python 做批量拆分

当你要拆的对象超过五个,手动操作就开始亏了。拆几十个零件的装配体,脚本是唯一解。核心就三行:切编辑模式、全选、调分离。真正有价值的是后面的重命名和整理。

import bpy def separate_loose_and_rename(obj, prefix="part"): """把目标物体按松散部件拆开,并按序号重命名""" bpy.ops.object.select_all(action='DESELECT') obj.select_set(True) bpy.context.view_layer.objects.active = obj bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.separate(type='LOOSE') bpy.ops.object.mode_set(mode='OBJECT') parts = [o for o in bpy.context.selected_objects if o.type == 'MESH'] # 按体积排序,方便把大件排前面 parts.sort(key=lambda o: o.dimensions.x * o.dimensions.y * o.dimensions.z, reverse=True) for i, o in enumerate(parts): new_name = f"{prefix}_{i:03d}" o.name = new_name o.data.name = f"{new_name}_mesh" print(f"共拆分出 {len(parts)} 个部件") return parts # 用法:选中要处理的物体后运行 target = bpy.context.active_object if target and target.type == 'MESH': separate_loose_and_rename(target, prefix="外壳")

separate的type参数除了'LOOSE',还支持'SELECTED'(选中项)和'MATERIAL'(按材质),跟菜单里的三个选项一一对应。按体积排序这一手是我自己加的,因为松散部件拆出来的顺序是按内部索引来的,完全没有可读性;按体积降序排,至少主结构会排在前面,找起来方便。

注意:脚本跑之前一定要存盘。批量拆分是不可撤销的(撤销一次只回退一个物体的状态,几十个物体够你按到手酸)。

4. 合并:顺序、基准与合并后的收尾

合并(Ctrl+J)在物体模式下把选中的网格物体合成一个。就这么一句话,但里面的默认行为足够写一整页注意事项。

4.1 Join 的坐标基准与“活动物体”逻辑

先说一个流传很广的误解:“最后选中的物体是合并后的主体”。这个说法在手动操作里“看起来”成立,因为 Blender 在你点击选择的时候会把最后点击的那个设为活动物体。但真正的规则是:合并到活动物体上。活动物体是在 Outliner 里高亮显示的那个,你可以在属性面板最上方看到它的名字。在脚本里,活动物体是你自己赋给view_layer.objects.active的那个,跟选择顺序毫无关系。

这个区别为什么重要?因为合并结果的所有“归属”都跟着活动物体走:

  • 合并后的物体名 = 活动物体的名字
  • 合并后的原点 = 活动物体的原点
  • 合并后的修改器 = 只有活动物体的修改器保留,其他物体的修改器全部丢弃
  • 合并后的父级 = 活动物体的父级
  • 合并后的集合归属 = 活动物体所在的集合
  • 合并后的变换 = 活动物体的变换,其他物体的顶点坐标被换算到活动物体的局部空间

所以我在合并前的固定动作是:先把要作为主体的那个物体选中并设为活动(在 Outliner 里点它),确认它的原点在合理位置,确认它上面挂的修改器是我想要的那一套,然后再加选其他物体。

import bpy def join_to(target, others): """把 others 合并到 target 上,target 必须是网格物体""" bpy.ops.object.select_all(action='DESELECT') for o in others: if o.type == 'MESH': o.select_set(True) target.select_set(True) bpy.context.view_layer.objects.active = target # 关键:显式指定 bpy.ops.object.join() return target target = bpy.data.objects.get("外壳_panel_000") others = [o for o in bpy.context.selected_objects if o is not target] if target and others: join_to(target, others)

另外提醒一个新手常犯的错:合并只能在物体模式做。在编辑模式按 Ctrl+J 什么都不会发生(它不会报错,所以你也不知道自己错了)。

4.2 合并后的三件套:重合点、法线、UV

合并完成的那一刻,你的模型通常处于一种“几何上拼在一起、数据上各说各话”的状态。必须做的收尾有三件事,顺序不能乱。

第一件,焊接重合点。合并只是把两组顶点放进同一个网格,接缝处如果两边本来就有重合顶点,它们不会自动焊上。进编辑模式,全选,M 键 → 按距离。阈值怎么定?我的经验是取模型最小特征尺寸的 1/100 到 1/1000。一个 10 厘米的零件,最小特征可能是 0.5 毫米的倒角,那阈值就用 0.0005 米。太小焊不上,太大会把该保留的细节吃掉。用一个保守值跑完,看一下状态栏提示“移除了 X 个顶点”,X 如果是 0,说明你阈值太小或者本来就没重合点。

第二件,重算法线。全选,Shift+N(重新计算外侧)。如果模型里有开放面、有内部结构,法线可能还是会乱。这时候切到面模式,看有没有黑面——黑面就是法线反了的面,选中它,Alt+N → 翻转。这一步不能省,因为法线错了,后面无论做布尔、做重网格、做烘焙全都会崩。

第三件,检查 UV。合并后的 UV 遵循一条规则:同名的 UV 层会合并成一张,不同名的会并列成多张。这意味着如果两个物体的 UV 层一个叫UVMap、一个叫UVMap.001,合并后你会有两张 UV 贴图,而材质里的贴图节点只认其中一张,另一半模型的贴图就跑到别的地方去了。检查方法是在编辑模式打开 UV 编辑器,看右上角的 UV 层下拉列表有几个。超过一个就要判断:是该统一名字,还是该重新展 UV。

实操心得:合并前把所有物体的 UV 层统一命名成UVMap,能省掉 90% 的贴图错位问题。批量改名在 UV 编辑器右上角的下拉框里点开就能改,但只能一个一个物体改,所以合并前先统一,比合并后补救容易得多。

4.3 哪些东西在 Join 时会被静默丢掉

这是本文最该记住的一节。Join 不会弹窗警告你“某些数据将丢失”,它直接干。以下这些,合并时一定要心中有数:

  • 修改器:只有活动物体的修改器保留。如果你给每个零件都加了倒角修改器,合并后只剩一个物体的倒角,其他全部消失,而且已经应用到几何上的部分不会回退,你会得到一个“部分倒角”的模型。
  • 形态键:如果活动物体没有形态键,其他物体的形态键会被丢弃;如果活动物体有,其他物体的形变数据通常不会正确参与。这一条我建议直接用“绝不合并带形态键的物体”来规避,需要合并就先应用或导出备份。
  • 物体级别的自定义属性:物体的自定义属性面板里那些键值对,只有活动物体的保留。
  • 动画数据:物体层级的关键帧只有活动物体的保留,其他物体上的动作数据变成孤儿。
  • 父子关系:只有活动物体的父级保留,其他物体的父级被抹掉,但顶点的世界坐标不受影响,所以视觉上不会跳。
  • 约束:跟修改器一样,只保留活动物体的约束。
  • 顶点组:同名的顶点组合并,不同名的全部保留成独立组。所以合并后可能会有几十个顶点组,做权重归一化会很烦。

规避思路就一条:把“只该保留一份”的东西集中到主体上,把“每个部件都不该丢”的东西在合并前先处理掉。修改器该应用的先应用,形态键该导出的先导出,动画该烘的烘。

4.4 布尔与重网格:另一种“合并”

有些合并需求用 Join 解决不了,因为它们要的是“几何层面的融合”而不是“数据层面的打包”。

布尔合并适合两个实体需要真正变成一整块的情况,比如把一个凸台焊到一个平面上、把一个圆柱掏进一个方块里。做法是给主体加布尔修改器,操作类型选并集,目标物体选另一个,然后应用修改器。布尔修改器里有个“材质”选项,默认是“索引”,意思是两个物体的材质索引按数值对应;改成“传递”,则会尽量按目标物体的实际材质分配去映射。做多材质模型时,这个选项一定要手动检查,我遇到过默认设置下整个零件变成单一材质的情况。

布尔最大的坑是非流形几何。两个物体如果有共面重合、有开放边界、有零厚度面,布尔的“精确”求解器会给出诡异结果:出现碎面、出现内部空腔、出现法线翻转。排查顺序是:先看图,在编辑模式选中所有非流形边(选择菜单 → 按特征全选 → 非流形),能修的先修,修不了就改用快速求解器试一次,或者干脆手工做拓扑。

重网格合并适合扫描件、雕塑、需要统一拓扑的有机形体。体素重网格把所有碎片融成一个外壳,代价是 UV 必然丢失、材质索引有概率丢失、细节精度取决于体素大小。体素大小的经验值是模型最小特征尺寸的 1/3 左右,太小会卡死,太大会把细节糊掉。我一般先用 0.01 米试一遍,看轮廓对不对,再往下调。

注意:重网格会改变顶点数,所有基于顶点索引的东西(顶点组权重、形状键、顶点色)都会失效。做之前一定另存版本。

5. 材质修复:拆分合并最容易崩的一环

如果只能记住这篇里的一个部分,我建议你记这一节。拆分合并导致的问题里,几何类的都还能手工修,材质类的往往要重做。

5.1 材质槽错位的三种成因

成因一:槽位重排。合并时,Blender 会先把所有物体的材质槽汇总成一个列表,然后重映射每个面的材质索引。如果两个物体有相同的材质,理论上会合并成一个槽;但实际表现跟版本、跟材质槽的“链接”设置都有关系。我遇到过的典型症状是:合并前每个零件颜色都对,合并后所有零件变成同一种颜色——因为所有面的材质索引都被映射成了 0。

成因二:材质槽的链接方式。材质槽面板里有一个“链接”下拉,默认是“数据”。如果模型是从别的文件链入或者从别的软件导入的,这个值可能是“物体”。链接到物体意味着材质指派存放在物体槽位上,而不是网格数据上。这种模型分离或合并之后,你会看到“属性面板里明明写着这个材质,模型上却是另一个”的诡异现象。判断方法很简单:左下角状态栏会显示当前槽位的链接对象。处理方式是双击那个链接下拉,切回“数据”。

成因三:物体级别的材质偏移。属性面板材质标签右侧有个“设置”,里面有一项“材质偏移”。如果有人动过这个值,槽位显示和实际渲染就会错开。这个值正常情况下应该是 0。

排查顺序我固定成三步:先看每个面的实际材质索引(编辑模式面模式,开“面信息”叠加层能看到),再看槽位列表的顺序,最后核对材质的链接方式。三步走完,问题基本就定位了。

5.2 贴图丢失与 UV 错乱的抢救流程

合并后出现粉红色、紫红色,或者模型一片纯白,说明贴图丢了。分两种情况。

情况一:图像文件的路径断了。移动过项目文件夹、从别人电脑上拷来的工程、用了相对路径但目录结构变了,都会这样。抢救方法是打开“外部数据”菜单,先做“查找缺失的文件”,指向原来的贴图目录,让它批量重链;然后做“使所有路径为相对路径”,把工程变成可移动的。如果工程要发给别人,最后一步做“打包资源”,把贴图塞进 .blend 文件里,这样就不怕路径问题。打包之后文件会变大,但交接的时候绝对是正确做法。

情况二:UV 层被换掉了。贴图在、路径也对,但图案完全错位。这就是 UV 层的问题。前面提过,同名 UV 层合并、不同名并列。如果模型有两套 UV(比如一套是原始展 UV、一套是烘焙用的),合并后材质节点里的 UV 贴图节点可能指向了错误的那一套。处理方法是在材质节点编辑器里,把 UV 贴图节点的“UV 贴图”下拉手动指定到正确的那张,或者干脆统一重命名。

批量检查贴图路径用这段脚本,一秒扫完全场:

import bpy, os missing = [] for img in bpy.data.images: if img.source != 'FILE': continue abs_path = bpy.path.abspath(img.filepath) if not os.path.exists(abs_path): missing.append((img.name, img.filepath, img.users)) for name, path, users in missing: print(f"缺失贴图: {name} | 路径: {path} | 引用数: {users}") print(f"共 {len(missing)} 张贴图路径失效")

“引用数”这一列很关键。引用数是 0 的说明是孤儿数据,直接删掉;引用数高的说明多个材质共用,修的时候要慎重,别删错。

5.3 用脚本批量重挂材质与清理空槽

分离产生一堆物体之后,最常见的脏数据是空材质槽和重复材质槽。空槽会让材质索引指向空气,渲染出来是默认灰;重复槽会让同一个材质在列表里出现好几次,你在 UI 里改了一个,另一个还是老样子,改半天没反应。

清理逻辑是:先删掉没有材质的空槽,再把指向同一材质的重复槽合并,最后把所有面的材质索引重新映射。脚本长这样:

import bpy def dedupe_material_slots(obj): """合并重复材质槽,删除空槽,并重映射所有面的材质索引""" mesh = obj.data if not mesh.materials: return unique_mats = [] # 去重后的材质列表 key_to_new = {} # 材质标识 -> 新槽位号 old_to_new = {} # 旧槽位号 -> 新槽位号 for i, slot in enumerate(obj.material_slots): mat = slot.material if mat is None: old_to_new[i] = None # 空槽待删除 continue key = mat.name_full if key not in key_to_new: key_to_new[key] = len(unique_mats) unique_mats.append(mat) old_to_new[i] = key_to_new[key] # 重映射面索引 for poly in mesh.polygons: new_idx = old_to_new.get(poly.material_index, 0) poly.material_index = 0 if new_idx is None else new_idx # 重建材质列表 mesh.materials.clear() for mat in unique_mats: mesh.materials.append(mat) print(f"{obj.name}: 槽位 {len(old_to_new)} -> {len(unique_mats)}") # 对选中物体批量执行 for obj in bpy.context.selected_objects: if obj.type == 'MESH': dedupe_material_slots(obj)

这段脚本我用了一年多,救过无数次。要注意的是,它对“空槽上的面”统一映射到 0 号材质,所以跑之前最好确认 0 号槽是个合理值,否则那些面会全部变成 0 号材质。更稳妥的做法是手动检查空槽里的面再决定。

5.4 顶点色、自定义属性与平滑标记

还有三样东西容易被忽略,但一旦丢了就不好补。

顶点色 / 颜色属性:合并时,同名的颜色属性层会合并,不同名的会各自保留成独立层。这听起来跟 UV 一样安全,但实际上不同版本的合并行为有差异,我遇到过图层名相同但数据类型不同(一边是字节色、一边是浮点色)时丢数据的情况。项目里用顶点色做遮罩的,合并前先统一数据类型。

自定义属性:网格数据上的自定义属性(属性面板里能看到)合并时保留,物体上的自定义属性只有活动物体的保留。如果游戏引擎里靠自定义属性驱动特效,这一条会直接导致功能失效。

平滑标记 / 自动平滑:这是版本差异最大的一处。较老的版本里,网格数据有个“自动平滑”开关和一个角度值;新版本(4.1 之后)取消了这个开关,改成用“按角度平滑”的修改器或者几何节点组来实现,快捷键入口是右键菜单里的“平滑着色(按角度)”。这意味着,你从老工程里合并进来的物体,平滑标记的存储方式可能和新版本的物体不一致,合并后整片区域变成硬边,或者整个模型糊成一团。

我的处理方式是:合并前把所有物体的平滑状态统一一次——全选,物体菜单里做一次“平滑着色”,然后对需要硬边的物体单独用“按角度平滑”,角度值统一成 30 度。统一之后合并,结果就是可预测的。

提示:如果合并后发现某个区域出现一片一片的硬边,先别改拓扑。选中那些面,右键看“平滑着色”是不是被关掉了;如果是,按一次就能恢复。这个症状 90% 是平滑标记问题,不是法线问题。

6. 故障速查表与踩坑记录

前面讲了原理,这一节讲症状。出问题的时候,人是没耐心从头读原理的,直接查表。

6.1 症状—原因—处置对照表

症状最可能的原因处置动作发生概率
合并后所有零件变成同一颜色材质索引全被映射到 0 号槽检查材质槽“链接”是否为数据;用 5.3 的脚本重映射高
合并后接缝处有黑线或破面重合点未焊、法线不一致M 按距离 + Shift+N 重算法线高
分离后模型体积变大、面数暴涨面分离导致边界顶点复制改用边分离,或分离后 M 焊接高
松散部件拆分直接炸出上百块模型是穿插拼装而非焊接一体先全选 M 按距离焊接,再决定是否拆高
合并后 UV 贴图错位两套 UV 层未同名UV 编辑器里统一图层名,材质节点重新指定中
合并后修改器消失只保留活动物体的修改器合并前把需要保留的修改器应用掉中
贴图全变粉红/纯白图像路径失效外部数据 → 查找缺失文件 → 打包资源中
分离出的部件不跟随骨骼顶点组跨部件、修改器继承异常检查顶点组,确认骨架修改器指向正确中
合并后局部出现硬边平滑标记存储方式不同统一做一次按角度平滑,角度 30 度中
布尔结果出现碎面非流形、共面重合清理非流形,换求解器,或手做拓扑低但严重

6.2 六个真实踩过的坑

坑一:先拆后焊,工作量翻倍。我第一次处理一个拼装外壳,直接按松散部件拆,结果炸出八十多个物体,每个都是薄片。正确顺序是先用一个极小的距离阈值把接缝焊上,让整个壳变成连通拓扑,再看需不需要拆。焊一次只要十秒,收拾八十个碎片要半天。

坑二:在关联复制的物体上改材质。两份模型看着是独立的,其实共用一个网格数据。我在其中一份上合并、清槽位,另一份跟着变,还以为是文件坏了。后来在 Outliner 切到“Blender 文件”视图,一眼看到那个网格的用户数是 2。此后我养成习惯:动材质前先看数据块用户数。

坑三:带着形态键做合并。一个角色的衣服上有形态键做褶皱变化,我为了省事把衣服和身体合并了,结果形态键全丢,动画直接废掉。教训是:带形态键的东西永远不合并,要合并先导出备份或者应用掉。

坑四:以为合并会自动处理材质去重。实际上相同材质在两个物体上的槽位不一定会合并成一个,我遇到过合并后槽位列表里同一个材质出现三次,改哪个都不对。手动跑一遍去重脚本才清爽。

坑五:布尔之前没检查共面。两个平面完全重合的时候做并集,结果是一堆碎片。补救方式是把其中一个物体整体沿法线方向偏移 0.0001 米,问题立刻消失。这个偏移量小到看不出来,但足以让求解器正常工作。

坑六:忘了紫外线和原点。合并前没统一原点,合并后做对称、做阵列、做旋转,轴心全在奇怪的地方。后来我把“统一原点”写进了自己的检查清单,跟“保存”一个级别。

实操心得:把这六个坑浓缩成三条硬规则——动之前存盘、合之前应用修改器、拆之前先焊。这三条能规避掉我遇到过的绝大多数灾难。

7. 串一遍完整流程:从拆到合的实战走查

讲完原理和坑,最后走一遍完整流程。我用一个典型的硬表面外壳来说明:一个由六块面板拼装成的外壳,需要把其中两块的倒角改小,然后重新合成一个整体交给下游。

7.1 需求拆解与拆分方案确定

先看模型结构。六块面板之间是贴合的,接缝处有重合顶点但没有焊上。这种情况如果我直接按松散部件拆,确实能拆成六块,但风险是某些面板内部还有细节件也是独立的,可能一起被拆出来。我实际的做法是:进编辑模式,打开线框叠加层,用框选 + 加选的方式手动选一块面板的所有面,P → 选中项。重复两次,把要改的两块拆出来。

为什么不用松散部件?因为这个模型的面板内部还嵌着一些小结构,它们跟面板不是焊接的,松散部件会把它们也拆成独立物体。手动选面虽然慢一点,但结果是精确的。这就是 1.2 节说的“拆分方式要匹配语义边界”。

拆之前我做三件事:Ctrl+A 应用缩放、统一原点、存一个_pre版本。拆之后立刻检查新物体的修改器列表,确认倒角修改器的参数是完整的。

7.2 编辑与回装

拆出来的两块改倒角,从 0.002 米改到 0.001 米。改完先别急着合,单独渲染一张看看边缘有没有破面。这一步很重要,因为倒角参数改小之后,如果原模型的边角存在极短的边,可能直接生成退化面,渲染时会出现黑斑。

确认没问题后开始回装。回装的顺序是:先把主体(那块没动过的、要作为合并基准的面板)设为活动物体,确认它的原点和修改器都是我要的,然后加选另外五块,Ctrl+J。

合并完成后立刻做三件套:全选 M 按距离(阈值 0.0005),Shift+N 重算法线,检查 UV 层数量。然后跑一遍 5.3 的材质去重脚本,把可能产生的重复槽清掉。最后单独渲染一张对比图,跟_pre版本并排看。

7.3 验收清单

交付前我把验收固定成六个检查项,全部通过才算完成。

  1. 几何:面数合理(合并后不应该比合并前总和多太多,多出来说明有重复面没焊)、无非法几何(编辑模式全选,看状态栏是否提示非流形边)。
  2. 法线:视图里没有黑面,打开背面剔除看有没有漏面。
  3. 材质:槽位数量等于设计数量,每个槽位有正确的材质,无空槽无重复。
  4. UV:UV 层数量符合预期,贴图在渲染视图里显示正常。
  5. 变换:物体缩放为 1,旋转为 0,原点在合理位置。
  6. 数据:无孤儿数据(Outliner 切到 Blender 文件视图,清理未使用的数据块),贴图路径正确或已打包。

这六项走完,模型才算真正“合干净了”。我知道这套流程听着麻烦,但熟练之后每项也就几秒钟,而它省下的是下游环节无穷无尽的扯皮。

最后再分享一个小技巧。如果你经常做类似的拆分合并,可以把 5.3 的去重脚本和 3.4 的拆分脚本放进启动文件,做成一个自己的工具面板,绑几个快捷键。这类重复劳动一旦被固化下来,你会发现自己在资产整理环节的速度是别人的两三倍——不是手快,是少走了那些没人告诉你的弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询