KaaPexei 发表于 2022-2-17 16:25

有关图片压缩整理

纹理压缩

有损压缩:是指对图像的压缩过程中,算法丢失一部分图像信息,降低了图像的质量,并且这种损失是不可逆的。有损压缩可以减少图像在内存和磁盘中占用的空间。使用有损压缩的图片如果在屏幕上显示,可能肉眼看起来影响不大,但如果用高分辨率打印机打印出来,那么图像质量就会有明显的受损痕迹

无损压缩:是指对图像的压缩过程中,不丢失图片原本的信息,任何时候都可以从无损压缩过的图片中恢复出原本的信息;无损压缩虽然能减小图像占用磁盘的大小,但不能减少图像占用的内存空间,因为实际的显示过程中,看图软件会先把图片还原成原本的信息。无损压缩能较好的保存图片质量,但压缩率较低
索引色和直接色

索引色:用一个数组来代表一种颜色,在存储图像的时候,存储一个数字的组合,同时存储数字到图像颜色的映射。这种方式只能存储有限种颜色,通常的用法是256种颜色

直接色:使用四个数字来代表一种颜色,这四个数字分别代表颜色中的红色R、绿色G、蓝色B以及透明度A。所以直接色可以表示2^32种颜色。当然,并非所有的直接色都直接这么多种变化,为压缩空间,可能有多种设定
点阵图和矢量图

点阵图:也叫位图,象素图。构成点阵图的最小单位是像素,位图就是由像素点阵列的排列来实现其显示效果。点阵图缩放会失真

矢量图:也叫做向量图。矢量图并不记录画面上每一点的信息,而是记录元素的形状以及颜色的算法,当你打开一副矢量图的时候,软件对图像对应的函数进行运算,将运算结果显示给你看,无论显示画面是大还是小,画面对应的算法是不变的,所以对画面进行倍数相当大的缩放,其显示效果仍然相同(不失真)
不同图片格式的特性

介绍了这些,那么我们来看看我们经常见到的图片的一些特性

JPEG 有损,直接色,点阵图 ;BMP 无损,直接色,点阵图;PNG-8 无损,索引色,点阵图;PNG-24 无损,直接色,点阵图

为什么png还有多种样式?那是因为PNG算法在把图像转化成图片的时候有很多参数和方式可以选择,这些参数和方式就决定了图像的压缩和成像品质
为什么需要纹理压缩?

Android平台默认支持格式:JPEG、PNG、GIF、BMP 和 WebP

iOS平台默认支持格式:JPEG、JPEG2000、PNG、GIF、BMP、ICO、TIFF、PICT、APNG、SVG、RAW

既然Android和iOS支持这么多图片格式,那么游戏中为什么没有”直接”使用JPEG或者PNG这两种都支持格式呢?下面进行下原因的分析

常见的图片文件格式,比如PNG,JPG,BMP等,是图像为了存储信息而使用的对信息的特殊编码方式。它存储在磁盘中,或者内存中,但是并不能被GPU所识别。这些文件格式当被读入后,还是需要经过CPU解压成bitmap,再传送到GPU端进行使用。
1 JPEG不支持透明度


一般在游戏中,有很多透明物件,而JPEG不支持透明度,所以没法使用
2 PNG不支持随机读取像素


PNG格式支持透明度,但是因为PNG的压缩算法是根据图片整体进行压缩(比如采用霍夫曼编码),像素和像素之间存在依赖关系,无法直接实现单个像素级别的解析,这就没办法发挥显卡的优势

间接寻址


上图为间接寻址模式中获取颜色数据的过程:GPU首先请求加载索引数据14;一旦有了索引数据就可以立即加载实际颜色值,来回四次最终得到颜色值。索引数据可能为FIFO缓冲区引入了性能隐患,芯片端保存颜色表的代价也十分大,颜色表可能和纹理缓存占的空间接近;所以最好避免使用颜色表这些全局数据。
3 无法减小显存占用


无论是JPEG或者PNG 的图片最终在显卡解码之后,都是RGBA纹理,(宽*高*4)无法减小显存的占用(256×256像素的图片,虽然文件格式、磁盘占用、内存占用大小不一样,但都是占用256Kb的显存空间,性能消耗大)

jpeg和png图片,从文件加载的时候都必须先在cpu先解码成原始的RGB(A)格式的数据

纹理压缩之后占用显存减少,一种比较简单的统计方案。你直接查看纹理压缩文件的大小就好了,比如纹理压缩文件是170k,那显存占用也是170k
纹理格式需求

正是因为传统的图片并没有考虑显卡的这种特性,所以很难满足三维应用中的要求,要满足显卡能使用的纹理格式,应该具有以下特点:
1 解析速度


在纹理操作中,读取纹理数据是关键步骤,所以解码速度至关重要,这一点是最应该考虑的
2 随机读取数据


能快速的随机读取任意像素,因为显卡中很多操作都是针对单个像素执行的,所以这一点也很重要
3 压缩率和纹理质量


既要保证一个不错的压缩效果,也要把纹理损失控制在一定范围内
4 压缩速度


通常纹理压缩在渲染前已经提前准备好,所以如果压缩的速度比解析速度慢,也是可以接受的。

说明:Unity在导入图片后,编辑器会卡住弹出一个进度条窗口,就是使用原图进行纹理压缩然后生成新的图片文件的过程。生成的文件的位置,你可以根据导入图片对应的.meta文件内的guid,去项目中的Library文件夹中查找,Unity打包图片的Assetbundle其实是根据这个文件来打包的,这已经不是你导入的那张原图
纹理压缩技术


纹理压缩不同于其他图片压缩方式(jpg,png),在使用中,不会在CPU中进行解压缩,而是直接把压缩内容传给GPU,而且在GPU中也不会一次把整张图片进行解压缩,只会在需要采样特定区域的纹理时对这一区域的纹理进行解压缩。
纹理压缩格式


为了满足以上4点,图形工程师开发了很多种纹理压缩格式,目前移动设备主流的纹理压缩格式有PVRTC、ETC/ETC2、ASTC,当然,还有其他的类型(比如:RGBA32、RGBA16、Alpha8等有兴趣的可以去了解下)

纹理压缩格式不需要CPU解码

ETC/ETC2纹理压缩格式

说到ETC ,就要说到OpenGL ES,OpenGL ES 是 OpenGL 三维图形 API 的子集,针对手机、平板和游戏主机等嵌入式设备而设计. OpenGL ES2 支持ETC 格式,所以我们可以在支持OpenGL ES2的显卡的设备上使用ETC这种纹理压缩格式;而ETC2 则需要OpenGL ES3才能支持,而OpenGL ES3已经发布了三四年,所以现在市面上的显卡都支持,也就是说,现在市面上的移动设备都支持ETC2这种纹理压缩格式了,可以放心使用了;

ETC不支持透明通道,所以以前很多项目的做法是用2张纹理来保存一张图片。ETC+Alpha8的组合形式,然后用自定义Shader合并。不过现在基本淘汰,可以直接使用ETC2代替。

Alpha压缩率适用机型

ETC1N6:1OpenGLES 2.0支持几乎所有市面上的Android机,所有iPhone

ETC2Y6:1OpenGLES3.0支持大部分高端Android机,iPhone 5S及以上

PVRTC纹理压缩格式

iPhone的图形芯片(PowerVR MBX)对 PVRTC 的压缩提供硬件支持,Apple推荐在开发iPhone应用程序时使用 PVRTC 压缩纹理格式。而且,根据图像成图质量以及占用内存等,有多种格式可选择(pvtrc_rgba2,pvrtc_rbga4等);缺点是需要图片的长宽相等而且为2的幂次方(针对这种情况,可以考虑把图片打包在一张2的幂次方图集内)

支持的Android机型需要GPU为 PowerVR系列
总结

在实际游戏引擎内(以Unity为例),图片显示流程大概是:原图 —> 纹理压缩(ETC/ETC2等)—>OpenGL ES3—>显卡—>游戏内显示;

而一张图片在游戏引擎中运行时占用的显存大小,与这种图片本身占用磁盘大小无关,只和图片的宽和高相关;

图片导入到Unity引擎中,之所以很慢,是因为在进行纹理压缩编码生成新的文件,这一步很耗时间;

AssetBundle打包图片,不是打包原图,而是针对你对图片设置的纹理压缩格式而生成的新的文件打包,所以ab文件的大小也和原图本身占用磁盘大小无关;

AssetBundle文件的大小与加载成正比关系,同时,纹理压缩格式的解码时间也有差异(差异不是很大)

平台的最佳纹理压缩格式是:astc_rgba_4x4 它兼顾了成像质量,内存占用,ab大小,解码时间,跨平台等因素
页: [1]
查看完整版本: 有关图片压缩整理