« 本周游戏 | 返回首页 | 模块的初始化 »

DXT 图片压缩

这两天在写 DDS 格式的解码程序。DDS 是微软为 DirectX 开发的一种图片格式,MSDN 上可以查到其文件格式说明:DDS File Reference

其中的 DXT 图片压缩格式,现在已经为绝大多数 3D 显卡硬件所支持。(它使用了由 S3 公司所发明的一种有损图象压缩算法。btw, 在我的那本书中,P232 有所提及)。DXT 格式 也叫作 S3TC ,现在可以被流行看图软件直接显示的图象格式中,只有 .dds 文件支持这种压缩。为了开发方便,我们的引擎也就支持了 .dds 文件的加载。

一起做引擎的同事希望即使在硬件不支持的时候,我们也能正常加载并使用贴图,所以便有了对 DXT 软解码的需求。

好在以前研究过一些,写起来也不麻烦。

DXT1 支持 1 bit 的 alpha 通道。这个其实是可选的。每个 4x4 的块可以根据需要有或没有这个透明通道。不需要 alpha 通道时,每个块可以有四种颜色(其中两个是插值得到的);需要 alpha 通道时,则只能有三种颜色,11 被保留用来描述透明的点。区分是否用通道,要根据每个块开始的两个高彩颜色值:color_0color_1 。如果 color_0 在数值上(当作无符号短整型)大于 color_1 则没有通道。

可惜的是,在 dds 文件的文件头中,没有任何一个地方描述了:整个图片是否有至少一个块包含了 alpha 通道。但是在 3d 程序中,却需要知道这一信息以使程序可以更高效的运行。

对于软解码程序,更需要知道这一信息。因为带通道时,我们需要把数据解码成 RGBA5551 的格式;而不带通道时,则需要解码成 RGB565 格式。

一开始我以为需要扫描整个数据段,检查是否至少有一个块的 color_0 小于等于 color_1 。实际看了几个用工具生成的 dds 文件才发现自己错了。nvidia 的 dxt tools 压缩 DXT1 图片时,需要手动指明是否需要 1 bit 的通道。如果你指定不带通道,那么每个 4x4 数据块头上的两个调色盘颜色值的大小次序是无关的(这样做,由于插值方案的差异,有可能得到更好一点的图象质量)。也就是说,只有压缩图片的人知道图片上是否有通道,而文件头上并无记录。


DXT3 就是在 DXT1 的基础上,增加了 4bit 的 alpha 通道,每个 4x4 块多用了 64bit 来保存这些 alpha 通道信息。(数据储存时,在每个数据块中,alpha 通道信息放在颜色信息的前面)

DXT5 对 alpha 通道的储存作了改进,有点意思,值得一提 :D 。它依旧用 64bit 储存 16 个 alpha 信息。前面 2 个字节(16bit)保存了当前块的最大 alpha 值和最小 alpha 值。接下来的 48 bit ,每个像素占用 3bit 空间,刚好描述 4x4 个像素。

alpha_0 大于 alpha_1 时,我们后面的 3bit 可以表示 8 级的插值;反之则保留 110 和 111 分别表示 alpha 为 0 和 255 的情况,中间可以有 6 级过度的插值。

关于 DXT3 和 DXT5 的压缩算法,在 MSDN 上也可以找到详细的文章:Textures with Alpha Channels


至于 DXT2 和 DXT4 实际用的不多,从数据压缩算法上来讲,它们完全等同于 DXT3 和 DXT5 。区别只在于颜色数据是否经过 alpha 预乘。

既然是否作 alpha 预乘都可以在 dds 文件中使用专门的 4 字节标识,我奇怪的是,DXT1 中那么重要的通道信息居然不在 dds 文件头中表示出来。真不知道设计文件格式的人怎么想的。 >_<

Comments

谢谢你共享,对鄙人帮忙很大!
b = color[index & 0x03].b; g = color[index & 0x03].g; r = color[index & 0x03].r; if (FMT_S3TC_DXT1 == fmtsrc) { a = ((index & 0x03) == 3 && c0 b = color[index & 0x03].b; g = color[index & 0x03].g; r = color[index & 0x03].r; if (FMT_S3TC_DXT1 == fmtsrc) { a = ((index & 0x03) == 3 && c0 <= c1) ? 0 : 255; } 判断Alpha通道是否存在的依据
b = color[index & 0x03].b; g = color[index & 0x03].g; r = color[index & 0x03].r; if (FMT_S3TC_DXT1 == fmtsrc) { a = ((index & 0x03) == 3 && c0 b = color[index & 0x03].b; g = color[index & 0x03].g; r = color[index & 0x03].r; if (FMT_S3TC_DXT1 == fmtsrc) { a = ((index & 0x03) == 3 && c0 <= c1) ? 0 : 255; } 判断Alpha通道是否存在的依据

Post a comment

非这个主题相关的留言请到:留言本