Sampling a depth cubemap to generate a 2d image

Started by
4 comments, last by mllobera 10 months, 2 weeks ago

New update: Okay here is what I got so far…

Objective: Generate a 360 degree view of a scene by sampling a depth cubemap using spherical coordinates. x and y represent the horizontal, or azimuth, and elevation angles respectively and the actual value at each pixel is the depth.

Output: So far I have not been able to achieve this. I am not getting any errors just a blank 2D image.

Here are what I consider to be the essential parts of the process. First creating a cubemap to store depth information,


# CREATE EMPTY CUBEMAP
######################
CM = 2
depth_map_fbo = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, depth_map_fbo)
depth_cube_tex = glGenTextures(1)
glBindTexture(GL_TEXTURE_CUBE_MAP, depth_cube_tex)

for i in range(6):
   glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL_DEPTH_COMPONENT32, CM*1024, CM*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
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_cube_tex, 0)
glDrawBuffer(GL_NONE)
glReadBuffer(GL_NONE)
glBindFramebuffer(GL_FRAMEBUFFER, 0)

Second, obtaining depth information for a simple scene made out of 3 cubes and a floor,

# CREATE A DEPTH CUBE
#####################
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,   0,       -90,     180, 'Z+', 'Back'],
               5: [   0, 180,         0,    -180, 'Z-', 'Front']}

# cubemap camera
cubemap_camera.width = CM*1024
cubemap_camera.height = CM*1024
cubemap_camera.zoom = 90.0

# # bind depth map buffer
glBindFramebuffer(GL_FRAMEBUFFER, depth_map_fbo)

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

# activate cubemap shader
cubemap_shader.use()

# create a projection matrix
projection = glm.perspective(glm.radians(90.0), 1.0, 0.1, 100)
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,
                          depth_cube_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 (lookat matrix)?
   view = cubemap_camera.get_matrix_view()
   cubemap_shader.set_uniform_mat4('view',view)

   # load textures
   cube_texture = texture_from_file(pth, Path(r'textures/marble.jpg') )
   floor_texture = texture_from_file(pth, Path(r'textures/metal.png') )

   # render cubes
   glBindVertexArray(cube_vao)
   glActiveTexture(GL_TEXTURE0)
   glBindTexture(GL_TEXTURE_2D, cube_texture)

   # update projection and view matrices for display camera
   cubemap_shader.set_uniform_mat4('projection', projection)
   view = cubemap_camera.get_matrix_view()  
   cubemap_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))
   cubemap_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))
   cubemap_shader.set_uniform_mat4('model', model)
   glDrawArrays(GL_TRIANGLES, 0, 36)

   # cube 3
   model = glm.mat4(1.0)
   model = glm.translate(model, glm.vec3(0.0, 2.0, 0.0))
   cubemap_shader.set_uniform_mat4('model', model)
   glDrawArrays(GL_TRIANGLES, 0, 36)

   glBindVertexArray(0) # remove binding

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

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

   glfw.swap_buffers(window)
# unbind fbo
glBindFramebuffer(GL_FRAMEBUFFER, 0)

Last, sampling the cubemap just created. This is the part that I am less certain. Particularly, the shaders...

# SAMPLING CUBEMAP
##################

hangle = np.array([0,360,0.05], dtype=np.float32)
vangle = np.array([-90,0, 0.05], dtype=np.float32)

width = int((hangle[1]-hangle[0]) / hangle[2])
height = int((vangle[1]-vangle[0]) / vangle[2])

# create scene buffer
scene_buffer = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, scene_buffer)

# attach a render buffer
rbo = glGenRenderbuffers(1)
glBindRenderbuffer(GL_RENDERBUFFER, rbo)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, width, height)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo)

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE):
       raise RuntimeError("ERROR.FRAMEBUFFER. Framebuffer is not complete!")

# send information to sampling shader
cubemap_sampling_shader.use()
cubemap_sampling_shader.set_uniform_vec3('hangle', hangle)
cubemap_sampling_shader.set_uniform_vec3('vangle', vangle)
cubemap_sampling_shader.set_uniform_int('cubetex', depth_cube_tex)

# #output ?
zbuffer = glReadPixelsf(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT)
zbuffer = zbuffer.reshape((height, width))
np.save('Scene', np.flipud(zbuffer))

glBindRenderbuffer(GL_RENDERBUFFER, 0)

As mention all I get for Scene (a numpy array) is a blank image made out of 0s.

My shaders for the sampling are: vertex,

#version 330 core
uniform vec3 hangle; // horizontal angular range and increment
uniform vec3 vangle; // vertical angular range and increment
out vec3 dir_vector;

void main(){
   for (float theta = vangle.x; theta < float(vangle.y); theta+= vangle.z){
       for (float psi = hangle.x;  psi <  float(hangle.y); psi+= hangle.z){
           float x = sin(radians(theta)) * cos(radians(psi));
           float z = sin(radians(theta)) * sin(radians(psi));
           float y = cos(radians(theta));
           dir_vector = vec3(x,y,z);
       }
   }  
}

and fragment,

#version 330 core

in vec3 dir_vector;
uniform samplerCube cubetex;

void main(){
           gl_FragDepth = texture(cubetex, dir_vector).z;
}
Advertisement
class Cube
{
 std::vector<2DTable> coordinates{};
};

I hope this helps.

You'll take a reference unit vector (like {1, 0, 0}) and rotate it however you desire to sample the cubemap, so most likely you're going to take a 2D position and normalize that relative to some 2D space that will define how much you rotate by in XZ and Y (or XY and Z if a Z-up criminal).

Are you perhaps asking about how you sample a cubemap that you've split apart (a collection of images arranged as +X, -X, +Y, -Y, +Z, -Z) given a unit vector? That's:

Vec3 ToCubeMapCoordinates(const Vec3& vector)
{
    const unsigned longestIndex = vector.Abs().MaxElementIndex();
    const float ma = fabsf(vector[longestIndex]);
    const bool dominantIsPositive = vector[longestIndex] > 0.0f;
    float sc = 0.0f, tc = 0.0f;
    float faceIndex = 0.0f;
    if (longestIndex == 0)
    {
        faceIndex = dominantIsPositive ? 0 : 1;
        sc = dominantIsPositive ? -vector.z : vector.z;
        tc = -vector.y;
    }
    else if (longestIndex == 1)
    {
        faceIndex = dominantIsPositive ? 2 : 3;
        sc = vector.x;
        tc = dominantIsPositive ? vector.z : -vector.z;
    }
    else if (longestIndex == 2)
    {
        faceIndex = dominantIsPositive ? 4 : 5;
        sc = dominantIsPositive ? vector.x : -vector.x;
        tc = -vector.y;
    }

    const float s = (sc / ma + 1.0f) / 2.0f;
    const float t = (tc / ma + 1.0f) / 2.0f;

    return Vec3(s, t, faceIndex);
}

Thank you @sprue and @acosix . What I am after is sampling the cubemap in order to create a ‘flat’ view of the surroundings of a place (the horizontal is orientation, the vertical is elevation and the pixel values are depth). I know the math to do this but I am uncertain about what should my shaders look like and also how to combine this with a framebuffer. If this makes sense…

Thank you @sprue and @acosix . No, my intention is to render a 2d ‘flat’ version of the depth cubemap after it has been sampled using spherical coordinates (i.e. horizontal being orientation angle, the vertical the elevation angle, and the pixel values the depth). I can handle the math part but I am uncertain how to create the actual image (i.e. what should the shaders look like and how to save the information after sampling). Thanks again.

This topic is closed to new replies.

Advertisement