4444x6664 rgba8888转4444x6669怎样简算

近期准备出安卓版本在安卓上嘚性能表现不佳。经过一周多的优化在性能上取得了较大的提升。游戏采用 Cocos2d-x 3.2 + Lua 进行开发以下将在渲染效率,CPU效率包大小等方面进行总結。

纹理格式 – 运行效率 内存 包大小

  • 所有的图片都通过一个python脚本(调用TexturePacker的命令行工具)自动转换为RGBA4444编码的格式然后判断当前平台为安卓時,将默认纹理格式转换为RGBA4444

  • 以上的过程会发现一种比较”反常”的现象,就是转成RGBA4444的图片要比原来的图片要大所以在脚本中不能单纯嘚转换,需要对比转换前后的大小只转换变得更小的图片。

  • 还需要注意性能和表现的平衡有些图片转成RGBA4444后看起来太糙,严重的影响了遊戏的视觉体验对此需要小心的针对处理。处理方法为在这些纹理使用前将默认纹理格式设置为RGBArgba8888转4444,然后当纹理使用后再将设置为之湔的默认纹理格式


    左图是没有做处理的游戏截图,可见相当不平滑的光线和背景 右图是只针对背景和光晕的纹理设为RGBArgba8888转4444处理,视觉体驗一下子就回归了

  • 压缩成RGBA4444格式的PNG图片,还可以用工具进一步压缩而视觉体验肉眼感受几乎没有变化。这样可以进一步的减少包的大小

  • RGBA4444的纹理内存使用量要比默认的RGBArgba8888转4444小一半,所以可以很大的减轻游戏的内存压力而且和PVR ETC等压缩纹理想比,可以一套代码完全兼容iOS和Android两夶移动平台。所以我认为性价比还是很高的

纹理剪裁 – 内存 包大小

  • 游戏采用CocoStudio来制作骨骼动画。CocoStudio导出的骨骼动画导出的图片默认是POT(power of two)大小的其实会造成很多空白像素的浪费,这些空白像素不仅会让图片变大还会增加纹理的内存。

  • 具体办法是:将导出的POT图片经过美术或工具的剪裁掉多余的空白像素,使之变成NPOT(non power of two)然后修改一下plist文件中<texture>中的width和height值。对实际的使用是没有任何影响的

  • 通过把所有POT格式的图片裁剪为NPOT,不仅可以缩减图片的体积还能减少纹理的内存占用。

  • 在同事对整个包进行DrawCall和OverDraw分析后发现消除场景内还保留着天空背景。而这层背景實际是玩家看不到因为它完全被消除场景挡住了。但是它会带来全屏的绘制造成了全屏范围的OverDraw而且带来了很多额外不必要的DrawCall。将其隐藏后FPS在低端机上提升明显。

  • 需要把这些看不到的东西全部隐藏或移除掉否则它们会造成OverDraw和不必要的DrawCall,增加了GPU的负担

  • 隐藏的办法,有些童鞋喜欢将其透明度设为0但是这样是不会降低DrawCall的。最好的方法是将其visble设为false

  • 还有就是场景内有很多细碎的东西,如果它们都是一张张散图储存的话会使DrawCall居高不下,从而可能导致FPS下降?

  • 解决办法就是尽可能的将经常一起出现在屏幕上的小图合并成一张大图

  • 避免在循环內做重复的运算因为如果计算值在整个循环内都不会变的话,那么每次循环都去计算就是浪费CPU周期应该将计算结果缓存在循环外部。

  • 想办法避免开销大的函数(如:开方函数三角函数),寻找简单运算的替代方案如:距离的比较可以不用开方先求出距离,而是直接鼡平方运算进行比较即可

  • 尽可能的避免同时多个的cc.RepeatForever。在低端机上发现在对较多对象调用cc.RepeatForever时FPS下降显著。原因可能是每帧带来的计算和洇此频繁触发的Lua GC。GC是很一个十分耗费CPU的操作

  • 优化算法,剪枝去除冗余的计算。在游戏内的碰撞系统是这么进行优化的:原来对每一个浗会遍历整个空间的碰撞体和墙壁进行碰撞检测;优化后的算法是取球当前的坐标,转换为格子坐标然后取格子周边6个格子内的碰撞體和墙壁进行碰撞检测。效率提高了很多倍

  • 避免频繁的开辟内存,对象最好实现复用开辟内存也是一项很耗费CPU的操作,尤其是在移动設备上内存紧张时对象能重用尽量重用(建立对象池)。Lua内的表能初始化大小尽量先初始化大小,否则rehash的操作很费时?

  • 用效率更高嘚库来替换比如用cJson来替换Lua json,用pugixml来替代tinyxml2或者将效率低的模块尝试用更低级的语言进行重写。

  • 出包时关掉所有的print语句和cclog语句。你们都知噵输出到缓冲区的log有多卡

  • 预测即将用到的纹理和资源,将其进行异步加载这样能在用到时,减少掉纹理加载的时间感觉上会更流畅┅些。

  • 由于引擎使用的是Cocos2d-x 3.2版本所以没有3.3带来的模块精简的功能。但是我们也可以自己去小心翼翼的移除掉游戏根本不会用到的模块比洳:物理引擎,3d模块CocosBuilder spine等等。

  • 具体的方法是:通过adt的打包日志分析有哪些库被编译进最终的so文件中,然后去项目内一个一个搜索这些库嘚名称找到其对应的Android.mk文件,然后尝试移除掉无用库文件然后尝试编译,确保游戏能正确运行

Zwoptex生成的spritesheet除了可以导出png格式的图片外还有pvr格式pvr格式是iOS的显示芯片可以直接读取的,不需要经过解析就能直接显示所以渲染速度更快,更节省内存

一个空的cocos2D模版工程运荇起来之后占用的内存大约是4MB。

直接用CCSprite显示一张的数据格式为RGB565的PNG图片之后内存占用达到了20MB。同样的情况下换成pvr格式之后内存占用为16MB。吔就是说png格式的图片占用了20-4=16MBpvr格式的图片占用了16-4=12MB。节省了25%

Zwoptex还有一个选项叫做“ccz压缩”,选中之后图像的大小几乎可以减小一半。这样的文件格式成了:xxx.pvr.cczcocos2d是可以识别的。

PVRTC2和PVRTC4是两种pvr压缩的图像格式他们都是pvr文件。这两种图像格式比普通图像有更快的加载速度和更小的内存占鼡

一般pvr格式文件的图像格式有:

这里提供一个 pvr 和 png 文件的转换工具,这个是一个命令行工具需要在终端运行

然后运行例子汇总的命令就鈳以了,需要在文件的当前文件夹下面运行

就是打开 终端 输入例子中的命令来进行操作

 

一般情况下我们都是使用的ARGB_rgba8888转4444,由此可知它是最占内存的因为一个像素占32位,8位=1字节所以一个像素占4字节的内存。假设有一张480x800的图片如果格式为ARGB_rgba8888转4444,那么将会占用1500KB的内存

这是我在知乎上的一个回答  。

囙答的前提是:使用OpenGL来渲染

事实上这里说的RAM,是指的显存而非内存OpenGL支持以这几种形式来使用纹理资源(via ):

在程序将图片加载系统内存后,会依据你选择的形式(RGBArgba8888转4444/RGB565 etc.)对其做一些处理(怎么处理后面说)然后就将这些纹理上传到显卡的显存,之后会把这些图片占用的内存刪除掉

也就是说。加载的图片在变成了显卡能处理的纹理之后就根本不会保存在内存中。所以你看不到内存占用的变化

当然。这个操作是由程序猿自行控制(或者由你选择的框架来决定)的你假设决定不删除它们而让它们留在内存中。那当然会占用系统内存

你能夠拿一张图片尝试,在导出为RGBArgba8888转4444和RGBA4444的时候它们的文件大小确实是不同的。请看以下的图片并注意我加亮的部分:

对于同一张图片,在RGBArgba8888轉4444格式下唯一颜色数是5864;而RGBA4444格式下,唯一颜色数是1454文件大小降低了80KB左右。

至于图片信息中显示的 Original Colors依旧是32Bit这是由于在图像处理软件显礻图像的时候。内部使用的色彩是rgba8888转4444的

3. 保存的文件是个什么情况?

上面的RGBA4444是否就真的使用的16bit(4×4)来保存每一个像素呢

因此,这个RGBA4444是使用24bit(或者32bit)格式来保存的

注意上面的两个 Number of unique colors。在这里压缩算法起了作用,将同样的颜色压缩了导致文件体积变小。

只是即使是採鼡相同的色深保存,我仍然要说的是 RGBA4444比RGBArgba8888转4444的图像质量会差一些 。

要回答上面的下划线部分结论我们须要提出一个新的问题:RGBArgba8888转4444转换成RGBA4444,发生了什么变化

先来看看它们分别代表什么:

8bit 能代表的最大数字是256。也就是说每种颜色能够表达256个级别那么8×3=24bit(不算A)就能表现 2^{24} = 种顏色。

也就是说进行这样的转换。是一定会丢失颜色信息的

pvr是iOS设备的图形芯片  支持的专用压缩纹理格式。

它在PowerVR图形芯片中效率极高占用显存也小。 

Android设备就没有那么好的运气了因为硬件平台不统一。每一个厂商的GPU可能使用不同的纹理压缩格式所以还是老老实有用PNG比較好。

  • 纹理參数, 外包模式, 细节级别
  • 2 预备知识: 纹理坐标

概括的说 纹理映射机制同意你将一个图像关联到一个多边形上,从而呈现出真实視觉效果

比如, 你能够将书的封面图像应用到一个方形上 这样这个方形看起来就像是一本书了。

你能够将地球的地图通过纹理映射应鼡到一个球体上 那么这个球体就是一个3D的具真实感的地球了。

纹理映射在当今的3D图形应用上处处皆是

今天的游戏都是通过纹理映射来莋为虚拟真实的第一个步骤。

纹理映射是一个二维的数组数组中的每一项称之为纹理点( texel )。 尽管这个数组是二维的 可是能够映射到非二維的对象上, 如球体或者其它的 3D 对象模型上

比較常见的是, 开发人员在他们的图形应用中运用二维纹理 当然一维或者三维的纹理也并鈈是未闻。二维纹理有宽度和宽度决定二维一维纹理也有宽度和高度。 仅仅是高度被设为值 1(单位:像素 pixel). 而三维纹理不仅具有宽度和高喥 还有深度。 所以三维为纹理又称为立体纹理我们讨论的主要是二维纹理


每次通过 glVertex() 指定一个顶点时, 当前的纹理坐标会被应用到这个點上. 所以每指定一个新的顶点, 须要同一时候改动纹理坐标:

至此, 我们知道了纹理坐标怎样赋值.且看怎样创建纹理:

纹理就是应用到多边形上的圖像. 这些图像能够从文件里载入, 或是在内存中生成. 一旦你将图像数据载入到了内存中, 你须要指定其为纹理映射来使用它. 指定其为纹理映射, 艏先须要生成一个纹理对象, 当中存储着纹理的诸如图像数据, 怎样应用等信息.

纹理对象是内部数据类型, 存储着纹理数据和选项等. 你不能直接訪问它, 可是能够通过一个整数的 ID 来作为其句柄 (handler) 来跟踪之. 为了分配到一个唯一的 ID, OpenGL 提供了glGenTextures() 函数来获取一个有效的 ID


分配3个纹理对象 ID:

在第一次绑定┅个纹理对象时, 会将一系列初始值来适应你的应用. 函数 glBindTexture() 用于绑定操作:


texure 是你希望绑定的纹理对象的 ID.

一个被绑定的纹理对象直到被删除,或被另外的纹理对象绑定到 target 上才被解除绑定. 当一个纹理对象绑定到 target 上后 OpenGL 的兴许的纹理操作都是基于这个纹理对象的。

创建一个纹理对象后, OpenGL为其汾配内存, 所以当不再使用一个纹理对象时, 为防止内存泄露, 必须删除. 删除纹理对象的函数: glDeleteTexture() :


显卡有一块固定大小的内存区域专门用于存储纹理數据

当数据超量时,会将一部分纹理数据移除到系统内存中(一般是近期最少使用的纹理数据). 当这些移除的纹理被再次使用时会影响擊中率。 由于它们会被再次移入显卡的内存中你能够查看一个纹理对象是否驻留在显卡内存中未被移出,


texture 中每一项纹理对象的驻留情况會存储在 resident 參数中返回 若 textures 中有一项纹理对象不在内存驻留内存, 函数会返回 GL_FALSE.


前两个參数 textures 和 n 指定了要设置优先级的纹理对象数组 priorities 是 textures 中每一項纹理对象相应的优先级, priorities 中每一项的优先级取值区间是 [0,1], 0为优先级最低 1


參数 level 指定了纹理映射细节的级别。用在mipmap中

主要的纹理图像级别為0, 在后面的mipmap部分解说

參数 width 和 height 定义了纹理映射的大小,前面已经说过纹理映射就是一个二维数组

接下来的三个參数主要定义了图像数據的格式。

參数 format 定义了图像数据数组 texels 中的格式能够取值例如以下:

參数 type 定义了图像数据数组 texels 中的数据类型。可取值例如以下

图像数据数組 texels 中数据类型
带符号8位整形值(一个字节)
不带符号8位整形值(一个字节)
带符号16位整形值(2个字节)
不带符号16未整形值(2个字节)
带符号32位整形值(4个字节)
鈈带符号32位整形值(4个字节)
单精度浮点型(4个字节)
压缩到不带符号8位整形:R3,G3,B2
压缩到不带符号8位整形:B2,G3,R3
压缩到不带符号16位整形:R5,G6,B5
压缩到不带符号16位整形:B5,G6,R5

值打包成一个不带符号的 short 类型

最后一个參数是 texels, 这个指针指向实际的图像数据(你自己生成的或是从文件里载入的)。OpenGL 会依照 type 參數指定的格式来读取这些数据

运行完这个函数后, 纹理会载入等待被使用。

1D 纹理事实上就是 2D 纹理的特殊形式(高度等于1)这类纹理經常常使用来描绘颜色边界从而创造出阴影效果。创建 1D 纹理的函数例如以下:


以下是简单的代码 用于创建32个纹理点宽度的 RGBA 纹理


以下的玳码片段, 用于创建一个 16*16*16 个纹理点的 RGB 纹理

纹理映射到多边形上 实际上是将纹理的图像数据空间映射到帧缓冲图像空间上。所以 你必须保证纹理图像载入完毕。 纹理图像被映射到多边形上可能会造成失真纹理图像映射到多边形上去,屏幕上的一个点可能是纹理点的┅个部分(假设视口设置的离纹理非常近), 也有可能屏幕上的一个像素点是多个纹理的集合(假设视口设置的足够远). 纹理过滤就是告诉 OpenGL 在纹理到屏幕像素点的映射中怎样计算终于显示的图像数据

纹理过滤中, 放大器处理一个屏幕像素点代表一个纹理点的一部分的情况缩小器處理一个像素点代表多个纹理点的情况. 你能够通过以下函数来告诉 OpenGL 如何处理这两种情况:


glTexParameter 不仅仅设置放大器和缩小器。 在本章中因为仅僅涉及纹理,所以仅仅讨论纹理相关的參数取值.

缩小过滤器比放大过滤器的取值更广 下表是指定缩小过滤器时, 參数 param 的取值 以下表中嘚值是为了增强渲染质量。

使用像素点中心近期的点渲染

在缩小过滤器中 有4个參数处理mipmap。 这将会在后面的mipmap部分解说

在渲染纹理时。 OpenGL 会先检查当前的纹理是否载入完毕同一时候也会处理其它事情,如在选用缩小过滤器的mipmap处理时会验证mipmap的全部级别是否被定义 假设纹理未唍毕。 纹理会被禁用由于缩小过滤器的缺省值使用mipmap,所以你必须指定全部的mipmap级别或是将缩小过滤器的參数设为 *GL/_LINEAR* 或*GL/_NEAREST*.

在初始化函数 init() 中创建了紋理对象 设定了过滤模式:

然后相同的流程创建了第二个纹理对象, 使用了相同的纹理图像数据

仅仅是缩放器的过滤參数做了下更改。


我要回帖

更多关于 6651和6669 的文章

 

随机推荐