学习进度
0/9 已勾只保存在这台浏览器;勾满 9 项即视为本题完成。
先把题目说成人话
这题表面上在问“怎样批量重算法线、重新铺开贴图坐标”,真正困难的却不是按下按钮,而是先分清哪些数据缺了、哪些数据已经被美术认真做好。批量处理不代表所有模型执行同一个动作,而是让所有模型经过同一套判断,再分别进入补充、保留或退回制作软件三条路线。
模型从制作软件送进引擎时,常装在一种交换文件(FBX)里。模型表面供颜色贴图定位的展开叫主贴图展开(UV0);专门给烘焙光照使用的另一套展开叫光照展开(UV2);告诉光照表面朝向的箭头叫法线(Normal)。引擎(Unity)能按明确规则补后一套烘焙展开或选择法线来源,却不能凭一个开关猜出美术希望怎样切开和排列主贴图展开。
这是虚构教学故事,不是个人项目经历。
场景:先让问题变得看得见
测试场景里并排放着一面石墙、一把金属刀和一个角色头部。它们都来自同一批模型,却带着完全不同的制作意图。
(右脑)画面:石墙烘焙以后,砖缝之间冒出几条不该出现的黑线;金属刀在灯下原本有一道贴着刀刃移动的锐利高光;角色脸上的眼影和唇色也准确贴着五官。小明按下“一键修复”,石墙黑缝少了一些,刀刃上的高光却被摊成一大片,整把刀像软塑料;角色眼角的颜色同时被扯向脸颊。工具没有报错,它只是把三件完全不同的数据全部重做了一遍。影片倒回去以后,石墙只补一张烘焙用展开图,刀保留原来的表面方向,角色头部则带着问题标记回到制作软件;三件资产终于各自解决自己的问题。
(左脑)对表:现在把三种画面分别贴回数据。石墙的黑缝可能来自缺失或重叠的 UV2,但必须结合展开图和烘焙结果确认;刀的高光变软,说明原有法线可能被重算覆盖;角色脸上的颜色错位,说明承载贴图绘制的 UV0 被改变。前两句描述的是观察到的结果,原因仍需逐项验证。UV 坐标没有长度单位;平滑角通常以度为单位;法线没有长度单位,却必须说明它处于模型、切线还是世界空间。这里首先得到的不是一条万能规则,而是三个不能混在一起的问题。
小明:先形成一个完整猜测
小明的第一反应很合理:既然问题成批出现,就在导入时对所有模型调用 RecalculateNormals(),再统一生成一套新 UV。石墙确实可能因此改善,所以这个想法并非毫无根据。
真正的问题出现在另外两件资产上。重算法线抹掉了金属刀原本的硬边与高光设计;Unity 自动生成的 Secondary UV 只是在准备烘焙用的 UV2,并没有替美术重新制作 UV0。小明的试验留下了一条有用结论:批量工具可以重复执行明确规则,却不能自动猜测艺术意图。
小红:把猜测放回现实检查
小红先把“一键修复”拆开,并且只预览将要发生的变化,不立即修改文件。报告会直接列出每件资产当前有什么、缺什么以及计划做什么。
- 石墙若是静态场景模型、又确实没有可用的 UV2,可以单独生成 UV2,再重新烘焙检查黑缝与重叠。
- 金属刀若带有美术制作的法线,就保留导入结果;只有项目规则明确允许的模型才重新计算。
- 角色的 UV0 若有拉伸、切缝或排布问题,工具只报告问题并退回制作软件,不假装 Unity 能理解五官应该怎样展开。
代码只负责执行这项已经做出的判断:
sealed class FbxRulePostprocessor : AssetPostprocessor
{
void OnPreprocessModel()
{
if (!RuleDb.TryGet(assetPath, out var rule)) return;
var importer = (ModelImporter)assetImporter;
if (rule.keepAuthoredNormals)
{
importer.importNormals = ModelImporterNormals.Import;
}
else
{
importer.importNormals = ModelImporterNormals.Calculate;
importer.normalSmoothingSource =
ModelImporterNormalSmoothingSource.FromAngle;
importer.normalSmoothingAngle = rule.smoothingAngle;
}
importer.generateSecondaryUV = rule.generateLightmapUv2;
}
}
这段 AssetPostprocessor / ModelImporter 代码能选择“保留法线还是重算”“是否生成 UV2”,却没有也不应该替代分类规则。每次只改变其中一项,再回到同一个场景检查高光或烘焙结果,才能知道是哪次改动产生了变化。
重算法线还会改变切线空间的基础。若模型使用按照旧法线与切线烘焙的 Normal Map,重算以后可能出现新的接缝或高光偏移,因此切线和法线贴图也必须一起回归。直接调用 Mesh.RecalculateNormals() 也不会自动恢复美术的平滑组:导入过程可能已经在 UV seam、硬边或材质边界把同一位置拆成多个顶点,函数会把这些分开的顶点当成彼此无关的数据。
我:用检查结果修正模型
我在小红的分流之外再补上一件事:判断正确,不代表批量执行一定安全。工具应当先扫描并给出预览报告,让人知道它准备改谁、改什么;确认以后再保存旧设置、执行导入并复查结果。某个资产验证失败,就恢复它原来的导入设置并重新导入,而不是让半成品继续留在项目里。
AssetDatabase.StartAssetEditing() 只会暂时停止自动导入,StopAssetEditing() 只会恢复导入流程,它们不是撤销按钮。真正的恢复仍要依靠修改前保存的 ModelImporter 值、版本控制记录和重新导入后的核对。
若工具必须直接改写网格,应生成带来源记录的独立 .asset,不要静默改动 FBX 子资源。若题目中的“UV 拆分”确实指 UV0,则要调用固定版本的 DCC 脚本,或接入真正实现切缝、展开与排布的算法;只打开 generateSecondaryUV 不能宣称已经重做 UV0。
(右脑)画面修正:影片重新播放。石墙被单独送去补一张烘焙用的展开图,回来后重新照灯检查;金属刀保留原来的表面方向,那道锋利高光仍在;角色头部没有进入自动修改,而是带着清楚的问题标记回到美术制作台。每件资产旁边还保留着修改前的照片和设置,新的结果不对,就能立刻回到原样。
(左脑)严格对表:完整顺序是扫描资产 → 判断数据用途 → 只预览差异(dry-run)→ 人工确认 → 保存旧值 → 执行一项修改 → 重新导入 → 在同一场景验证 → 成功则记录,失败则恢复。法线只对规则允许的资产切换 Import 或 Calculate;UV2 只对适用的静态模型生成;没有真正实现切缝、展开和排布时,UV0 只能报告或交给 DCC。验收要分别看刀的高光、贴图接缝、UV 重叠和烘焙结果,而不是只看脚本是否运行成功。
目前没有证据证明你开发过这款 FBX 批处理工具。你的公开网站可以证明你做过 Unity Editor 工具和资产工作流,但不能替你补写这项具体工具的真实经历;本页只能保留为设计方案。
老师解惑
- UV0 与 UV2:UV0 通常承载颜色、法线等材质贴图;UV2 常用于烘焙光照。都叫 UV,不代表可以互相替换。
- 导入法线与重算法线:Import 保留文件里的法线;Calculate 让 Unity 按规则重算。选择依据是资产意图,不是哪个按钮更自动。
- Importer 与网格副本:能通过
ModelImporter声明的规则就留在导入设置里;必须改网格时,生成可追溯副本比覆盖来源更安全。 - 可重跑:同一份输入和同一版规则再次执行,应得到相同结果;失败后必须知道资产是否真的恢复。
工具分工
- C# 扫描器:读取导入设置和网格信息,先报告候选修改。
AssetPostprocessor/ModelImporter:执行已经确认的法线来源、平滑角和 UV2 规则。- DCC:处理 UV0 艺术布局和其他需要理解模型用途的工作。
- 版本控制与代表场景:一个负责找回原状,一个负责证明画面没有被改坏。
记忆钩子
先问数据是谁做的、用来做什么,再决定自动生成、原样保留,还是退回给人。
这是教学场景,不是目标项目的真实捕获或个人经历。
下一步:验证这条因果
UV 接缝怎样拆分顶点,以及 ModelImporter、法线、切线与派生 Mesh 的修改边界。
教学边界:它使用固定网格做 dry-run,不读取或写回真实 FBX,也不能替代 DCC、版本控制和项目批处理验收。