Unity的图片优化和TexturePacker的使用

最近在做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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容