PDA

View Full Version : Variance shadow mapping: artifacts when using filtered shadow map



Vexator
03-17-2016, 09:03 AM
I've implemented standard variance shadow mapping and it works as expected if I use an unfiltered 32-bit floating-point render target during the shadow pass:

2239

However, if I setup a render target with linear, bilinear or trilinear filtering instead, all fragments that lie behind a certain distance from the camera are in shadow:

2240

Here I have moved a little closer:

2241

What's happening here?!

Common vertex shader:


void main() {
vec4 objectSpacePosition = in_position;

vec4 worldSpacePosition = in_modelMatrix*objectSpacePosition;
vec4 cameraSpacePosition = in_viewMatrix*worldSpacePosition;


#ifdef LIGHT_COUNT
#if LIGHT_COUNT > 0
for (int i = 0; i < LIGHT_COUNT; i++) {
Light light = in_lights[i];

v_lightSpacePosition[i] = light.viewMatrix * worldSpacePosition;
}
#endif
#endif

vec4 screenSpacePosition = in_projectionMatrix*cameraSpacePosition;

gl_Position = screenSpacePosition;
}

Shadow pass shader:


void main() {
#ifdef LIGHT_COUNT
#if LIGHT_COUNT > 0
Light light = in_lights[0];
vec4 lightSpacePosition = v_lightSpacePosition[0];

vec4 lightProjection = light.projectionMatrix * lightSpacePosition;
vec2 shadowMapCoords = (lightProjection.xy/lightProjection.w)*0.5+0.5;

float depth = lightProjection.z;

gl_FragColor = vec4(depth, depth*depth, depth, depth*depth);
#endif
#endif
}

Fragment shader:


float shadowMapping(sampler2D shadowMap, vec2 texCoords, float lightDistance, int shadowIndex, float varianceOffset){
vec4 shadow = texture2D(shadowMap, texCoords);

vec2 moments = vec2(1.0, 1.0);

if (shadowIndex == 0) {
moments = shadow.rg;
} else if (shadowIndex == 1) {
moments = shadow.ba;
}


float variance = moments.y - (moments.x*moments.x);
variance = max(variance, varianceOffset);


float difference = lightDistance - moments.x;
float upperBound = variance / (variance + difference*difference);

return max(upperBound, step(lightDistance, moments.x));
}


void main() {
vec3 totalDiffuseColor = vec3(0.0);

#ifdef LIGHT_COUNT
#if LIGHT_COUNT > 0
for (int i = 0; i < LIGHT_COUNT; i++) {
Light light = in_lights[i];

vec4 lightSpacePosition = v_lightSpacePosition[i];

vec4 lightProjection = light.projectionMatrix * lightSpacePosition;
vec2 shadowMapCoords = (lightProjection.xy/lightProjection.w)*0.5+0.5;

vec3 diffuse_color = 1.0;

/* fresnel term, attenuation, etc. */

#ifdef SHADOW_MAPPING
diffuse_color *= shadowMapping(shadowMap, shadowMapCoords, lightProjection.z, light.shadowIndex, in_varianceOffset);
#endif


totalDiffuseColor += diffuse_color;
}
#endif
#endif

gl_FragColor = vec4(totalDiffuseColor, 1.0);
}

Spoops
03-17-2016, 10:13 AM
I see you mention trilinear filtering, but have you generated mipmaps for the shadow map? It looks like the far fragments try to lookup on a texture lod that does not exist.

Vexator
03-18-2016, 03:29 AM
I see you mention trilinear filtering, but have you generated mipmaps for the shadow map? It looks like the far fragments try to lookup on a texture lod that does not exist.

D'oh! Of course, that was the problem! It's painfully obvious to me now.

Thank you!