Android OpenGL ES的纹理压缩策略,迎合alpha



我有一款2d Android游戏,目前导致某些设备内存不足。我在不同时间使用了许多png文件(总计约10mb)。在游戏的某些时候,这些内容中的大多数需要同时显示。

我试过降低图像的分辨率,但我对这样做后的质量不满意。

我读过一些关于如何解决这些内存问题的帖子,据我所知,纹理压缩是最好的方法(如果我错了,请随时纠正我)。我也看过这篇文章,介绍了如何确定设备上支持哪种纹理压缩格式,我理解这部分内容:

我的问题有两个:

  1. 我的大多数纹理需要alpha。我知道默认情况下ETC1不支持alpha,但我也知道当使用ETC1时,您可以创建一个单独的alpha压缩,如下所述:http://sbcgamesdev.blogspot.com/2013/06/etc1-textures-loading-and-alpha.html。该链接展示了如何使用NDK应用alpha。我正在努力理解如何使用标准的OpenGL ES Java包装器来做到这一点。下面是我目前如何处理纹理(即没有纹理压缩)。我如何将其转换为处理压缩纹理,我需要分别加载alpha ?

    GLGraphics glGraphics;
    FileIO fileIO;
    String fileName;
    int textureId;
    int minFilter;
    int magFilter;
    public int width;
    public int height;
    private boolean loaded = false;
    public Texture(GLGame glGame, String fileName) {
        this.glGraphics = glGame.getGLGraphics();
        this.fileIO = glGame.getFileIO();
        this.fileName = fileName;
        load();
    }
    public void load() {
        GL10 gl = glGraphics.getGL();
        int[] textureIds = new int[1];
        gl.glGenTextures(1, textureIds, 0);
        textureId = textureIds[0];
        InputStream inputStream = null;
        try {
            inputStream = fileIO.readAsset(fileName);
            Bitmap bitmap = BitmapFactory.decodeStream(inputStream);            
            gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
            GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
            setFilters(GL10.GL_NEAREST, GL10.GL_NEAREST);
            gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
            width = bitmap.getWidth();
            height = bitmap.getHeight();
            bitmap.recycle();
        } catch (IOException e) {
            throw new RuntimeException("Couldn't load texture '" + fileName + "'", e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // do nothing
                }
            }
        }
        loaded = true;
    }
    public void reload() {
        load();
        bind();
        setFilters(minFilter, magFilter);
        glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0);
    }
    public void setFilters(int minFilter, int magFilter) {
        this.minFilter = minFilter;
        this.magFilter = magFilter;
        GL10 gl = glGraphics.getGL();
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, minFilter);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, magFilter);
    }
    public void bind() {
        GL10 gl = glGraphics.getGL();
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
    }
    public void dispose() {
        loaded = false;
        GL10 gl = glGraphics.getGL();
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
        int[] textureIds = { textureId };
        gl.glDeleteTextures(1, textureIds, 0);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
    }
    public boolean isLoaded() {
        return loaded;
    }
    public void setLoaded(boolean loaded) {
        this.loaded = loaded;
    }
    
  2. 我的理解是,我必须为我的每个图像提供4个压缩纹理(每种格式一个)和一个未压缩的PNG,以支持广泛的设备。我担心的是这将导致所需的磁盘大小增加。有什么解决办法吗?例如,使用压缩纹理,以降低我的游戏的内存使用,而不会导致游戏的磁盘大小爆炸?

与其以4种不同的压缩格式提供alpha纹理,不如将alpha从图像中分离出来,并使用ETC1来处理图像的颜色,甚至可能使用alpha部分。棘手的部分是,你必须将每个图像的alpha分离到单独的纹理文件中,然后为OpenGL ES编写一个片段着色器,使用两个采样器从这些纹理对中采样并重新组合它们。着色器代码应该是这样的:

uniform sampler2D   sampler_color;
uniform sampler2D   sampler_alpha;
varying vec2        texCoord;
void main()
{
    vec3 vColor = texture2D(sampler_color, texCoord);
    float fAlpha = texture2D(sampler_alpha, texCoord);
    gl_FragColor = vec4(vColor, fAlpha);
}

这将在超过99%的Android设备上工作,并允许所有的alpha纹理被压缩,这不仅使它们更小,而且它们也会加载得更快。

最新更新