Home > 程序/算法 > DXT++纹理压缩格式

DXT++纹理压缩格式

October 14th, 2009 Leave a comment Go to comments

本文来源于Colt McAnlis在09GDC上名为Texturing Massive Terrain的演讲稿,可以在这个页面上找到ppt下载。我只是把原文提到的算法翻译整理了一下,DXT++这个名称以及下面所有插图都来自原文。

DXT++和DXT是什么关系呢?先了解一下什么是DXT。

DXT是现在绝大多数显卡都支持的一种纹理压缩格式,根据是否带有alpha以及具体的压缩算法等因素分为DXT1~DXT5几种格式,它们的具体差别请自行阅读相关资料,也可以参考云风的这篇文章

DXT压缩的基本概念,以不带alpha的DXT1为例,是把纹理分成若干4×4像素的block,对于每个block记录16个像素颜色中的“最大值”和“最小值”(把颜色值当成一个整型来比较),然后每个像素用2bit数据在最大最小值间进行线性插值,也就是说4×4的区域内只会出现4种颜色。这样一来两个最大最小颜色占32bit(采用r5g6b5),加上16个像素每个2bit一共用64bit来表示4×4区域,平均是4bpp(bit per pixel),相对于原始的24bpp来说获得了1:6的压缩比。

dxt1

DXT对应的文件格式是微软开发的dds,相当于直接把DXT数据保存成文件,另外添加了一些诸如mipmap,cubemap之类的支持。这样从文件中加载时读入内存就直接是DXT格式,而不用运行时再进行压缩。

这里有个问题,这个1:6的压缩比貌似惊人,但是相对于jpg格式采用相近的画质能轻松获得1:20以上的压缩比来说,还是有较大差距的。而且当今的3d游戏绝大部分的资源容量都耗费在纹理上,如果能用jpg格式存储纹理,无疑能极大减小游戏安装包的尺寸,节省和安装后的硬盘空间,或者缩短用户的下载时间。虽然jpg不能带alpha通道,但我们完全可以把alpha通道单独分离出来存储。

用jpg的问题在于,显卡本身不支持jpg这种压缩格式,读取jpg纹理后必须在内存中解压成原始的rgb纹理才可以使用,所以节省了存储/网络带宽的代价是牺牲了显存。

有办法两全其美吗?也就是既能节省存储/带宽,又能节省显存?方法是有的,就是jpg解压之后,再在运行时将纹理压缩成DXT,但是这样无疑又增加了系统加载纹理资源的时间负担。关于实时DXT压缩算法,可以参考这篇Real-Time DXT Compression

那有办法三全其美吗……答案仍然是有的!这就是Colt提出的DXT++文件格式,简单说就是将DXT数据进一步压缩减小文件尺寸(有些情况下甚至能超过jpg的压缩比),另外加载时能直接解压成DXT,而不用先解压jpg,再进行昂贵的实时DXT压缩。当然比起直接读取dds还是要慢,否则dds也没有存在的必要了。

基本思路是,DXT格式里可能有大量的冗余数据,首先压缩是针对4×4的block的,每个block存了两个颜色,但是对于整个纹理来说,不同的block里很可能有相同(或相近)的颜色,这里就产生了数据冗余(数据压缩的基本要点就是消除冗余嘛)。所以我们现在把纹理中所有block的颜色都拎出来放在一起,所有剩下的像素颜色index放在一起,分开来压缩。

dxt2

首先压缩颜色。我们先把纹理中所有block的最大最小颜色都统计出来,按出现的频率高低排序并进行哈夫曼编码。所谓哈夫曼编码就是对于出现频率高的数据赋予较少的bit数,出现频率低的赋予较长的bit数,以达到数据压缩的目的。当然,编码要避免歧义。

dxt3

在这里的应用就是,在文件的开头按出现频率由高到低列出所有的block颜色,然后对于每个block的最大最小颜色,只存储它们的哈夫曼编码。要说明的是,如果每个block的最大最小颜色都不同,那么这种存储方式会占用更多的空间,在这种情况下,我们可以将某些相近的颜色当成同一种颜色来处理,具体的阈值可以在实际应用中调整。

block颜色搞定了,剩下就是每个像素的颜色index。我们仍然采用哈夫曼编码压缩,但是显然不能以2bit为单位进行编码,而是把所有的数据都拼起来成为一个连续的bit stream,然后用一个合适的粒度来编码。距作者Colt说这个粒度在8~16bit之间,实际应用中可以尝试不同的粒度并比较最终的数据大小,选择最小的那个(反正是离线处理,多花一些时间也是值得的)。

解压的过程是反过来的,如果压缩过程看明白了,解压也不需要再赘述。下面提供原文中的两个例子,当然这两张纹理都比较特殊,所以数据看起来相当诱人。

dxt4

dxt5

需要说明的是,DXT++在压缩时实际上已经牺牲了画质。而较高端的3d游戏对画质要求较高,可能会让用户根据自己显存大小选择是否采用压缩纹理(或者自动设置),在这种情况下,DXT/DXT++就不太适合了,可能jpg + 实时DXT压缩才是正确的选择。如果需要压缩,就先解压再动态DXT压缩;如果不需要压缩,就直接使用解压后的jpg纹理,仍然可以获得高画质。当然jpg也是有损的,好在画质高低可以由你选择。

Categories: 程序/算法 Tags:
  1. No comments yet.

 

Spam Protection by WP-SpamFree