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:
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):
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?
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.