Uinty3D性能优化之贴图科普篇
-
一、贴图和纹理
-
图像文件格式和纹理格式的区别:
常用的图像文件格式有BMP,TGA,GIF,JPG(有损不透明),PNG(无损透明)等;
常用的纹理格式有R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8, A8R8G8B8等。
- 文件格式
是图像为了存储信息而使用的对信息的特殊编码方式,它存储在磁盘中,或者内存中,但是并不能被GPU所识别,因为以向量计算见长的GPU对于这些复杂的计算无能为力。这些文件格式当被游戏读入后,还是需要经过CPU解压成R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8, A8R8G8B8等像素格式,再传送到GPU端进行使用。
- 纹理格式
是能被GPU所识别的像素格式,能被快速寻址并采样。
所以在Unity中无论导入图片是什么格式,Unity都会转换成纹理格式,便于GPU读取使用。
OpenGL ES 2.0支持以上提到的R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8,A8R8G8B8等纹理格式,其中R5G6B5,A4R4G4B4,A1R5G5B5每个像素占用2个字节(BYTE),R8G8B8每个像素占用3个字节,A8R8G8B8每个像素占用4个字节。
- 纹理压缩格式(硬件压缩格式)Hardware Accellerated Formats
- DXT(PC) ETC(Android) PVRTC(IOS)
把纹理进一步压缩(Texture Compression),不像png那样采用霍夫曼编码,而是针对GPU硬件的格式来压缩。
如果GPU不支持当前压缩格式,Unity就会将纹理转换成RGB(A),会造成解压过程消耗,内存中会保存两个纹理。
- 图片大小计算:
一个像素占的字节 = 位数(bit)/ 8 因为一个字节8位
一张图片占的内存大小 = 宽 * 高 * 一个像素占的字节
比如一张512*512纹理,R5G6B5格式 = 512 * 512 *(一个像素占的字节) = 512*512*2/1024k= 0.5M
如果你使用的是mipmap贴图,那么其大小将是普通图片的1.3倍左右。
-
二、选择纹理压缩格式的衡量标准
- 解析速度
在纹理操作中,读取纹理数据是关键步骤,所以解码速度至关重要。
- 随机读取数据
能快速的随机读取任意像素
- 压缩率和纹理质量
既要保证一个不错的压缩效果,也要把纹理损失控制在一定范围内
- 压缩速度
通常纹理压缩在渲染前已经提前准备好,所以如果压缩的速度比解析速度慢,也是可以接受的。
- 压缩格式
使用目标平台支持的纹理压缩格式时,GPU可以直接使用它,没有增加消耗;
如果目标平台不支持纹理压缩格式时,默认会将纹理解压成RGBA32或RGB32,然后与压缩的纹理一起存储在内存中,会增加纹理加载时间和增加额外内存。
三、纹理压缩格式
- png:
是无损压缩有透明,也是占体积最少的。都是基于整幅图片下进行的压缩,比如霍夫曼编码等,这样像素和像素之间在解码的过程中存在依赖关系,无法直接实现单个像素级别的解析,这就发挥不了显卡的并发能力,更重要的是问题在于无论是png还是jpg最终在显存中解码后都是RGBA的纹理格式,因此并无法减少显存的占用率。比如一张256*256的RGBA纹理,无论是png还是jpg格式,虽然文件大小不一样,在显卡中的大小仍然是256*256*4的显存空间
- Jpg
是有损压缩不透明。
- DXT压缩原理:(PC端)
是一种有损纹理压缩算法,可以说DXT是目前应用最广泛的纹理压缩格式,可以认为所有的PC端显卡都支持DXT压缩。在多数纹理压缩选择中都是最佳方案,几乎可以认为是PC下的标准压缩格式。
基本思想是把4x4的像素块压缩成一个64或128位的数据块,是有损压缩方式。
比如一个4*4像素R8G8B8格式的占的体积 = (4*4)(像素)*24(位)=384
压缩后体积=2(像素,左上右下两个位置)*16(位) + 16(位置索引)*2(位) = 64
DXT1中,4*4等于16个像素,然后将左上C0和右下C1保存两个颜色,其它颜色由这两个颜色进行差值计算,差值得到的另外两个颜色的公式为:C2= 2/3*C0 + 1/3*C1, C3 = 1/3*C0 + 2/3*C1。16位中R&B各占5位,但是G占了6位,这是因为人眼对绿色最为敏感。DXT1可以表示透明或不透明,通过左上大于右下表示完全不透明,反之表示透明。
DXT1:4*4占用64位颜色
DXT2/DXT3:4*4占用64位颜色加(64位表示透明信息也就是一个像素4位)=128位8个字,(0-3位表示透明,4-7表示颜色)主要用于Alpha通道较锐利,对比强烈的材质,比如镂空或部分半透材质等。
DXT4/DXT5:4*4占用64位颜色加(64位表示透明信息(2个8位透明值+16个3位索引值))=128位8个字
- ETC:(Android移动端)
ETC1:不支持透明通道,所以仅能用于不透明纹理
ETC1(不带Alpha)OpenGL2.0支持、ETC2(带Alpha)OpenGL3.0才支持
压缩格式要求分辩率为2的次幂
- PVR:(IOS)
DXT的细节上缺陷明显,原因是当把纹理分成4*4区域后,块之间是独立的,虽然简化了压缩算法,但丢失了相邻间的相似性。而PVR则会考虑区域的右侧、下侧和右下侧区域的关联性。
压缩格式要求压缩分辩率是正方形,分辩率为2的次幂
PowerVR Texture Compression,PVRTC格式与基于块的压缩格式,比如S3TC、ETC的不同之处是,它使用2张双线性放大的低分辨率图,根据精度和每个像素的权重,融合到一起来呈现纹理,并且2-bpp和4-bpp都支持ARGB数据。PVRTC格式压缩比高,也是有损压缩。
-
四、Unity纹理格式总结
每位的像素点(bpp)是单个像素所需的存储量。该值越低,可以在GPU的缓存中存储的像素越多,从而可以更快地进行纹理访问。
可变比特率(VBR)表示每个像素的比特不是固定值,而是取决于实际内容。VBR仅适用于Crunch压缩,并且仅适用于磁盘上的纹理大小。内存中的大小与使用基础纹理格式时的大小相同(例如,对于RGB Crunched DXT1,使用RGB Compressed DXT1)。
Crunch compression是DXT或ETC Texture压缩之上的一种有损压缩格式(部分数据在压缩过程中丢失)。纹理在CPU上被解压缩为DXT或ETC,然后在运行时加载到GPU。
Crunch compression可能需要很长时间才能压缩,但是在运行时进行解压缩非常快。
质量和压缩时间之间的平衡:质量越高,文件大小越大。为了获得最佳性能,尤其是在移动设备上,请使用应用程序所针对的硬件所支持的每像素位数最少的格式,只要该格式能够提供足够的视觉质量即可。
使用目标平台支持的纹理压缩格式时,GPU可以直接使用它,没有增加消耗;
如果目标平台不支持纹理压缩格式时,默认会将纹理解压成RGBA32或RGB32,然后与压缩的纹理一起存储在内存中,会增加纹理加载时间和增加额外内存。
-
五、总结
- Android采用RGBA Compressed ETC2 8 bits 不透明:RGB Compressed ETC 4 bits
- iOS采用RGBA Compressed PVRTC 4 bits 不透明:RGB Compressed PVRTC 4 bits
- 如果PVRTC不够清晰可选用ASTC格式,ASTC有更好的质量和灵活性,并且压缩速度比PVRTC快
- PC采用RGBA Compressed DXT5 不透明:RGBA Compressed DXT1
- 渐变纹理采用RGBA32
- 要求清晰可采用:
- (高清晰无压缩)采用RGBA32
- (中清晰中压缩)RGBA16+Dithering 要采用Floyd Steinberg抖动处理,不然渐变很明显,拉伸就会差距
- (低清晰高压缩)ETC2 PVRTC
-
ASTC压缩率和清晰度都会比较好,支持的GPU :
tvOS (all), iOS (A8以上,5S以下), Android (PowerVR 6XT, Mali T600 series, Adreno 400 series, Tegra K1)
2016年之后的安卓手机基本支持这种压缩格式
iphone6以及之后的机型支持这种压缩格式 -
六、优化
- 在Unity中根据不同平台,不同功能要求的清晰度来设置不同的压缩格式。
参考:
https://blog.csdn.net/wanglang3081/article/details/8869589
https://blog.csdn.net/weixin_33814685/article/details/86431607?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1
https://blog.csdn.net/ynnmnm/article/details/44983545