0

Latest update

Objective: Generate a 360 degree view of a scene by sampling at a given angular_step, a depth cubemap using spherical coordinates. Where 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 am not getting any errors just a blank 2D image.I do not know why I am not getting any output

Here is another try at this... . FYI, depth_cube_tex is the depth cubemap generated previously with depth information.

My sampling procedure is as follows,

# SAMPLING CUBEMAP
##################
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS)

# angular ranges and step
angle_step = 0.025
hangle = np.array([0, 360, angle_step], dtype=np.float32)
vangle = np.array([-90, 90, angle_step], dtype=np.float32)

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

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

# generate color depth buffer and attache it to scene fbo
color_depth = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, color_depth)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, None)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_depth, 0) 

# send information to sampling shader
cubemap_sampling_shader.use()
cubemap_sampling_shader.set_uniform_int('depth_cube', depth_cube_tex)
cubemap_sampling_shader.set_uniform_vec3('display_size', glm.vec2(width,height))
cubemap_sampling_shader.set_uniform_float('angle_step', angle_step)

#draw onto my scene buffer
glBindVertexArray(quad_vao)
glBindTexture(GL_TEXTURE_2D, color_depth)
glDrawArrays(GL_TRIANGLES, 0, 6)
glBindVertexArray(0)

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

glBindFramebuffer(GL_FRAMEBUFFER, 0) #scene_buffer

The cubemap_sampling shader program has the following vertex shader,

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

void main()
{ 
    gl_Position = vec4(aPos.x, aPos.y, 0.0f, 1.0f);
}

and fragment shader

#version 330 core

uniform samplerCube depth_cube; 
uniform float angle_step;
uniform vec2 display_size; // width, height of display

out float depth;

void main()
{
    // psi is the horizontal deviation angle
    float psi = (gl_FragCoord.x - display_size.x / 2 ) * radians(angle_step);
    
    // theta is the vertical deviation angle
    float theta = (gl_FragCoord.y - display_size.y / 2 ) * radians(angle_step);
    
    // create sampling direction vector
    float x = sin(theta) * sin(psi);
    float z = cos(theta) * sin(psi);
    float y = cos(theta);
    depth = texture(depth_cube,vec3(x,y,z)).s;
}

And my quad_vao is just two triangles spaning the entire screen in Normalize Device Coords.

marcos
  • 134
  • 15
  • @Rabbid76 If indeed, this is as you suggest, what I do not understand is why it would it not give me an error before? Say on the two previous lines – marcos Jul 03 '23 at 15:07
  • Actually - first, what are you currently trying to achieve: a visualization of the depth cubemap? If so, try ``gl_FragColor = vec4(vec3(texture(cubetex, dir_vector).r), 1.0)``. Second, can we see more of your Python code other than the snippets provided by the error messages? – new Q Open Wid Jul 03 '23 at 15:51
  • @newQOpenGLWidget Just updated my post. My intention is to get the depth values into an array... if this makes sense. – marcos Jul 04 '23 at 11:11
  • @Spektre In this [comment](https://stackoverflow.com/questions/74043244/creating-accurate-3d-information-using-opengl) you mentioned the possibility of doing this. Could you provide some guidance on how to accomplish this? – marcos Jul 15 '23 at 16:49
  • Are you able to render it correctly and the problem is saving the data from the framebuffer? – karlphillip Jul 19 '23 at 00:16
  • @karlphillip I do not think that the issue is saving the output as I have done this many times in the past. I believe the problem is that I am not rendering anything – marcos Jul 19 '23 at 06:49
  • 2
    @marcos please consider sharing a complete/compilable/minimal application to reproduce the issue. The more code you share, the less work others are expected to do and therefore, the higher is the probability that someone will stop by to help you. The 50 rep bounty is a nice touch but there's a lot of work involved here and it's just not enough to get people to invest their time in this problem. Anyway, good luck! – karlphillip Jul 19 '23 at 18:13
  • @karlphillip I am always uncertain about how much code to share. I have been voted down for doing so. But I will take this into consideration. In any case, I think that I worked most of the issues myself. Thanks – marcos Jul 20 '23 at 16:21
  • 1
    @marcos The trick is to write just the bare minimum required to reproduce the issue. It sounds like a simple concept but this is what people struggle the most to understand. They usually just remove some parts of the application they are working on (that they don't want to share) and post the resulting code here as part of the question. The resulting code is usually much bigger than the minimum required to reproduce the issue. So the ideal solution is to **write a completely new application from scratch** that contains only the essential code to reproduce the issue. Good luck! – karlphillip Jul 20 '23 at 18:56

1 Answers1

-2

Okay... here is somewhat what I was after. I decided to read in a cubemap (borrowed from LearnOpenGL see link) rather than to use the depth cubemap I had originally generated in order to simplify my thinking. In the process of doing this I realized several things for which I really do not have a good answer (I will point these out).

Here is the code,

def loadCubemap(faces : list[str]) -> int:

    textureID = glGenTextures(1)
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureID)

    for i in range(len(faces)):
        try:
            img = Image.open(faces[i])

            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, img.width, img.height, 0, GL_RGB, GL_UNSIGNED_BYTE, img.tobytes())

            img.close()
            
        except:
            print("Cubemap texture failed to load at path: " + faces[i])


    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_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)
    glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS)


    return textureID

# initialize camera
camera = Camera(position=(0,0,0))
camera.width = 720
camera.height = 360

# initialize opengl
glfw.init()
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
glfw.window_hint(glfw.DEPTH_BITS, 32) # not here before

# create glfw window
window = glfw.create_window(camera.width, camera.height, 'LearnOpengl', None, None)
if window is None:
    print("Failed to create GLFW window")
    glfw.terminate()           
glfw.make_context_current(window)

# load textures
# -------------
faces = [
    pth / "textures/skybox/right.jpg",
    pth / "textures/skybox/left.jpg",
    pth / "textures/skybox/top.jpg",
    pth / "textures/skybox/bottom.jpg",
    pth / "textures/skybox/front.jpg",
    pth / "textures/skybox/back.jpg",
]

cubemapTexture = loadCubemap(faces)

# Create VAO and VBO of quad for rendering

# vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
#    positions /  texture   
quad_vertices = np.array( 
    [-1.0,  1.0,  0.0, 1.0,
    -1.0, -1.0,  0.0, 0.0,
    1.0, -1.0,  1.0, 0.0,

    -1.0,  1.0,  0.0, 1.0,
    1.0, -1.0,  1.0, 0.0,
    1.0,  1.0,  1.0, 1.0],
    dtype= np.float32)

# create quad vao and vbo
quad_vao = glGenVertexArrays(1)
glBindVertexArray(quad_vao)

quad_vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)
glBufferData(GL_ARRAY_BUFFER, quad_vertices.nbytes, quad_vertices, GL_STATIC_DRAW)

# position attribute
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * 4, ctypes.c_void_p(0)) 
glEnableVertexAttribArray(0) # position

# texture attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * 4, ctypes.c_void_p(2 * 4))

# compile shader
shader = Shader(pth / 'depthcube/shader/equirectangular.vs', pth / 'depthcube/shader/equirectangular.fs')

# SAMPLING PARAMETERS
hangle = np.array([0, 360, 0.025])
vangle = np.array([-90, 90, 0.025])
width = int((hangle[1]-hangle[0]) / hangle[2])
height = int((vangle[1]-vangle[0]) / vangle[2])

A pointer here. The dimensions of width and height can have as a maximum the value of GL_MAX_VIEWPORT_DIMS. Hence, there is a limit to how fine the sampling can be. I would be interested to learn if this can be increased somehow?


# CREATE FRAMEBUFFER

fbo = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, fbo)

texcolor = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texcolor)
#glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, None)
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width, height, 0, GL_RED, GL_FLOAT, None)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texcolor, 0)
glBindFramebuffer(GL_FRAMEBUFFER, 0)


# RENDER
glViewport(0,0,width,height)

# use shader
shader.use()

shader.set_uniform_float('hrange', hangle[1]-hangle[0])
shader.set_uniform_float('vrange', vangle[1]-vangle[0])
shader.set_uniform_vec2('size_display', glm.vec2(width,height))
shader.set_uniform_vec2('offset', glm.vec2(-90.0, -45.0))
shader.set_uniform_int('depthcube', 0) #cubemapTexture WHY 0???

Here is an interesting point I was stucked for the longest time. I thought that in order to introduce the cubemap to my shader I needed to pass on the texture id of the cubemap, ie. the value returned by the loadCubemap() function above. I was wrong, instead for the process to work I just passed the value of 0. I do not have a good explanation as to why this is so perhaps someone could explain this.

# render to fbo
glBindFramebuffer(GL_FRAMEBUFFER, fbo)

glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT)


shader.use()
glBindVertexArray(quad_vao)
glDrawArrays(GL_TRIANGLES, 0, 6)
#glBindVertexArray(0)

glfw.swap_buffers(window)

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

# unbind
glBindFramebuffer(GL_FRAMEBUFFER, 0)

The shaderprogram has the following vertex shader,

#version 330 core

layout (location = 0) in vec2 position; 


void main(){
    gl_Position = vec4(position.x, position.y, 0.0f, 1.0f);
}

and fragment shader,

#version 330 core

uniform vec2 size_display;
uniform vec2 offset;
uniform float hrange;
uniform float vrange;
uniform samplerCube depthcube;

//out vec4 color;
out float color;

void main()
{
    // scale coordinates (0,1)
    vec2 scaleCoord = gl_FragCoord.xy / size_display;

    // convert offset to radians
    vec2 off = vec2(radians(offset.x), radians(offset.y));

    // center the view
    vec2 angles = ((scaleCoord * 2.0) - vec2(1.0)) * vec2(radians(hrange/2), radians(vrange/2)) + off;

    // sampling vector
    vec3 samplingVec = vec3(-cos(angles.y) * cos(angles.x), sin(angles.y), cos(angles.y) * sin(angles.x));

    // output
    color = texture(depthcube, samplingVec).r;

Using the above, I am able to sample a cubemap using spherical coordinates (adapted to OpenGl specs).

marcos
  • 134
  • 15