为网格自动计算平滑法线并烘焙到顶点色 / 切线 / TEXCOORD,用于背面外扩描边,解决硬边处描边断裂。工具本体是纯编辑器 C#,与渲染管线无关。
📦 安装
Window > Package Manager → 左上角 + → Install package from git URL... → 粘贴:
https://github.com/AleFeng/OutlineSmoothNormalsGenerator.git?path=/Packages/com.alefeng.outlinesmoothnormalsgenerator#1.8.1
⚠️ 装完还必须导入一个 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.1 更新内容
三轮功能迭代(1.6 → 1.7 存储格式重构 → 1.8 三语化)之后,生成器窗口涨到了 3732 行,占整个包的一半,并且同一段视觉逻辑已经开始出现互相漂移的拷贝。这一版把它清理掉。
配色与样式收敛到单一来源
新增 Editor/UI/OutlineEditorStyles.cs,集中全部主色板、文字色与 GUIStyle。
此前强调色 ColorAccent = new Color(0.33f, 0.78f, 1f) 在三个文件里各定义一遍(生成器窗口、语言切换、材质面板),改一处配色要记得改三处;深色字 new Color(0.05f, 0.05f, 0.08f) 这类字面量散落 10 次。
五份分段按钮合成一份
「高亮分段按钮」——选中时强调底 + 深色粗体字,未选中时卡片底 + 灰字,hover 提亮一档 —— 此前有五份各自独立的实现:顶部页签、存储方式、存储空间、顶点色通道对、语言切换。五份的视觉规则完全相同,只有字号与高度不同。
漂移已经发生了:顶点色那一份的未选中底色比另外四份浅一档(下面变化 ①)。现在规则只写一遍,差异作为参数传入。「高亮动作按钮」(保存 / 另存为 / 生成)同理,三份合成一份。
新增 Editor/UI/OutlineEditorGUI.cs 收纳这些绘制基元。
界面重绘不再每帧分配 GUIStyle
这是唯一你可能感觉得到的改进。 生成器窗口的绘制路径上原有 20 处 new GUIStyle,现为 0。
最严重的是「网格信息」那一块:每画一行就新建两个 GUIStyle,八个 TEXCOORD 通道全列出来时,光是把鼠标划过窗口,每一个事件都要分配二十多个样式对象加同样多的 RectOffset。窗口里本来就有一段注释写明了「IMGUI 是立即模式,复用同一个对象是安全的」——这条纪律此前只对一半的样式生效。
窗口内的重复逻辑合并
- 通道状态到颜色的映射原有三处,其中一处的灰色抄漏了(下面变化 ②)。
- 三个清除操作(顶点色 / 切线 / TEXCOORD)各自内联了同一套八步前后处理:抓快照 → 记 Undo → 改数据 → 标脏 → 刷新状态。漏掉其中任何一步都不会报错,只会静默坏掉一件事 —— 少抓快照就还不回去,少标脏保存按钮就不亮。现在收敛成一个入口,以后加第四个清除操作不可能再漏。
- 预览材质的属性写入、以及预览视口里两段一模一样的网格绘制循环。
描边预览材质在创建时就配置完整
此前新建材质只设了颜色 / 宽度 / 宽度模式三项,存储方式那四项要等第一次重绘才补上。渲染结果没有变化 —— 每次重绘前本来就会补 —— 但「新建出来的材质是半配好的」本身是个等着被踩的坑。
按职责重排 #region
此前开头 478 行完全不在任何 region 内(快照、可写性、通道状态、网格缓存、配色、尺寸常量、生命周期全在裸区),而「UI 主界面」一路吞掉了另外两个同级区,收尾是三个连续的 #endregion。现在每个成员都归属到与其职责一致的区里,最深两层。
注释里会漂移的数字已清除
若干注释写着「最小窗口 860 + 初始分隔线 420 下视口由 202px 缩到 166px」这类派生算术,以及「英文标签约 115px」这类实测文字宽度(随编辑器主题与 DPI 变化)。这些数字在常量被调整后立刻失真,且失真时没有任何提示。现已删除,只保留「为什么是这个值、重新取值时要看什么」。
🎨 界面上的三处细微变化
都是消除「本该一致却不一致」的取值。
| # | 位置 | 变化 |
|---|---|---|
| ① | 顶点色 RG / GB / BA 按钮的未选中底色 |
变暗一档,与其余四组分段按钮统一 |
| ② | 数据状态卡右上角「未写入」徽标的灰 | 与同语义的其他状态点统一 |
| ③ | 「生成」按钮 hover 时的底色亮度 | 与「保存」/「另存为」两颗统一 |
② 和 ③ 的差值分别是 0.02 的分量差与 2% 的亮度差,基本看不出来;① 是三处里最明显的,但也只有一档。
✨ 主要功能
- 平滑法线生成 —— 角度加权平均、可调合并容差,跨硬边得到连续外扩方向。
- 三种存储方式 —— 顶点色(八面体,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」是识别旧版数据的强提示而非判定。
- 切线空间要求网格有合法切线(导入设置
Tangents≠None)。 - 重新导入模型可能让已烘数据失配 —— 因此切线空间推荐配合「导入时自动烘焙」使用。
- Pass 模板声明了全部 8 个 TEXCOORD,对顶点带宽敏感的项目请按文档「极简写法」手写 vert。
- Scene 视图叠加每次重绘都会重算,仅建议排查时打开。
- 菜单路径无法本地化 —— Unity 的
MenuItem要求常量字符串,三种语言下都是Tools > Smooth Normal Generator。 - 不支持 Undo —— 工具提供自己的会话快照还原。
- 顶点合并采用格点取整,恰好跨越格边界的两点仍会被分开。
📖 文档
总览 / 快速上手:
简体中文 ·
English ·
日本語