Search code examples
opengl-esopengl-es-2.0opengl-3

How to render to a depth texture in OpenGL ES on Android


I need to render to a depth texture in Android, and I'm having trouble figuring it out. I've searched the web for several days and pieced together the following code:

  public void loadFrameBuffer(Context context, int resourceId) {
    final Bitmap bitmap = createBitmap(context, resourceId);

    // generate the framebuffer and texture object names
    glGenFramebuffers(1, frameBuffers, 0);
    glGenTextures(2, textures, 0);

    // bind color texture and load the texture mip level 0 texels are RGB565
    // no texels need to specified as we are going to draw into the texture
    glBindTexture(GL_TEXTURE_2D, textures[COLOR_TEXTURE]);
    texImage2D(GL_TEXTURE_2D, 0, GL_RGB, bitmap, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    // bind depth texture and load the texture mip level 0
    // no texels need to specified as we are going to draw into
    // the texture
    glBindTexture(GL_TEXTURE_2D, textures[DEPTH_TEXTURE]);
    texImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, bitmap, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    // bind the framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffers[0]);

    // specify texture as color attachment
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
      GL_TEXTURE_2D, textures[COLOR_TEXTURE],
      0);

    checkStatus();

    // specify texture as depth attachment
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
      GL_TEXTURE_2D, textures[DEPTH_TEXTURE],
      0);

    checkStatus();
  }

checkStatus() for the color attachment is a success: GL_FRAMEBUFFER_COMPLETE however checkStatus() for the depth attachment is: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT

The documentation for the error states:

Not all framebuffer attachment points are framebuffer attachment complete. This means that at least one attachment point with a renderbuffer or texture attached has its attached object no longer in existence or has an attached image with a width or height of zero, or the color attachment point has a non-color-renderable image attached, or the depth attachment point has a non-depth-renderable image attached, or the stencil attachment point has a non-stencil-renderable image attached.

Color-renderable formats include GL_RGBA4, GL_RGB5_A1, and GL_RGB565. GL_DEPTH_COMPONENT16 is the only depth-renderable format. GL_STENCIL_INDEX8 is the only stencil-renderable format.

I'm at a loss, even with this description as to what is wrong. The color attachment works just fine.

EDIT:

I'm seeing this in the log just before the previously mentioned status error:

<core_glTexImage2D:501>: GL_INVALID_OPERATION

It appears to be because of this line of code:

texImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, bitmap, 0);

EDIT:

I got past the errors. I switched from the call to:

texImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, bitmap, 0);

which is actually wrong, and now I'm calling:

glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texWidth, texHeight, 0, GL_DEPTH_COMPONENT, GLES30.GL_FLOAT, null);

which works. I don't see any more errors. However, I'm still having a problem.

Here's the model rendered straight to the default buffer (straight to the window):

enter image description here

But when I try to render the texture attached to my FrameBuffer mapped to the following:

// x, y, z, S, T
      new float[]{
        // left triangle
        -1f, -1f, 0f, 0f, 0f, //
        1f, 1f, 0f, 1f, 1f, //
        -1f, 1f, 0f, 0f, 1f, //

        // right triangle
        -1f, -1f, 0f, 0f, 0f, //
        1f, -1f, 0f, 1f, 0f, //
        1f, 1f, 0f, 1f, 1f //
      }
    );

It stretches the texture:

enter image description here

If I remove the left triangle and render I get this:

enter image description here

And lastly, rendering the depth texture gives me all red:

enter image description here


Solution

  • Everything is working correctly, there is nothing to fix

    Your depth texture is correct. There is nothing left to fix.

    The problem seems to be that you expect something with more contrast. I downloaded your picture and enhanced the contrast in an image editor:

    Higher contrast image

    You can see that there are details in there! A lot of data has been lost here, however. Your screen probably has an 8-bit framebuffer at best, and then I download the result as a JPEG which quantizes it even further, but... the original depth texture itself is probably 16 bits. So I can guess that we've probably lost ~10 bits by the time I've made the above image.

    To put it another way, the depth buffer has 16 bits of precision, but the screen only has like 8. So, of course you can't see the details. The data is fine.

    Why is it red?

    Of course it's red. It's an image with only one channel. If you want to color it differently, use texture swizzling, or apply a color in the fragment shader.