Android OpenGL ES文档里对 ETC1 格式进行了简要介绍。选用 ETC1 进行纹理压缩的好处有,

  • ETC1 是OpenGL ES 2.0支持的格式,Android 2.2以后的机型基本全部支持
  • 将PNG资源替换为ETC1资源后,APK大小能显著降低
  • ETC1比PNG资源占用更小的运行时内存

这里具体介绍下使用ETC1进行纹理压缩的详细步骤。

Android对ETC1的支持

ETC1资源读取

Android SDK内已经集成了ECT1资源的读取,相关的类为,

在使用的时候基本几句就可以搞定读取问题,

ETC1Util.ETC1Texture texture = ETC1Util.createTexture(new FileInputStream(new File("xxx.pkm")));
// 读取宽高
int width = texture.getWidth();
int height = texture.getHeight();
// 读取内容
byte[] buffer = texture.getData().array();

上述代码读取到的buffer可以直接进行渲染,

ByteBuffer data = texture.getData();
GLES10.glCompressedTexImage2D(GLES10.GL_TEXTURE_2D, 0, ETC1.ETC1_RGB8_OES, width, height, 0, data.remaining(), data);

ETC1Util里面提供了更简便的loadTexture方法,可以一步到位完成上述逻辑。不过若是最终渲染逻辑在JNI层实现的话,则可以通过上面的方法调用Java里的ETC1Util获取到具体的数据信息后再传回JNI中,避免在JNI层再进行ETC1文件格式的解析处理。

Android SDK内的apidemo里面有一个ETC1显示的具体例子,代码在 com.example.android.apis.graphics.CompressedTextureActivity 中。

ETC1/PNG格式转化

Android SDK中也附带了ETC1格式转换工具:etc1tool,etc1tool的使用比较简单,

etc1tool --encode xxx.png // png文件转成pkm文件
etc1tool --decode xxx.pkm // pkm文件转成png文件

对于不需要透明信息的图片来说,etc1tool已经足够使用。但是若图片含有透明信息,那么用etc1tool进行处理就不那么方便了。ETC1格式不支持透明信息,透明需要通过其它方式进行处理。Mali Texture Compression Tool是一个更为强大的ETC1图片压缩工具,具体的使用这里不再赘述。

ETC1透明处理策略

Mali Texture Compression Tool不仅仅能导出不包含透明信息的ETC1格式图片,还可以导出其它一些信息,用以帮助进行透明信息处理。其网站上的这篇文章具体介绍了解决透明的三种处理方式。这里简单介绍下自己选用的第一种方式。

文件生成

简单来说,第一种方式就是通过Mali导出ETC1格式文件,将原图片的alpha通道信息写入ETC1文件后半部分。比如,一张原图为1024x1024的图片,导出后会变为1024x2048的ETC1格式文件。

渲染方法

在进行渲染的时候,自定义shader,每一个位置的颜色通过图片上半部分与下半部分组合而来。Mali SDK Samples里面有示例代码,这里直接看下其代码中用到的shader,

  • vertex shader,
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;
}
  • fragment shader,
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;
}

可以看到,在上面的vertex shader中,对传入坐标进行处理,分别获取图片上半部分坐标及alpha通道对应坐标,在fragment shader中,将上下两部分颜色信息组合起来获得最终输出的颜色。

总结

在Android上进行纹理压缩的首选是将纹理压缩成ETC1格式,使用ETC1的好处在文章开头已经说过。实际进行这项工作的时候会发现,可以找到足够的资料与例子来帮助解决遇到的各种问题。从性价比来说,使用ETC1是值得的。