知识早班车:
1、当n大于1时,2的n次幂一定能被4整除;证明:2^n = 2^2*2^(n-1) = 4*2^(n-1)
2、4的倍数不一定都是2的次幂;证明:4*3 = 12;12不是2的次幂
3、Pixel(像素)是组成图片的最小单位;Byte(字节)是计算机文件大小的最小单位;bit(比特/位)是计算机处理运算的最小单位
4、常见的RGB 16位,ARGB/RGBA 16位,RGB 24位,ARGB/RGBA 32位等,它们通常的通道分配有:
RGB 16位 = RGB565 = RGB555(最高位不用)、ARGB/RGBA 16位 = ARGB4444 = RGBA4444 = ARGB1555 = RGBA5551
RGB 24位 = RGB888、ARGB/RGBA 24位 = ARGB/RGBA6666、RGB 32位 = RGB888(剩余8位保留)、ARGB/RGBA 32位 = ARGB/RGBA8888
知识始发站
我们在做资源优化时,必然少不了对纹理格式的处理。任何图片文件格式在导入Unity后,都会被转换为Textur2D格式,然后我们可以在Texture2D的导入设置选项中根据不同平台,设置纹理的压缩格式。
那么,为什么我们需要纹理压缩格式呢?
例如RGB565、ARGB4444、ARGB1555、RGB888、ARGB8888等未经压缩的图片格式,是能够被GPU直接读取的原生纹理格式。但在低端硬件设备或移动平台下,有两个问题需要解决:内存+带宽;
1、例如ARGB8888格式中一个像素占4Byte,如果是512x512分辨率内存占用为:512x512x4B=1048576B=1M,这种内存消耗在低端设备上根本无法接受。
2、另一个重要的是数据传输时的带宽,带宽是发热的元凶,在渲染3D场景时,会有大量的贴图被传输到GPU,若不限制,总线带宽很快就会成为瓶颈,手机秒变暖手宝,严重的还会影响渲染性能。
因此我们需要一种内存占用既小又能被GPU读取的格式——压缩纹理格式。纹理压缩对应的算法是以某种形式的固定速率有损向量量化将固定大小的像素块编码进固定大小的字节块中。
有损:表示对于渲染来说,有损压缩是可以接受的,一般选择压缩格式时需要在纹理质量和文件大小上寻求一个平衡。
固定速率压缩:因为GPU需要能够高效的随机访问一个像素,这意味着对任意像素,解码速度不该有太大的变化。因此,我们常见的贴图压缩算法都是有损压缩。相反例如zip则是一种可变速率压缩。
向量量化:一种量化技术,将一组大量的点(向量)分成具有近似相同数量的最接近它们的点的组。每个组用它的质心点表示,因此存在数据误差,适用于有损压缩。放到纹理压缩中来理解,就例如将4x4块像素的颜色以2个基色来表示。
编码和解码速度:一般来说编码速度慢没关系,因为通常纹理压缩只需要在游戏打包时进行一次,对于用户运行时体验完全没有影响。但解码速度必须足够快,而且基本上不影响渲染性能。
压缩比:通常以比特率或每像素的平均比特数(bits per pixel, bpp)表示,常见的为1~8bpp。一般RGB原生纹理的像素指24位,即每个像素占24位(bit),现有4bpp表示每个像素占4位(bit),那么可认为4bpp表示压缩比例为6:1.
另Unity对导入的资源图片,不管是PNG格式还是JPG格式,统统都会转换为Unity引擎当中处理的Texture2D格式,
为什么我们不使用png,jpg这类常见的压缩格式?
尽管jpg、png的压缩率很高,但并不适合纹理,主要问题是不支持像素的随机访问,这对GPU相当不友好,GPU渲染时只使用需要的纹理部分,我们总不能为了访问某个像素去解码整张纹理吧?不知道顺序,也不确定相邻的三角形是否在纹理上采样也相邻,很难有优化。这类格式更适合下载传输以及减少磁盘空间而使用。
常见的纹理压缩格式
1、ETC
ETC主要是android的标准压缩方案,其原理简单来说是将4x4的像素块编码为2x4或4x2像素的两个块的方法,每个块指定一个基色,每个像素的颜色通过一个编码为相对这些基色偏移的灰度值确定。
Unity中常用的ETC压缩格式有:
RGB ETC1 4bit:4bit/pixel,对RGB压缩比为6:1,不支持alpha,绝大多数android设备都支持
RGB ETC2 4 bit:4 bits/pixel,对RGB压缩比6:1。不支持Alpha,ETC2兼容ETC1,压缩质量可能更高,但对于色度变化大的块误差也更大,需要在OpenGL ES 3.0和OpenGL 4.3以上版本。
RGBA ETC2 8bit:8 bits/pixel,对RGBA压缩比4:1。支持完全的透明通道,版本要求同上。
RGB +1bit Alpha ETC2 4bit:4 bits/pixel。支持1bit的Alpha通道,也就是只支持镂空图,图片只有透明和不透明部分,没有中间的透明度。
2、DXT 原名S3TC(S3 Texture Compression )
其原理简单来说,是由一对低精度的“基色”来描述一个4x4的RGB像素块,并允许每个像素在这些基色之间指定一个插值。S3TC有多种变体,每种都是为特定类型的图像数据设计,但它们都是将4x4的像素块转换为64位或128位的数据
3、ASTC
ASTC是android和ios平台下的一种高质量压缩方式,支持android5.0和iphone6以上的机型。它也是一种基于块的有损压缩算法,它的块是固定大小为128位,虽然是固定大小,但是与先前格式的固定4x4块不同,每个纹理在这128位中可以有不同大小的块,可以是方形和非方形,2D纹理编码中,它从4x4到12x12像素都有,对应的压缩比1~8bpp。它的特点有很多:
较高的灵活性:支持1-4分量的贴图
压缩率/质量灵活可变:根据不同图片会选择不同压缩率级别的算法
支持2D/3D贴图
跨平台:iOS、安卓、PC
同时支持LDR和HDR:BC6H虽然支持HDR但不支持Alpha通道
块越大,压缩质量越差,但是图片越小。ASTC的比特率可以在小数级变化,这种技术称为BISE(Bounded Integer Sequence Encoding)。
但要注意的是尽管纹理可以被编码为1-4通道图像,但是解码后的值总是以RGBA格式输出。在LDR sRGB模式下,颜色值以8位整数的形式返回,而如果是HDR则将以16位浮点数的形式返回。
在设置ASTC格式时,还需注意一下几点:
法线贴图:尽量选择4x4,避免丢失过多数据。
细节处的贴图:选择4x4或6x6,否则会丢失细节。
一般贴图:选择6x6或8x8。4、无关紧要但尺寸特别大的图:可考虑8x8或10x10或12x12,不然打包出来太大。
通过以上三种压缩格式我们发现,三种压缩算法中都用到了4x4的像素块做运算,这让我们不禁想到了,我们在做图片优化时,经常要求美术同学在输出图片尺寸时,保持长宽为4的倍数的原因了,这样我们在对导入Unity的图片进行压缩格式设置的时候才会起到更好的效果。不过,可能还会有同学提出疑问,为什么有时候会听到把图片尺寸设置成2的次幂呢?这是因为最初的ETC压缩算法不支持透明通道,图片宽高要求必须是2的次幂,还有就是ios平台下的PVRTC压缩格式也是仅支持2的次幂,再加上当n>1时,2^n一定为4的倍数,所以就有要求图片输出尺寸为2的次幂的要求;好在现在andorid平台大多都支持ETC1/ETC2,ETC2是ETC的扩展,支持透明通道,且图片宽高只要是4的倍数即可;PC平台也都支持DXT1/DXT5/DXT5,DXT3和DXT5原理同DXT1一样,只不过DXT3和DXT5支持透明通道,也要求图片宽高只要是4的倍数即可;ASTC格式也被越来越多的平台所支持,所以我们在设置图片尺寸时,只需要设置为4的倍数即可。还有就是Generate Mip Maps如果是开启状态下,只有2的次幂才能被压缩,因为生成mipmap必须是2的次幂的像素。
知识小贴士:
1、对于图片,现在有sprite atlas来对图片进行管理,凡是要打进图集的图片,尺寸可以不必遵循4的倍数设置,因为这些图最终是打进图集的,我们可以对图集进行4的倍数的设置,不过这样做的一个缺点就是图集为了保持设置的4的倍数的尺寸,不得不浪费一些空间,所以最好的方式还是将所有图片都设置成4的倍数;不进图集的图片的尺寸就要严格控制了,最好为4的倍数,且最大尺寸不要超过1024x1024。
2、关闭Read/Write 选项,因为开启此选项将会允许从脚本来访问纹理数据,将会产生纹理数据的副本, 副本会占用内存, 等同于一个纹理数据会有双倍的内存消耗。
3、Filter Mode设为Bilinear的纹理:Trilinear 三线性过滤(三线性插值), 纹理会在不同的 mip 水平之间进行模糊 ,从而增加 GPU 开销。
4、Sprite纹理关闭Mipmap:Mipmap 开启后,内存会是未开启 Mipmap 的 1.33 倍, 因为 Mipmap 会生成一组长宽依次减少一倍的纹理序列, 一直生成到 1*1。Mipmap 提升 GPU 效率; 一般用于 3D 场景或角色, UI 不建议开启。
5、Wrap 模式设置为 Clamp:Wrapmode 使用了 Repeat 模式, 容易导致贴图边缘出现杂色
以PC平台为例:
如果导入的图片长宽尺寸为4的倍数也为2的次幂,则会被压缩为RGB Compressed DXT1 或者RGBA Compressed DXT5;
如果导入的图片长宽为4的倍数但不为2的次幂,则会被压缩为(NPOT)RGB Compressed DXT1 或者(NPOT)RGBA Compressed DXT5;
如果导入的图片长宽不为4的倍数也不为2的次幂,则会被压缩为(NPOT) RGB24/(NPOT) RGBA32,这些都为未压缩格式,非常占用内存