Текстура GLES20 не работает на некоторых устройствах

Я попытался добавить довольно простое расширение поверх пример проекта Android OpenGL 2.0, чтобы добавить текстурирование к основным формам. Это кажется довольно простым, но на некоторых устройствах (Samsung Nexus S, LG Optimus 3D, Samsung Galaxy S) текстура просто не отображается.

На самом деле это проблема, с которой я столкнулся в гораздо более крупном проекте, но я смог воспроизвести проблему в простом проекте, приведенном ниже, в надежде, что кто-то здесь поймет, где в моем коде возникают проблемы или как их устранить. специально спроектировать текстуры GL для этих устройств (возможно, есть проблемы с устройствами).

Чтобы дать представление о том, как используется этот объект: в методе onSurfaceCreated GLSurfaceView.Renderer я создаю экземпляр объекта Square(), а в методе onDrawFrame я вызываю метод draw() Square. Однако весь соответствующий код для работы с текстурами должен появиться в этом классе Square, который почти полностью идентичен собственному примеру Google.

Заранее большое спасибо всем, кто возьмется за это.

class Square {

private final String vertexShaderCode =
    // This matrix member variable provides a hook to manipulate
    // the coordinates of the objects that use this vertex shader
    "uniform mat4 uMVPMatrix;" +

    "attribute vec4 vPosition;" +
    "attribute vec2 a_TexCoordinate;" +

    "varying vec2 v_TexCoordinate;" +

    "void main() {" +
    // the matrix must be included as a modifier of gl_Position
    "  gl_Position = vPosition * uMVPMatrix;" +
    "  v_TexCoordinate = a_TexCoordinate;" +
    "}";

private final String fragmentShaderCode =
    "precision mediump float;" +

    "uniform sampler2D u_Texture;" +

    "varying vec2 v_TexCoordinate;" +

    "void main() {" +
    "  gl_FragColor = texture2D(u_Texture, v_TexCoordinate);" +
    "}";

private final FloatBuffer vertexBuffer;
private final FloatBuffer textureBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;

// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = { -0.5f,  0.5f, 0.0f,   // top left
                                -0.5f, -0.5f, 0.0f,   // bottom left
                                 0.5f, -0.5f, 0.0f,   // bottom right
                                 0.5f,  0.5f, 0.0f }; // top right

final float[] previewTextureCoordinateData =
    {
        0.0f, 1.0f,
        0.0f, 0.0f, 
        1.0f, 1.0f,
        1.0f, 0.0f
    };

private int textureDataHandle;
private int textureUniformHandle;
private int textureCoordinateHandle;

private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };

private int loadTexture(final Context context, final int resourceId)
{
    final int[] textureHandle = new int[1];

    GLES20.glGenTextures(1, textureHandle, 0);

    if (textureHandle[0] != 0)
    {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;   // No pre-scaling

        // Read in the resource
        final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);

        // Bind to the texture in OpenGL
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);

        // Set filtering
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);

        // Load the bitmap into the bound texture.
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

        // Recycle the bitmap, since its data has been loaded into OpenGL.
        bitmap.recycle();
    }

    if (textureHandle[0] == 0)
    {
        throw new RuntimeException("Error loading texture.");
    }

    return textureHandle[0];
}

public Square(Context context) {
    // initialize vertex byte buffer for shape coordinates
    ByteBuffer bb = ByteBuffer.allocateDirect(
    // (# of coordinate values * 4 bytes per float)
            squareCoords.length * 4);
    bb.order(ByteOrder.nativeOrder());
    vertexBuffer = bb.asFloatBuffer();
    vertexBuffer.put(squareCoords);
    vertexBuffer.position(0);

    // initialize byte buffer for the draw list
    ByteBuffer dlb = ByteBuffer.allocateDirect(
    // (# of coordinate values * 2 bytes per short)
            drawOrder.length * 2);
    dlb.order(ByteOrder.nativeOrder());
    drawListBuffer = dlb.asShortBuffer();
    drawListBuffer.put(drawOrder);
    drawListBuffer.position(0);


    ByteBuffer texCoordinates = ByteBuffer.allocateDirect(previewTextureCoordinateData.length * 4);
    texCoordinates.order(ByteOrder.nativeOrder());
    textureBuffer = texCoordinates.asFloatBuffer();
    textureBuffer.put(previewTextureCoordinateData);
    textureBuffer.position(0);

    // prepare shaders and OpenGL program
    int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
                                               vertexShaderCode);
    int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
                                                 fragmentShaderCode);

    textureDataHandle = loadTexture(context, R.drawable.color_texture);

    mProgram = GLES20.glCreateProgram();             // create empty OpenGL Program
    GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
    GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
    GLES20.glLinkProgram(mProgram);                  // create OpenGL program executables
}

public void draw(float[] mvpMatrix) {
    // Add program to OpenGL environment
    GLES20.glUseProgram(mProgram);

    // get handle to vertex shader's vPosition member
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

    // Enable a handle to the triangle vertices
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    // Prepare the triangle coordinate data
    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                 GLES20.GL_FLOAT, false,
                                 vertexStride, vertexBuffer);

    textureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "a_TexCoordinate");
    GLES20.glVertexAttribPointer(textureCoordinateHandle, 2, GLES20.GL_FLOAT, false, 
            0, textureBuffer);
    GLES20.glEnableVertexAttribArray(textureCoordinateHandle);

    textureUniformHandle = GLES20.glGetUniformLocation(mProgram, "u_Texture");
    MyGLRenderer.checkGlError("glGetUniformLocation");
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureDataHandle);
    GLES20.glUniform1i(textureUniformHandle, 0);      

    // get handle to shape's transformation matrix
    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    MyGLRenderer.checkGlError("glGetUniformLocation");

    // Apply the projection and view transformation
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
    MyGLRenderer.checkGlError("glUniformMatrix4fv");

    // Draw the square
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
                          GLES20.GL_UNSIGNED_SHORT, drawListBuffer);

    // Disable vertex array
    GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}

person Daniel Smith    schedule 06.11.2012    source источник


Ответы (1)


Я предполагаю, что это проблема степени двойки.

По умолчанию для параметра GL_TEXTURE_WRAP параметра glTexParameter установлено значение GL_REPEAT, а текстуры, использующие GL_REPEAT, должны иметь размер, равный степени двойки:

Точно так же, если ширина или высота изображения текстуры не являются степенью двойки и либо GL_TEXTURE_MIN_FILTER установлен на одну из функций, требующих MIP-карты , либо GL_TEXTURE_WRAP_S или GL_TEXTURE_WRAP_T не установлен на GL_CLAMP_TO_EDGE, тогда блок изображения текстуры вернет (R, G, B, A) = (0, 0, 0, 1).

Вы можете начать с текстуры степени двойки, но когда вы используете BitmapFactory.decodeResource для создания растрового изображения, оно полезно (?) масштабирует его в зависимости от плотности устройства. Так, например, если вы загружаете исходную текстуру 512 * 512 из папки drawable на устройстве HDPI, я полагаю, что она масштабируется в 1,5 раза, поэтому у вас остается что-то, что не является Po2.

Это приводит к тому, что ваши текстуры не работают на множестве устройств, потому что все эти устройства имеют плотность, которая заставляет вас генерировать недопустимые размеры текстур.

Решением в этом случае будет размещение вашей исходной текстуры (степень двойки) в папке ресурсов drawable-nodpi, что предотвратит любое масштабирование на основе плотности. Либо так, либо используйте CLAMP_TO_EDGE, который не заботится о Po2.

person Tim    schedule 06.11.2012
comment
Это действительно решает проблему! Теперь мне нужно выяснить, как применить это к входящему изображению с камеры... которое почти наверняка не будет Po2 на многих телефонах. Я почитаю больше о GL_CLAMP_TO_EDGE и посмотрю, как лучше всего использовать текстуры nPo2 — знаете ли вы какие-нибудь полезные ресурсы? - person Daniel Smith; 07.11.2012
comment
Вы должны быть в порядке с текстурами NPO2, если вам не нужна обертка texcoord. Я бы просто установил все на CLAMP_TO_EDGE, если только вам не нужна эта функциональность. - person Tim; 07.11.2012
comment
Вы можете проверить расширение GL_OES_texture_npot, чтобы проверить, поддерживаются ли текстуры, отличные от степени двойки. Подробнее читайте здесь khronos.org/registry/gles/extensions/OES/ OES_texture_npot.txt Список поддерживаемых расширений практически для всех возможных устройств Android можно найти на сайте GLBenchmark, например: glbenchmark.com/ - person keaukraine; 07.11.2012
comment
Самый простой способ — взять изображение с камеры и поместить его в текстуру, которая является Po2, и извлечь ее как подтекстуру. Но в любом случае перемещение камеры на GPU будет медленным. - person mercurytw; 04.02.2013