java - 在 Android OpenglEs 2.0 中加载纹理导致根本没有颜色

标签 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

已解决。问题是我以错误的字节顺序(无字节序/大字节序)获取字节缓冲区,并按照 @samgak 的建议交换第一个和第二个字节。

最佳答案

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

internalformat Specifies the internal format of the texture. Must be one of the following symbolic constants: GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA.

使用 glTexImage2D 上传 RGB 565 纹理时,您应该为格式内部格式以及GLES20传递GLES20.GL_RGB。 GL_UNSIGNED_SHORT_5_6_5 用于类型。同样,对于 RGBA 4444 纹理,传递 GLES20.GL_RGBAGLES20.GL_UNSIGNED_SHORT_4_4_4_4

例如:

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 时指定渲染缓冲区的内部格式

关于java - 在 Android OpenglEs 2.0 中加载纹理导致根本没有颜色,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31120835/

相关文章:

java - UsbDevice requestPermission 被拒绝,且未显示请求对话框提示

java - ArrayList源码中容量检查IF条件有什么用

android - Firebase 离线功能和 addListenerForSingleValueEvent

java - Dagger 在 Android 应用程序中生成重复的 `XXXDialogFragment_MembersInjector` 类(程序类型已存在)

javascript - 更快地加载大图像

java - 正则表达式 Java - 检查发票号码

android - 在时间戳 Firebase 上建立索引

java - 高效的 Java 语言构造来检查字符串是否为 pangram?

c# - 优化.NET中大系列数据的存储和处理

java - 为什么一个对象在线程中初始化并从 main 访问时有时为 null?