ETC纹理压缩的实战教程
ETC(EricssonTexture Compression)爱立信纹理压缩是2005年初与爱立信合作研 发的一种有损纹理压缩技术,是最通用的纹理压缩格式,几乎所有的安卓设备都可以支持ETC压缩的GPU加速。它是一种为感知质量设计的有损算法,其依据是人眼对亮度改变的反应要高于色度改变。
ETC1
原始的ETC1压缩提供了对24位RGB数据6倍的压缩比率,不过ETC1并不提供Alpha的压缩,不过现在有方法来支持[Alpha](http://malideveloper.arm.com/cn/develop-for-mali/sample-code/etcv1-texture-compression-and-alpha-channels/)。
ETC1把4x4的像素单元压成64位的数据块,4x4的像素单元首先被水平或垂直分割为2个4x2的区块,每个区块都被赋予一个基础颜色(使用RGB444+RGB444或RGB555+RGB333的组合的形式),一个3位的亮度索引以及像素索引。每个像素的颜色等于所属子块基础颜色加上索引指向的亮度修正。具体的像素索引查表方式,这篇High-Quality,Low-Complexity Texture Compressionfor Mobile Phones文档中有详细的说明,这里就不再赘述了。
ETC2
ETC2是ETC1的扩展并且兼容ETC1,提供了更高质量的RGB压缩,并且支持Alpha通道。但是只是在OpenGL ES 3.0和OpenGL4.3上才被要求支持,而对于现在的移动手持设备来说,绝大多数只支持OpenGL ES 2.0。
*GL_COMPRESSED_RGB8_ETC2:RGB888格式,类似于ETC1
*GL_COMPRESSED_RGBA8_ETC2_EAC:RGBA8888格式
*GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:压缩的RGBA格式,像素要么是全透要么是不透明
EAC跟ETC1/ETC2是类似的,主要用于1-2通道数据的情况。
*GL_COMPRESSED_R11_EAC:1通道unsigned数据
*GL_COMPRESSED_SIGNED_R11_EAC:1通道signed数据
*GL_COMPRESSED_RG11_EAC:2通道unsigned数据
*GL_COMPRESSED_SIGNED_RG11_EAC:2通道signed数据
移动端ETC压缩方案
因为ETC压缩在Android设备上的良好兼容性以及不错的显示表现,现在的大多数对内存要求比较高的手游在Android平台都采用了ETC压缩的方案,而为了解决ETC1不支持Alpha通道的问题,我们可以采用了2张纹理混合的方式来实现带有Alpha通道的ETC1压缩,压缩工具我们使用ARM Mali提供的工具MaliGPU Texture CompressionTool。
* 提取 ALPHA 通道
首先要从源纹理中提取Alpha通道,压图工具tct中的压缩选项有2个方法给我们来处理Alpha通道提取,Create altas和Createseparate compressed image,分别对应纹理拼图和单独alpha封装。
* 纹理拼图
如图所示,纹理拼图的方式会生成一个文件,总体纹理的高度增加了。这种方法的好处是只有一个文件,也就是只需要一个纹理采样器,纹理加载代码的改动很少,着色器的代码改动也较少,但是纹理样本只能在一个方向上正确包裹,并且缩放会减慢着色器的执行。
使用的时候基本不用改纹理加载部分的代码。
顶点着色器代码:
1 2 3 4 5 6 7 8 9 10 11 | attribute vec4 a_v4Position; attribute vec2 a_v2TexCoord; varying vec2 v_v2TexCoord; varying vec2 v_v2AlphaCoord; void main() { v_v2TexCoord = a_v2TexCoord * vec2(1.0, 0.5); v_v2AlphaCoord = v_v2TexCoord + vec2(0.0, 0.5); gl_Position = a_v4Position; } |
片元着色器代码:
1 2 3 4 5 6 7 8 9 10 11 | precision mediump float ; uniform sampler2D u_s2dTexture; varying vec2 v_v2TexCoord; varying vec2 v_v2AlphaCoord; void main() { vec4 v4Colour = texture2D(u_s2dTexture, v_v2TexCoord); v4Colour.a = texture2D(u_s2dTexture, v_v2AlphaCoord).r; gl_FragColor = v4Colour; } |
* 单独alpha封装
如图所示,这种方法会生成两个文件,alpha通道作为一个单独的纹理,更加灵活,允许混合和匹配alpha/颜色通道,允许以两个方向包裹纹理,但是需要两个纹理采样器。
使用的时候需要加载第二个纹理。
1 2 3 4 | glActiveTexture(GL_TEXTURE0); loadCompressedMipmaps(TEXTURE_FILE, TEXTURE_FILE_SUFFIX, &iTexName); glActiveTexture(GL_TEXTURE1); loadCompressedMipmaps(ALPHA_FILE, TEXTURE_FILE_SUFFIX, &iAlphaName); |
在设置着色器统一变量时,分配第二个纹理采样器,再绑定到第二纹理单元上:
1 2 3 4 | iLocSampler = glGetUniformLocation(iProgName, "u_s2dTexture" ); glUniform1i(iLocSampler, 0); iLocSamplerAlpha = glGetUniformLocation(iProgName, "u_s2dAlpha" ); glUniform1i(iLocSamplerAlpha, 1); |
在片元着色器中,再一次合并这两个纹理样本。
1 2 3 | vec4 colour = texture2D(u_s2dTexture, v_v2TexCoord); colour.a = texture2D(u_s2dAlpha, v_v2TexCoord).r; gl_FragColor = colour; |
cocos2dx ETC图片的解码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | bool Image::initWithETCData( const unsigned char * data, ssize_t dataLen) { const etc1_byte* header = static_cast < const etc1_byte*= "" >(data); //检查ETC文件头 if (! etc1_pkm_is_valid(header)) { return false ; } _width = etc1_pkm_get_width(header); _height = etc1_pkm_get_height(header); if (0 == _width || 0 == _height) { return false ; } if (Configuration::getInstance()->supportsETC()) { //old opengl version has no define for GL_ETC1_RGB8_OES, add macro to make compiler happy. #ifdef GL_ETC1_RGB8_OES _renderFormat = Texture2D::PixelFormat::ETC; _dataLen = dataLen - ETC_PKM_HEADER_SIZE; _data = static_cast |