0
\$\begingroup\$

i have a strange problem with shadow acne. It occurs when the light direction is parallel to a plane. Here is my code and a few images of the problem:

//this calculates the shadow matrix


    //########## 1st phase ##########

    float rangeX = globalData::windowWidth;
    float rangeY = globalData::windowHeight;

    m_ProjectionMatrix =  glm::ortho<float>(-rangeX,rangeX,-rangeY,rangeY, 0.1f,(float)globalData::zFar); // zfar is 500.0f here

    m_viewProjection = glm::lookAt(
                                glm::vec3(lightPosition.x,lightPosition.y,lightPosition.z),
                                glm::vec3(
                                            ((lightDirection.x==lightPosition.x) ? lightDirection.x+0.1f : lightDirection.x),
                                            ((lightDirection.y==lightPosition.y) ? lightDirection.y+0.1f : lightDirection.y),
                                            ((lightDirection.z==lightPosition.z) ? lightDirection.z+0.1f : lightDirection.z)
                                        ),
                                glm::vec3(0,1,0)
                            );

    glm::mat4 biasMatrix(
        0.5, 0.0, 0.0, 0.0,
        0.0, 0.5, 0.0, 0.0,
        0.0, 0.0, 0.5, 0.0,
        0.5, 0.5, 0.5, 1.0
    );

    glm::mat4 depthModelMatrix = glm::mat4(1.0);
    //this is used in the 2 phase, when i render the world objects
    mDepthBiasMVP = biasMatrix * m_ProjectionMatrix * m_viewProjection * depthModelMatrix;



    //Now for each object in the "world", i use another function like this:

    glm::mat4 depthMVP = m_ProjectionMatrix * m_viewProjection * objectModelMatrix;

    shader.sendUniform4x4("depthMVP", &depthMVP);


    //########## 2nd pahase ##########

    //for each object in the world, i send this uniform

    shader.sendUniform4x4("DepthBiasMVP", mDepthBiasMVP);



//in the shader i use this function to get the shadow "visibility"

float getVisibility(sampler2D shadowMap, vec4 vShadowCoord, float windowWidth, float windowHeight)     
{                                                               
    float visibility = 1.0;          
    float bias = 0.0001;                                         
    float highResolutionShadowNearDistance = 20.0f;
    int PCF = 0;
    int blurRange = 1;
    float shadowFactor = 0.01;
    vec2 texelSize = 1.0 / textureSize(shadowMap, 0);


    for(int x = -blurRange; x <= blurRange; ++x)
    {
        for(int y = -blurRange; y <= blurRange; ++y)
        {
            int index = (PCF==1) ? int(4.0*random(floor(vWorldPos.xyz*100.0), y))%4 : y%16;
            vec4 vShadowSmooth = vec4(vShadowCoord.x + poissonDisk[index].x/windowWidth, vShadowCoord.y + poissonDisk[index].y/windowHeight, (vShadowCoord.z-bias)/vShadowCoord.w, 1.0);
            float pcfDepth = texture(shadowMap, vShadowSmooth.xy + vec2(x, y) * texelSize).r; 
            visibility += vShadowSmooth.z - bias > pcfDepth ? -shadowFactor : shadowFactor;        
        }    
    }

    return visibility;
}   

and the results are here:

this is OK: enter image description here

this is OK: enter image description here

this is WRONG: enter image description here

the problems are marked with a red circle. Thanks a lot!

EDIT: this is the result of Peethor suggestion (maybe i havent implemented it well, please help :) ):

enter image description here

and this is de modified shader function:

float getVisibility(sampler2D shadowMap, vec4 vShadowCoord, float windowWidth, float windowHeight, camera _camera, vec3 vNormal)     
{      

    float visibility = 1.0;          
    float bias = 0.0001;                                         
    float highResolutionShadowNearDistance = 20.0f;
    int PCF = 0;
    int blurRange = 2;
    float shadowFactor = 0.01;
    vec2 texelSize = 1.0 / textureSize(shadowMap, 0);

    for(int x = -blurRange; x <= blurRange; ++x)
    {
        for(int y = -blurRange; y <= blurRange; ++y)
        {
            int index = (PCF==1) ? int(4.0*random(floor(vWorldPos.xyz*100.0), y))%4 : y%4;
            vec4 vShadowSmooth = vec4(vShadowCoord.x + poissonDisk[index].x/windowWidth, vShadowCoord.y + poissonDisk[index].y/windowHeight, (vShadowCoord.z-bias)/vShadowCoord.w, 1.0);
            float pcfDepth = texture(shadowMap, vShadowSmooth.xy + vec2(x, y) * texelSize).r; 
            visibility += vShadowSmooth.z - bias > pcfDepth ? -shadowFactor : 0.001;        
        }    
    }

    // vNormal is a vertex normal and _camera.direction is the current light direction
    float parallelLightFactor =  dot(vNormal, _camera.direction);
    visibility *= parallelLightFactor;


    return visibility;
}   
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

This is to be expected. When the light is parallel to the surface, you would need an infinite resolution to make it smooth. Obviously that's not possible. In my game, I solved this by getting the normal of the light surface, getting the light direction, and taking the dot product of those vectors. When you darken everything in the shadow, multiply the 'shadow factor' with that dot product. The shadows will get darker when the light is perpendicular to the surface, and lighter when the light is parallel to the surface. Actually, the shadows disappear when parallel to the surface. But when you have global illumination on your object, you won't notice.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks Peethor, but i get darkness in all objects of the world. how do you think i should implement your solution in my function (getVisibility)? thanks! \$\endgroup\$ Commented Mar 26, 2016 at 17:17
  • \$\begingroup\$ This is a useful quick solution. It looks weird though when you have surfaces that happen to be parallel to the lightvector and that obviously should be in shadow (for example a roof over them). Also note that this particular problem is helped by "stabilising" the shadowmap so it at least stay in place when you rotate and move the player, otherwise these parallel surfaces will exhibit horrible strobing and banding. \$\endgroup\$ Commented Oct 28, 2017 at 9:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .