Problems with cubemap to store depth values

I have amended the post to reflect some suggested changes and added additional information
I have been struggling to be able to generate a cubemap where I can store depth information from a scene- I am using the following two stackoverflow references a, b, to help me build a cubemap with depth values of a scene. However I have been having multiple problems and I have no idea how to resolve these.

Here are bits of my code:

def create_empty_cubemap(self) -> None:
    '''creates an empty cubemap ready to store depth values'''
    
    self.cube_tex = glGenTextures(1)
    glBindTexture(GL_TEXTURE_CUBE_MAP, self.cube_tex)
    
    for i in range(6):
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL_DEPTH_COMPONENT32F, 1024, 1024,
                        0, GL_DEPTH_COMPONENT, GL_FLOAT, None)

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST) # or GL_LINEAR?
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    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)

    glBindTexture(GL_TEXTURE_CUBE_MAP, 0)

The function I use to create the depth cubemap is the following,

def create_depthcube(self) -> None:
    '''Generates a cubemap with depth values'''

    #                   pitch, yaw, name
    rotation_dict = {0: [  0,  90,   'X+'],
                     1: [  0, -90,   'X-'],
                     2: [-90, 180,   'Y+'],
                     3: [ 90, 180,   'Y-'],
                     4: [  0, 180,   'Z+'],
                     5: [  0,   0,   'Z-']}
    
    self.cube_framebuffer = glGenFramebuffers(1)
    glBindFramebuffer(GL_FRAMEBUFFER, self.cube_framebuffer)
    # glClear(GL_DEPTH_BUFFER_BIT)
    
    # use shader
    self.cubemap_shader.use()

    # generate a cubemap camera
    cubemap_camera = Camera(self.camera.position)
    cubemap_camera.width = 1024
    cubemap_camera.height = 1024
    cubemap_camera.zoom = 90.0

    # set viewport size to cubemap size
    glViewport(0, 0, 1024, 1024)

    # create a projection matrix
    projection = glm.perspective(glm.radians(90.0), 1.0, 0.1, 100)
    self.cubemap_shader.set_uniform_mat4('projection', projection)        

    # create depthcube by capturing depth 
    for i in range(6):

        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 
                               GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, self.cube_tex, 0) #PROBLEM1
        
        # all fine?
        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE):
            raise RuntimeError("ERROR.FRAMEBUFFER. Framebuffer is not complete!")

        # clear colors and buffers
        glClearColor(0.1,0.1,0.1,0.1)
        glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT)

        # rotate camera
        cubemap_camera.pitch = rotation_dict[i][0]
        cubemap_camera.yaw =  rotation_dict[i][1]
        cubemap_camera.update_camera_vectors()
        
        # update view (look at matrix)?
        view = cubemap_camera.get_matrix_view()
        self.cubemap_shader.set_uniform_mat4('view',view)
        self.cubemap_shader.set_uniform_mat4('projection', projection)

        # render scene
        self.render()

    # unbind fbo
    glBindFramebuffer(GL_FRAMEBUFFER, 0)

    # reset viewport dimensions to display dimensions
    glViewport(self.camera.width, self.camera.height) 

My vertex shader is as follows:

#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{   
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

And my fragment shader is minimal

#version 330 core
// depth cubemap

out float fragmentDepth;

void main()
{    
    //fragmentDepth = gl_FragCoord.z;
}

I get an error at PROBLEM1 location with the glFramebufferTexture2D function. Is this the right way to generate a cubemap to store the depth of a scene? What am I doing wrong?

I am including the render() function below. All of the code is an adaptation of Vries code. This displays two cubes and a floor which are rendered into another framebuffer with a color attachment. This is then copy to the default display buffer. I wanted to be able to press a key and capture the depth information of a scene in a cubemap.

    def render(self) -> None:
        '''Render objects'''

        #  use normal display shader
        self.shader.use()
        
        # render cubes
        model = glm.mat4(1.0)
        glBindVertexArray(self.cube_vao)
        glActiveTexture(GL_TEXTURE0)
        glBindTexture(GL_TEXTURE_2D, self.cube_texture)

        # cube 1
        model = glm.translate(model, glm.vec3(-1.0, 0.0, -1.0))
        self.shader.set_uniform_mat4('model', model)
        glDrawArrays(GL_TRIANGLES, 0, 36)

        # cube 2
        model = glm.mat4(1.0)
        model = glm.translate(model, glm.vec3(2.0, 0.0, 0.0))
        self.shader.set_uniform_mat4('model', model)
        glDrawArrays(GL_TRIANGLES, 0, 36)
        glBindVertexArray(0) # remove binding

        # render floor
        glBindVertexArray(self.floor_vao)
        glBindTexture(GL_TEXTURE_2D, self.floor_texture)
        self.shader.set_uniform_mat4('model', glm.mat4(1.0))
        glDrawArrays(GL_TRIANGLES, 0, 6)
        glBindVertexArray(0) # remove binding

        # Render to default  (display) buffer
        glBindFramebuffer(GL_FRAMEBUFFER, 0)
        glDisable(GL_DEPTH_TEST)
        glClearColor(1.0, 1.0, 1.0, 1.0)
        glClear(GL_COLOR_BUFFER_BIT)

        self.screen_shader.use()
        glBindVertexArray(self.quad_vao)
        glBindTexture(GL_TEXTURE_2D, self.texture_color_buffer)
        glDrawArrays(GL_TRIANGLES, 0, 6)

You didn’t really state what your problem here was, or the symptom of a problem. But it’s pretty obvious. All you’ve done is allocated an FBO “handle”. You haven’t actually allocated any “buffers” like COLOR, DEPTH, etc. as textures or renderbuffers and then attached them to the FBO. So there’s nothing to clear here. And OpenGL is telling you that by throwing a GL error (probably).

So… again. You didn’t state what the problem was (or the symptom of the problem). So we’re left to guess.

I see your code where you create and allocate storage for the GL cube map texture, storing its handle in self.cube_tex? Are you sure you’ve called that code at this point? Did you check for GL errors after creating and allocating the GL texture to verify that there are no GL errors flagged?

That’s because the current framebuffer has no attachments. You need to attach one of the cube map faces with e.g. glFramebufferTextureLayer. The glClear command inside the loop should work.

Which error? Also, is this occurring on the first iteration (i==0) or a subsequent iteration (i>0)? If it’s the latter, check that self.render() isn’t messing up the state for subsequent iterations.

Thank you @Dark_Photon. I have been having difficulties creating a cubemap that would store the depth of a scene. This is my main objective . I have been guiding myself using the references that I posted on my message. Would the solution to the first error be clearing the framebuffer after I call the glFramebufferTexture2D() in the loop of the create_empty_cubemap() function. Regarding the second one. Yes, I called the first function, create_empty_cubemap() to generate space for self.cube_tex. I then call create_depthcube()

@GClements Good observation! I just ran the code and it appears is on the second iteration. What should I look for in the self.render() function?

Anything that could create the conditions for which glFramebufferTexture2D is documented as generating an error. E.g. binding zero to GL_FRAMEBUFFER, deleting or overwriting the texture, overwriting the variable storing the texture handle, etc.

@GClements OK thank you. I can see that I am binding a framebuffer to 0 inside this function. I will go through it.

Ok… I think that I have made some headway. I believe that now I am successfully building a cubemap (with the depth). However, when I output each side of the cube I get the same result (the front side) although I am rotating the camera around.

The create_depthcube() function is only triggered once I press a certain key. Otherwise I am viewing my scene (two cubes and a floor) using a different shader. I want to be able to roam around the scene and at some point store the scene depth via a cubemap.

I am wondering whether I am not getting the 6 different sides because I am calling a different shader??? But if so, then how can I manage to roam around ? (if this makes any sense?)

    def create_empty_cubemap(self) -> None:
        '''creates an empty cubemap ready to store depth values'''
        
        self.depth_map_fbo = glGenFramebuffers(1)
        
        self.depth_tex = glGenTextures(1)
        glBindTexture(GL_TEXTURE_CUBE_MAP, self.depth_tex)
        
        for i in range(6):
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL_DEPTH_COMPONENT32, 1024, 1024,
                            0, GL_DEPTH_COMPONENT, GL_FLOAT, None)

        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST) # or GL_LINEAR?
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
        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)

        # attach depth texture to  fbo depth buffer
        glBindFramebuffer(GL_FRAMEBUFFER, self.depth_map_fbo)
        glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, self.depth_tex,0)
        glDrawBuffer(GL_NONE)
        glReadBuffer(GL_NONE)
        glBindFramebuffer(GL_FRAMEBUFFER, 0)

Here is the code to build the depth cubemap,

    def create_depthcube(self) -> None:
        '''Generates a cubemap with depth values'''

        #                   pitch, yaw, rel_pitch, rel_yaw, name, location
        rotation_dict = {0: [   0,  90,         0,      90, 'X+', 'Right'],
                         1: [   0, -90,         0,    -180, 'X-', 'Left'],
                         2: [ -90, 180,       -90,      90, 'Y+', 'Top'],
                         3: [  90, 180,       180,       0, 'Y-', 'Bottom'],
                         4: [   0, 180,       -90,     180, 'Z+', 'Back'],
                         5: [   0,   0,         0,    -180, 'Z-', 'Front']}
        
        # create empty cubemap
        self.create_empty_cubemap()
        
        # generate a cubemap camera
        cubemap_camera = Camera(self.camera.position)
        cubemap_camera.width = 1024
        cubemap_camera.height = 1024
        cubemap_camera.zoom = 90.0

        # # bind depth map  buffer
        glBindFramebuffer(GL_FRAMEBUFFER, self.depth_map_fbo)
        
        # set viewport size to cubemap size
        glViewport(0, 0, 1024, 1024)

        # create a projection matrix
        projection = glm.perspective(glm.radians(90.0), 1.0, 0.1, 100)
        self.cubemap_shader.set_uniform_mat4('projection', projection)        

        # RENDER TO DEPTH CUBEMAP
        for i in range(6):

            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,
                                    self.depth_tex, 0)
            
            # all fine?
            if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE):
                raise RuntimeError("ERROR.FRAMEBUFFER. Framebuffer is not complete!")

            # clear colors and buffers
            glClearColor(0.1,0.1,0.1,0.1)
            glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT)

            # rotate camera
            cubemap_camera.pitch = rotation_dict[i][0] #0
            cubemap_camera.yaw =  rotation_dict[i][1] #1
            cubemap_camera.update_camera_vectors()
            
            # update view (look at matrix)?
            view = cubemap_camera.get_matrix_view()
            self.cubemap_shader.set_uniform_mat4('view',view)

            # render scene
            self.render()

            # #output ?
            zbuffer = glReadPixelsf(0, 0, 1024, 1024, GL_DEPTH_COMPONENT, GL_FLOAT)
            zbuffer = zbuffer.reshape((1024, 1024))
            np.save(f'{rotation_dict[i][5]}_cube', np.flipud(zbuffer))

        # unbind fbo
        glBindFramebuffer(GL_FRAMEBUFFER, 0)

        # reset viewport dimensions to display dimensions
        glViewport(0,0, self.camera.width, self.camera.height) 

Here is the code I use for rendering. Note that here I am using a different shader (and camera) than the one I am using to generate the depth cubemap.

    def render(self) -> None:
        '''
        Render objects
        '''
        # render cubes
        glBindVertexArray(self.cube_vao)
        glActiveTexture(GL_TEXTURE0)
        glBindTexture(GL_TEXTURE_2D, self.depth_texture)

        # update projection and view matrices for display camera
        projection = glm.perspective(glm.radians(self.camera.zoom), self.camera.width/self.camera.height, 0.1, 100) # here is where we have near and far
        self.display_shader.set_uniform_mat4('projection', projection)
        view = self.camera.get_matrix_view()
        self.display_shader.set_uniform_mat4('view',view)

        # cube 1
        model = glm.mat4(1.0)
        model = glm.translate(model, glm.vec3(-1.0, 0.0, -1.0))
        self.display_shader.set_uniform_mat4('model', model)
        glDrawArrays(GL_TRIANGLES, 0, 36)

        # cube 2
        model = glm.mat4(1.0)
        model = glm.translate(model, glm.vec3(2.0, 0.0, 0.0))
        self.display_shader.set_uniform_mat4('model', model)
        glDrawArrays(GL_TRIANGLES, 0, 36)
        glBindVertexArray(0) # remove binding

        # render floor
        glBindVertexArray(self.floor_vao)
        glBindTexture(GL_TEXTURE_2D, self.floor_texture)
        self.display_shader.set_uniform_mat4('model', glm.mat4(1.0))
        glDrawArrays(GL_TRIANGLES, 0, 6)
        glBindVertexArray(0) # remove binding

I managed to fix the issue that I had (this was due to my limited understanding of OpenGL). The following version of the render function. Allows me to use the same rendering function when navigating or generating the depth cube from my location.

def render2(self, shader: Shader, camera: Camera, projection:glm.mat4) -> None:
    '''
    Render objects
    '''
    # render cubes
    glBindVertexArray(self.cube_vao)
    glActiveTexture(GL_TEXTURE0)
    glBindTexture(GL_TEXTURE_2D, self.depth_texture)

    # update projection and view matrices for display camera
    shader.set_uniform_mat4('projection', projection)
    view = camera.get_matrix_view() 
    shader.set_uniform_mat4('view',view)

    # cube 1
    model = glm.mat4(1.0)
    model = glm.translate(model, glm.vec3(0.0, 0.0, -5.0))
    shader.set_uniform_mat4('model', model)
    glDrawArrays(GL_TRIANGLES, 0, 36)

    # cube 2
    model = glm.mat4(1.0)
    model = glm.translate(model, glm.vec3(-5.0, 0.0, 0.0))
    shader.set_uniform_mat4('model', model)
    glDrawArrays(GL_TRIANGLES, 0, 36)
    glBindVertexArray(0) # remove binding

    # render floor
    glBindVertexArray(self.floor_vao)
    glBindTexture(GL_TEXTURE_2D, self.floor_texture)
    shader.set_uniform_mat4('model', glm.mat4(1.0))
    glDrawArrays(GL_TRIANGLES, 0, 6)
    glBindVertexArray(0) # remove binding