Rendering shadow cubemap in one pass with geometry shader

Problem Description

I’m following (loosely) along the point light shadow map tutorial on LearnOpenGL to render a depth cubemap with a geometry shader in one pass. However I haven’t been successful in doing so.

When I render the scene without shadows I get this image.

However when I tried to render it with depth map I get this weird result.

And when I comment out the shadow render pass completely I still get this exact result, which leads me to guess that the depth values are not rendered to the cubemap at all, and the cubemap pointer is just pointing to some weird image in memory. I’ve read somewhere online that textures cannot be used as buffer storage until they are ‘complete’, but I don’t know how to check texture completeness.

Below is the code where I generated the cube map:

GLtexCubeMap::GLtexCubeMap(GLint internalFormat, GLenum format, GLenum type, GLsizei width, GLsizei height, GLint level, const GLvoid *data)
    : internalFormat(internalFormat), format(format), glDataType(type), width(width), height(height), level(level), data(data)
{
    //level default = 1, data default = nullptr
    GL(glGenTextures(1, &id));
    bind();
    for (int i = 0; i <= level; i++)
    {
        for (GLuint face = 0; face < 6; face++)
        {
            GL(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, i, internalFormat,
                            width, height, 0, format, type, data));
        }
    }
    GL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
    GL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
    GL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
    GL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
    GL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE));
    GL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0));
    GL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, level));
    unbind();
}

First I generate depth framebuffers and cubemaps for each light

for (size_t i = 0; i < MAX_LIGHTS; i++)
{
    dfb[i] = new GLframebuffer;
    depthCubemap[i] = new GLtexCubeMap(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_FLOAT, shadowWidth, shadowHeight);
}

Then I bind the cubemaps to corresponding framebuffers

void Ogl_PbrShadowmap_Renderer::configurShadowmap()
{
    for (int i = 0; i < MAX_LIGHTS; i++)
    {
        dfb[i]->bindAndDo(std::function([this, i]() -> void {
            GL(glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthCubemap[i]->ID(), 0));
            GL(glDrawBuffer(GL_NONE));
            GL(glReadBuffer(GL_NONE));
            auto fbStatus = GL(glCheckFramebufferStatus(GL_FRAMEBUFFER));
            if (fbStatus != GL_FRAMEBUFFER_COMPLETE)
            {
                LOG_ERR("Framebuffer incomplete!");
                BREAK_POINT;
            }
        }));
    }
}

Here is my full render pass

void Ogl_PbrShadowmap_Renderer::renderPass()
{
    //render shadowmaps
    GL(glViewport(0, 0, shadowWidth, shadowHeight));
    for (size_t i = 0; i < scene->numLights; i++)
    {
        dfb[i]->bindAndDo(std::function([this, i]() -> void {
            GL(glEnable(GL_DEPTH_TEST));
            GL(glClearColor(FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX));
            GL(glClear(GL_DEPTH_BUFFER_BIT));

            configurShader(ShaderMode::SHADOW_MAP, i);
            scene->render(*shaderProgram, false);

            GL(glDisable(GL_DEPTH_TEST));
        }));
    }

    //render final result to a texture that shows up in an ImGui window
    fb->bindAndDo(std::function([this]() -> void {
        GL(glViewport(0, 0, viewWidth, viewHeight));
        GL(glEnable(GL_DEPTH_TEST));
        GL(glClearColor(0.7f, 0.7f, 0.7f, 1.0f));
        GL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
        configurShader(ShaderMode::LIGHTING);
        scene->render(*shaderProgram, true);
        configurShader(ShaderMode::LIGHT_SOURCE);
        for(int i = 0; i < scene->numLights; i++)
        {
            renderLightSource(scene->lights[i]);
        }
        GL(glDisable(GL_DEPTH_TEST));
    }));
}

Some of the macros I defined for debugging purposes:

#define LOG_INFO(msg) std::cout << "[Info] " << msg << std::endl
#define LOG_ERR(msg) std::cerr << "[Error] " << msg << std::endl
#define LOG_WARN(msg) std::cout << "[Warning] " << msg << std::endl
#define BREAK_POINT LOG_WARN("Execution Paused from file: '" << __FILE__ << "', line " << __LINE__) std::raise(SIGINT)

inline bool glCheckError_(const char * file, int line)
{
    bool err = false;
    GLenum errorCode;
    while ((errorCode = glGetError()) != GL_NO_ERROR)
    {
        std::string error;
        switch (errorCode)
        {
            case GL_INVALID_ENUM:                  error = "GL_INVALID_ENUM"; break;
            case GL_INVALID_VALUE:                 error = "GL_INVALID_VALUE"; break;
            case GL_INVALID_OPERATION:             error = "GL_INVALID_OPERATION"; break;
            case GL_STACK_OVERFLOW:                error = "GL_STACK_OVERFLOW"; break;
            case GL_STACK_UNDERFLOW:               error = "GL_STACK_UNDERFLOW"; break;
            case GL_OUT_OF_MEMORY:                 error = "GL_OUT_OF_MEMORY"; break;
            case GL_INVALID_FRAMEBUFFER_OPERATION: error = "GL_INVALID_FRAMEBUFFER_OPERATION"; break;
        }
        LOG_ERR(error << " occured at file '" << file << "', line " << line);
        err = true;
        // std::raise(SIGINT);
    }
    return err;
}
#define glCheckError if(glCheckError_(__FILE__, __LINE__)) std::raise(SIGINT)
#define GL(glFunctionCall) glFunctionCall; glCheckError

And here are my shaders.

Shadow Map Vertex Shader

#version 410 core

layout (location = 0) in vec3 vPos;

uniform mat4 mat_model;

void main(){
    gl_Position = mat_model * vec4(vPos, 1.0);
}

Shadow Map Geometry Shader

#version 410 core

layout (triangles) in;
layout (triangle_strip, max_vertices=18) out;

uniform mat4 shadowMatrices[6];

out vec4 FragPos;

void main(){
    for(int face = 0; face < 6; face++){
        gl_Layer = face;
        for(int i = 0; i < 3; i++){
            FragPos = gl_in[i].gl_Position;
            gl_Position = shadowMatrices[face] * FragPos;
            EmitVertex();
        }
        EndPrimitive();
    }
}

Shadow Fragment Shader

#version 410 core

in vec4 FragPos;

uniform vec3 lightPos;
uniform float far_plane;

void main()
{
    // get distance between fragment and light source
    float lightDistance = length(FragPos.xyz - lightPos);
    
    // map to [0;1] range by dividing by far_plane
    lightDistance = lightDistance / far_plane;
    
    // write this as modified depth
    gl_FragDepth = lightDistance;

}  

Can anyone spot what I did wrong?

Edit: I’m running this on macOS Catalina 10.15.5

Source: Windows Questions C++

LEAVE A COMMENT