• coco2d-x 纹理研究


    版权声明:本文为博主原创文章。未经博主同意不得转载。 https://blog.csdn.net/xiebaochun/article/details/25926121

    1.通常情况下用PVR格式的文件来进行图片显示的时候,在执行速度和内存消耗方面都要比PNG格式要快和小。

    普通情况下PVR消耗的内存比PNG消耗的内存小25%左右。PVR格式能够用ZWoptex导出。PVR是apple芯片能直接读取和显示的文件.

     

    2.图片抗锯齿处理。

    图片放大时的处理:

    图片在放大的时候会出现锯齿。纹理类提供了setAntiAliasTexParameters()函数来处理抗锯齿。当图片放大的时候会使用相邻的四个像素进行混合运算。

    从而消除锯齿。可是会让图片产生模糊的感觉。

    左边使用的就是setAntiAliasTexParameters()。和右边的图片相比无锯齿。可是有模糊的感觉.右边的图片使用的是setAliasTexParameters()。不模糊,可是有锯齿。

    图片缩小时的抗锯齿处理:

    使用minmap,缩小时效果会更好。左边使用minmap

     

    1. CCSprite *imgMipMap = CCSprite::create("Images/logo-mipmap.pvr");  
    2. if( imgMipMap )  
    3. {  
    4.     imgMipMap->setPosition(ccp( s.width/2.0f-100, s.height/2.0f));  
    5.     addChild(imgMipMap);  
    6.   
    7.     // support mipmap filtering  
    8.     ccTexParams texParams = { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE };      
    9.     imgMipMap->getTexture()->setTexParameters(&texParams);  
    10. }  

    3.cocos2d-x的纹理类支持从多种格式的文件载入图片.包括:.png , .jpeg, .pvr, .gz, .webp, .tiff, .ccz等格式的文件 。

     

    4.最大限度的通过一些方法和手段减小纹理对内存消耗.

    (1) 通过CCTexture2D来改动纹理的像素格式。能够降低纹理占用的内存大小和渲染的速度。

    使用16-bit纹理

    最高速地降低纹理内存占用的办法就是把它们作为16位颜色深度的纹理来载入。

    cocos2d默认的纹理像素格式是32位颜色深度。

    假设把颜色深度减半,那么内存消耗也就能够降低一半。并且这还会带来渲染效率的提升,大约提高10%。

    你能够使用CCTexture2D对象的类方法setDefaultAlphaPixelFormat来更改默认的纹理像素格式。代码例如以下:

    1. CCTexture2D::setDefaultAlphaPixelFormat(kCCTexture2DPixelFormat_RGB565);  

    这里有个问题:首先,纹理像素格式的改变会影响后面载入的全部纹理。

    因此,假设你想后面载入纹理使用不同的像素格式的话,必须再调用此方法。并且又一次设置一遍像素格式。

    其次,假设你的CCTexture2D设置的像素格式与图片本身的像素格式不匹配的话,就会导致显示严重失真。比方颜色不正确,或者透明度不正确等等。

    有哪些比較实用的纹理像素格式呢?

    1. generate 32-bit textures: kCCTexture2DPixelFormat_RGBA8888 (default)  
    2. generate 16-bit textures: kCCTexture2DPixelFormat_RGBA4444  
    3. generate 16-bit textures: kCCTexture2DPixelFormat_RGB5A1  
    4. generate 16-bit textures: kCCTexture2DPixelFormat_RGB565 (no alpha)  


     

    RGBA8888是默认的格式。

    对于16位的纹理来说,使用RGB565能够获得最佳颜色质量。由于16位全部用来显示颜色:总共同拥有65536总颜色值。可是。这里有个缺点。除非图片是矩形的,并且没有透明像素。

    所以RBG565格式比較适合背景图片和一些矩形的用户控件。

    RBG5A1格式使用一位颜色来表示alpha通道。因此图片能够拥有透明区域。

    仅仅是,1位似乎有点不够用,它仅仅能表示32768种可用颜色值。并且图片要么仅仅能全部是透明像素。或者全部是不透明的像素。由于一位的alpha通道的缘故。所以没有中间值。

    可是你能够使用fade in/out动作来改变纹理的opacity属性。

    假设你的图片包括有半透明的区域,那么RBGA4444格式非常实用。

    它同意每个像素值有127个alpha值,因此透明效率与RGBA8888格式的纹理区别不是非常大。可是,由于颜色总量降低至4096,所以。RBGA4444是16位图片格式里面颜色质量最差的。

    如今,你能够得到16位纹理的不足之处了:它由于颜色总量的降低,有一些图片显示起来可能会失真,并且可能会产生“梯度”。


     

    (2)避免一个接一个地载入PNG和JPG纹理(他们之间至少等待一帧)

     

    cocos2d里面纹理载入分为两个阶段:1.从图片文件里创建一个UIImage对象。2.以这个创建好的UIImage对象来创建CCTexture2D对象。

    这意味着,当一个纹理被载入的时候,在短时候内。它会消耗两倍于它本身内存占用的内存大小。

    (译注:为什么仅仅是短时间内呢?由于autoRelease pool和引用计数的关系,暂时创建的UIImage对象会被回收。)

    当你在一个方法体内。接二连三地载入4个纹理的时候,这个内存问题会变得更加糟糕。由于在这种方法还没结束之前,每个纹理都会消耗两倍于它本身的内存。

    我不是非常确定。如今的cocos2d是否仍然如此。

    或者这样的情况是否仅仅适用于手工引用计数管理。也许ARC不会如此呢?我习惯于按顺序载入纹理。可是在载入下一个纹理之前要等待一帧。

    这将会使得不论什么纹理载入的消耗对内存的压力降低。

    由于等待一帧。引用计数会把暂时的UIImage对象释放掉。降低内存压力。此外,在兴许的文章中,假设你想在背景线程中按序载入纹理的话。也能够採用这样的方法。

     

    (3)iphone中不要使用jpg来创建纹理对象

    cocos2d-iphone使用JPG纹理的时候有一个问题。由于JPG纹理在载入的时候,会实时地转化为PNG格式的纹理。这意味着cocos2d-iphone载入纹理是非常慢的(这里有演示),并且JPG纹理将消耗三倍于本身内存占用大小的内存。

    一个2048*2048大小的纹理会消耗16M的内存。当你载入它的时候,在短时间内,它将消耗32MB内存。如今。假设这个图片是JPG格式,你会看到这个数字会达到48MB。由于额外的UIImage对象的创建。尽管,终于内存都会降到16M,可是。那一个时刻的内存飙高,足以让os杀死你的游戏进程,造成crash。影响用户体验。

    JPG不论在载入速度和内存消耗方面都非常差。所以。千万不要使用JPG!

    (4)忽视文件图片大小

    这样的情况,我见到非常多。它乍听起来可能认为有点荒诞。但事实如此,由于它须要关于文件格式的知识,而这些知识并非每个程序猿都了解的。我常常听到的论断就是“嘿!我的程序不可能有内存警告。我全部的图片资源加起来还不到30MB!”。

    怎么说呢,由于图片文件大小和纹理内存占用是两码事。

    假设他们是帐篷。图片文件就相当于帐篷被装在行李箱。可是。假设你想要使用帐篷的话,它必须被撑起来,被“膨胀”。

    图片文件和纹理的关系与此相似。图片文件大多是压缩过的。它们被使用的话必须先解压缩,然后才干会GPU所处理,变成我们熟知的纹理。

    一个2048*2048的png图片,採用32位颜色深度编码,那么它在磁盘上占用空间仅仅有2MB。可是。假设变成纹理,它将消耗16MB的内存!

    当然,降低纹理占用内存大小是有办法滴。

    第一个办法就是:上面的条款(1),用SetDefaultAlphaPixelFormat()来实现.尽量使用颜色深度为16bit的图片。

    可是条款 (1)也提到了,假设图片本身的颜色深度假设是32位。可是用SetDefaultAlphaPixelFormat()设置成16位。则可能让图片失真.图片看起来就非常模糊。这当然是有解决的办法,庆幸我们有TexturePacker(TP)工具.

    TP有一个特性叫做“抖动”,它能够使得原本由于颜色数量降低而产生的失真问题得到改善。(TP里面有非常多抖动算法。关于这些算法,读者能够參考我翻译的还有一篇文章)。

    特别是在拥有Retina显示的像素密度下。你差点儿看不出16位与32位的纹理之间的显示区别。当然。前提是你须要採用“抖动”算法。

    第二个办法是:使用NPOT纹理

    NOPT是“non power of two”的缩写,译作“不是2的幂”。NPOT stands for “non power of two”. 在cocos2d1.x的时候。你必须在ccConfig.h文件里开启对NPOT的支持,可是,cocos2d 2.x就不须要了。它默认是支持NPOT的。全部3代(iphone 3GS)以后的ios设置都支持cocos2d 2.x(由于它们支持OpenGL ES2.0)。所以也都能支持NPOT纹理。

    假设纹理图集(texture atlas)使用NPOT的纹理,它将有一个具大的优势:它同意TP更好地压缩纹理。因此,我们会更少地浪费纹理图集的空白区域。并且,这样的纹理在载入的时候,会少使用1%到49%左右的内存。并且你能够使用TP强制生成NPOT的纹理。(你仅仅须要勾选“allow free size”就可以)

    为什么要关心NPOT呢?由于苹果的OpenGL驱动有一个bug,导致假设使用POT的纹理,则会产生额外33%的内存消耗

     

    第三个办法是:默认使用PVR格式的纹理

    TP让你能够创建PVR格式的纹理。除了PVR纹理支持NPOT外,它们不仅能够不是2的幂,并且还能够不是方形的。

    PVR是最灵活的纹理文件格式。

    除了支持标准的未压缩的RGB图片格式外,不支持有损压缩的pvrtc格式。

    另外,未压缩的pvr格式的纹理的内存消耗非常地低。不像png图片那样要消耗2倍于本身内存占用大小的内存。pvr格式仅仅须要消耗纹理本身内存大小再加上一点点处理该图片格式的内存大小。

    pvr格式的一个缺点就是,你不能在Mac上面打开查看。可是。假设你安装了TP的话,就能够使用TP自带的pvr图片浏览器来浏览pvr格式的图片了。(强烈建议大家购买TP,支持TP,不要再盗版了)

    使用PVR格式的文件差点儿没有缺点。

    此外,它还能够极大地提高载入速度。后面我会解释到。

    使用pvr.ccz文件格式

    在三种可选用的pvr文件格式中。优先选择pvr.ccz格式。

    它是专门为cocos2d和TP设计的。在TP里面,这是它生成的最小的pvr文件。并且pvr.ccz格式比其他不论什么文件格式的载入速度都要快

    当在cocos2d里面使用pvr格式的纹理时,仅仅使用pvr.ccz格式,不要使用其他格式!由于它载入速度超快,并且载入的时候使用更少的内存。

    当视觉察觉不出来的时候,能够考虑使用PVRTC压缩

    PVR纹理支持PVRTC纹理压缩格式。它主要是採用的有损压缩。假设拿PVRTC图片与JPG图片作对照的话,它仅仅有JPG图片中等质量,可是。最大的优点是能够不用在内存里面解压缩纹理。

    这里把32位的png图片(左边)与最佳质量的PVRTC4(4位)图片(点击图片查看完整的大小)作对照:

    注意,在一些高对照度的地方,明显有一些瑕疵。

    有颜色梯度的地方看起来还好一点。

    PVRTC肯定不是大部分游戏想要採用的纹理格式。可是。它们对于粒子效果来说,非常适用。

    由于那些小的粒子在不停地移动、旋转、缩放,所以你非常难看出一些视觉瑕疵。

    预先载入全部的纹理

    就像标题所说,尽你所能。一定要预先载入全部的纹理。假设你的全部的纹理加起来不超过80MB内存消耗的话(指的是拥有Retina显示的设备,非Retina的减半考虑)。你能够在第一个loading场景的时候就全部载入进来。

    这样做最大的优点在于。你的游戏体验会表现得非常平滑,并且你不须要再操心资源的载入和卸载问题了。

    这样也使得你能够让每个纹理都使用合适的纹理像素格式。并且能够更方便地找出其他与纹理无关的内存问题。由于假设与纹理有关,那么在第一次载入全部的纹理的时候,这个问题就会暴露出来的。假设全部的纹理都载入完成,这时候再出现内存问题,那么肯定就与纹理无关了。而是其他的问题了。

    假设你知道问题与纹理无关的话,那么你查找剩下的内存问题将会变得更加简单。并且你避免了前面说的这样的情况:当2048*2048的纹理载入的时候,它本来仅仅须要消耗16MB内存,可是短时间会冲到32MB内存。后面会提出一种方法来解决“间歇性内存飙高”(“译者发明滴”)的方法。

    (译者:希望下次开发人员的对话中“间歇性内存飙高”的说法会出现。呵呵)

    依照纹理size从大到小的顺序载入纹理

    由于载入纹理时额外的内存消耗问题。所以,採用按纹理size从大到小的方式来载入纹理是一个最佳实践。

    假设,你有一个占内存16MB的纹理和四个占用内存4MB的纹理。

    假设你首先载入4MB的纹理,这个程序将会使用16MB的内存,而当它载入第四张纹理的时候。短时间内会飙到20MB。这时。你要载入16MB的那个纹理了,内存会立即飙到48MB(4*4 + 16*2),然后再降到32MB(4*4 + 16)。

    可是。反过来,你先载入16MB的纹理,然后短时候内飙到32MB。

    然后又降到16MB。

    这时候,你再依次载入剩下的4个4MB的,这时,最多会彪到(4*3 + 4*2 + 16=36)MB。

    在这两种情况下,内存的峰值使用相差12MB,要知道,可能就是这12MB会断送你的游戏进程的小命哦。

    避免在收到内存警告消息的时候清除缓存

    我有时候看到了一种奇怪的“自己开枪打自己的脚”的行为:纹理已经全部在Loading场景里面载入完成了。这时候,内存警告发生了,然后cocos2d就会把没有使用的纹理从缓存中释放掉。

    听起来不错,没有使用到的纹理都被释放掉了。可是!

    你刚刚把全部的纹理都载入进来。还没有进入不论什么一个场景中(此时全部的纹理都被当作“unused”),可是立即被全部从texture cache中移除出去。可是,你又须要在其他场景中使用它们。

    怎么办?你须要接着推断,假设有纹理没有载入,就继续载入。

    可是。一载入,由于“间歇性内存飙高”,又立即收到了内存警告。再释放,再推断,再载入。

    。。。 我的天,这是一个死循环啊!

    这也能解释为什么有些童鞋。在loading场景完了之后进入下一个场景 的时候非常卡的原因了。

    如今,当我收到内存警告的时候。我的做法是----什么也不做。内存警告仍然在发生,可是,它仅仅是在程序刚開始载入的时候。

    我知道这是为什么,由于“间歇性内存飙高”嘛,所以,我不去管它。(可是。假设是游戏过程中再收到内存警告,你就要注意了,由于这时候可能你有内存泄漏了。!!)

    我有时候会想办法改善一下,通过移除掉一些不使用的纹理和一些仅仅有在非常特殊的场景才会使用的图片(比方settings界面,玩家是不常常訪问的)。然后。无论什么时候,当我须要某张图片的时候,我会首先检查一下该sprite frame是否在cache中,假设没有就载入。

    你会在后面看到详细的做法。

    理解在什么时候、在哪里去清除缓存

    不要随机清除缓存,也能够心想着释放一些内存而去移除没有使用的纹理。

    那不是好的代码设计。有时候。它甚至会添加载入次数,并多次引发“间歇内存飙高”。分析你的程序的内存使用。看看内存里面究竟有什么,以及什么应该被清除,然后仅仅清除该清除的。

    你能够使用dumpCachedTextureInfo方法来观察哪些纹理被缓存了

    这种方法的输出例如以下:(为了清楚起见,我把那些与-hd后缀有关的信息屏蔽掉了) 

    1. cocos2d: "ingamescorefont.png" rc=9 name=ingamescorefont-hd.png id=13 128 x 64 @ 32 bpp => 32 KB  
    2. cocos2d: "ui.png" rc=15 name=ui-hd.png id=5 2048 x 2048 @ 16 bpp => 8192 KB  
    3. cocos2d: "ui-ingame.png" rc=36 name=ui-ingame-hd.png id=8 1024 x 1024 @ 16 bpp => 2048 KB  
    4. cocos2d: "digits.png" rc=13 name=digits-hd.png id=10 512 x 64 @ 16 bpp => 64 KB  
    5. cocos2d: "hilfe.png" rc=27 name=hilfe-hd.png id=6 1024 x 2048 @ 32 bpp => 8192 KB  
    6. cocos2d: "settings.png" rc=8 name=settings-hd.png id=9 1024 x 1024 @ 16 bpp => 2048 KB  
    7. cocos2d: "blitz_kurz.png" rc=1 name=(null) id=12 50 x 50 @ 32 bpp => 9 KB  
    8. cocos2d: "gameover.png" rc=8 name=gameover-hd.png id=7 1024 x 2048 @ 32 bpp => 8192 KB  
    9. cocos2d: "home.png" rc=32 name=home-hd.png id=4 2048 x 2048 @ 16 bpp => 8192 KB  
    10. cocos2d: "particleTexture.png" rc=2 name=(null) id=11 87 x 65 @ 32 bpp => 22 KB  
    11. cocos2d: "stern.png" rc=2 name=(null) id=2 87 x 65 @ 32 bpp => 22 KB  
    12. cocos2d: "clownmenu.png" rc=60 name=clownmenu-hd.png id=1 1024 x 2048 @ 32 bpp => 8192 KB  
    13. cocos2d: CCTextureCache dumpDebugInfo: 13 textures using 60.1 MB (纹理总共占用的内存大小!!!

      )  


    上面包括了非常多实用的信息。纹理的大小、颜色深度(bpp)和每个被缓存的纹理在内存中所占用大小等。这里的“rc”代表纹理的“引用计数”。假设这个引用计数等于1或2的话。那么意味着。这个纹理当前可能不会须要使用了,此时。你能够放心地把它从纹理cache中移除出去。

    你仅仅移除你知道在当前场景下不太可能会被使用的纹理(即上面介绍的引用计数为1或2的情况),这是一个明智的做法。另外,仅仅移除那些占用内存大的纹理。假设一个纹理仅仅占几个kb的内存,其他移不移除都没什么太大的影响。

    (译注:这就和程序优化一样,不要做过多的细节优化。不要过早优化,要找到性能的瓶颈,然后再重点优化,以20%的时间换取80%的效率。过早和过多细节优化对于大多数程序而言,是须要极力避免的)。

    SpriteFrames retain textures!

    上面提到的样例中,纹理的引用计数可能有点让人看不懂。

    你会发现,纹理集有非常高的retain count。即使你知道这些纹理集中的纹理当前并没有被使用。

    你可能忽略了一件事:CCSprteFrame会retain它的纹理。

    因此,假设你使用了纹理集,你要全然移除它不是那么easy。由于,由这个纹理集产生的sprite frame还是保留在内存中。所以。你必须调用CCSpriteFrameCache的removeSpriteFramesFromTexture方法,能彻底清除纹理缓存中的纹理集。(译注:记住,不是你调用对象的release方法了。对象的内存就会被释放掉。而是引用计数为0了。内存才会被删除)

    [[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFramesFromTexture:uncachedTexture];

     你也能够使用 removeSpriteFramesFromFile,并指定一个纹理集的.plist文件来清除缓存起来的精灵帧(spriteframes).

    加入 SpriteFrames 非常耗时, 每次都是!

    Note: 这一点仅仅针对cocos2d v1.0有效,而cocos2d v2.x在载入之前会预先推断。

    这样看起来有点无知(innocent):

    [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"ui.plist"];
  • 相关阅读:
    判断大文件是否上传成功(一个大文件上传到ftp,判断是否上传完成)
    hbase的region
    把hdfs数据写入到hbase表
    eclipse和scala整合,打包配置文件及打包步骤
    sparkStreaming 读kafka的数据
    脚本put数据到hdfs
    Hive的自定义函数
    Ftp客户端需要TSL功能的文件上传
    Hive中的数据库、表、数据与HDFS的对应关系
    一文了解RPC框架原理
  • 原文地址:https://www.cnblogs.com/ldxsuanfa/p/10735578.html
  • Copyright © 2020-2023  润新知