1.5.0
为网格自动计算平滑法线并烘焙到顶点色 / 切线 / TEXCOORD,用于背面外扩描边,解决硬边处描边断裂。工具本体是纯编辑器 C#,与渲染管线无关。
⚠️ 从 1.4.x 升级必读
本版存在破坏性变更,且升级 UPM 包本身不足以完成升级 —— 请按下面的顺序做。
1.5.0 起平滑法线默认存切线空间,材质属性 Smooth Normal Space 的默认值同样是 Tangent Space;而 1.4.x 及更早烘焙的数据全是对象空间的。两边对不上时,描边整体偏斜,且不产生任何编译错误或运行时报错。
两条修法二选一:
如果使用了Sample中的Shader,则需要先重新导入Sample场景以获得更新后的Shader。
| 做法 | 结果 |
|---|---|
| 重新烘焙一次(推荐) | 顺带获得蒙皮支持,SkinnedMeshRenderer 的描边不再随动画撕开 |
把材质的 Smooth Normal Space 改回 Object Space |
行为与 1.4.x 完全一致,不动任何网格数据 |
走「导入时自动烘焙」的模型无需干预 ——
GetVersion()已提升,Unity 会自动重新导入并重烘所有命中模型。
📦 安装
Window > Package Manager → 左上角 + → Install package from git URL... → 粘贴:
https://github.com/AleFeng/OutlineSmoothNormalsGenerator.git?path=/Packages/com.alefeng.outlinesmoothnormalsgenerator#1.5.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.5.0 更新内容
存储空间:让顶点色 / TEXCOORD 存储也跟得上骨骼动画
这是本版的全部主题。新增一个与「存进哪个通道」正交的维度 —— 方向本身写在哪个空间里:
| 存储空间 | 存什么 | 适用 |
|---|---|---|
| 对象空间 | 绑定姿势下的对象空间方向,解码即用 | 仅静态模型 |
| 切线空间 | 相对每个顶点自身 TBN 的坐标,解码时用蒙皮后的法线与切线重建 | 静态与蒙皮模型都正确(默认) |
解决的问题:SkinnedMeshRenderer 蒙皮时,Unity 会变换 POSITION / NORMAL / TANGENT,但 COLOR 与 TEXCOORD 原样传递、不参与蒙皮。于是存进这两处的对象空间方向会「顶点跟着骨骼走、外扩方向却停在绑定姿势」,关节一弯描边就撕开。
为什么切线空间能修好它:切线空间坐标是蒙皮不变量。设 S = a·T + b·B + c·N,蒙皮对该顶点近似施加一个旋转 R,而 N、T 都被 Unity 一并变换,于是 a·T' + b·B' + c·N' = R·S —— 正是蒙皮后应有的方向,而 (a, b, c) 恒定不变。这与法线贴图能在骨骼动画上正常工作是同一个道理。
要点:
- 不占用切线。切线在这里只作重建用的「基」,法线贴图照常可用。要求模型导入设置的
Tangents≠None。 - 精度不变。编码的仍是单位方向,顶点色仍是八面体 8-bit、误差约 1°。
- 切线通道存储不适用该选项(会覆盖掉重建基所必需的切线本身),且本就不需要 —— Unity 会把
tangent.xyz当方向一起蒙皮。工具与材质面板在该模式下自动置灰。 - 工具窗口、「导入自动烘焙」页签、材质面板三处都可切换,三者必须选成一致。
其他新增
- 共享库新增两个函数(
Shader/OutlineSmoothNormals.hlsl,仍是解码与外扩的唯一真源):OSN_TangentToObject(Gram-Schmidt 重新正交化 +tangent.w手性 + 退化切线保护)与OSN_ResolveSmoothNormalSpace(按存储空间归一,自动跳过切线通道与顶点法线对照两档)。C# 侧镜像新增OutlineSmoothNormalsCodec.ObjectToTangent/TangentToObject。 - 健康检查新增切线合法性判据:切线空间存储下,缺法线 / 缺切线报 Error;零向量、与法线共线、手性为 0 的退化切线按比例报出(全退化 Error,部分 Warning)。
- 工具窗口 Tooltip:「存储方式」三个按钮与「存储空间」两个按钮各配悬停说明,写明各自的精度、占用与冲突;原先常驻的 HelpBox 相应收起,面板更紧凑。
变更
- 术语:
StorageMode.TangentSpace的显示名由「切线空间」改为**「切线通道」(材质面板Tangent Space→Tangent Channel)—— 它表示「存进切线通道**」,与新增的「存储空间」撞名会让人无从分辨。枚举成员名未改,不影响序列化。 - 窗口默认尺寸提取为常量,便于调整。
修复
- 自动烘焙配置会被静默改写:
OutlineNormalsSettings.NormalSpace此前 getter 做归一而 setter 不做,导致 IMGUI 的x = EnumPopup(…, x)读回写不是恒等操作 —— 把存储方式切到「切线通道」的那一帧,会把用户选的切线空间擦成对象空间并写进ProjectSettings/。 - 健康检查报告不随选择刷新:切换存储方式 / 存储空间后报告仍是上一次的结论,最坏是缓存着一份「无异常」而当前选择实为 Error,面板一条警告都不给。
- 文档中的 Shader 示例参数不符:
OSN_ApplyOutlineOffset自1.3.0起为 4 个参数,而三份 README 的示例仍写 3 个,照抄编译不过。同时补上了OSN_ResolveSmoothNormalSpace这一步,并清理了「一律以对象空间存储」等已过期的表述。
✨ 主要功能
- 平滑法线生成 —— 角度加权平均、可调合并容差,跨硬边得到连续外扩方向。
- 三种存储方式 —— 顶点色(八面体编码,8-bit 下误差约 1°)/ 切线通道 / TEXCOORD(
TEXCOORD0–TEXCOORD7,共 8 个通道)。 - 两种存储空间 —— 对象空间 / 切线空间,与存储方式正交;切线空间让蒙皮模型的描边正确跟随骨骼动画。
- 导入时自动烘焙 —— 命中文件名后缀的模型(重)导入即烘焙,非破坏性、零手动操作;配置持久化到
ProjectSettings/,另有自定义规则 / 自定义存储两个扩展委托。 - 广泛的目标来源 —— 场景对象、Mesh 资产、模型(
.fbx等)、预制体,自动遍历层级、复选框多选批量处理。 - 实时描边预览 —— 内嵌视口,同屏显示所有勾选网格,法线可视化对比、通道状态检查、屏幕 / 世界空间描边对照。
- 网格健康检查 —— 生成 / 烘焙前扫描非法数据(缺法线、退化三角、NaN、退化切线、重合顶点过多等),手动二次确认、自动烘焙跳过。
- 数据安全 —— 阻止对只读资产的假保存、批量另存为独立 Mesh、会话快照还原。
- 描边 Shader(Sample) —— URP / Built-in 两版,两 Pass(描边 + 基础 NPR),描边来源支持 8 个 UV 通道,基础色调试档位,自定义材质面板。
⚠️ 已知限制
- HDRP 未提供描边 Shader,可参照文档自行移植(解码逻辑通用,仅渲染 Pass 需适配)。
- 存储方式与存储空间无法从数据反推 —— 两种空间存的都只是一条单位方向。工具与材质选得不一致时不会报错,只是描边偏斜或撕开。排查描边异常请先核对这两项。
- 切线空间要求网格有合法切线(导入设置
Tangents≠None)。UV 退化处的切线构不成正交基,这些顶点的描边会退化为沿原始顶点法线外扩,健康检查会按比例报出。 - 重新导入模型可能让已烘数据失配 —— 烘焙用的是当时的切线,若之后改用不同的切线生成方式重新导入,需重新烘焙。因此切线空间推荐配合「导入时自动烘焙」使用,那样烘焙发生在导入管线内、切线生成之后,天然同源。
- 不支持 Undo ——
Undo.RecordObject不可靠地跟踪网格顶点数据;工具提供自己的会话快照还原。 - 顶点合并采用格点取整,恰好跨越格边界的两点仍会被分开;容差需远小于模型最小特征尺寸。
- 通道状态最高只说「可能是平滑法线」—— 编码后与普通顶点色 / UV 在数据上不可区分。
- 世界空间宽度模式下
_OutlineWidth是世界单位,对很大 / 很小的模型需相应调整。
📖 文档
总览 / 快速上手:
简体中文 ·
English ·
日本語