Search code examples
c++openglrender-to-texture

Cube map render texture creation works in GL 3.2 but gives an error in 4.3


I'm using the following code to create a cube map render target:

glGenTextures( 1, &id );
glBindTexture( GL_TEXTURE_CUBE_MAP, id );

glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );

// Creates a renderbuffer object to store depth info.
GLuint bufferId;
glGenRenderbuffers( 1, &bufferId );

glBindRenderbuffer( GL_RENDERBUFFER, bufferId );
glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height );
glBindRenderbuffer( GL_RENDERBUFFER, 0 );

glGenFramebuffers( 1, &fboId );
glBindFramebuffer( GL_FRAMEBUFFER, fboId );

const GLenum externalFormat = GL_RGBA;
const GLenum internalFormat = GL_RGBA8;
const GLenum dataType = GL_UNSIGNED_BYTE;

for (int i = 0; i < 6; ++i)
{
    glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalFormat,
                 size, size, 0, externalFormat,
                 dataType, nullptr );
    glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, id, 0 );
}

glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, bufferId );

It doesn't generate any OpenGL errors in OpenGL 3.2 context but when running the same code under OpenGL 4.3 context, debug output callback gives the following error on glFramebufferTexture2D:

OpenGL: Framebuffer unsupported. Attachment COLOR_ATTACHMENT0 unsupported because it uses an inconsistent texture due to invalid texture parameters.

I'm running the code on Windows 8.1 with NVIDIA GeForce GT 750M, driver 337.88. Am I creating the render target wrongly?


Solution

  • I'm not sure what the error is about, but maybe related to a mismatch between size, width and height.

    Regardless, there's a few other issues:

    1. Each loop iteration simply overrides GL_COLOR_ATTACHMENT0. Perhaps change to GL_COLOR_ATTACHMENT0 + i.

    2. However this means you must write to every layer in the cube when ever a fragment passes the depth test.

    3. You also have a single renderbuffer with just one layer which means a single depth test is used for all sides of your cube.

    I don't think the above is what you want anyway.

    • To render to a cube texture you could bind each layer in turn, rendering the whole scene multiple times each from a new projection. This is the old way of doing it.

      Example

    • To render to a cube texture with just one rendering pass you can bind the entire cube map (which is really just 6 layers of a regular texture) to GL_COLOR_ATTACHMENT0. For depth testing, you create and bind another cube texture (or renderbuffer, but not sure if you can) with GL_DEPTH_COMPONENT to GL_DEPTH_ATTACHMENT. Then in the geometry shader you duplicate everything 6 times, one for each face of your cube. gl_Layer in the geometry shader is used to set which face in the cube the primitive draws to. Come to think of it you could also use instancing (glDrawElementsInstanced/gl_InstanceID) to duplicate your geometry 6 times which would shift computation from the geometry shader to the vertex shader and may be a little faster if your vertex operations are expensive.

      Example

    In both cases you'll need a 90 degree projection matrix with aspect ration 1 and will need to rotate the view to match the cube faces (this is pretty fiddly so save your code for future use). So create 6 the view matrices and pass them in to either the geometry or vertex shader depending on whether you use instancing or not.