I recently came across the beloved OpenGL black screen when working on offscreen texture rendering for an iOS application using OpenGL ES 2.0. This once again showed to me that OpenGL is very hard to debug.
The main problem was that calling
texture2D() in a fragment shader would always return black color values ([0, 0, 0, 1] RGBA floats), although I was sure that I uploaded a valid texture to the GPU. However, it turned out that the texture was actually incomplete. By default, OpenGL requires the texture to provide a complete mipmap for minification. This is because
GL_TEXTURE_MIN_FILTER is initially set to
GL_LINEAR_MIPMAP_LINEAR, one of four minifying functions that require a mipmap. I noticed that
texture2D() always needs a complete mipmap in such a case, even if it should display a texture of mipmap level “0” – which means the original texture.
One solution to this is obviously to generate the mipmap, which can be done automatically with
glGenerateMipmap(). However, many mobile devices still have only limited Non Power of Two (NPOT) texture support. As explained in this helpful blog post, limited support means that it is possible to use NPOT textures (with texture wrap mode “clamp”), but mipmaps cannot be generated. It will result in an OpenGL error 1282 (
GL_INVALID_OPERATION) upon calling
glGenerateMipmap() and the screen will stay black.
Another solution is to disable mipmapping, which can lead to visible aliasing artifacts. For this,
GL_TEXTURE_MIN_FILTER must be set to
GL_NEAREST for this after activating the correct texture unit with
glActiveTexture() and binding the texture with
glBindTexture(). Don’t forget to set the texture wrapping to
GL_CLAMP_TO_EDGE in order to use NPOT textures as explained before.