在开发游戏的过程中很多刚入门的新人对于游戏中的图片与纹理资源这两个概念都会混淆起来,一知半解,原先我自己也一样。所以很多时候可能都是搜索下网络上的知识告诉你哪种纹理的压缩格式好就随意的去采用,一般都不会去深究相应的原理。最近自己在做DEMO时候碰到一个问题,感觉需要整理一下,这里就全用来做记录。
图片的概念
图片在百度百科中的定义是指由图形、图像等构成的平面媒体。图片的格式很多人物立绘,但总体上可以分为点阵图和矢量图两大类,我们常用BMP、JPG等格式都是点阵图形,而SWF、CDR、AI等格式的图形属于矢量图形。
我们电脑上手机上经常看到的.png、.jpg、bmp、.gif等文件,之所有这么多种,这主要是由于图片的性质以及存储算法决定的。
关于图片的特性
对于图片来说一般需要了解以下三个特性
有损和无损压缩
有损压缩:是指对图像的压缩过程中,算法丢失一部分图像信息,降低了图像的质量,并且这种损失是不可逆的。有损压缩可以减少图像在内存和磁盘中占用的空间。使用有损压缩的图片如果在屏幕上显示,可能肉眼看起来影响不大,但如果用高分辨率打印机打印出来,那么图像质量就会有明显的受损痕迹
无损压缩:是指对图像的压缩过程中,不丢失图片原本的信息,任何时候都可以从无损压缩过的图片中恢复出原本的信息;无损压缩虽然能减小图像占用磁盘的大小,但不能减少图像占用的内存空间,因为实际的显示过程中,看图软件会先把图片还原成原本的信息。无损压缩能较好的保存图片质量,但压缩率较低
索引色和直接色
索引色:用一个数组来代表一种颜色,在存储图像的时候,存储一个数字的组合,同时存储数字到图像颜色的映射。这种方式只能存储有限种颜色游戏开发素材,通常的用法是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这两种都支持格式呢?下面进行下原因的分析
JPEG不支持透明度
一般在游戏中,有很多透明物件unity 图片压缩格式,而JPEG不支持透明度,所以没法使用
PNG不支持随机读取像素
PNG格式支持透明度,但是因为PNG的压缩算法是根据图片整体进行压缩(比如采用霍夫曼编码)unity 图片压缩格式,像素和像素之间存在依赖关系,无法直接实现单个像素级别的解析,这就没办法发挥显卡的优势
无法减小显存占用
无论是JPEG或者PNG 的图片最终在显卡解码之后,都是RGBA纹理,无法减小显存的占用(256×256像素的图片,虽然文件格式、磁盘占用、内存占用大小不一样,但都是占用256Kb的显存空间,性能消耗大)
纹理格式需求
正是因为传统的图片并没有考虑显卡的这种特性,所以很难满足三维应用中的要求,要满足显卡能使用的纹理格式,应该具有以下特点:
解析速度
在纹理操作中,读取纹理数据是关键步骤,所以解码速度至关重要,这一点是最应该考虑的
随机读取数据
能快速的随机读取任意像素,因为显卡中很多操作都是针对单个像素执行的,所以这一点也很重要
压缩率和纹理质量
既要保证一个不错的压缩效果,也要把纹理损失控制在一定范围内
压缩速度
通常纹理压缩在渲染前已经提前准备好,所以如果压缩的速度比解析速度慢,也是可以接受的。
说明:Unity在导入图片后,编辑器会卡住弹出一个进度条窗口,就是使用原图进行纹理压缩然后生成新的图片文件的过程。生成的文件的位置,你可以根据导入图片对应的.meta文件内的guid,去项目中的Library文件夹中查找,Unity打包图片的Assetbundle其实是根据这个文件来打包的,这已经不是你导入的那张原图
纹理压缩格式
为了满足以上4点,图形工程师开发了很多种纹理压缩格式,目前移动设备主流的纹理压缩格式有PVRTC、ETC/ETC2、ASTC,当然,还有其他的类型(比如:RGBA32、RGBA16、Alpha8等有兴趣的可以去了解下)
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的组合形式,然后用自定义Shder合并。不过现在基本淘汰,可以直接使用ETC2代替。如若想深入了解算法的,可以在这查看:
PVRTC纹理压缩格式
iPhone的图形芯片(PowerVR MBX)对 PVRTC 的压缩提供硬件支持,Apple推荐在开发iPhone应用程序时使用 PVRTC 压缩纹理格式。而且,根据图像成图质量以及占用内存等,有多种格式可选择(pvtrc_rgba2,pvrtc_rbga4等);缺点是需要图片的长宽相等而且为2的幂次方(针对这种情况,可以考虑把图片打包在一张2的幂次方图集内)
ASTC纹理压缩格式
ASTC中ARM研发的一种较新的贴图压缩格式,从IOS9(A8架构,现在都iOS12了)开始支持ASTC压缩格式 ,相对于PVRTC2/4而言,ASTC(4X4)的压缩比会增加到0.25,不过显示效果也会好很多,而且不要求图片长宽相等且为2的幂次方。而且Android设备也支持,可以说,ASTC是目前最好的纹理压缩格式,默认首选。(由于这是最新压缩格式,所以可能存在部分机型不适配或者软件解压的时候存在bug)
总结
在实际游戏引擎内(以Unity为例),图片显示流程大概是:原图 —> 纹理压缩(ETC/ETC2等)—>OpenGL ES3—>显卡—>游戏内显示;
而一张图片在游戏引擎中运行时占用的显存大小,与这种图片本身占用磁盘大小无关,只和图片的宽和高相关;
图片导入到Unity引擎中,之所以很慢,是因为在进行纹理压缩编码生成新的文件,这一步很耗时间;
AssetBundle打包图片,不是打包原图,而是针对你对图片设置的纹理压缩格式而生成的新的文件打包,所以ab文件的大小也和原图本身占用磁盘大小无关;
AssetBundle文件的大小与加载成正比关系,同时,纹理压缩格式的解码时间也有差异(差异不是很大)
平台的最佳纹理压缩格式是:astc_rgba_4x4 它兼顾了成像质量,内存占用,ab大小,解码时间,跨平台等因素
参考
[1]: "常用纹理和纹理压缩格式"/ynnmnm/ar
[2]:"Unity Compressed"/Manual
[3]: "移动设备的纹理压缩方案"/article/deta
[4]: "关于U3D贴图格式压缩"/s/blog
[5]: "各种移动GPU压缩纹理的使用方法"/luming1979/
[6] "版本分布情况"/a
More:【微信公众号】 u3dnotes
免责声明:部分资源收集整理自网络,版权归原作者所有,仅作学习交流使用,不用于任何商业途径,如不慎该资源侵犯了您的权利,请通知我及时删除,谢谢。