一般的压缩方案
做移动平台,终究都是要考虑纹理压缩的问题
IOS/PVR平台上一般会选用PVRTC格式,这个格式压缩还是很给力。
Android上设备种类很多,支持的格式各有不同。如果平台能支持下载前检测设备来区分下载的APK,我们可以选择相应的压缩格式。但是很多平台都只要一个APK,我们就只能选择最稳妥的ETC1+Alpha方案了。
这个方案用过的应该都知道并不是特别好,首先肯定要生成两张图,然后压缩比还不错,但是压缩质量就有点不行了。在普通的纹理上用还可以,但是在UI这种要足够清晰的地方就有些尴尬。
思路
我想要解决的问题是怎么在保证一定的压缩比情况下,尽可能保证图像质量,给ETC1+Aplha找一个替换的方案。
一个彩色图片中到底那些对清晰度感受更重要?
一般的RGB图,三个通道是等价的,要压缩一起压缩。要损品质一起损,有没有其他的图片表达方式更适合压缩?
我做了这个使用YCrCb 2020标准的YUV转换,我觉得还可以,就是下图第二个。
效果图
第一次尝试,HSV方案
HSV就是用色调(H),饱和度(S),明度(V)来表示色彩的方式,具体的介绍HSV百度百科。在这种表达方式中保证H的精度就可以在最终的图上保证相当的显示效果,于是我们将其他通道都压缩一半的分辨率。对一个RGBA图来说转换成HSVA图能有7/16的压缩比,不是很高,但是已经是可以接受的了。
然后我们实验一下……
简直辣眼睛,这是什么鬼
将过滤模式改为point,之后这个现象变成了一些紫色的斑点,而且颗粒感好强。
进一步研究发现这时因为色相H的采样过滤造成的,色相一般是这个样子
展开成直线就变成了这样
最终我们时将h存入图片的一个通道上,也就是i这个直线的取值变成了0~1之间。然后我们从图上采样会发生什么呢?
设想像素A是偏黄的红色,取值应该是0.05左右。相邻的一个像素B是偏紫的红色,去只是0.95左右。
这时会发生什么?在双线性采样的时候中间的屏幕像素会被插值……然后两个像素中间就会变成一个小小的从红色到红色的彩虹
显然我们是不能接受point采样的效果,我们还得想想别的办法。
YUV方案
YUV就是使用色差进行颜色编码,由亮度信号Y和两个色差新号B-Y(即U)、R-Y(即V)组成。 YUV百度百科
Y这个亮度实际上是由RGB三个通道叠加出来的,显然保证Y基本就保证了清晰度。
这个YUV最早是给电视用的,经过了很多年发展,出了几代标准,更多的覆盖了色度空间上的颜色范围。
内圈是HDTV709,外圈是UHDTV2020
那么显然我们就用2020标准就好了。
实现YUV
RGBA图转换到YUVA图的工具
第一步就是如何生成YUVA图,这个显然让美术提供会有些强人所难,我们需要由一个自动化工具来做这件事情。
这种转换就是一次两个坐标系之间的坐标转换,重点就是生成正确的转换矩阵。这个矩阵要怎样生成我是靠wiki来解决的,感谢wiki。如果有兴趣可以自己去搜一下,代码里的注释也写了网址。
基本的流程:读取图片,转换成YUV,分通道压缩,生成纹理。
最后就是这个样子,反正能看代码,就不细说了。
最后这个invert matrix是为了写shader方便弄的,就是反转换矩阵。总不能在shader里面还每次都算一遍这个矩阵。
注意:其实还需要一个PostProcesser脚本,将所有生成的YUV图转成apha8格式,毕竟我们只需要一个通道。
显示Shader
这就真没啥可说的,分别采样4个图,然后用反转矩阵做一下乘法,就完事了。
可能的优化
采样4次,也是很废。实际上是可以变成采样两次的,Y一个alpha8图,UVA用一个RGB图。采样两次,压缩比还是7/16。
矩阵现在是写死在shader里面,也可以改成参数的,应该能快一点。
YUVA生成工具甚至可以改为之间修改material的,一劳永逸。