1
\$\begingroup\$

I'm doing point light shadow mapping and I'm tearing my hair out trying to iron out a bug.

The problem is shadows are basically "inverted" somehow, or wierdly placed. Here's five screenshots; you can approximate where the light is by looking at the cube:

http://s23.postimg.org/um29e8tk9/image.png

http://s1.postimg.org/ftvzeta5q/image.jpg

http://s3.postimg.org/gd8ymwiqa/image.jpg

http://s18.postimg.org/k4608nvd4/image.jpg

http://s3.postimg.org/aqclpfg7m/image.jpg

I've gone through the code a multiple times and I just can't find why this is happening.

Relevant parts:

Definitions:

const uint32_t CUBEMAP_NUM_FACES = 6;   // posX => negX => posY => negY => posZ => negZ

const Vec3 CUBEMAP_DIRECTION_VECTORS[CUBEMAP_NUM_FACES] = { Vec3(1.0f, 0.0f, 0.0f), Vec3(-1.0f, 0.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f),
                                                            Vec3(0.0f, -1.0f, 0.0f), Vec3(0.0f, 0.0f, 1.0f), Vec3(0.0f, 0.0f, -1.0f) };

const Vec3 CUBEMAP_UP_VECTORS[CUBEMAP_NUM_FACES] = { Vec3(0.0f, 1.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f), Vec3(0.0f, 0.0f, -1.0f),
                                                     Vec3(0.0f, 0.0f, -1.0f), Vec3(0.0f, 1.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f) };

Creating FBO/texture sampler:

OmnidirectionalShadowmap::OmnidirectionalShadowmap(Logger& logger, const uint32_t size, const uint32_t numFaces) : mLogger(logger)
{
    GLCALL(glGenFramebuffers(1, &mFramebuffer));
    GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer));

    GLCALL(glGenTextures(1, &mShadowmap));
    GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, mShadowmap));
    GLCALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
    GLCALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
    GLCALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
    GLCALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
    GLCALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE));
    for (uint32_t i = 0; i < numFaces; i++)
        GLCALL(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT32F, size, size, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0));
    GLCALL(glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, mShadowmap, 0));
    GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, 0));

    GLenum fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
    {
        JONS_LOG_ERROR(mLogger, "Framebuffer incomplete");
        throw std::runtime_error("Framebuffer incomplete");
    }
    GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));

    GLCALL(glGenSamplers(1, &mTextureSampler));
    GLCALL(glSamplerParameteri(mTextureSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
    GLCALL(glSamplerParameteri(mTextureSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
    GLCALL(glSamplerParameteri(mTextureSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
    GLCALL(glSamplerParameteri(mTextureSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
    GLCALL(glSamplerParameteri(mTextureSampler, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE));
    GLCALL(glBindSampler(OpenGLTexture::TEXTURE_UNIT_SHADOW_OMNI, mTextureSampler));
}

Overall alghoritm for point lights:

for (const RenderableLighting::PointLight& pointLight : lighting.mPointLights)
{
            // depth pass for each face
    GLCALL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebuffer));
    GLCALL(glDrawBuffer(GL_NONE));
    GLCALL(glViewport(0, 0, (GLsizei)mShadowmapResolution, (GLsizei)mShadowmapResolution));
    for (uint32_t face = 0; face < CUBEMAP_NUM_FACES; face++)
    {
        GLCALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mShadowmap, 0));
        Mat4 lightViewMatrix = glm::lookAt(pointLight.mLightPosition, pointLight.mLightPosition + CUBEMAP_DIRECTION_VECTORS[face], CUBEMAP_UP_VECTORS[face]);
        Mat4 lightProjMatrix = glm::perspective(90.0f, 1.0f, Z_NEAR, Z_FAR);
        PointLightShadowPass(renderQueue, lightProjMatrix, lightViewMatrix);      // draws all objects using the provided matrices
    }

    GLCALL(glEnable(GL_STENCIL_TEST));
    GLCALL(glViewport(0, 0, (GLsizei)mWindowWidth, (GLsizei)mWindowHeight));
    // stencil pass first to elimiate fragments that dosnt need to be lit
    mGBuffer.BindNullForDrawing();
    PointLightStencilPass(pointLight);

    mGBuffer.BindFinalForDrawing();
    mOmnidirectionalShadowmap.BindForReading();
    PointLightLightingPass(pointLight, lighting.mGamma, lighting.mScreenSize);   // final lighting pass
    GLCALL(glDisable(GL_STENCIL_TEST));
}

The depth vertex/fragment shader is very simple and just writes the depth.

Here's the point light lighting fragment shader:

const std::string gShadingFragmentShader = 
"#version 420                                                                                                                           

layout(std140) uniform UnifPointLight                                                                                                   
{                                                                                                                                       
    mat4 mWVPMatrix;                                                                                                                    
    vec4 mLightColor;                                                                                                                   
    vec4 mLightPos;                                                                                                                     
    vec4 mGamma;                                                                                                                        
    vec2 mScreenSize;                                                                                                                   

    float mLightIntensity;                                                                                                              
    float mMaxDistance;                                                                                                                 
} UnifPointLightPass;                                                                                                                   

layout (binding = 2) uniform sampler2D unifPositionTexture;                                                                             
layout (binding = 3) uniform sampler2D unifNormalTexture;                                                                               
layout (binding = 4) uniform sampler2D unifDiffuseTexture;                                                                              
layout (binding = 7) uniform samplerCube unifShadowmap;                                                                                 

out vec4 fragColor;                                                                                                                     

float VectorToDepthValue(vec3 Vec)                                                                                                      
{                                                                                                                                       
    vec3 AbsVec = abs(Vec);                                                                                                             
    float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z));                                                                          

    const float f = 100.0;                                                                                                              
    const float n = 0.1;                                                                                                                
    float NormZComp = (f + n) / (f - n) - (2 * f * n) / (f - n) / LocalZcomp;                                                           

    return (NormZComp + 1.0) * 0.5;                                                                                                     
}                                                                                                                                       

void main()                                                                                                                             
{                                                                                                                                       
    vec2 texcoord = gl_FragCoord.xy / UnifPointLightPass.mScreenSize;                                                                   

    vec3 worldPos = texture(unifPositionTexture, texcoord).xyz;                                                                         
    vec3 normal   = texture(unifNormalTexture, texcoord).xyz;                                                                           
    vec3 diffuse  = texture(unifDiffuseTexture, texcoord).xyz;                                                                          
    normal        = normalize(normal);                                                                                                  

    vec3 positionDiff = (UnifPointLightPass.mLightPos.xyz - worldPos);                                                                  

    float storedDepth = texture(unifShadowmap, positionDiff);                                                                           
    float visibility = 0.0;                                                                                                             
    if (storedDepth + 0.0001 > VectorToDepthValue(positionDiff))                                                                        
        visibility = 1.0;                                                                                                               

    float dist = length(positionDiff);                                                                                                  
    float attenuation = clamp(1.0 - dist*dist * (1 / (UnifPointLightPass.mMaxDistance * UnifPointLightPass.mMaxDistance)), 0.0, 1.0);   
    attenuation *= attenuation;                                                                                                         

    vec3 lightDir = normalize(positionDiff);                                                                                            
    float angleNormal = clamp(dot(normalize(normal), lightDir), 0, 1);                                                                  

    fragColor = vec4(diffuse, 1.0) * visibility * (angleNormal * attenuation *                                                          
                UnifPointLightPass.mLightIntensity * UnifPointLightPass.mLightColor);                                                   
}        

The lighting part itself works as expected, the shadows don't. To me it looks correct, but I don't understand why the shadows are so disjointed?

\$\endgroup\$

closed as off-topic by Josh Aug 1 '14 at 15:23

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions about debugging a problem in your project must present a concise selection of code and context so as to allow a reader to diagnose the issue without needing to read all of your code or to engage in extensive back-and-forth dialog. For more information, see this meta thread." – Josh
If this question can be reworded to fit the rules in the help center, please edit the question.

  • \$\begingroup\$ Try changing 'vec3 positionDiff = (UnifPointLightPass.mLightPos.xyz - worldPos);' to this: vec3 positionDiff = (worldPos-UnifPointLightPass.mLightPos.xyz); \$\endgroup\$ – János Turánszki May 17 '14 at 16:03
  • \$\begingroup\$ Experimented with it, but almost everything is in shadow as a result \$\endgroup\$ – KaiserJohaan May 17 '14 at 18:41
  • 1
    \$\begingroup\$ Yeesh, can you embed the images into your question next time? The linked image site is very inconvenient and included popups to download an Android app. \$\endgroup\$ – Andon M. Coleman May 17 '14 at 20:33
  • 1
    \$\begingroup\$ Nevertheless, it sounds as though the camera's orientation is wrong when you draw into the cube faces. To debug this, consider drawing your scene to a color cubemap from your camera's position. Now, sample your cubemap like you did for depth in the shadow shader, but output to fragColor. If everything is working, you will not notice any significant warping as you rotate the camera. If any of the faces are wrong, you will know it immediately. You could just write the depth directly to fragColor, but depending on scene complexity that may not give enough visual cues. \$\endgroup\$ – Andon M. Coleman May 17 '14 at 21:43
  • \$\begingroup\$ What do you mean by a color cubemap? Draw each cube face to a specific color? By the camera, do you mean the lights "camera" or the main camera? \$\endgroup\$ – KaiserJohaan May 18 '14 at 11:49

Browse other questions tagged or ask your own question.