mcpField
T
texture-packing-variant-stripping

texture-packing-variant-stripping

github.com/adevra开发1644 次安装31 周前更新
代码仓库

关于 texture-packing-variant-stripping

面向 Unity 开发者的优化参考,聚焦移动端和 Web 构建中的纹理与着色器变体管理。内容讲解通道打包,以及关键字和变体数量的精简方法。

主要功能

  1. 01RGBA 纹理通道打包
  2. 02URP 遮罩贴图约定
  3. 03shader_feature 与 multi_compile 使用建议
  4. 04URP 功能与关键字剔除
  5. 05移动端和 Web 纹理压缩

适用场景

  1. 01合并灰度贴图以减少纹理采样
  2. 02减少着色器变体与构建体积
  3. 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
最佳实践
  1. 将视觉上最重要的遮罩放在 Green 通道中。 人眼对绿色最敏感,而且大多数纹理压缩格式(ETC2、ASTC、DXT)会给绿色分配更多位数。

  2. 将相关数据打包在一起。 如果金属度和光滑度总是一起采样,就将它们放在同一张纹理中。

  3. 打包遮罩纹理时使用线性色彩空间(而非 sRGB)。在 Unity 中,对任何非颜色数据(粗糙度、AO、金属度、高度)的纹理导入设置取消勾选“sRGB”。

  4. 一次纹理采样可替代四次。 不要这样写:

    // 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_featuremulti_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×4Android(ES 3.0+)、iOS8最佳所有移动端平台的首选
ASTC 6×6Android(ES 3.0+)、iOS3.56良好用于不太重要的纹理
ASTC 8×8Android(ES 3.0+)、iOS2可接受背景/远处纹理
ETC2Android(ES 3.0+)、WebGL 2.04-8良好WebGL 2.0 默认格式
ETC1Android(ES 2.0)4尚可不支持 Alpha 通道
PVRTCiOS(较旧版本)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 或自定义着色器)

来源

评论

支持 Markdown