【问题标题】:Android OpenGL ES 2.0 Only Limited To 16 Textures In Memory?Android OpenGL ES 2.0 只限于内存中的 16 个纹理?
【发布时间】:2016-02-10 06:09:03
【问题描述】:

基本上,当我在 Android Studio 中使用 OpenGL ES 2.0 开发应用程序时,我遇到了一个无法解决的大问题,并且困扰了我大约一周。

所以,每当我在内存中加载超过 16 个,可能是 17 个任意大小的纹理,并尝试通过 Genymotion 中的模拟器或华硕平板电脑以 2D 显示它们时,它要么开始显示与我之前不同的图像在该特定索引处绑定,或者根本不显示。然而,如果我通过我的三星 Galaxy S6 运行它,它运行良好。但是,如果我加载 16 个或更少的纹理,它可以在我测试它的所有设备上运行,包括模拟器。

这让我尝试了一个小实验,看看它是否会显示字母图像 a-z,每个字母都是 16x16 png,所有这些都在我的可绘制文件夹中。当显示每个字母时,屏幕上的大小将是 80x80,因此我可以看到它们。所以我试图让它运行“a”到“z”。在模拟器和我的平板电脑上,它只显示“a”到“o”,在“p”应该在的末尾有一个“z”,并停在那里。在我的三星 Galaxy 上,它实际上显示了“a”到“z”,并做了它应该做的事情。考虑到我在常量中将纹理数量设置为 27 甚至更高,这对于为什么它无法在其他设备上正确加载没有任何意义。我希望我能解释我的问题。而且我很确定它们都能够加载超过 16 个纹理,所以我的代码一定有问题。我不会展示整个项目,而是向您展示可能存在问题的相关领域。任何帮助表示赞赏并提前感谢。这是我的代码:

常量:

public class Constants
{
public static final int BYTES_PER_FLOAT = 4;
public static final int POSITION_COMPONENT_COUNT = 4;
public static final int NORMAL_COMPONENT_COUNT = 3;
public static final int COLOR_COMPONENT_COUNT = 4;
public static final int TEXTURE_COORDS_COMPONENT_COUNT = 2;

public static final String A_COLOR = "a_Color";
public static final String A_POSITION = "a_Position";
public static final String A_NORMAL = "a_Normal";
public static final String A_TEXTURECOORDS = "a_TextureCoords";
public static final String U_MVMATRIX = "u_MVMatrix";
public static final String U_MVPMATRIX = "u_MVPMatrix";
public static final String U_TEXTURE_UNIT = "u_Texture_Unit";
public static final String U_LIGHTPOS = "u_LightPos";
public static final String V_COLOR = "v_Color";
public static final String V_POSITION = "v_Position";
public static final String V_NORMAL = "v_Normal";

public static float SCREEN_WIDTH;
public static float SCREEN_HEIGHT;

public static int NUMBER_OF_TEXTURES = 27;
}

Texture.java:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;

import static android.opengl.GLES20.*;

public class Texture
{
public static int[] texture;

public static void Load(Context context, int resourceId, int index)
{
    //glGenTextures(Constants.NUMBER_OF_TEXTURES, texture, starting_index);
    //int n: specifies the number of texture names to be generated
    //int[] textures: specifies an array in which the generated texture names are stored
    //int offset: the starting index of your array!

    Bitmap bitmap;
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inScaled = false;

    // loading texture
    bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);

    // ...and bind it to our array
    glActiveTexture(GL_TEXTURE0 + index);
    glBindTexture(GL_TEXTURE_2D, texture[index]);

    // create nearest filtered texture
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    // Use Android GLUtils to specify a two-dimensional texture image from our bitmap
    GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);

    // Clean up
    bitmap.recycle();
}

public static void Delete(int[] texture, int starting_index)
{
    try
    {
        glDeleteTextures(1, texture, starting_index);
    }
    catch(Exception e)
    {
        return;
    }

}
}

Quad.java

import static android.opengl.GLES20.*;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

public class Quad
 {
public float vertices[];
public float colors[];
public float texture_coords[];

public FloatBuffer vertexBuffer;
public FloatBuffer textureBuffer;
public FloatBuffer colorBuffer;

public Quad(float x1, float y1, float z1, float w1,
            float x2, float y2, float z2, float w2,
            float x3, float y3, float z3, float w3,
            float x4, float y4, float z4, float w4,
            float red, float green, float blue, float alpha,
            float u1, float v1, float u2, float v2)
{
    vertices = new float[]{x1, y1, z1, w1,
            x2, y2, z2, w2,
            x3, y3, z3, w3,
            x4, y4, z4, w4};

    colors = new float[]{red, green, blue, alpha,
            red, green, blue, alpha,
            red, green, blue, alpha,
            red, green, blue, alpha};

    ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(vertices.length * Constants.BYTES_PER_FLOAT);
    vertexByteBuffer.order(ByteOrder.nativeOrder());
    vertexBuffer = vertexByteBuffer.asFloatBuffer();
    vertexBuffer.put(vertices);
    vertexBuffer.position(0);

    ByteBuffer colorByteBuffer = ByteBuffer.allocateDirect(colors.length * Constants.BYTES_PER_FLOAT);
    colorByteBuffer.order(ByteOrder.nativeOrder());
    colorBuffer = colorByteBuffer.asFloatBuffer();
    colorBuffer.put(colors);
    colorBuffer.position(0);

    texture_coords = new float[]{u1, v1,
            u2, v1,
            u1, v2,
            u2, v2};
    ByteBuffer textureByteBuffer = ByteBuffer.allocateDirect(texture_coords.length * Constants.BYTES_PER_FLOAT);
    textureByteBuffer.order(ByteOrder.nativeOrder());
    textureBuffer = textureByteBuffer.asFloatBuffer();
    textureBuffer.put(texture_coords);
    textureBuffer.position(0);
}

public void Draw_Polygon(int index, int program, float[] modelview_projection_matrix, float[] modelview_matrix)
{
    int aPositionHandle = glGetAttribLocation(program, Constants.A_POSITION);
    glEnableVertexAttribArray(aPositionHandle);
    glVertexAttribPointer(aPositionHandle, Constants.POSITION_COMPONENT_COUNT, GL_FLOAT, false, 0, vertexBuffer);

    int aColorHandle = glGetAttribLocation(program, Constants.A_COLOR);
    glEnableVertexAttribArray(aColorHandle);
    glVertexAttribPointer(aColorHandle, Constants.COLOR_COMPONENT_COUNT, GL_FLOAT, false, 0, colorBuffer);

    int aTextureCoordsHandle = glGetAttribLocation(program, Constants.A_TEXTURECOORDS);
    glEnableVertexAttribArray(aTextureCoordsHandle);
    glVertexAttribPointer(aTextureCoordsHandle, Constants.TEXTURE_COORDS_COMPONENT_COUNT, GL_FLOAT, false, 0, textureBuffer);

    int uModelViewProjectionMatrixHandle = glGetUniformLocation(program, Constants.U_MVPMATRIX);
    glUniformMatrix4fv(uModelViewProjectionMatrixHandle, 1, false, modelview_projection_matrix, 0);

    int uModelViewMatrixHandle = glGetUniformLocation(program, Constants.U_MVMATRIX);
    glUniformMatrix4fv(uModelViewMatrixHandle, 1, false, modelview_matrix, 0);

    int uTextureUnit = glGetUniformLocation(program, Constants.U_TEXTURE_UNIT);
    glUniform1i(uTextureUnit, index);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glDisableVertexAttribArray(aPositionHandle);
    glDisableVertexAttribArray(aColorHandle);
    glDisableVertexAttribArray(aTextureCoordsHandle);
}
}

Text.java:

import android.opengl.Matrix;

import static android.opengl.GLES20.*;

public class Text
{
public Quad[] poly = new Quad[1];
public String string;
public int char_width;
public int char_height;

void Draw()
{
    int texture_num = 0;
    int x_pos = 0;
    int y_pos = 0;
    for (int i = 0; i < string.length(); i++)
    {
        char character = string.charAt(i);

        switch(character)
        {
            case ' ':
            {
                texture_num = 0;
                break;
            }
            case 'a':
            {
                texture_num = 1;
                break;
            }
            case 'b':
            {
                texture_num = 2;
                break;
            }
            case 'c':
            {
                texture_num = 3;
                break;
            }
            case 'd':
            {
                texture_num = 4;
                break;
            }
            case 'e':
            {
                texture_num = 5;
                break;
            }
            case 'f':
            {
                texture_num = 6;
                break;
            }
            case 'g':
            {
                texture_num = 7;
                break;
            }
            case 'h':
            {
                texture_num = 8;
                break;
            }
            case 'i':
            {
                texture_num = 9;
                break;
            }
            case 'j':
            {
                texture_num = 10;
                break;
            }
            case 'k':
            {
                texture_num = 11;
                break;
            }
            case 'l':
            {
                texture_num = 12;
                break;
            }
            case 'm':
            {
                texture_num = 13;
                break;
            }
            case 'n':
            {
                texture_num = 14;
                break;
            }
            case 'o':
            {
                texture_num = 15;
                break;
            }
            case 'p':
            {
                texture_num = 16;
                break;
            }
            case 'q':
            {
                texture_num = 17;
                break;
            }
            case 'r':
            {
                texture_num = 18;
                break;
            }
            case 's':
            {
                texture_num = 19;
                break;
            }
            case 't':
            {
                texture_num = 20;
                break;
            }
            case 'u':
            {
                texture_num = 21;
                break;
            }
            case 'v':
            {
                texture_num = 22;
                break;
            }
            case 'w':
            {
                texture_num = 23;
                break;
            }
            case 'x':
            {
                texture_num = 24;
                break;
            }
            case 'y':
            {
                texture_num = 25;
                break;
            }
            case 'z':
            {
                texture_num = 26;
                break;
            }
        }

        Matrix.setIdentityM(OpenGL.model_matrix, 0);
        Matrix.translateM(OpenGL.model_matrix, 0, OpenGL.model_matrix, 0, 0.0f, 0.0f, 0.0f);
        Matrix.multiplyMM(OpenGL.matrix_ortho_projection_and_view, 0, OpenGL.matrix_ortho_projection, 0, OpenGL.model_matrix, 0);

        glUseProgram(Shader.textured_colored_shader_program);

        poly[0] = new Quad(x_pos + 0.0f, y_pos + 0.0f, 0.0f, 1.0f,
                x_pos + char_width, y_pos + 0.0f, 0.0f, 1.0f,
                x_pos + 0.0f, y_pos + char_height, 0.0f, 1.0f,
                x_pos + char_width, y_pos + char_height, 0.0f, 1.0f,
                1.0f, 1.0f, 1.0f, 1.0f,
                0.0f, 0.0f, 1.0f, 1.0f);

        poly[0].Draw_Polygon(texture_num, Shader.textured_colored_shader_program, OpenGL.matrix_ortho_projection_and_view, OpenGL.model_matrix);

        x_pos += char_width;

        if (x_pos >= Constants.SCREEN_WIDTH)
        {
            x_pos = 0;
            y_pos += char_height;
        }
    }
}
}

OpenGL.java:

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.opengl.GLSurfaceView.Renderer;
import static android.opengl.GLES20.*;

import android.util.Log;
import android.opengl.Matrix;

public class OpenGL implements Renderer
{
public static Context context;

public static final float[] matrix_ortho_projection = new float[16];
public static float[] model_matrix = new float[16];
private final float[] matrix_view = new float[16];
public static final float[] matrix_ortho_projection_and_view = new float[16];
public int get_width, get_height;
public static boolean texture_loading_enabled = true;
Text text = new Text();

public OpenGL(Context context)
{
    this.context = context;
}

public static void Load_Textures()
{
    switch(State.game_state)
    {
        case State.LOGO:
        {
            Texture.Delete(Texture.texture, 0);

            glFlush();

            Texture.texture = new int[Constants.NUMBER_OF_TEXTURES];

            glGenTextures(Constants.NUMBER_OF_TEXTURES, Texture.texture, 0);

            Texture.Load(context, R.drawable.c64_space, 0);
            Texture.Load(context, R.drawable.c64_a, 1);
            Texture.Load(context, R.drawable.c64_b, 2);
            Texture.Load(context, R.drawable.c64_c, 3);
            Texture.Load(context, R.drawable.c64_d, 4);
            Texture.Load(context, R.drawable.c64_e, 5);
            Texture.Load(context, R.drawable.c64_f, 6);
            Texture.Load(context, R.drawable.c64_g, 7);
            Texture.Load(context, R.drawable.c64_h, 8);
            Texture.Load(context, R.drawable.c64_i, 9);
            Texture.Load(context, R.drawable.c64_j, 10);
            Texture.Load(context, R.drawable.c64_k, 11);
            Texture.Load(context, R.drawable.c64_l, 12);
            Texture.Load(context, R.drawable.c64_m, 13);
            Texture.Load(context, R.drawable.c64_n, 14);
            Texture.Load(context, R.drawable.c64_o, 15);
            Texture.Load(context, R.drawable.c64_p, 16);
            Texture.Load(context, R.drawable.c64_q, 17);
            Texture.Load(context, R.drawable.c64_r, 18);
            Texture.Load(context, R.drawable.c64_s, 19);
            Texture.Load(context, R.drawable.c64_t, 20);
            Texture.Load(context, R.drawable.c64_u, 21);
            Texture.Load(context, R.drawable.c64_v, 22);
            Texture.Load(context, R.drawable.c64_w, 23);
            Texture.Load(context, R.drawable.c64_x, 24);
            Texture.Load(context, R.drawable.c64_y, 25);
            Texture.Load(context, R.drawable.c64_z, 26);

            break;
        }

        case State.TITLE:
        {
            break;
        }

        case State.GAME:
        {
            break;
        }
    }

    texture_loading_enabled = false;
}

private void Controls()
{

    switch(State.game_state)
    {
        case State.LOGO:
        {
            break;
        }

        case State.TITLE:
        {
            break;
        }

        case State.GAME:
        {
            break;
        }
    }

}

@Override
public void onDrawFrame(GL10 glUnused)
{
    Controls();

    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    switch(State.game_state)
    {
        case State.LOGO:
        {
            text.Draw();
            break;
        }

        case State.TITLE:
        {
            break;
        }

        case State.GAME:
        {
            break;
        }
    }
}

@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height)
{
    // Set the OpenGL viewport to the same size as the surface.
    Log.d("TAG", "onSurfaceChanged()");

    get_width = width;
    get_height = height;

    glViewport(0, 0, width, height);

    for(int i = 0; i < 16; i++)
    {
        matrix_ortho_projection[i] = 0.0f;
        matrix_view[i] = 0.0f;
        model_matrix[i] = 0.0f;
        matrix_ortho_projection_and_view[i] = 0.0f;
    }

    Matrix.orthoM(matrix_ortho_projection, 0, 0.0f, (float) get_width, (float) get_height, 0.0f, 0.0f, 1.0f);
}

@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
{
    Log.d("TAG", "onSurfaceCreated()");

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

    Shader.Create_Texture_Colored_Shader(context);
    Shader.Create_Colored_Shader(context);

    text.char_width = 80;
    text.char_height = 80;
    text.string = "abcdefghijklmnopqrstuvwxyz";

    switch(State.game_state)
    {
        case State.LOGO:
        {
            if (texture_loading_enabled == true)
                Load_Textures();
            break;
        }

        case State.TITLE:
        {
            break;
        }

        case State.GAME:
        {
            break;
        }
    }
}
}

Shader.java:

import android.content.Context;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import static android.opengl.GLES20.*;

public class Shader
{
public static int textured_colored_shader_program;
public static int colored_shader_program;

public static String readTextFileFromRawResource(final Context context, final int resourceId)
{
    final InputStream inputStream = context.getResources().openRawResource(resourceId);
    final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
    final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

    String nextLine;
    final StringBuilder body = new StringBuilder();

    try
    {
        while ((nextLine = bufferedReader.readLine()) != null)
        {
            body.append(nextLine);
            body.append('\n');
        }
    }
    catch (IOException e)
    {
        return null;
    }

    return body.toString();
}

public static int loadShader(int type, String shaderCode)
{
    int shader = glCreateShader(type);
    glShaderSource(shader, shaderCode);
    glCompileShader(shader);
    return shader;
}

public static void Create_Texture_Colored_Shader(Context context)
{
    int vertexShader = loadShader(GL_VERTEX_SHADER, readTextFileFromRawResource(context, R.raw.vertex_shader));
    int fragmentShader = loadShader(GL_FRAGMENT_SHADER, readTextFileFromRawResource(context, R.raw.fragment_shader));
    textured_colored_shader_program = glCreateProgram();
    glAttachShader(textured_colored_shader_program, vertexShader);
    glAttachShader(textured_colored_shader_program, fragmentShader);
    glLinkProgram(textured_colored_shader_program);
}

public static void Create_Colored_Shader(Context context)
{
    int vertexShader = loadShader(GL_VERTEX_SHADER, readTextFileFromRawResource(context, R.raw.vertex_shader_no_texture));
    int fragmentShader = loadShader(GL_FRAGMENT_SHADER, readTextFileFromRawResource(context, R.raw.fragment_shader_no_texture));
    colored_shader_program = glCreateProgram();
    glAttachShader(colored_shader_program, vertexShader);
    glAttachShader(colored_shader_program, fragmentShader);
    glLinkProgram(colored_shader_program);
}
}

【问题讨论】:

    标签: java android opengl-es-2.0


    【解决方案1】:

    纹理对象的总数通常不受限制。至少不在任何合理的范围内,理论上你会在某个时候运行可以由 GLuint 表示的 id。但是你会在这种情况发生之前很久就耗尽内存。因此,唯一实际的限制通常是用于纹理数据的内存量。

    但是,纹理units 的数量非常有限。快速查看您的代码,这就是您遇到的问题。从您的纹理加载代码:

    glActiveTexture(GL_TEXTURE0 + index);
    glBindTexture(GL_TEXTURE_2D, texture[index]);
    

    您要做的是保持所有纹理绑定,为每个纹理使用不同的纹理单元。然后在绘制时,选择着色器从哪个纹理单元采样:

    glUniform1i(uTextureUnit, index);
    

    这是一个完全有效的方法...直到你用完纹理单元。这正是发生的事情。

    纹理单元的最大数量取决于实现,可以通过以下方式查询:

    GLint maxUnits = 0;
    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxUnits);
    

    此值的最小值为 8。因此,除非您检查该值并找到更多,否则您只能依靠 8 个纹理单元。

    如果您需要 8 个以上的纹理,并且希望您的代码能够跨设备可靠地运行,那么保持所有纹理绑定的有点非传统的方法将不起作用。

    最简单的方法是你做大多数人做的事情:在绘制之前绑定你想要使用的纹理。为此,您始终可以使用纹理单元 0。因此您可以删除对 glActiveTexture() 的所有调用,只需在 Draw_Polygon() 方法中进行绑定调用,而不是 glUniform1i() 调用:

    glBindTexture(GL_TEXTURE_2D, texture[index]);
    

    【讨论】:

    • 天哪,它成功了!非常感谢!基本上我按照你说的做了并注释掉了 glActiveTexture(GL_TEXTURE0 + index) 和 glUniform1i(uTextureUnit, index),并在 Texture.Load() 方法和 Quad.Draw() 中使用了 glBindTexture(GL_TEXTURE_2D, texture[index])方法,它就像一个魅力。看起来我不仅需要它来加载纹理,还需要它来绘制纹理。之后,如果有我不使用的纹理,我会简单地删除它们。你拯救了我的一天。我给了你一些麻烦;)
    猜你喜欢
    • 2012-04-08
    • 1970-01-01
    • 2011-09-06
    • 2011-05-19
    • 2016-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多