1.7.0
为网格自动计算平滑法线并烘焙到顶点色 / 切线 / 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°,直接存 xyz 是 5e-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 = 旧格式」提示失效。
-
材质面板上
TEXCOORD0–3四档的说明此前写作「读取…的 xy」、TEXCOORD4–7写作「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 的工具也会命中。
- 切线空间要求网格有合法切线(导入设置
Tangents≠None)。UV 退化处的切线构不成正交基,这些顶点的描边会退化为沿原始顶点法线外扩,健康检查会按比例报出。 - 重新导入模型可能让已烘数据失配 —— 烘焙用的是当时的切线,若之后改用不同的切线生成方式重新导入,需重新烘焙。因此切线空间推荐配合「导入时自动烘焙」使用。
- Pass 模板声明了全部 8 个 TEXCOORD,因为存储来源是材质上运行时切换的。对顶点带宽敏感、且确定只用一个通道的项目,请按文档「极简写法」手写 vert。
- Scene 视图叠加每次重绘都会重算,仅建议排查时打开。
- 不支持 Undo ——
Undo.RecordObject不可靠地跟踪网格顶点数据;工具提供自己的会话快照还原。 - 顶点合并采用格点取整,恰好跨越格边界的两点仍会被分开;容差需远小于模型最小特征尺寸。
- 世界空间宽度模式下
_OutlineWidth是世界单位,对很大 / 很小的模型需相应调整。
📖 文档
总览 / 快速上手:
简体中文 ·
English ·
日本語