Skip to content

1.8.0

Choose a tag to compare

@AleFeng AleFeng released this 21 Jul 04:22

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

📦 安装

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

https://github.com/AleFeng/OutlineSmoothNormalsGenerator.git?path=/Packages/com.alefeng.outlinesmoothnormalsgenerator#1.8.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.8.0 更新内容

编辑器界面三语化

270 条界面文案 × 3 种语言,覆盖工具窗口的两个页签、网格健康检查条目、描边材质的 Inspector,以及全部 Console 日志。默认中文。

两个页签的标题区下方各有一个 中文 / English / 日本語 切换按钮,点击即时生效 —— 左右两栏、提示框、对话框、材质面板一并跟着变,不需要重开窗口。

几条设计取舍

语言偏好存 EditorPrefs,不存 ProjectSettings/
后者随工程进版本管理。把个人的语言偏好提交上去,只会让协作者之间反复互相覆盖。存 EditorPrefs 意味着它是按机器保存的:切语言不会给同事制造 diff,换一个 Unity 工程打开本包,你的选择仍然保持。

英文术语在三种语言下始终原样保留。
TEXCOORD1 (mesh.uv2)SkinnedMeshRenderertangent.xyzRead/Write 这类标识不翻译;中文与日文的存储方式按钮保留并列的英文名(顶点色 / Vertex Color頂点カラー / Vertex Color)。描边 Shader 那两个下拉的选项Vertex Color / TexCoord0 …)也保持英文 —— 它们与 Shader 属性的浮点取值一一对应,翻译等于切断与文档的对应关系。

界面术语与三份 README 逐条对齐。
存储方式 / Storage Mode / 保存方式,切线空间 / Tangent Space / 接線空間,命中规则 / Match Rules / 判定ルール…… 文档里读到的名字,界面上都能找到同一个词。这是本次最费工夫、也最容易被忽略的部分 —— 文档与界面各说各话,多语言就等于没做。

菜单路径无法本地化。
Unity 的 MenuItem 要求常量字符串,Tools > Smooth Normal Generator 在三种语言下都是英文。

界面布局

英文 / 日文的参数名比中文长约一半(「显示平滑法线」6 字 对 Show smooth normals 19 字符),几处按中文尺寸定死的地方需要放开:

  • 预览参数栏由 220px 加宽到 256px,并把标签宽度显式钉死。此前用的是 Unity 默认值,而那个值是按整个窗口宽度算的(currentViewWidth * 0.45),与这一栏实际多宽毫无关系 —— 窗口拉得越宽,标签算得越宽,到某个点就会比整栏还宽、整条被裁。中文标签短,一直没暴露。
  • 生成按钮改为可换行,状态徽标与「全选 / 清空 / 清除」等按钮改用 MinWidth
  • 预览视口的操作提示与左上角模式徽标改为按实际文字宽度测量:放不下就不画(半句被裁的提示比没有更糟),底板也不再是写死的 220px(此前会越过视口右缘、糊到右侧参数栏的标题上)。
  • 左右分栏的全部尺寸常量集中到一处,右栏保留量改为跟随参数栏宽度派生,不再写死。

改进

  • Console 日志前缀统一为 [OutlineSmoothNormals] 此前散着三套写法([SmoothNormal] 10 处、[OutlineSmoothNormals] 6 处、[平滑法线] 1 处),按任何一个在 Console 里过滤都会漏掉一大半;最后那个还是中文前缀,界面切到英文 / 日文后会出现「中文前缀 + 英文正文」。现收敛到单一常量。前缀本身不随语言变 —— 它是过滤用的固定标识,跟着语言变的话,同一个工程里换过语言的日志就再也过滤不到一起了。
  • 「导入自动烘焙」页签的存储通道 / 存储空间两个下拉此前完全绕过本地化,三语下恒显示英文枚举名,而生成器页签里同一个枚举是本地化的 —— 同一份配置两个页签一中一英。更糟的是它会显示成 Tangent Space,那正是本项目刻意废弃的旧称,且恰好紧挨着下面那个真正的「存储空间」字段。

修复

三语化过程中逐条比对界面文案与实际代码,挖出几处说法与实现不符的既存问题:

  • 顶点色面板把存储方式描述成了已废弃的旧方案。「选定通道对的 XY 分量将被写入,Z 分量通过重建得到」描述的是文档中明确点名废弃的「存 XY + 重建 Z + 按法线定符号」—— 那个方案恰恰会在它本该修复的硬边角上把描边裂开。实际写入的是八面体编码的两个参数,与法线的原始 XY 没有对应关系。
  • 顶点色通道角色的「分量 ↔ 模式」对应是反的。 G 通道 标注为「八面体 X/Y(RG/GB 模式)」,按位置读是 X↔RG、Y↔GB,而实际写入是 RG → g=oct.yGB → g=oct.x,恰好相反;B 通道 同理。现改为「RG 模式:八面体 Y / GB 模式:八面体 X」这样的显式写法,读法唯一。
  • 网格健康检查对切线退化后果的断言不准确。「零向量 / 与法线共线 / 手性异常」三种成因被统一断言为「描边退化为沿原始顶点法线外扩」,但解码侧只检查 Gram-Schmidt 残量、不检查 tangent.w:手性异常时基照样构造成功,只是副切线塌成零向量,结果是错误方向而非顶点法线。现已拆开分述。
  • 「网格信息」里各 TEXCOORD 行显示的是元素个数,不是分量数。 此前记的是 GetUVs 取回的条目数 —— 通道非空时它恒等于顶点数,于是八个通道显示同一个数字,信息量为零;而文档描述的一直是分量数。分量数(2 / 3 / 4)才是判断通道被谁占用、以及识别 1.6.x 旧格式(3 分量)的依据。顺带省掉 8 次「把整份 UV 数组拷到托管侧、只为数一下长度」的调用。
  • Scene 视图叠加的抽稀是静默的。 1.7.0 起顶点数过多时会自动抽稀,三份文档都写明「面板上会如实写出『已按 1/N 采样显示』」,但那句提示从未被实现 —— 步长算了、点也跳了,就是没告诉用户。看到线段稀疏时无从判断是数据有问题还是被抽稀了,正是这句提示要防的误判。
  • TEXCOORD 存储方式的提示末行仍写着「多占一个 UV 通道的顶点带宽(3 个 float / 顶点)」,是 1.6.x 三分量时代的残留,与同一条提示第二行的「两个 float32,每顶点 8 字节」自相矛盾。
  • 「导入自动烘焙」页签的副标题仍写「命中后缀的模型」,而 1.7.0 起命中条件已是后缀与文件夹两项。

✨ 主要功能

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

⚠️ 已知限制

  • HDRP 未提供描边 Shader,可参照文档自行移植。
  • 存储方式与存储空间无法从数据反推 —— 存的都只是一条单位方向。工具与材质选得不一致时不会报错,只是描边偏斜或撕开。
  • 两分量 UV 与贴图 UV 在数值上无法区分 —— 通道状态最高只能报「有数据」;反过来「分量数为 3」是识别旧版数据的强提示而非判定
  • 切线空间要求网格有合法切线(导入设置 TangentsNone)。
  • 重新导入模型可能让已烘数据失配 —— 因此切线空间推荐配合「导入时自动烘焙」使用。
  • Pass 模板声明了全部 8 个 TEXCOORD,对顶点带宽敏感的项目请按文档「极简写法」手写 vert。
  • Scene 视图叠加每次重绘都会重算,仅建议排查时打开。
  • 菜单路径无法本地化 —— Unity 的 MenuItem 要求常量字符串,三种语言下都是 Tools > Smooth Normal Generator
  • 不支持 Undo —— 工具提供自己的会话快照还原。
  • 顶点合并采用格点取整,恰好跨越格边界的两点仍会被分开

📖 文档

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

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

完整 CHANGELOG