質問

I have a 2d Android game that is currently causing certain devices to run out of memory. I have a number of PNGs (about 10 MBs in total) that I use in the game at various times. At some points in the game, the majority of these need to be displayed at the same time.

I have tried just decreasing the resolution of my images but I am not happy with the quality after doing so.

I have read a number of posts about how to solve these memory issues and as far as I can see, texture compression is the best approach (feel free to correct me if I am wrong). I have also seen this post that covers how to determine which texture compression formats are supported on a device and I understand this part of things: Android OpenGL Texture Compression

My question is two-fold:

  1. Most of my textures require alphas. I know that by default ETC1 does not support alpha, but I also know that when using ETC1 you can create a separate alpha compression as described here: http://sbcgamesdev.blogspot.com/2013/06/etc1-textures-loading-and-alpha.html. Shown in that link is how to apply the alphas using the NDK. I am battling to understand how to do this using the standard OpenGL ES Java wrappers though. Below is how I currently handle textures (i.e. no texture compression). How would I convert this to handle compressed textures where I need to load the alphas separately?

    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. My understanding is that I would have to provide 4 compressed textures (one for each format) and a fall back uncompressed PNG for each of my images to support a wide range of devices. My concern is the required disk size increase that this will cause. Is there any solution to this? i.e. use compressed textures in order to lower the memory usage of my game without causing the size of the game on disk to explode?

役に立ちましたか?

解決

Rather than providing alpha textures in 4 different compression formats, a better approach is to split the alpha from the images and use ETC1 for the color and possibly even the alpha part of the images. The tricky part is that you must separate the alpha from each image into separate texture files and then write a fragment shader for OpenGL ES that samples from these texture pairs using two samplers and re-combines them. The shader code would be like this:

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);
}

This will work on over 99% of Android devices and allow all of your alpha textures to be compressed, which not only makes them smaller, but they will load faster too.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top