# OpenGL ES 2.0 Shadow Mapping - depth only FBO not working due to GL_FRAMEBUFFER_INCOMPLETE_ATTACHMEN

I am trying to add simple shadow mapping to my friends gles 2.0 app. It is based on gles 2.0 and PowerVR SDK.

The problem I encounter is that FBO is not valid due to GL_FRAMEBUFFER_INCOMPLETE_ATTACHMEN (0x8CD6) error checked with glCheckFramebufferStatus() function.

I also checked call to glTexImage2D() function with glGetError() and it returns GL_INVALID_OPERATION (0x0502).

Here is my code:

GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

GLuint depthTexture;
glGenTextures(1, &depthTexture);
glBindTexture(GL_TEXTURE_2D, depthTexture);

GLenum error = glGetError(); // no error
// I tried also various combination of GL_DEPTH_COMPONENT16 formats and internal formats
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
error = glGetError(); // GL_INVALID_OPERATION (0x0502) error

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);

GLenum framebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
GLenum framebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); // GL_FRAMEBUFFER_INCOMPLETE_ATTACHMEN(0x8CD6)
printf("Invalid framebuffer\n");
}


I have no idea what is wrong. I tested in on integrated Intelu GPU and Nvidia 1080 Ti GPU so this in probably not a driver bug.

You will need to implement multiple alternative solutions depending on the GPU.

Not only are there many choices (GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT32F, and GL_DEPTH24_STENCIL8, GL_UNSIGNED_INT_24_8_OES) every GPU has different levels of support and fallbacks must be implemented.

It is possible the GPU does not support depth textures at all and you'll have to encode depth into a color texture.

You must check for presence (or absence) of the GL_OES_depth_texture extension and other extensions.

In case it is missing you must render the depth to a color texture and use a regular depth buffer (not a depth texture) to do your depth testing while drawing the depth values to a color texture. This is wasteful but the only way some GPUs can do it.

The most likely supported values for a depth texture would be glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0);

(Don't forget the GL_UNSIGNED_SHORT: the driver may not accept GL_FLOAT and you're not sending any values anyway.)

Make sure to call glBindTexture (GL_TEXTURE_2D, 0); before calling glFramebufferTexture2D.

You can't have the texture bound to both the framebuffer and a texture unit at the same time and the driver may see this as an error even if you unbind the texture later before rendering.

To create a depth buffer instead use:

        glGenRenderbuffers(1, &depth_buffer);GL_ERROR_CHECK;
if(depth_buffer){
glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);GL_ERROR_CHECK;

glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);GL_ERROR_CHECK;

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_buffer);GL_ERROR_CHECK;
}


You can encode the depth as an RGB value (for example: r = depth; g = fract(depth * 256); ) and write those to a color texture also attached to the same frame buffer. Then decode those values back into a depth value when reading the color texture depth = r + (g * (1.0/256.0));

The shadow test will then have to be done as shader code rather than using a depth sampler. You may need to tweak the encoding due to the (de)normalisation of the color values.

You have to tell OpenGL that you are not going to use the color component by calling glDrawBuffer(GL_NONE);. So, the updated code would look like this:

...
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
glDrawBuffer(GL_NONE);

GLenum framebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
...


That should complete your framebuffer attachments!

If it does not, it could be that your current framebuffer configuration is not supported for whatever reason. You could try if a combined depth-stencil-buffer would work:

...
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL, 1024, 1024, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0);
...
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
glDrawBuffer(GL_NONE);
...

• glDrawBuffer(GL_NONE); is not required for complete FBO, it is also not available on GLES 2.0. – Kacper Kleczewski Dec 12 '17 at 19:03

To solve this problem I must use normalized unsigned integer (or short ) for depth texture type: GL_UNSIGNED_INT

glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);


I am not sure why Nvidia drivers doesn't support floating point depth buffer, still, everything works now.