美文网首页Unityunity3D技术分享小钱钱
Unity的图片优化和TexturePacker的使用

Unity的图片优化和TexturePacker的使用

作者: 光明程辉 | 来源:发表于2017-03-31 15:54 被阅读673次

    最近在做Unity的内存优化,代码几乎没有可以优化的地方了,那么只有从图片素材进行。

    在Unity里渲染的大小分别是:
                      442  X 563  =  0.9MB
                      512  X 1024 = 0.5MB
                      512  X 512  =  256KB
                      256 X 256  =  64kB
                      256 X 563 = 0.5MB
                      442 X 256 = 442KB
    

    看出来区别了吧!无论怎么比较,就是 2次方的正方形最优!

    在项目中,尽可能是使用ETC1和PVRTV4等GPU直接支持的图片格式,不仅内存占用低、性能也更好;当出现质量不及格时,再逐步的提升压缩格式,来满足需要。

    RGB16

    2、如果我的图片不是2次方的正方形,能做得到吗?

    而RGB16,是主要针对一些,不带透明通道,同时长宽又不是2的次方的图片;对于这些图片,使用RGB16可以降低一半的内存,但是效果会略逊于RGB32。

    几种纹理格式的对比?

    格式 .................. 内存占用 ..........质量 .........透明 ....... 二次方大小 ..........建议使用场合
    RGBA32 ..... 1 .....★★★★★ .....有 .....无需 .....清晰度要求极高
    RGBA16+Dithering .....1/2 .....★★★★ .....有 .....无需 .....UI、头像、卡牌、不会进行拉伸放大
    RGBA16 .....1/2 .....★★★ .....有 .....无需 .....UI、头像、卡牌,不带渐变,颜色不丰富,需要拉伸放大
    RGB16+Dithering .....1/2 .....★★★★ .....无 .....无需 .....UI、头像、卡牌、不透明、不会进行拉伸放大
    RGB16 .....1/2 .....★★★ .....无 .....无需 .....UI、头像、卡牌、不透明、不渐变,不会进行拉伸放大
    RGB(ETC1) + Alpha(ETC1) .....1/4 .....★★★ .....有 .....需要二次方,长宽可不一样 尽可能默认使用,在质量不满足时再考虑使用上边的格式
    RGB(ETC1) .....1/8 .....★★★ .....无 .....需要二次方,长宽可不一样 .....尽可能默认使用,在质量不满足时再考虑使用上边的格式
    PVRTC4 .....1/8 .....★★ .....无 .....需要二次方正方形,长宽一样 尽可能默认使用,在质量不满足时再考虑使用上边的格式
    内存占用,相对于RGBA32做比较

    01.png 02.png

    认识TexturePacker的界面:

    Data Format:

    导出什么引擎数据,默认cocos2d,下拉列表中有很多,基本常用的引擎都支持了

    Data File :

    导出文件位置(后缀名.plist)

    Texture Format:

    纹理格式,默认png

    Image format:

    图片像素格式,默认RGBA8888...根据对图片质量的需求导出不同的格式

    Dithering:

    抖动,默认NearestNeighbour,(如果图像上面有许许多多的“条条”和颜色梯度变化)将其修改成FloydSteinberg+Alpha;

    Scale:

    让你可以保存一个比原始图片尺寸要大一点、或者小一点的spritesheet。比如,如果你想在spritesheet中加载“@2x"的图片(也即为Retina-display设备或者ipad创建的)。但是你同时也想为不支持高清显示的iphone和touch制作spritesheet,这时候只需要设置scale为 1.0,同时勾选autoSD就可以了。也就是说,只需要美工提供高清显示的图片,用这个软件可以自己为你生成高清和普清的图片。

    Algorithm TexturePacker:

    里面目前唯一支持的算法就是MaxRects,即按精灵尺寸大小排列,但是这个算法效果非常好,因此你不用管它。

    Border/shape padding:

    即在spritesheet里面,设置精灵与精灵之间的间隔。如果你在你的游戏当中看到精灵的旁边有一些“杂图”的时候,你就可以增加这个精灵之间的间隔。

    Extrude:

    精灵边界的重复像素个数. 这个与间隔是相对应的--如果你在你的精灵的边边上看到一些透明的小点点,你就可以通过把这个值设设置大一点。

    Trim:

    通过移除精灵四周的透明区域使之更好地放在spritesheet中去。不要担心,这些透明的区域仅仅是为了使spritesheet里面的精灵紧凑一点。--当你从cocos2d里面去读取这些精灵的时候,这些透明区域仍然在寻里。(因为,有些情况下,你可能需要这些信息来确定精灵的位置)

    Shape outlines:

    把这个选项打开,那么就能看到精灵的边边。这在调试的时候非常有用。

    AddSprite:

    添加图片Add Folder:根据文件夹添加图片

    Publish:

    导出资源文件(.plist和png)

    补充一下:

    针对大图往往是许多小图合成的图集 Atlas,且 GPU 在处理图片是总是需要 POT (Power of Two)大小的纹理这两个特性。另一种处理方式是把小图挤到半张 Atlas 里面,另外半张放透明度信息。
    这样处理, iOS 平台下的开销也就不会太大。其实,由于小图往往长宽比不规则,很多原本需要一张大图的Atlas完全可以放在半张图里面。
    本文主要是介绍后面的处理方式。当然,也就讲个思路。先把图片排到半张图得到半图,再利用 Mali 的 TCT 工具获得所需的全图。最后写一个 Shader 来得到完整的图片信息。

    Unity 相关知识点

    Unity 对于平台不支持的压缩格式,会默认转为 RGBA 32bpp。而 Android 平台普遍支持的含透明度格式为 RGBA 16bpp。如果采用 RGB ETC 4bpp 的两幅图,那么需要 8bpp(使用该格式可能会导致遮罩出现问题)。如果能够把两幅图放在同一张纹理里面,那么能够再节省一半,大概4bpp(shader处理的时候会比较消耗GPU)。

    Unity 在为 Android 打包时,默认对JPG采用 ETC1,对PNG采用 RGBA 16。

    NPOT 的图最终会被转为 POT 的图,而且 Unity 会把 NPOT 的图会被转为 RGBA 32 格式 (GUI Texture 支持 NPOT,图片格式不会改,但是最终送 GPU 的时候还是会转为 POT)。

    几种主要的纹理格式

    1. DXT
      DXT 是 DirectX 提供的一种压缩格式。只能针对 POT 格式纹理进行处理。DDS 文件采用此种压缩方式进行文件存储。
      支持的纹理格式 占用空间

    DXT1 RGB5A1 4bpp 压缩比 4:1
    DXT2 RGBA4444 8bpp 压缩比 2:1
    DXT3 RGBA4444 同上 同上
    DXT4 通过线性插值生成 Alpha 同上 同上
    DXT5 同上 同上 同上
    DXT纹理压缩

    1. ETC
      ETC1 (Ericsson Texture Compression) 仅仅支持 RGB 4bpp 的图,不支持 Alpha 通道。OpenGL ES 3.0 能支持 ETC2,但是 Android 4.3 才开始支持 GLES 3.0。
      ETC1 采用 4X4 的像素区域编入64位空间,也就是 4bpp。
      ETC 将像素区域分为4X2(2X4)两个部分。每个部分有一个基色,在基色基础上给两个部分分别 444RGB 的偏移或者 555RGB/333RGB的偏移。每个部分还有3位的亮度选择。 Each pixel is then offset from the base color by adding one of four signed values to the base color for its half of the 4×4 group.
      关于ETC2的官方介绍ETC 纹理压缩和 Alpha 通道处理ETC 拼接图 Shader 的编码
    2. PVRTC
      PVRTC 分 4bpp 和 8bpp。具体的编码方式参见 Wiki

    代码优化:

    使用string.png 贴图压缩优化.png comparetag获取图片.png 使用对象池.png

    架构设计优化

    场景优化.png 生命周期优化.png Resources和Instance加载区别优化.png 创建资源方式.png destroy和instance的优化区别.png

    下面看看对比:(MB 的变化)

    //  LoadResources 和 Instance 的对比
    // 协程
    IEnumerator LoadResources()
    {
        //清除干净以免影响测试结果 
        Resources.UnloadUnusedAssets ();
    
        // 5s 秒后看结果 (72.8MB)
        yield return new WaitForSeconds (5.0f);
    
        // 通过 Resource.load 加载资源 (72.8MB)
        GameObject tank = Resources.Load("Role/Tank")as GameObject;
        yield return new WaitForSeconds (0.5f);
    
        // Instantiate 一个资源出来 (75.3MB)
        GameObject tankInst = GameObject.Instantiate(tank,Vector3.zero,Quaternion.identity) as GameObject;
        yield return new WaitForSeconds (0.5f);
    
        // Destory 一个资源 (没有立刻释放,存在镜像缓存,事实上内存变化非常小)(74.7MB)
        GameObject.Destroy(tankInst);
        yield return new WaitForSeconds (0.5f);
    
        // 释放无用资源 (72.3MB)
        tank = null;
        Resources.UnloadUnusedAssets (); // 这个是内存 的释放是完美的
        yield return new WaitForSeconds (0.5f);
    }
    

    以 AssetBundle.Load 和 Instance 优化对比

      IEnumerator LoadAssets()
    {
        // 清除干净以免影响测试结果 (59.9MB)
        Resources.UnloadUnusedAssets();
    
        // 等待 5 秒以看到效果
        yield return new WaitForSeconds(5.0f);
        // 创建一个 www类 (62.0MB)
        WWW bundle = new WWW(path);
        yield return bundle;
        yield return new WaitForSeconds (0.5f);
    
        // AssetBundle.Load 一个资源 (64.5MB)
        Object obj = bundle.assetBundle.Load("tank");
        yield return new WaitForSeconds (0.5f);
    
        // Instantiate 一个资源出来 (65.6MB)
        GameObject tankInst = Instantiate(obj) as GameObject;
        yield return new WaitForSeconds (0.5f);
    
        // Destory 一个资源  (63.9MB)
        GameObject.Destroy(tankInst);
        yield return new WaitForSeconds (0.5f);
    
        // Unload Resources  (63.7MB)
        bundle.assetBundle.Unload(false); // 支持映射
        yield return new WaitForSeconds(0.5f);
    
        // 释放无用资源 (61.8MB)
        // obj = null;
        //  Resources.UnloadUnusedAssets();
        yield return new WaitForSeconds (0.5f);
    }
    
    AssetBundle和instance.png Assetbundle和Instance优化的区别.png

    通过静态绑定的方法 来 Instantiate 一个资源

    IEnumerator InstResources()
    {
        // 62.0MB
        Resources.UnloadUnusedAssets ();
        yield return new WaitForSeconds (5.0f);
    
        // 64.4MB
        GameObject tank = Resources.Load("Role/Tank")as GameObject;
        GameObject inst = GameObject.Instantiate (tank,Vector3.zero,Quaternion.identity)as GameObject;
        yield return new WaitForSeconds (1f);
    
        // 64.0MB
        GameObject.Destroy (inst);
        yield return new WaitForSeconds (1f);
    
        //释放无用资源 (6.23MB)
        tank = null;
        Resources.UnloadUnusedAssets ();
        yield return new WaitForSeconds (1f);
    }
    
    通过静态绑定.png

    总结:

    .png 01.png 02.png 03.png

    相关文章

      网友评论

        本文标题:Unity的图片优化和TexturePacker的使用

        本文链接:https://www.haomeiwen.com/subject/kykvottx.html