关于 texture-packing-variant-stripping
面向 Unity 开发者的优化参考,聚焦移动端和 Web 构建中的纹理与着色器变体管理。内容讲解通道打包,以及关键字和变体数量的精简方法。
主要功能
- 01RGBA 纹理通道打包
- 02URP 遮罩贴图约定
- 03shader_feature 与 multi_compile 使用建议
- 04URP 功能与关键字剔除
- 05移动端和 Web 纹理压缩
适用场景
- 01合并灰度贴图以减少纹理采样
- 02减少着色器变体与构建体积
- 03为移动端打包纹理选择压缩格式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ adevra/ unity-shader-agent-skills --skill texture-packing-variant-stripping安装后在 Claude Code 中输入/texture-packing-variant-stripping确认已加载。
详细介绍
Unity 移动端的纹理打包与着色器变体剥离
触发条件
当用户正在处理 Unity 中的纹理优化、通道打包、着色器变体膨胀、构建大小、着色器编译时间或着色器关键字管理时,请使用此技能。触发词包括:“通道打包”“纹理打包”“遮罩贴图”“MODS 贴图”“RMA”“着色器变体”“变体剥离”“multi_compile”“shader_feature”“构建大小”“着色器关键字”“Always Included Shaders”“IPreprocessShaders”,或用户抱怨着色器编译缓慢或构建体积过大。
纹理通道打包
通道打包会将多张灰度贴图存储在一张 RGBA 纹理中。这可以减少纹理采样次数和内存带宽占用——后者是移动端的主要瓶颈。
标准 URP 打包约定
Mask Map (single RGBA texture):
R = Metallic
G = Occlusion (AO)
B = Detail mask (or custom)
A = Smoothness
最佳实践
-
将视觉上最重要的遮罩放在 Green 通道中。 人眼对绿色最敏感,而且大多数纹理压缩格式(ETC2、ASTC、DXT)会给绿色分配更多位数。
-
将相关数据打包在一起。 如果金属度和光滑度总是一起采样,就将它们放在同一张纹理中。
-
打包遮罩纹理时使用线性色彩空间(而非 sRGB)。在 Unity 中,对任何非颜色数据(粗糙度、AO、金属度、高度)的纹理导入设置取消勾选“sRGB”。
-
一次纹理采样可替代四次。 不要这样写:
// BAD — 4 texture samples half metallic = SAMPLE_TEXTURE2D(_MetallicMap, sampler_MetallicMap, uv).r; half ao = SAMPLE_TEXTURE2D(_AOMap, sampler_AOMap, uv).r; half detail = SAMPLE_TEXTURE2D(_DetailMask, sampler_DetailMask, uv).r; half smooth = SAMPLE_TEXTURE2D(_SmoothnessMap, sampler_SmoothnessMap, uv).r;改用:
// GOOD — 1 texture sample half4 masks = SAMPLE_TEXTURE2D(_MaskMap, sampler_MaskMap, uv); half metallic = masks.r; half ao = masks.g; half detail = masks.b; half smooth = masks.a;
进阶:地形打包
Unity 的 Shader Graph 17.4 地形采样示例展示了进阶打包方法,可将 4 层地形的纹理采样次数从 12 次减少到 5 次:
- CNS 打包:在一组数据中打包颜色(RGB)、法线(RG → 重建 B)和光滑度
- CHNOSArray:将颜色、高度、法线、遮挡、光滑度打包进纹理数组
HLSL 通道打包辅助函数
// Pack 4 grayscale values into RGBA
half4 PackChannels(half metallic, half ao, half detail, half smoothness)
{
return half4(metallic, ao, detail, smoothness);
}
// Reconstruct normal Z from XY (for normal map packing)
half3 UnpackNormalRG(half2 normalRG)
{
half3 n;
n.xy = normalRG * 2.0h - 1.0h;
n.z = sqrt(1.0h - saturate(dot(n.xy, n.xy)));
return n;
}
着色器变体管理
变体爆炸问题
每个 multi_compile 关键字都会使变体数量成倍增加:
#pragma multi_compile _ _A → 2 variants
#pragma multi_compile _ _A _B → 3 variants
#pragma multi_compile _ _A
#pragma multi_compile _ _B → 2 × 2 = 4 variants
#pragma multi_compile _ _A
#pragma multi_compile _ _B
#pragma multi_compile _ _C → 2 × 2 × 2 = 8 variants
加上 URP 内置关键字(阴影、雾效、附加光源等),单个着色器就可能生成数千种变体。
影响
- 构建时间:复杂着色器需要 20 分钟以上
- 构建大小:着色器数据超过 100MB(对 WebGL 至关重要)
- 运行时内存:变体切换会额外产生 20-40% 的绘制调用
- WebGL:着色器编译会阻塞主线程——变体越多,卡顿时间越长
shader_feature 与 multi_compile 的区别
shader_feature | multi_compile | |
|---|---|---|
| 编译内容 | 仅编译构建中材质使用的变体 | 编译所有组合 |
| 适用场景 | 材质专属开关 | 全局状态(雾效、阴影) |
| 未使用的变体 | 自动剥离 | 无论是否使用都会包含 |
| 本地版本 | shader_feature_local(最多 64 个) | multi_compile_local(最多 64 个) |
| 全局版本 | shader_feature(最多 256 个) | multi_compile(最多 256 个) |
规则:对所有可控功能(材质开关、功能标志)使用 shader_feature。只有 Unity 全局控制的功能(雾效、光照贴图、实例化)才使用 multi_compile。
推荐的 URP 关键字剥离设置
如果未使用以下功能,请剥离对应关键字:
// In an IPreprocessShaders implementation:
static readonly ShaderKeyword[] STRIP_KEYWORDS = new[]
{
new ShaderKeyword("_ADDITIONAL_LIGHTS"), // If using main light only
new ShaderKeyword("_ADDITIONAL_LIGHT_SHADOWS"), // If no additional light shadows
new ShaderKeyword("_SHADOWS_SOFT"), // If using hard shadows only
new ShaderKeyword("_MIXED_LIGHTING_SUBTRACTIVE"), // If not using mixed lighting
new ShaderKeyword("_SCREEN_SPACE_OCCLUSION"), // If not using SSAO
new ShaderKeyword("LIGHTMAP_SHADOW_MIXING"), // If not mixing lightmaps + realtime
new ShaderKeyword("_LIGHT_LAYERS"), // If not using light layers
new ShaderKeyword("_REFLECTION_PROBE_BLENDING"), // If not blending reflection probes
new ShaderKeyword("_REFLECTION_PROBE_BOX_PROJECTION"), // If not using box projection
};
URP 资源功能剥离
在 URP 资源设置中,禁用未使用的功能,以避免编译相关变体:
- 附加光源 → 关闭(仅使用方向光时)
- 阴影 → 减少级联数量,禁用软阴影
- 后期处理 → 不使用时禁用
- HDR → 不需要时禁用
- 环境反射 → 使用烘焙反射时禁用
平台条件编译
// Include features only on platforms that can handle them
#if defined(SHADER_API_MOBILE) || defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)
// Mobile/WebGL: simplified path
#define MOBILE_SHADER 1
#else
// Desktop: full features
#define MOBILE_SHADER 0
#endif
half4 frag(Varyings i) : SV_Target
{
half4 color = SampleAlbedo(i.uv);
#if !MOBILE_SHADER
// Desktop-only: normal mapping, specular, etc.
color.rgb += SampleNormalAndLight(i);
#endif
return color;
}
适用于移动端/网页的纹理压缩
| 格式 | 平台 | 每像素位数 | 质量 | 备注 |
|---|---|---|---|---|
| ASTC 4×4 | Android(ES 3.0+)、iOS | 8 | 最佳 | 所有移动端平台的首选 |
| ASTC 6×6 | Android(ES 3.0+)、iOS | 3.56 | 良好 | 用于不太重要的纹理 |
| ASTC 8×8 | Android(ES 3.0+)、iOS | 2 | 可接受 | 背景/远处纹理 |
| ETC2 | Android(ES 3.0+)、WebGL 2.0 | 4-8 | 良好 | WebGL 2.0 默认格式 |
| ETC1 | Android(ES 2.0) | 4 | 尚可 | 不支持 Alpha 通道 |
| PVRTC | iOS(较旧版本) | 2-4 | 不定 | 旧格式,优先使用 ASTC |
对于通道打包纹理:使用 ASTC 4×4 或 ETC2,并采用线性色彩空间。避免将 PVRTC 用于遮罩贴图——它的块压缩会在锐利的遮罩边界处造成明显伪影。
优化检查清单
- 所有灰度贴图都已打包到共享纹理的 RGBA 通道中
- 打包纹理以 Linear(而非 sRGB)模式导入
- 最重要的遮罩数据使用绿色通道
- 材质开关使用
shader_feature,而非multi_compile - 尽可能使用本地关键字(
shader_feature_local) - 在 URP Asset 中禁用未使用的 URP 功能
- 清理 Always Included Shaders 列表(Project Settings > Graphics)
- 添加用于剔除未使用关键字的 IPreprocessShaders 剥离脚本
- 检查构建日志中的变体数量(查找 "Compiled N shader variants")
- 所有移动端纹理均使用 ASTC 或 ETC2 压缩
- 从项目中移除 Standard Shader(改用 Simple Lit 或自定义着色器)
来源
- Unity 6 URP 通道打包纹理 — https://docs.unity3d.com/6000.3/Documentation/Manual/urp/shaders-in-universalrp-channel-packed-texture.html
- Unity Learn:面向移动端的纹理通道打包 — https://learn.unity.com/course/3d-art-optimization-for-mobile-gaming-5474/
- Shader Graph 17.4:地形打包 — https://docs.unity3d.com/Packages/[email protected]/manual/Shader-Graph-Sample-Terrain-Packing.html
- Polycount Wiki:通道打包 — http://wiki.polycount.com/wiki/ChannelPacking
- Unity 6:着色器变体剥离 — https://docs.unity3d.com/6000.3/Documentation/Manual/shader-variant-stripping.html
- Unity 博客:剥离可脚本化着色器变体 — https://unity.com/blog/engine-platform/stripping-scriptable-shader-variants
- Unity 博客:着色器变体优化技巧 — https://unity.com/blog/engine-platform/shader-variants-optimization-troubleshooting-tips
- Unity 手册:shader_feature 与 multi_compile 的区别 — https://docs.unity3d.com/2020.2/Documentation/Manual/SL-MultipleProgramVariants.html
- TheGamedev.Guru:着色器变体剥离 — https://thegamedev.guru/unity-cpu-performance/shader-variants-stripping/
- Unity Web:移除未使用资源 — https://docs.unity3d.com/2022.3/Documentation/Manual/web-optimization-remove-resources.html
- Unity Learn:优化图形 — https://learn.unity.com/tutorial/optimizing-graphics-in-unity