在 Android 中使用 Open GL Framebuffer 时如何转换图像

Posted

技术标签:

【中文标题】在 Android 中使用 Open GL Framebuffer 时如何转换图像【英文标题】:How do I transform an image when using Open GL Framebuffer in Android 【发布时间】:2015-02-08 12:06:06 【问题描述】:

我正在使用名为 Cine.io 的实时流媒体库将流中的帧保存到 android 的外部存储。

库本身使用 Google 的 Grafika 资源。

当我简单地设置一个 ByteBuffer 然后用 GLReadPixel 读取它时,图像被正确保存但它被反转了,因为 GL 图像具有 BMP 坐标的反向坐标。

为了解决这个问题,我认为最有效的解决方案是使用开放的 GL 帧缓冲区来渲染传入的帧,用正交矩阵翻转它,然后用 GLReadPixel 读取它以将其保存到磁盘。

这是我认为可以实现这一目标的代码:

   int width = getWidth();
    int height = getHeight();
    ByteBuffer buf = ByteBuffer.allocateDirect(width * height * 4);
    buf.order(ByteOrder.LITTLE_ENDIAN);


    int[] fboId = new int[1];
    int[] rendId = new int[1];
    float[] projMatrix = new float[16];

    GLES20.glGenFramebuffers(1, fboId, 0);
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId[0]);

         GLES20.glGenRenderbuffers(1, rendId, 0);
    GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, rendId[0]);
    GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER,GLES20.GL_RGBA4, width, height);


    switch(GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER))
        case GLES20.GL_FRAMEBUFFER_COMPLETE:
            Log.i(TAG, "Is Complete");
            break;
        case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
            Log.i(TAG, "Incomplete Attachment");
            break;
        case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
            Log.i(TAG, "Missing Attachment");
            break;
        default: Log.i(TAG, "Something else is going on");

    
    GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_RENDERBUFFER,rendId[0] );
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId[0]);
    GLES20.glViewport(0,0,width, height);
    android.opengl.Matrix.orthoM(projMatrix, 0, 0, width, height, 0, -1, 1);


  GLES20.glReadPixels(0, 0, width, height,
            GLES20.GL_RGBA4, GLES20.GL_UNSIGNED_BYTE, buf);

    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);




    GlUtil.checkGlError("glReadPixels");
    buf.rewind();

但是,该代码只是生成了一个空的空白图像文件(大小为 3.6kb)。此外,变量“buff”的块在调用 GLreadpixels 方法后仅由 0 组成。

最后,如果我将 BindFramebuffer 设置为 0(默认绑定)并将其放置在 GLReadPixels 调用之前,那么图像确实会显示但仍然反转 - 就像 Framebuffer 命令从未与它交互过一样。

我该如何解决这个问题?

PS:很多人都在尝试回答以下问题的先前更复杂的版本 - 但它的症结仍然存在。

【问题讨论】:

【参考方案1】:

您始终可以手动翻转缓冲区。我没有使用过java的ByteBuffer,所以下面的代码可能不起作用,但它主要只是一个例子。

//num channels being the amount of channels per pixel, 4 representing r, g, b, a
int num_channels = 4;
//allocate buffer size
ByteBuffer new_buf = ByteBuffer.allocateDirect(width * height * num_channels);
buf.order(ByteOrder.LITTLE_ENDIAN);

//calculate row size of image
int row_size = width * num_channels
//loop through the contents of the buffer and flip it
for (int y = 0; y < height; ++y) 
    for (int x = 0; x < row_size; ++x) 
        //put a row of bytes starting from the end of the buffer into the start of the new buffer
        new_buf.put(buf.get(((height - (y + 1)) - x) * row_size));
    

但是,这非常慢,并且在更新循环中绝对不理想。相反,在您的渲染代码中,您可以使用矩阵更改纹理投影到屏幕上的方式。尝试像这样使用glOrtho

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0f, window_width, window_height, 0.0f, -1.0f, 1.0f);

这应该在渲染时翻转你的纹理。尝试使用不同的值进行试验和摆弄以感受一下。

【讨论】:

肯定经历了帧缓冲区的歌舞,所以我可以达到选项#2。如果找出Android中的匹配方法,我的问题。我会的。 我认为您为选项 #2 提供的代码适用于 GL v1.0。由于我在 Android 中使用 GL 2.0,因此在找到将转换绑定到帧缓冲区的匹配代码时遇到了一些麻烦。 哦,对了。抱歉,我对 openGL 还是很陌生,但我想我仍然可以尝试帮助。所以,我有点困惑,你是从默认帧缓冲区(0)中读取像素并将它们保存到磁盘吗?为什么你需要你在它上面创建的帧缓冲区呢?您不应该绑定到您在上面创建的帧缓冲区 ID 吗?我要做的是在渲染时绑定到您在上面创建的帧缓冲区,以便将其渲染到帧缓冲区(它不会显示在屏幕上)。渲染时应用矩阵变换,然后,您可以从此帧缓冲区读取像素。 您能否也显示您的渲染代码,以便我可以看到您正在做什么来尝试帮助?谢谢:) 刚刚用新代码更新了问题。希望人们可以提供帮助-我已经为此工作了几个小时,但仍然无法正常工作。顺便说一句,你是对的,我错误地从默认帧缓冲区读取数据 - 这让我相信帧缓冲区正在渲染,而事实并非如此。

以上是关于在 Android 中使用 Open GL Framebuffer 时如何转换图像的主要内容,如果未能解决你的问题,请参考以下文章

Android GL deadlock timeout error

如何在白色背景上的Open GL ES中显示彩色纹理画笔?

在 iO 中使用 Open GL 1.0 模板缓冲区进行屏蔽的问题

Open GL ES 中的动画网格

open gl 到底是个啥东西啊

编译c ++项目/ Open GL时出现纹理错误