Search code examples
c++uwpopengl-estexturesopengl-es-2.0

Reading from frame buffer object with OpengGLES2 (Angle)


Programming an UWP app using Angle to run OpenGL ES, I am facing an issue with the basic operation of reading from a frame buffer object with glReadPixels. Starting from the Visual Studio Template "OpenglES2 Application (Android,iOS,Windows Universal)", I can retrieve the default scene rendering into a memory buffer.

Initialization :

void SimpleRenderer::InitFbo()
{
    int buf_size = tex_height*tex_width * 4;
    mReadBuf = new char[buf_size];
    memset(mReadBuf, 123, buf_size); // arbitrary value to detect changes
}

Draw function :

void SimpleRenderer::Draw()
{

    // drawing calls here
    // (...)

    glReadPixels(0, 0, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, mReadBuf);
    // success : mReadBuf is updated with pixel values
}

But if I just create a frame buffer object, try to draw in there and retrieve the result, glReadPixels doesn't return any value, and glError returns INVALID_FRAMEBUFFER_OPERATION.

Initialization :

void SimpleRenderer::InitFbo()
{
    glGenFramebuffers(1, &mRenderFbo);
    glGenTextures(1, &mRenderTexture);
    int buf_size = tex_height*tex_width * 4;
    char*buf = new char[buf_size];
    memset(buf, 255, buf_size);

    glBindTexture(GL_TEXTURE_2D, mRenderTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, 4,
            tex_width,
            tex_height,
            0, GL_RGBA, GL_UNSIGNED_BYTE,
            buf);

    glBindFramebuffer(GL_FRAMEBUFFER, mRenderFbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
            mRenderTexture, 0);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    delete[] buf;

    mReadBuf = new char[buf_size];
    memset(mReadBuf, 123, buf_size);
}

Draw function :

void SimpleRenderer::Draw()
{
    glBindFramebuffer(GL_FRAMEBUFFER, mRenderFbo);

    // drawing calls here
    // (...)

    glReadPixels(0, 0, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, mReadBuf);

    // failure : mReadBuf is unchanged

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

*edit - solved thanks to the comment below. Correct initialization of the texture is

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
    tex_width,
    tex_height,
    0, GL_RGBA, GL_UNSIGNED_BYTE,
    buf);

For the record, before it got resolved I was checking the framebuffer status with glCheckFramebufferStatus, which returned GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT.


Solution

  • The 3rd parameter of the OpenGL function glTexImage2D specifies the internal format of the texture buffer.

    In the method SimpleRenderer::InitFbo you pass 4 to the internal format, which is not a valid parameter. Use GL_RGBA instead:

    glTexImage2D(GL_TEXTURE_2D, 0, 
            GL_RGBA,   // <-----------------------
            tex_width,
            tex_height,
            0, GL_RGBA, GL_UNSIGNED_BYTE,
            buf);
    

    Note, valide parameters for the internal format (in OpenGL ES 2.0) are GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA.

    If you use glGetError after the call of glTexImage2D you will get GL_INVALID_VALUE which is generated if internal format is not an accepted format. In addition, this causes an GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error (when checking with glCheckFramebufferStatus), if the texture is attached to a frame buffer and glReadPixels causes a INVALID_FRAMEBUFFER_OPERATION error.