在Android OpenglEs 2.0中加载纹理导致完全没有颜色

时间:2015-06-29 16:27:08

标签: java android optimization opengl-es textures

之前我有我的工作方法,但我看到有些东西没有优化。我试图使用RGB_565或ARGB_4_4_4_4颜色方案上传纹理,但我使用3个字节用于第一个,4个用于最后一个。事实上,如果我使用RGB_565,我只需要2个字节(5 + 6 + 5位),第二个相同。所以我做了我的修改,但现在它不起作用,而且肯定是我忘了或做坏了。

如果您想比较,可以在函数中找到已注释掉的旧代码。 任何帮助将不胜感激。

这是代码:

///
//  Load texture from InputStream
//
private int loadTexture(InputStream is) {
    int[] textureId = new int[1];
    Bitmap reversedBitmap, bitmap;

    BitmapFactory.Options opts = new BitmapFactory.Options();
    opts.inPreferredConfig = Bitmap.Config.RGB_565;
    reversedBitmap = BitmapFactory.decodeStream(is, null, opts);
    //reversedBitmap = BitmapFactory.decodeStream(is);

    if (reversedBitmap == null) {
        throw new RuntimeException("Texture.loadTexture: depuracion");
    }

    int width = reversedBitmap.getWidth();
    int height = reversedBitmap.getHeight();

    Matrix flip = new Matrix();
    flip.postScale(1f, -1f);
    bitmap = Bitmap.createBitmap(reversedBitmap, 0, 0, width, height, flip, true);

    reversedBitmap.recycle();

    // int bitmapFormat = bitmap.getConfig() == Bitmap.Config.ARGB_8888 ? GLES20.GL_RGBA : GLES20.GL_RGB;

    int bitmapFormat = bitmap.getConfig() == Bitmap.Config.ARGB_4444 ? GLES20.GL_RGBA4 : GLES20.GL_RGB565;

    byte[] buffer = null;
    /*
    if (bitmapFormat == GLES20.GL_RGB) {
        buffer = new byte[width * height * 3];
    } else if (bitmapFormat == GLES20.GL_RGBA) {
        buffer = new byte[width * height * 4];
    }
    */
    if (bitmapFormat == GLES20.GL_RGB565) {
        buffer = new byte[width * height * 2];
    } else if (bitmapFormat == GLES20.GL_RGBA4) {
        buffer = new byte[width * height * 2];
    }

    int[] pixelArray;
    pixelArray = new int[width * height];
    bitmap.getPixels(pixelArray, 0, width, 0, 0, width, height);

    for (int y = 0; y < height; y++) {

        for (int x = 0; x < width; x++) {
/*
            int pixel = pixelArray[x + y * width];

            if (bitmapFormat == GLES20.GL_RGB) {
                buffer[(y * width + x) * 3 + 0] = (byte) ((pixel >> 16) & 0xFF);
                buffer[(y * width + x) * 3 + 1] = (byte) ((pixel >> 8) & 0xFF);
                buffer[(y * width + x) * 3 + 2] = (byte) ((pixel >> 0) & 0xFF);
            } else if (bitmapFormat == GLES20.GL_RGBA) {
                buffer[(y * width + x) * 4 + 0] = (byte) ((pixel >> 16) & 0xFF);
                buffer[(y * width + x) * 4 + 1] = (byte) ((pixel >> 8) & 0xFF);
                buffer[(y * width + x) * 4 + 2] = (byte) ((pixel >> 0) & 0xFF);

                // ALPHA CHANNEL
                buffer[(y * width + x) * 4 + 3] = (byte) ((pixel >> 24) & 0xFF);
            }
*/
            int pixel = pixelArray[x + y * width];

            if (bitmapFormat == GLES20.GL_RGB565) {
                /*
                buffer[(y * width + x) * 3 + 0] = (byte) ((pixel >> 11) & 0x1F); // color rojo empieza en el bit 11 y se debe hacer and logico con 1F pues ocupa 5 caracteres
                buffer[(y * width + x) * 3 + 1] = (byte) ((pixel >> 5) & 0x3F);
                buffer[(y * width + x) * 3 + 2] = (byte) ((pixel >> 0) & 0x1F);
                */
                byte red = (byte) ((pixel >> 11) & 0x1F);
                byte green = (byte) ((pixel >> 5) & 0x3F);
                byte blue = (byte) ((pixel >> 0) & 0x1F);

                // desplazamos red tres dígitos a la izquierda que es lo que queda libre del byte al ser red de 5 bits
                byte first_byte = (byte) (red << 3);
                // desplazamos tres bits a la derecha y aplicamos la máscara con and lógico
                byte auxiliar_green_first_byte = (byte) ((green >> 3) & 0x7); // máscara 7 => 0000000111
                // ya podemos calcular el primer byte
                first_byte = (byte) (first_byte | auxiliar_green_first_byte);
                // creamos un nuevo auxiliar para manejar la parte baja de green
                // desplazamos la parte baja de green cinco bits y hacemos un and lógico con la máscara E0 para dejar hueco a blue
                byte auxiliar_green_second_byte = (byte) ((green << 5) & 0xE0); // máscara E0 => 11100000
                // ya podemos calcular el segundo byte = auxiliar_green_second_byte | blue;
                byte second_byte = (byte) (auxiliar_green_second_byte | blue);

                // almacenamos los resultados del pixel
                buffer[(y * width + x) * 2 + 0] = first_byte;
                buffer[(y * width + x) * 2 + 1] = second_byte;


            } else if (bitmapFormat == GLES20.GL_RGBA4) {
                /*
                buffer[(y * width + x) * 4 + 0] = (byte) ((pixel >> 16) & 0xFF);
                buffer[(y * width + x) * 4 + 1] = (byte) ((pixel >> 8) & 0xFF);
                buffer[(y * width + x) * 4 + 2] = (byte) ((pixel >> 0) & 0xFF);

                // ALPHA CHANNEL
                buffer[(y * width + x) * 4 + 3] = (byte) ((pixel >> 24) & 0xFF);
                */
                byte red = (byte) ((pixel >> 8) & 0xF);
                byte green = (byte) ((pixel >> 4) & 0xF);
                byte blue = (byte) ((pixel >> 0) & 0xF);

                byte alpha = (byte) ((pixel >> 12) & 0xF);

                // movemos red 4 bits a la izquierda y aplicamos máscara 11110000
                byte first_byte = (byte) ((red << 4) & 0xF0);

                // tras haber desplazado red procedemos a calcular definitivamente fist_byte con red or green
                first_byte = (byte) (first_byte | green); // green ya está desplazado en los 4 últimos bits, no hace falta manipularlo

                // movemos blue 4 bits a la izquierda y aplicamos máscara 11110000
                byte second_byte = (byte) ((blue << 4) & 0xF0);
                // tras haber desplazado blue procedemos a calcular definitivamente second byte con la operación nuevo blue OR LOGICO alpha
                second_byte = (byte) (second_byte | alpha); // alpha ya está desplazado en los 4 últimos bits, no hace falta manipularlo

                buffer[(y * width + x) * 2 + 0] = first_byte;
                buffer[(y * width + x) * 2 + 1] = second_byte;
            }
        }


    }
    ByteBuffer byteBuffer = null;
    /*
    if (bitmapFormat == GLES20.GL_RGB) { // 3 bytes, 1 por canal
        byteBuffer = ByteBuffer.allocateDirect(width * height * 3);
    } else if (bitmapFormat == GLES20.GL_RGBA4) { // 4 bytes, 1 por canal
        byteBuffer = ByteBuffer.allocateDirect(width * height * 4);
    }
    */
    if (bitmapFormat == GLES20.GL_RGB565) { // 3 bytes, 1 por canal
        byteBuffer = ByteBuffer.allocateDirect(width * height * 2);
    } else if (bitmapFormat == GLES20.GL_RGBA4) { // 4 bytes, 1 por canal
        byteBuffer = ByteBuffer.allocateDirect(width * height * 2);
    }
    byteBuffer.put(buffer).position(0);

    GLES20.glGenTextures(1, textureId, 0);

    this.m_TextureID = textureId[0];

    bind();

    setFilters(this.m_MinifyFilter, this.m_MagnifyFilter);
    // GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR );
    // GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR );
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);


    /*

    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, bitmapFormat, width, height, 0,
                bitmapFormat, GLES20.GL_UNSIGNED_BYTE, byteBuffer);
     */

    if (bitmapFormat == GLES20.GL_RGB565) {
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, bitmapFormat, width, height, 0,
                bitmapFormat, GLES20.GL_UNSIGNED_SHORT_5_6_5, byteBuffer);
    } else if (bitmapFormat == GLES20.GL_RGBA4) {
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, bitmapFormat, width, height, 0,
                bitmapFormat, GLES20.GL_UNSIGNED_SHORT_4_4_4_4, byteBuffer);
    }

    // this.textureId = textureId[0];
    pixelArray = null; // Para que el recolector libere el tamaño del array de la imagen en memoria

    if ((m_MinifyFilter == GLES20.GL_LINEAR_MIPMAP_LINEAR) ||
            (m_MinifyFilter == GLES20.GL_LINEAR_MIPMAP_NEAREST) ||
            (m_MinifyFilter == GLES20.GL_NEAREST_MIPMAP_LINEAR) ||
            (m_MinifyFilter == GLES20.GL_NEAREST_MIPMAP_NEAREST)) {

        GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
    }

    unbind();
    bitmap.recycle();
    try {
        is.close();
    } catch (IOException e) {
        Log.e("Texture.loadTexture: ", " error closing inputStream");
    }
    return textureId[0];
}

编辑1:

我正在编辑,因为我看到一个非常差的结果将rgb888纹理转换为rgb565纹理。我按照@samgak建议的方法,一切看起来都运行正常,但颜色非常扭曲。我正在上传两个捕获,第一个是rgb888并且看起来很好,就像它应该做的那样。第二个是带有扭曲的转换。这种转变是正常的吗?如果是,我将不得不将纹理保持在完整的rgb888格式

精心绘制的框架: enter image description here

多维数据集是碰撞系统的绑定卷

enter image description here

解决。问题是我以错误的字节顺序获取bytebuffer(无字节/大端)并将第一个和第二个字节交换为@samgak建议。

1 个答案:

答案 0 :(得分:2)

GLES20.GL_RGB565GLES20.GL_RGBA4不是传递给glTexImage2D的有效值:

  

internalformat 指定纹理的内部格式。必须是以下符号常量之一:GL_ALPHA,GL_LUMINANCE,GL_LUMINANCE_ALPHA,GL_RGB,GL_RGBA。

使用glTexImage2D上传RGB 565纹理时,您应该为格式 internalformat 传递GLES20.GL_RGB,为传递GLES20.GL_UNSIGNED_SHORT_5_6_5输入的。同样,对于RGBA 4444纹理,请传递GLES20.GL_RGBAGLES20.GL_UNSIGNED_SHORT_4_4_4_4

e.g:

GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, width, height, 0,
    GLES20.GL_RGB, GLES20.GL_UNSIGNED_SHORT_5_6_5, byteBuffer);

GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0,
    GLES20.GL_RGBA, GLES20.GL_UNSIGNED_SHORT_4_4_4_4, byteBuffer);

GLES20.GL_RGB565GLES20.GL_RGBA4用于在调用glRenderbufferStorage

时指定渲染缓冲区的内部格式