1
\$\begingroup\$

I'm implementing exponential shadow maps, and so far I have directional lights and spotlights working perfectly. Currently I'm trying to apply this technique to point lights with cubemapped shadows.

The theory is, the visibility factor should be computed as a product of two exponentials:

visibility = exp(k*occluderDepth) * exp(-k*receiverDepth)

So the shadow map, instead of storing depth, stores exp(koccluderDepth), and the lighting pass multiplies this value by exp(-kreceiverDepth).

My shadowmap renderer looks like this:

//vsh
#version 410

uniform mat4 viewProjectionMatrix;
uniform mat4 modelTransform;

in vec4  position;

void main (void) {
    gl_Position = viewProjectionMatrix * modelTransform * position;
}

//fsh
#version 410

float lightExponentialConstant = 200.0;

layout (location = 0) out float exponentialMapVaule;

void main (void) {
    exponentialMapVaule = exp(lightExponentialConstant * map_01(gl_FragCoord.z, lightNearPlane, lightFarPlane));
} 

The visibility for spot and directional lights is computed like so and it works perfectly:

uniform sampler2D shadowmapSampler;
uniform mat4 shadowSpaceProjection;

float map_01(float x, float v0, float v1) {
  return (x - v0) / (v1 - v0);
}

float computeVisibility(vec3 position) { 

    mat4 shadowBias = mat4(
        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
    );

    vec4 shadowCoord =  shadowBias * shadowSpaceProjection * vec4(position, 1.0);
    float occluder = texture(shadowmapSampler, shadowCoord.xy/shadowCoord.w).r;
    float reciever = map_01(shadowCoord.z/shadowCoord.w, lightNearPlane, lightFarPlane);
    float visibility = clamp(occluder * exp(-lightExponentialConstant * reciever), 0.0, 1.0);

    return visibility;

}

So basically the receiver value is the depth of the world position projected into shadow space, and then mapped to the range [0, 1] according to the offset of that point from the shadow projection's near plane and far plane.

I have tried to apply the same logic to the cubemap shadowmap for my point light:

float map_01(float x, float v0, float v1) {
  return (x - v0) / (v1 - v0);
}

uniform samplerCube shadowCubemap;

float computeVisibility(vec3 position, vec3 lightPosition) {

    vec3 ray = position - lightPosition;
    float distance = length(ray);
    float occluder = texture(shadowCubemap, normalize(ray)).r;
    float receiver = map_01(distance, lightNearPlane, lightFarPlane);
    float visibility = clamp(occluder * exp(-lightExponentialConstant * reciever), 0.0, 1.0);

    return visibility;

}

So here, if I understand correctly, distance should be the length of the vector between the world position and the light position (which should be equivalent to the reprojected depth from the 2D example). But everything is occluded when I use this method.

What am I missing? In particular, I'm not sure if I am not confident about the cubemap sampling, or if I'm missing some step to ensure that the distance value used to compute the receiver value is actually being transformed correctly to shadow space.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.