About Texture Compression Techniques
常见疑问
- 为什么要使用纹理压缩?
- 这些ETC,ASTC等纹理压缩格式与常见的JPG,PNG,TGA图片格式有什么区别?
- 那么有什么压缩格式呢?不同平台又如何选择合适的压缩格式呢?
- 对美术同学来说,为什么贴图常有二次幂,4的倍数这种要求?
- GPU是怎么读贴图的呢?
为什么要有纹理压缩?什么是纹理压缩?
- 从GPU如何使用纹理说起
左边是图片格式的读取流程,右边是纹理格式的读取流程。过程都是,CPU读取纹理到系统内存(图片会解压,纹理不会),然后传递到GPU,GPU再读取所需的具体内容。
在Unity中,当GPU无法直接读取压缩过的纹理时,CPU会将读取进来的纹理解压成未压缩的原生纹理(如RGB24,RGBA32等),再传输到GPU进行后面的内容。
- 为什么要有纹理压缩?
从上面的流程可以很明显的看到,虽然GPU可以直接读取R5G6B5,R8G8B8A8等未压缩的原生纹理,但是在低端硬件设备,特别是移动平台下,内存和带宽问题就显得尤为严重。
因此为了缓解移动平台最重要的带宽、功耗和内存问题,我们需要一种内存占用又小又能被GPU读取,同时还要保证速度和质量的格式——压缩纹理格式。
- 什么是纹理压缩?
纹理压缩对应的算法是以某种形式的固定速率有损向量量化(lossy vector quantization )将固定大小的像素块编码进固定大小的字节块中。
- 固定速率:GPU需要高效的随机访问某个像素。
- 有损:对于渲染来说,有损压缩是可以接受的,一般选择压缩格式时需要在纹理质量和文件大小上寻求一个平衡。
- 向量量化:是一种量化技术,将一组大量的点(向量)分成具有近似相同数量的最接近它们的点的组。每个组用它的质心点表示,因此存在数据误差,适用于有损压缩。放到纹理压缩中来理解,就是例如将4×4块像素的颜色以2个基色来表示。
- 解码速度:解码速度快,基本上不能影响到渲染性能。
常见的几种纹理压缩格式介绍
S3TC
历史:S3开发了一种称为S3纹理压缩(S3 Texture Compression)(S3TC)的方案,该方案被选作DirectX的标准,并称为DXTC。在DirectX 10中,它称为BC(用于块压缩(for Block Compression))。
共同属性:DXTC / BC压缩方案有七个变体,它们具有一些共同的属性。编码是在4×4的纹素块上完成的。每个块存储两个参考值颜色,并为该块中的16个纹素的每一个保存一个插值因子。在解码时,每个纹素的颜色等于存储两个参考值颜色或从两种颜色中插值。
PS. 由于版权专利一般用于Windows平台。
- BC1 Block (S3TC/DXT1)
- 两个16位参考RGB值(R5G6B5)+ 每个纹素2位插值因子 = 共64位。
- BC1有两种模式:一种不支持透明,其他两种颜色插值有1:2和3:5两种选择;另一种支持透明,其他两种颜色为全透明和1:1的颜色插值。
- 第一种模式中其他两种颜色插值选择1:2还是3:5是跟GPU硬件相关,大部分都是选择1:2,在nVidia的一些GPU中选择的是3:5。
- 另外,不同硬件对于RGB565到RGB888的转换实现不同,所以根据以上原因,相同的压缩纹理在不同的硬件上最终效果不同。
- 如何判断BC1格式是表示不透明还是具有1位alpha的贴图,是通过两个颜色值color_0和color_1来实现的,如果color_0的数值大于color_1则表示贴图是完全不透明的,反之则表示具有一位透明信息。因为只有一位Alpha信息,所以只能表示透明或不透明。
- 压缩率为6:1,即4bpp(bits per pixel)。
- 是转换比最高的一种,在不需要高精度也不需要alpha值时可以使用。
- PS.BC1的信息丢失主要集中在比较细的边界上,可提高分辨率来解决,高宽放大41%(1.41*1.41=1.9881),这样整个纹理差不多是以前的2倍,但总的压缩率还能保持为3:1。另一个损失是24位RGB转为了16位颜色,看上去差别不大,但对于渐变色带来说就有一些细微差别了。
- BC2 Block (DXT2/DXT3)
- 两个16位参考RGB值(R5G6B5)+ 每个纹素2位RGB插值因子 + 每个纹素4位Alpha值 = 共128位。
- 压缩率:8bpp。
- 支持了透明度,但是它的透明度只有4位共16种数值。
- PS.DXT2和DXT3的不同之处在于,DXT2中颜色是已经完成了Premultiplied by alpha操作(已完成颜色与alpha的混合,当透明度发生改变时,直接改变整体颜色值,不必再单独复合),DXT3的Alpha信息则是相对独立的,之所以要区分开了则是为了适应不同的需要,因为有些场合需要独立的Alpha信息。
- BC3 Block (DXT4/DXT5)
- 两个16位参考RGB值(R5G6B5)+ 每个纹素2位RGB插值因子 + 两个8位参考Alpha值 + 每个纹素3位Alpha插值因子 = 共128位。
- 压缩率:8bpp。
- 每个纹理像素可以选择参考Alpha值或六个中间值之一。
- PS.在这里插值的方法有两种,一种用于表示具有完全透明和完全不透明的状态,另一种则是仅在给出的极端值alpha_0和alpha_1中进行插值。区分的方法也是通过比较alpha_0和alpha_1的大小来实现的,如果alpha_0大于alpha_1,则通过插值计算剩下的6个中间alpha值;否则,只通过插值计算4个中间alpha值,alpha_6直接赋值0,alpha_7直接赋值255。
- BC4 Block (ATI1/3Dc+)
- 只有一个通道,像BC3中的Alpha一样编码。两个8位参考R值 + 每个纹素3位R插值因子 = 共64位。
- 压缩率:4bpp。
- 支持无符号[0,1]和有符号[-1,1]。
- BC5 Block (ATI2/3Dc)
- 两个通道,每个通道都像BC3中的Alpha一样编码。两个8位参考RG值 + 每个纹素3位RG插值因子 = 共128位.
- 压缩率:8bpp。
- 适合法线贴图。取值范围同BC4。
- BC6H 与 BC7 共同之处
- BC6H Block
- BC7 Block
- 在Mode0中,PB只有4位,所以只允许使用前16种分区模式。其他都是6位,可以使用所有的64位分区模式。
- 新定义的P-bit,可以把颜色多扩充一位。RGB444->RGB555,使用P-bit作为新的RGB的最低位;与直接存储RGB555相比,这样每个颜色节省了2bit。
- Mode1中使用了P-shared,和P-bit概念是一样的,只是所有的颜色都使用这一位数值去扩充。
- 如上图,Mode0的构成为 1位mode + 4位partition ID + 3个分区*( 每个分区2个RGB444 + 2个P-bit)+ 45个插值因子 = 128位。
- 如上图,在Mode4和Mode5中,有两个独立的index tables,可以用来分别保存四个通道中的一个。Rotation字段用来表示哪个通道有独立的索引表。
ETC
历史:Ericsson Texture Compression,是由 Khronos 支持的开放标准,在移动平台中广泛采用。它是一种为感知质量设计的有损算法,其依据是人眼对亮度改变的反应要高于色度改变。类似于DXTC,ETC也是把4×4的像素块压缩成一个64或128位的数据块,也是有损压缩。
PS.人眼对亮度改变的反应要高于色度改变的科学依据如下:视细胞分为视锥细胞和视杆细胞。视锥细胞是感受强光和颜色的细胞,而视杆细胞对暗光敏感。在人的视网膜内约含有600万~800万个视锥细胞,12000万个视杆细胞,分布于视网膜的不同部位。
- ETC1
- 原理:将4×4的tile分成2块(2×4或4×2),每个块储存一个参考颜色,每个像素的颜色通过一个编码为相对于这些基色偏移的灰度值确定。
- 1位Flip值控制划分块的水平或竖直 + 1位Diff值控制参考颜色的模式(两个参考颜色是R4G4B4*2或者R5G5B5+R3G3B3) + 每个子块包含一个参考颜色和一个3位的修饰表索引 + 每个纹素的2位索引值 = 64位。
- 压缩率:4bpp。
- 如何确定Flip?ETC1的编码流程:对于每个4×4的block,尝试所有的编码可能性,取解码后和原来差距缩小的一种编码。包括是否flip,每个subblock用哪一列的插值表,每个像素取哪一列的差值。
- 如何确定Diff?两个subblock的RGB值相差在 [-4,3]区间(5位表示的情况下,对应常规的8位表示是[-32,24]),第一个subblock的RGB值用5位表示,第二个用3位差值表示。
- ETC2
- ETC2 RGB+1Alpha
- EAC
PVRTC
历史:PVRTC (PowerVR Texture Compression)由Imagination公司专为PowerVR显卡核心设计,由于专利原因一般它只被用于苹果的设备,仅Iphone、Ipad和部分PowerVR的安卓机支持。这可能是这几种压缩格式中最不公开的技术。
原理:PVRTC不同于DXTC和ETC这类基于块的算法,而将整张纹理分为了高频信号和低频信号,低频信号由两张低分辨率的图像A和B表示,这两张图在两个维度上都缩小了4倍,高频信号则是全分辨率但低精度的调制图像M,M记录了每个像素混合的权重。要解码时,A和B图像经过双线性插值(bilinearly)宽高放大4倍,然后与M图上的权重进行混合。
- PVRTC 4bpp
- PVRTC 2bpp
- 思想和PVRTC 4bpp是一样的,只是A,B两张图在宽度上被再压缩了一半。相当于把8×4的block都打在原来的64位中。
- Mode也有了新的含义。当Mode是0时,每个纹素的插值因子只有1位,解码时颜色选择A或B的颜色。当Mode是1时,只有一半的纹素有2位插值因子(像棋盘格),这些有插值因子的纹素像PVRTC 4bpp一样插值颜色,剩下的没有插值因子的纹素,从周围已有颜色的2或4个纹素颜色插值。
- PVRTC2 4bpp
- PVRTC2 2bpp
- 把A和B图再折一半的思想用到PVRTC2 4bpp上。
ASTC
历史:ASTC(Adaptive Scalable Texture Compression),由ARM和AMD联合开发,2012年发布,是较新的一种压缩格式,唯一一个不受专利权影响的压缩格式。ASTC在压缩率、图像质量、种类上都挺不错的,也正在逐步代替前三种,最大的缺点可能就是兼容性还不够完善和解码时间较长,但以现在移动端的发展趋势来看,GPU计算能力越来越难成为瓶颈,因此非常有希望在以后能成为统一的压缩格式。
特点:
- 可支持1-4个通道。
- 可接受的质量。对Normalmap和RGBA比较重要。
- 支持LDR和HDR。
- 跨平台。
- 可自主选择的压缩率。2D Texture的话从0.89bpp到8bpp都可以选择。
- 支持2D/3D Texture。
原理:
ASTC同样是基于block的压缩方式,但块的大小却较支持多种尺寸,比如从基本的4×4到12×12,而且块的宽高也不限于pot,比如6×5;每个块内的内容用128bits来进行存储,因而不同的块就对应着不同的压缩率。
全局信息:
- Dynamic range (LDR/HDR)
- Texture dimension (2D/3D)
- Tile size
- Output color space (sRGB/RGB)
每个Block的信息:
- Weight grid size
- Weight range (for BISE-decoding)
- Weight values
- Number of partitions
- Partition pattern ID
- Color endpoint modes
- Color endpoint data
- Number of planes (1 or 2)
- Plane-to-channel assignment
支持情况:
- IOS:苹果从A8处理器开始支持 ASTC,iPhone6及iPad mini 4以上iOS设备支持,2014年的iPhone 5s及iPad mini 3以前的设备不支持。
- 安卓:安卓主流压缩格式正在从ETC2转向ASTC。
- 所有支持OpenGL ES 3.1和部分支持OpenGL ES 3.0的GPU。
建议:
- 无Alpha通道的贴图建议压缩格式为ASTC 8×8。如果贴图为法线贴图,建议压缩格式为ASTC 5×5。有更高要求的贴图(比如面部、场景地面),可以设置压缩格式为ASTC 6×6,法线贴图为ASTC 4×4。有Alpha通道的贴图建议压缩格式为ASTC 5×5。有更高要求的贴图(比如特效、UI),可以设置压缩格式为ASTC 4×4。
- 在同一压缩格式下,贴图容量不变,有无Alpha通道对压缩结果有很大影响,带Alpha通道的贴图压缩质量下降。
- ASTC压缩的算法比较智能,它会为变化更大的通道RGB或者A分配更高的权重,而且对于单色图,RGB通道内容一样时,使用较低的像素占用就可以达到很好的效果。对于单色图完全没有必要使用R8压缩格式,而是应该将RGB通道填充一样的信息,选择ASTC较低的像素占比,如ASTC8x8。
总结与比较
压缩格式 | Block Size | 压缩比 | 质量 | 要求 | 备注 |
---|---|---|---|---|---|
BC1/DXT1 | 64 bit | 4bpp | medium | 4的倍数 | RGB or RGB + 1-bit Alpha |
BC2/DXT2/DXT3 | 128 bit | 8bpp | medium | RGBA (uncompressed 4-bit Alpha | |
BC3/DXT4/DXT5 | 128 bit | 8bpp | medium | 4的倍数 | RGBA (compressed Alpha |
BC4 | 64 bit | 4bpp | medium+ | 单通道 | |
BC5 | 128 bit | 8bpp | medium+ | 双通道,适合normalmap | |
BC6H | 128 bit | 8bpp | very high | HDR | |
BC7 | 128 bit | 8bpp | very high | 用于替代BC1-BC5的高质量方案 | |
ETC1 | 64 bit | 4bpp | medium+ | 2的整数次幂,并且宽高相同 | |
ETC2 | 64 bit | 4bpp | high | 4的倍数 | RGB or RGB + 1-bit Alpha |
EAC | 64 bit | 4bpp | high | 单通道 | |
PVRTC | 64 bit | 4×4=4bpp;8×4=2bpp | medium+;low | 2的整数次幂,并且宽高相同,最小为8 | |
PVRTC2 | 64 bit | 4×4=4bpp;8×4=2bpp | high;medium/low | ||
ASTC | 128 bit | 4×4=8bpp to 12×12=0.89bpp | 1-4个通道,LDR/HDR |
PS.使用目标平台不支持的纹理压缩格式时,纹理将解压缩为 RGBA 32 并与压缩纹理一起存储在内存中。发生这种情况时,解压缩纹理会损失时间,并因为存储两次而导致内存损失。此外,所有平台都有不同的硬件,并经优化后可最有效地处理特定压缩格式;因此,选择不兼容的格式会影响游戏的性能。
常见疑问解答
- 为什么要使用纹理压缩?
- 缓解内存、带宽和缓存问题。
- 这些ETC,ASTC等纹理压缩格式与常见的JPG,PNG,TGA图片格式有什么区别?
- 常见的JPG,PNG,TGA是图片格式,是图片文件的存储格式,用于图片文件的存储和传输,通常在磁盘文件,内存以及网络传输中使用。
- ETC,ASTC等是纹理格式,纹理格式是显卡能够直接进行采样的纹理数据的格式,是向显卡中加载纹理时使用的数据格式。
- 尽管像JPG,PNG的压缩率很高,但并不适合纹理,主要问题是不支持像素的随机访问,这对GPU相当不友好。
- 那么有什么压缩格式呢?不同平台又如何选择合适的压缩格式呢?
- 常见的纹理压缩格式有DXTC、ATITC、ETC、PVRTC、ASTC等。
- 不同平台的一般选择
- PC:BC6H,BC7
- 安卓:ETC2,并从ETC2转向ASTC,ASTC在Android 5.0/OpenGL ES 3.1后支持,市场大部分机型都支持(98.5%),要处理好兼容性问题。
- IOS:在iPhone6以上(包含)都支持ASTC,6以下可以选择PVRTC2。
- 对美术同学来说,为什么贴图常有二次幂,4的倍数这种要求?
- 除了纹理压缩格式的要求外
- 二次幂更优。在递归或循环时,如果一个数是2的幂次方,可以被2整除,而且商也是2的幂次方。OpenGL API支持非2的幂次方,是因为考虑到易用性隐藏了细节,在内部处理了一些必要的拉伸或填充操作。
- POT比NPOT性能提升约30%
- GPU是怎么读贴图的呢?
- GPU当中完成对贴图访问(寻址以及获取数据)的模块是以硬件形式存在的。它是不可编程的,你只能从它所提供的几种方式当中选一种。这是第一个因素。
- 贴图在GPU的内存(显存)当中,一般都不是以行优先或者列优先方式存储的,而是以“块”为单位存储的。就好像一块一块马赛克组成的墙面那样,每个马赛克对应的像素在内存上是连续存储的。这是第二个因素。
- 上述几个因素(硬件寻址+贴图在内存上的特殊排布方式+压缩算法要求)决定了一款GPU在设计的时候就会将这个“块”的最小尺寸固定下来。有的GPU是2×2像素,有的是4×4像素,还有的是8×8像素。所以当贴图的尺寸不是这些“块”的整数倍的时候,当贴图被传送到GPU内存(显存)的时候,就会被拉伸或者在四周(一般是右侧和下侧)填充无用数据,使其成为这些“块”的整数倍。(称为pitch)
参考
- *TEXTURE COMPRESSION TECHNIQUES
- 你所需要了解的几种纹理压缩格式原理
- 游戏中的压缩纹理格式
- 简单聊聊手游开发中使用的ETC2
- 常用纹理和纹理压缩格式
- Khronos Data Format Specification v1.1 rev 9
- ASTC纹理压缩格式详解
- Unity Documentation
- 几种主流贴图压缩算法的实现原理
- OpenGL支持非二次幂纹理的底层原理是什么?
- strom-etc2-gh07
- StromAkenineGH05
- PVRTC Specification and User Guide
- Khronos Data Format Specification v1.1 rev 9
- ETC2 Compression in a Nutshell
- Ericsson-ETC2-SIGGRAPH_Aug12
- ASTC-GDC2013
- StromPetterssonGH07