ETC纹理压缩的实战教程

发表于2016-06-20
评论0 1.14w浏览

  ETC(EricssonTexture Compression)爱立信纹理压缩是2005年初与爱立信合作研 发的一种有损纹理压缩技术,是最通用的纹理压缩格式,几乎所有的安卓设备都可以支持ETC压缩的GPU加速。它是一种为感知质量设计的有损算法,其依据是人眼对亮度改变的反应要高于色度改变。


ETC1

  原始的ETC1压缩提供了对24RGB数据6倍的压缩比率,不过ETC1并不提供Alpha的压缩,不过现在有方法来支持[Alpha](http://malideveloper.arm.com/cn/develop-for-mali/sample-code/etcv1-texture-compression-and-alpha-channels/)

  ETC14x4的像素单元压成64位的数据块,4x4的像素单元首先被水平或垂直分割为24x2的区块,每个区块都被赋予一个基础颜色(使用RGB444+RGB444RGB555+RGB333的组合的形式),一个3位的亮度索引以及像素索引。每个像素的颜色等于所属子块基础颜色加上索引指向的亮度修正。具体的像素索引查表方式,这篇High-Quality,Low-Complexity Texture Compressionfor Mobile Phones文档中有详细的说明,这里就不再赘述了。

 

ETC2

  ETC2ETC1的扩展并且兼容ETC1,提供了更高质量的RGB压缩,并且支持Alpha通道。但是只是在OpenGL ES 3.0OpenGL4.3上才被要求支持,而对于现在的移动手持设备来说,绝大多数只支持OpenGL ES 2.0

  *GL_COMPRESSED_RGB8_ETC2RGB888格式,类似于ETC1

  *GL_COMPRESSED_RGBA8_ETC2_EAC:RGBA8888格式

  *GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:压缩的RGBA格式,像素要么是全透要么是不透明

  EACETC1/ETC2是类似的,主要用于1-2通道数据的情况。

  *GL_COMPRESSED_R11_EAC1通道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 altasCreateseparate 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_castchar*="">(malloc(_dataLen * sizeof(unsigned char)));
        memcpy(_data, static_cast<const unsigned="" char*="">(data) + ETC_PKM_HEADER_SIZE, _dataLen);
        return true;
#endif
    }
    else
    {
        CCLOG("cocos2d: Hardware ETC1 decoder not present. Using software decoder");
 
         //if it is not gles or device do not support ETC, decode texture by software
        int bytePerPixel = 3;
        unsigned int stride = _width * bytePerPixel;
        _renderFormat = Texture2D::PixelFormat::RGB888;
 
        _dataLen =  _width * _height * bytePerPixel;
        _data = static_castchar*="">(malloc(_dataLen * sizeof(unsigned char)));
 
        if (etc1_decode_image(static_cast<const unsigned="" char*="">(data) + ETC_PKM_HEADER_SIZE, static_cast(_data), _width, _height, bytePerPixel, stride) != 0)
        {
            _dataLen = 0;
            if (_data != nullptr)
            {
                free(_data);
            }
            return false;
        }
 
        return true;
    }
    return false;
}

  腾讯GAD游戏程序交流群:484290331

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引