Search code examples
opengl3dshadow-mapping

How to use biasing with cubemap shadows


So, I have yet another problem with shadow mapping. I'm trying to implement point lights with cubemap shadows, and almost have it working.

I'm rendering the shadows into a cubemap of type DEPTH_COMPONENT16. I do 6 passes, using exactly the same shaders as I do for my directional and spot lights. I don't have any colour outputs, and just use the depth buffer. I figure that that should be fine, as the cubemap is pretty much just 6 spotlights, where I have no issues.

Now, my problem comes when I try and sample shadow map in my lighting shader. My renderer is deferred, so I have to reconstruct all positions from depth.

When I sample from the cubemap, I do this:

// world space fragment to light
vec3 cubeNorm = normalize(w_pos - LB.position);
vec3 absNorm = abs(cubeNorm);
bool xy = absNorm.x > absNorm.y;
bool yz = absNorm.y > absNorm.z;
bool zx = absNorm.z > absNorm.x;
// which cube face will be sampled from
int index = 0; 
if (xy && !zx) index = 0 + int(cubeNorm.x < 0.f);
if (yz && !xy) index = 2 + int(cubeNorm.y < 0.f);
if (zx && !yz) index = 4 + int(cubeNorm.z < 0.f);
// point in light space with small normal offset
vec4 sc = LB.matArr[index] * vec4(w_pos+w_surf*0.06f, 1.f); 
vec4 shadcrd = vec4(cubeNorm, sc.z / sc.w * 0.5f + 0.5f);
// normals in view space
float bias = get_bias(v_surf, dirToPoint);
float vis = sample_shadow(shadcrd, bias, texShad);

The get_bias function:

float get_bias(vec3 _normal, vec3 _lightDir) {
    float lightDot = dot(_normal, -_lightDir);
    float magicTan = sqrt(1.f - lightDot * lightDot) / lightDot;
    return clamp(0.006f * magicTan, 0.f, 0.03f);
}

The sample_shadow function:

float sample_shadow(vec4 _sc, float _bias, samplerCubeShadow _tex) {
    return texture(_tex, vec4(_sc.xyz, _sc.w-_bias));
}

And, this is what I get. I have the distance roll off disabled to make the problem easier to see: problem

So, the biasing does not work properly. Adjusting the get_bias function to return larger values does not seem to help. The only thing that removes the acne seems to be adding a huge constant bias, which obviously is not an option.

For comparison, here is a spotlight, rendered in the same place with a 90 degree fov. The shader uses the same get_bias function, and the near/far planes are the same (0.2/5.0): noproblem

No biasing issues. I could just use 6 spotlights (without the rounded edges), but I'd rather use a cubemap. What am I doing wrong?


Solution

  • Solved it. 'Twas a simple oversight. The line:

    vec3 cubeNorm = normalize(w_pos - LB.position);
    

    should have been normal offset:

    vec3 cubeNorm = normalize((w_pos+w_surf*0.06f) - LB.position);
    

    Also, there is a way better way to get the depth to compare in the answer here. It does require the near and far planes though.