如何拼接 PVR 压缩贴图
2d 游戏通常都用到很多图素,由于显卡硬件特性,我们又不会把单个图素放在独立贴图中。这样会导致渲染批次过多。在移动设备上,非常影响渲染效率。
所以,游戏运行时,这些图素一般都会合并在很少几张贴图上。要么采用离线合并的方式(利用 texture packer 这样的工具),或者在运行时使用装箱算法。
最近,朱光同学一直在为 ejoy2d 编写运行时合并图素的模块。今天我们讨论了一下他做的诸多尝试。
为了提高贴图利用率,我们可以把大的不规则图素先拆分成小块,然后再组合起来。对于不压缩的贴图,怎么拆分合并都是没有问题的。而对于压缩贴图就要多一些技巧了。
对于 S3TC(DXT) 和 ETC 等压缩贴图,都是按 block 为单位压缩的。block 大小通常是 4x4 像素,block 之间是没有关联的。所以只要按 block 为单位切开,再合并时就不会有任何损失。
PVR 贴图要复杂的多。
了解 PVR 算法可以读一下这一篇 paper 。
这里简述一下:
PVR (4bpp 版本)贴图以 4x4 block 为单位,为每个 block 生成一个 Color A 和一个 Color B 。然后在采样的时候,根据 A 和 B 以及这个像素对应的两个 Modulation bits 插值计算出这个像素的 Color 。
如果仅是这样,对之前的 S3TC 算法改进就有限。
S3TC 压缩有一个问题是在跨越 block 边界上的两个像素很容易发生明显的跳变。这是因为每个 block 独立编码造成的。PVR 是这样解决这个问题的:
贴图上每 2x2 个像素,都会对应到 4 个 block 。也就是说,采样时,会从周围的四个 block 取到 4 对 Color A/B 。每个像素的采样都先先从这对 Color 中做线性插值,再利用 Modulation bits 再插一次。
也就是说,PVR 贴图上的每个像素最终的颜色,都贴图上一个 8x8 区域相关。这可以在同等压缩比下,提高图像的质量。但坏处是即使是采用一些简化算法, PVR 的压缩代价还是很大;如果使用 CPU 解压缩,也会很慢。在 N7 上测试,一张 2048 的贴图,pvr4 编码需要 1.7s , 解码 1.4s 。
但如果根据 pvr 算法的特性,我们还是可以找到方法绕过编解码过程来做切分合并工作:
把每个单独的图素的轮廓外留下 4 像素宽的空白带(alpha 为 0 ),再进行离线 PVR 压缩。作为边界上的空 block ,我们只需要保留其中 Color A/B 的 32bit ,而抛弃 32bit 的 Modulation Data 。因为后 32bit 一定是固定值(0xAAAAAAAA) 。
ps. 这里要提一下 PVR 贴图为 alpha 通道做了特别的优化。只为需要 alpha 通道的 block 描述 alpha 通道,且专门为 alpha 为 0 的情况做了处理。
在合并后,也同样需要让图素间保持 2 倍 block ,也就是 8 像素的距离。
Comments
Posted by: aramisliu | (5) January 19, 2015 01:05 PM
Posted by: loveyou | (4) January 13, 2015 03:39 PM
Posted by: dwing | (3) January 9, 2015 11:01 AM
Posted by: zz | (2) January 9, 2015 10:22 AM
Posted by: dwing | (1) January 8, 2015 09:38 PM