Skip to content

1.7.0

Choose a tag to compare

@AleFeng AleFeng released this 20 Jul 13:25

为网格自动计算平滑法线并烘焙到顶点色 / 切线 / TEXCOORD,用于背面外扩描边,解决硬边处描边断裂。工具本体是纯编辑器 C#,与渲染管线无关。

⚠️ 升级必读

本版改了 TEXCOORD(UV)存储的数据格式只有把平滑法线存在 UV 通道的项目需要动手

你用的存储方式 要做什么
顶点色 / 切线通道 什么都不用做 —— 这两种格式一个字节没变
TEXCOORD(UV) 重新烘焙一次

旧的 UV 数据在 1.7.0无法被解码,症状是描边方向整体错乱、不报任何错

之所以不留兼容开关:顶点装配会把缺失分量补 0,两分量数据与「z 恰好为 0」的三分量数据在着色器里逐位相同,任何运行时判据都存在真实反例。留一个「格式」开关只会把「选错了不报错」的坑再挖深一层,所以直接换掉。

📦 安装

Window > Package Manager → 左上角 +Install package from git URL... → 粘贴:

https://github.com/AleFeng/OutlineSmoothNormalsGenerator.git?path=/Packages/com.alefeng.outlinesmoothnormalsgenerator#1.7.0

⚠️ 装完还必须导入一个 Sample,否则没有描边 Shader。
核心包刻意不含 Shader —— 这是「不引入任何渲染管线依赖」的代价。
Package Manager 里选中本包 → Samples按你的管线导入其中一个

Sample Shader 名
Outline Shader (URP) & Demo OutlineSmoothNormalsGenerator/Outline URP
Outline Shader (Built-in RP) & Demo OutlineSmoothNormalsGenerator/Outline Built-in

安装成功后菜单栏出现 Tools → Smooth Normal Generator

🆕 1.7.0 更新内容

UV 存储改为两分量八面体

TEXCOORD 通道由「三分量原始方向」改为「两分量八面体编码」,每顶点省 4 字节,并与顶点色路径统一到同一套编码。

精度上没有实质代价:两个 float32 的八面体往返角度误差约 5e-6°,直接存 xyz5e-7° —— 同属浮点舍入噪声那一档。作为参照,顶点色的 8-bit 八面体是 0.34°,高 7 万倍却一直够用。

顺带更正一处此前从未写明的事实:Unity 的 Project Settings → Player → Vertex Compression 默认会在构建时把切线与除 lightmap UV 外的 TEXCOORD 压到 fp16,因此打包产物里切线通道与 UV 通道的实际误差约 6e-3°。仍远优于顶点色,不影响选型,但「切线 / UV 是无损 float32」这个说法本就不成立。文档已更正。

Scene 视图法线叠加

「法线可视化」面板新增 「在 Scene 视图中显示」 开关,把平滑法线与原始法线画到场景中的真实对象上,作用于所有勾选的场景网格。

关键在于 SkinnedMeshRenderer 取的是当前姿势BakeMesh),因此可以在动画播放时验证切线空间存储 —— 法线应始终贴着表面走;关节处若像扇子一样散开,就是数据烘的空间与材质选的对不上。

这件事此前一直验证不了:内嵌预览渲的是绑定姿势的静态网格,而「蒙皮动画下描边不撕开」恰恰是 1.5.0 引入切线空间的核心卖点

  • 顶点数过多时统一抽稀(所有网格用同一个步长,否则看到的疏密差是假象),并在面板上如实写出「已按 1/N 采样显示」。
  • 做成工具窗口的开关而非 MonoBehaviour 组件,以保持本包零运行时占用;关闭窗口即自动停止绘制。
  • 直接选中的 Mesh 资产没有场景位置,会被跳过并提示。

导入自动烘焙:命中规则可叠加

命中规则改为两个可独立勾选的条件,同时勾选时取交集(都满足才烘焙):

条件 说明
按文件名后缀 文件名以指定后缀结尾,大小写不敏感。默认开启,后缀默认 _Outline
按文件夹路径 资产位于指定文件夹及其子目录之下。把文件夹拖进对象槽即可。
  • 文件夹按路径前缀匹配到目录分隔符为止,因此 Assets/Characters2/Assets/Old/Characters_backup/ 这类同级目录不会被误命中。
  • 默认仍是「只开后缀、后缀 _Outline」,与 1.6.0 行为完全一致。
  • 任一条件留空、或两个都不勾,均不命中任何模型 —— 空配置绝不会被解读成「命中一切」。
  • 把模型改名加后缀、或拖进目标文件夹,都不会触发 Unity 重导入,工具会侦测这类移动并补一次重导入(此前只对改名生效,现在两种都管)。

改进

  • 平滑法线计算消除每顶点堆分配 —— 此前每个唯一顶点位置都要 new 一个 List<Vector3>,十万顶点级别的网格上这是主要的 GC 压力来源。改为遍历三角形时就地累加,另加量化键预计算、空间哈希混合与字典容量预留。结果逐位不变:累加顺序与原先「按插入顺序求和」完全一致,不涉及浮点结合律重排。
  • 批量烘焙增加可取消的进度反馈 —— 此前批量处理大网格时 Unity 无任何反馈地卡住。取消时保留已处理的网格(快照已抓,可用「还原」回退),日志写明「已处理 N / M 个」。
  • 生成 / 导入日志现在写明数据格式(如 TEXCOORD1(八面体 2×float))。存储方式 / 存储空间 / 数据格式三者但凡与材质对不上都不报错,事后排查时这行日志常常是唯一还留着的线索。
  • 材质面板的 TEXCOORD 档说明收敛到一处生成,并固定挂上迁移提示 —— 材质面板是描边出问题时最先被打开的地方,而旧数据在 GPU 侧无从检测,这里是唯一能提醒到人的位置。

修复

  • 「还原本次修改」会把网格上每个非空 UV 通道升成 4 分量 —— 包括 FBX 的主贴图 UV,顶点缓冲无声翻倍,且对 .asset 网格会直接落盘。原因是快照统一用 List<Vector4> 读写,而 SetUVs 严格按传入列表的类型设定通道分量数。现在按通道记录原始维度、还原时分派到对应的 Vector2/3/4 重载。

    这个缺陷自「会话快照」功能引入起一直存在,且会让本版新增的「分量数为 3 = 旧格式」提示失效。

  • 材质面板上 TEXCOORD03 四档的说明此前写作「读取…的 xy」、TEXCOORD47 写作「xyz」,而当时实际全部存 xyz —— 前四条一直是错的。现已统一。

版本策略已写进 CHANGELOG

本包对语义化版本有一处明确的例外,现已写在 CHANGELOG 顶部:

烘焙数据格式的破坏性变更走 MINOR,而不是 MAJOR。
作为补偿,这类变更一律在该版本条目开头以 ⚠ 破坏性变更 小节单独列出。

理由是本包的产物是烘进网格的一串数字:格式一改,存量数据就必须重烘,没有中间状态。严格按 SemVer 处理的话,每次编码格式改进都得升一个大版本号,版本号会迅速膨胀到与功能规模完全不成比例。

请把注意力放在「版本条目开头有没有 ⚠ 破坏性变更」,而不是版本号的位数上。 API / Shader 函数签名的兼容性仍然照常遵守 SemVer。

✨ 主要功能

  • 平滑法线生成 —— 角度加权平均、可调合并容差,跨硬边得到连续外扩方向。
  • 三种存储方式 —— 顶点色(八面体,8-bit×2,误差约 1°)/ 切线通道(tangent.xyz)/ TEXCOORD(八面体,float×2,误差约 5e-6°,共 8 个通道)。
  • 两种存储空间 —— 对象空间 / 切线空间,与存储方式正交;切线空间让蒙皮模型的描边正确跟随骨骼动画。
  • 描边 Pass 模板 —— 两步接进自己的 Shader,URP / Built-in 各一份,描边逻辑随包升级。
  • 导入时自动烘焙 —— 命中规则(文件名后缀 / 文件夹路径,可叠加取交集)的模型(重)导入即烘焙,非破坏性、零手动操作;配置持久化到 ProjectSettings/,另有自定义规则 / 自定义存储两个扩展委托。
  • 广泛的目标来源 —— 场景对象、Mesh 资产、模型(.fbx 等)、预制体,自动遍历层级、复选框多选批量处理
  • 实时描边预览 —— 内嵌视口同屏显示所有勾选网格,法线可视化对比、通道状态检查、屏幕 / 世界空间描边对照。
  • Scene 视图法线叠加 —— 场景中的真实对象上叠加法线,蒙皮按当前姿势求值,可在动画播放时验证。
  • 网格健康检查 —— 生成 / 烘焙前扫描非法数据(缺法线、退化三角、NaN、退化切线、重合顶点过多等),手动二次确认、自动烘焙跳过。
  • 数据安全 —— 阻止对只读资产的假保存、批量另存为独立 Mesh、会话快照还原。
  • 描边 Shader(Sample) —— URP / Built-in 两版,两 Pass(描边 + 基础 NPR),描边来源支持 8 个 UV 通道,基础色调试档位,自定义材质面板。

⚠️ 已知限制

  • HDRP 未提供描边 Shader,可参照文档自行移植(解码逻辑通用,仅渲染 Pass 需适配)。
  • 存储方式与存储空间无法从数据反推 —— 存的都只是一条单位方向。工具与材质选得不一致时不会报错,只是描边偏斜或撕开。排查描边异常请先核对这两项,以及生成日志里记的数据格式。
  • 两分量 UV 与贴图 UV 在数值上无法区分 —— 因此新格式的通道状态最高只能报「有数据」。反过来「分量数为 3」成了识别旧版数据的提示,但同样只是强提示:网格合并会把 UV 维度统一取最大,其他把三分量方向写进 UV 的工具也会命中。
  • 切线空间要求网格有合法切线(导入设置 TangentsNone)。UV 退化处的切线构不成正交基,这些顶点的描边会退化为沿原始顶点法线外扩,健康检查会按比例报出。
  • 重新导入模型可能让已烘数据失配 —— 烘焙用的是当时的切线,若之后改用不同的切线生成方式重新导入,需重新烘焙。因此切线空间推荐配合「导入时自动烘焙」使用
  • Pass 模板声明了全部 8 个 TEXCOORD,因为存储来源是材质上运行时切换的。对顶点带宽敏感、且确定只用一个通道的项目,请按文档「极简写法」手写 vert。
  • Scene 视图叠加每次重绘都会重算,仅建议排查时打开。
  • 不支持 Undo —— Undo.RecordObject 不可靠地跟踪网格顶点数据;工具提供自己的会话快照还原。
  • 顶点合并采用格点取整,恰好跨越格边界的两点仍会被分开;容差需远小于模型最小特征尺寸。
  • 世界空间宽度模式下 _OutlineWidth世界单位,对很大 / 很小的模型需相应调整。

📖 文档

总览 / 快速上手:
简体中文 ·
English ·
日本語

详细使用文档(含迁移指引、界面详解、Shader 接入说明):
简体中文 ·
English ·
日本語

完整 CHANGELOG