Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 10 of 12

Thread: Cascaded shadow mapping and split values

Hybrid View

  1. #1
    Intern Newbie
    Join Date
    Jan 2014
    Posts
    33

    Cascaded shadow mapping and split values

    Reposting, as the last topic is bugged and cant be edited.

    I'm having issues where shadows vanish at certain angles and I want to ascertain I am using the correct values to determine the split index.

    Code cpp:
    CameraFrustrum CalculateCameraFrustrum(const float minDist, const float maxDist, const Vec3& cameraPosition, const Vec3& cameraDirection, Vec4& splitDistance, const Mat4& camView)
        {
            CameraFrustrum ret = { Vec4(1.0f, 1.0f, -1.0f, 1.0f), Vec4(1.0f, -1.0f, -1.0f, 1.0f), Vec4(-1.0f, -1.0f, -1.0f, 1.0f), Vec4(-1.0f, 1.0f, -1.0f, 1.0f),
                                   Vec4(1.0f, -1.0f, 1.0f, 1.0f), Vec4(1.0f, 1.0f, 1.0f, 1.0f), Vec4(-1.0f, 1.0f, 1.0f, 1.0f), Vec4(-1.0f, -1.0f, 1.0f, 1.0f), };
     
            const Mat4 perspectiveMatrix = glm::perspective(70.0f, 1920.0f / (float)1080.0f, minDist, maxDist);
            const Mat4 invMVP = glm::inverse(perspectiveMatrix * camView);
     
            for (Vec4& v : ret)
            {
                v = invMVP * v;
                v /= v.w;
            }
     
            splitDistance= ret[4];
            splitDistance+= ret[5];
            splitDistance+= ret[6];
            splitDistance+= ret[7];
            splitDistance /= 4;
     
            return ret;
        }

    The splitDistance.Z is what I use as each split maxdistance. It is multiplied by the main cameras view matrix before being sent to the shader (as the index determination is done in view space). Is this correct?
    Last edited by Dark Photon; 04-14-2014 at 05:53 AM.

  2. #2
    Senior Member OpenGL Guru Dark Photon's Avatar
    Join Date
    Oct 2004
    Location
    Druidia
    Posts
    3,368
    I don't understand what you're doing here and why. It doesn't mesh with what you say you're trying to do.

    Why aren't you just computing your split distances in eye-space, for instance:

    Code :
          double d_uniform = mix( near, far, percent );  // Linear
          double d_log     = near * pow( ( far / near ), percent );  // Log
          d                = mix( d_uniform, d_log, blend_f ); // Practical split scheme

  3. #3
    Intern Newbie
    Join Date
    Jan 2014
    Posts
    33
    I was talking about the split distances used for the comparison in the shader; I already have the computation of split distances working.

    For example, here's the lighting fragment shader I use:

    Code :
    #version 420                                                                                                                   
     
    const float DEPTH_BIAS = 0.00005;                                                                                               
     
    layout(std140) uniform UnifDirLight                                                                                                     
    {                                                                                                                               
        mat4 mVPMatrix[4];                                                                                                          
        mat4 mCamViewMatrix;    
        vec4 mSplitDistance;                                                                                                    
        vec4 mLightColor;                                                                                                           
        vec4 mLightDir;                                                                                                             
        vec4 mGamma;                                                                                                                
        vec2 mScreenSize;                                                                                                           
    } UnifDirLightPass;                                                                                                             
     
    layout (binding = 2) uniform sampler2D unifPositionTexture;                                                                     
    layout (binding = 3) uniform sampler2D unifNormalTexture;                                                                       
    layout (binding = 4) uniform sampler2D unifDiffuseTexture;                                                                      
    layout (binding = 6) uniform sampler2DArrayShadow unifShadowTexture;                                                            
     
    out vec4 fragColor;                                                                                                             
     
    void main()                                                                                                                     
    {                                                                                                                               
        vec2 texcoord = gl_FragCoord.xy / UnifDirLightPass.mScreenSize;                                                             
     
        vec3 worldPos = texture(unifPositionTexture, texcoord).xyz;                                                                 
        vec3 normal   = normalize(texture(unifNormalTexture, texcoord).xyz);                                                        
        vec3 diffuse  = texture(unifDiffuseTexture, texcoord).xyz;                                                                  
     
        vec4 camPos = UnifDirLightPass.mCamViewMatrix * vec4(worldPos, 1.0);                                      
     
        int index = 3;                                                                        
        if (camPos .z > UnifDirLightPass.mSplitDistance.x)                                                                           
           index = 0;                                                                                                              
        else if (camPos .z > UnifDirLightPass.mSplitDistance.y)                                                                      
           index = 1;                                                                                                            
        else if (camPos .z > UnifDirLightPass.mSplitDistance.z)                                                                      
           index = 2;                                                                                                              
     
        vec4 projCoords = UnifDirLightPass.mVPMatrix[index] * vec4(worldPos, 1.0);                                                  
        projCoords.w    = projCoords.z - DEPTH_BIAS;                                                                                
        projCoords.z    = float(index);                                                                                             
        float visibilty = texture(unifShadowTexture, projCoords);                                                                   
     
        float angleNormal = clamp(dot(normal, UnifDirLightPass.mLightDir.xyz), 0, 1);                                               
     
        fragColor = vec4(diffuse, 1.0) * visibilty * angleNormal * UnifDirLightPass.mLightColor;                                    
    }

    So my idea is that splitDistance.z for each splits frustrum (from 1st post) is what I use for mSplitDistance.x/y/z (depending on split). Is this the correct values to compare against?

  4. #4
    Senior Member OpenGL Guru Dark Photon's Avatar
    Join Date
    Oct 2004
    Location
    Druidia
    Posts
    3,368
    Quote Originally Posted by TheKaiser View Post
    I was talking about the split distances used for the comparison in the shader; I already have the computation of split distances working.
    Ok, I'll take your word for that. The compute logic bothers me.

    I'm having issues where shadows vanish at certain angles and I want to ascertain I am using the correct values to determine the split index.

    Code glsl:
    ...
        int index = 3;                                                                        
        if (camPos .z > UnifDirLightPass.mSplitDistance.x)                                                                           
           index = 0;                                                                                                              
        else if (camPos .z > UnifDirLightPass.mSplitDistance.y)                                                                      
           index = 1;                                                                                                            
        else if (camPos .z > UnifDirLightPass.mSplitDistance.z)                                                                      
           index = 2;                                                                                                              
    ...
    Assuming the distances are correct, this looks reasonable. Have you printed the values to ensure that they are correct?

  5. #5
    Intern Newbie
    Join Date
    Jan 2014
    Posts
    33
    Quote Originally Posted by Dark Photon View Post
    Ok, I'll take your word for that. The compute logic bothers me.
    I know it's confusing, I'm using the term "split distances" in two places, the first one (which I assumed you were refering to in your first post and code) is this:

    Code :
    void CalculateShadowmapCascades(std::array<float, gNumShadowmapCascades>& nearDistArr, std::array<float, gNumShadowmapCascades>& farDistArr, const float nearDist, const float farDist)
        {
            const float splitWeight = 0.75f;
            const float ratio = nearDist / farDist;
     
            nearDistArr[0] = nearDist;
            for (uint8_t index = 1; index < gNumShadowmapCascades; index++)
            {
                const float si = index / (float)gNumShadowmapCascades;
     
                nearDistArr[index] = splitWeight * (nearDist * powf(ratio, si)) + (1 - splitWeight) * (nearDist + (farDist - nearDist) * si);
                farDistArr[index - 1] = nearDistArr[index] * 1.005f;
            }
            farDistArr[gNumShadowmapCascades - 1] = farDist;
        }

    which I borrowed from the nvidia cascaded shadow map sample, and it works fine, for example for near_z = 0.1f and far_z = 100.0f I get the splits {6, 12, 18, 100} which looks reasonable enough to me.


    The second one is the uniform I send to the shader I posted, since I can't just use the unmodified values from CalculateShadowmapCascades() directly. I reason that for each split, while constructing that splits frustrum I sample the average Z of the far corners of the frustrum like this:

    Code :
    splitDistance= ret[4];
    splitDistance+= ret[5];
    splitDistance+= ret[6];
    splitDistance+= ret[7];
    splitDistance /= 4;

    Code :
    splitDistance= lighting.mCameraViewMatrix * splitDistance;
    splitDistances[cascadeIndex] = splitDistance.z;       // splitDistances is sent to the shader

    And that is what I'm not sure if I'm doing correctly. The nvidia sample uses a different comparison for the splits, like this:

    Code :
    far_bound[i] = 0.5f*(-f[i].fard*cam_proj[10]+cam_proj[14])/f[i].fard + 0.5f;

    But me doing deferred shading figured it would be much easier and less code to do the comparison in view space?
    Last edited by TheKaiser; 04-15-2014 at 01:15 AM.

  6. #6
    Senior Member OpenGL Guru Dark Photon's Avatar
    Join Date
    Oct 2004
    Location
    Druidia
    Posts
    3,368
    Quote Originally Posted by TheKaiser View Post
    The second one is the uniform I send to the shader I posted, since I can't just use the unmodified values from CalculateShadowmapCascades() directly.
    Why not? I do, for forward and deferred shading.

    I reason that for each split, while constructing that splits frustrum I sample the average Z of the far corners of the frustrum like this:

    Code :
    splitDistance= ret[4];
    splitDistance+= ret[5];
    splitDistance+= ret[6];
    splitDistance+= ret[7];
    splitDistance /= 4;

    Code :
    splitDistance= lighting.mCameraViewMatrix * splitDistance;
    splitDistances[cascadeIndex] = splitDistance.z;       // splitDistances is sent to the shader
    Yeah, this is what's confusing to me. First, averaging a bunch of 4D (3D actually) points doesn't make sense to me. Second, the points you're averaging are just the corners of the far clip in NDC. Once you average that, you get a point, not split distances. Third, you want the eye-space Z split distances (which you already computed in CalculateShadowmapCascades, but whatever...), so you really just want to pass through an inverse PROJECTION transform to get that. If you include the VIEWING transform, as you are here, you're going to end up in world space, and that's generally not what you want to work with in your shader. Eye space is preferred for a number of reasons (you call it an MVP here too, which it's not).
    Last edited by Dark Photon; 04-15-2014 at 05:35 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •