mpen
mpen

Reputation: 283043

How to improve the quality of my shadows?

First, a screenshot:

As you can see, the tops of the shadows look OK (if you look at the dirt where the tops of the shrubs are projected, it looks more or less correct), but the base of the shadows is way off.

The bottom left corner of the image shows the shadow map I computed. It's a depth-map from the POV of the light, which is also where my character is standing.

Here's another shot, from a different angle:

Any ideas what might be causing it to come out like this? Is the depth of the shrub face too similar to the depth of the ground directly behind it, perhaps? If so, how do I get around that?

I'll post the fragment shader below, leave a comment if there's anything else you need to see.

Fragment Shader

#version 330

in vec2 TexCoord0;
in vec3 Tint0;
in vec4 WorldPos;
in vec4 LightPos;

out vec4 FragColor;

uniform sampler2D TexSampler;
uniform sampler2D ShadowSampler;
uniform bool Blend;

const int MAX_LIGHTS = 16;
uniform int NumLights;
uniform vec3 Lights[MAX_LIGHTS];
const float lightRadius = 100;

float distSq(vec3 v1, vec3 v2) {
    vec3 d = v1-v2;
    return dot(d,d);
}

float CalcShadowFactor(vec4 LightSpacePos)
{
    vec3 ProjCoords = LightSpacePos.xyz / LightSpacePos.w;
    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjCoords.y + 0.5;
    float Depth = texture(ShadowSampler, UVCoords).x;
    if (Depth < (ProjCoords.z + 0.0001))
        return 0.5;
    else
        return 1.0;
}

void main()
{
    float scale;

    FragColor = texture2D(TexSampler, TexCoord0.xy);

    // transparency
    if(!Blend && FragColor.a < 0.5) discard;

    // biome blending
    FragColor *= vec4(Tint0, 1.0f);

    // fog
    float depth = gl_FragCoord.z / gl_FragCoord.w;
    if(depth>20) {
        scale = clamp(1.2-15/(depth-19),0,1);
        vec3 destColor = vec3(0.671,0.792,1.00);
        vec3 colorDist = destColor - FragColor.xyz;
        FragColor.xyz += colorDist*scale;
    }

    // lighting
    scale = 0.30;
    for(int i=0; i<NumLights; ++i) {
        float dist = distSq(WorldPos.xyz, Lights[i]);
        if(dist < lightRadius) {
            scale += (lightRadius-dist)/lightRadius;
        }
    }

    scale *= CalcShadowFactor(LightPos);
    FragColor.xyz *= clamp(scale,0,1.5);
}

I'm fairly certain this is an offset problem. My shadows look to be about 1 block off, but I can't figure out how to shift them, nor what's causing them to be off.


Looks like "depth map bias" actually:

Not exactly sure how to set this....do I just call glPolygonOffset before rendering the scene? Will try it...

Setting glPolygonOffset to 100,100 amplifies the problem:

I set this just before rendering the shadow map:

GL.Enable(EnableCap.PolygonOffsetFill);
GL.PolygonOffset(100f, 100.0f);

And then disabled it again. I'm not sure if that's how I'm supposed to do it. Increasing the values amplifies the problem....decreasing them to below 1 doesn't seem to improve it though.

Notice also how the shadow map in the lower left changed.

Vertex Shader

#version 330

layout(location = 0) in vec3 Position;

layout(location = 1) in vec2 TexCoord;
layout(location = 2) in mat4 Transform;
layout(location = 6) in vec4 TexSrc; // x=x, y=y, z=width, w=height
layout(location = 7) in vec3 Tint; // x=R, y=G, z=B

uniform mat4 ProjectionMatrix;
uniform mat4 LightMatrix;

out vec2 TexCoord0;
out vec3 Tint0;
out vec4 WorldPos;
out vec4 LightPos;

void main()
{
    WorldPos = Transform * vec4(Position, 1.0);
    gl_Position = ProjectionMatrix * WorldPos;
    LightPos = LightMatrix * WorldPos;
    TexCoord0 = vec2(TexSrc.x+TexCoord.x*TexSrc.z, TexSrc.y+TexCoord.y*TexSrc.w); 
    Tint0 = Tint;
}

Upvotes: 3

Views: 2905

Answers (3)

Kaganar
Kaganar

Reputation: 6570

While world-aligned cascaded shadow maps are great and used in most new games out there, it's not related to why your shadows have a strange offset with your current implementation.

Actually, it looks like you're sampling from the correct texels on the shadow map just on where the shadows that are occurring are exactly where you'd expect them to be, however your comparison is off.

I've added some comments to your code:

vec3 ProjCoords = LightSpacePos.xyz / LightSpacePos.w; // So far so good...
vec2 UVCoords;
UVCoords.x = 0.5 * ProjCoords.x + 0.5; // Right, you're converting X and Y from clip
UVCoords.y = 0.5 * ProjCoords.y + 0.5; // space to texel space...
float Depth = texture(ShadowSampler, UVCoords).x; // I expect we sample a value in [0,1]
if (Depth < (ProjCoords.z + 0.0001)) // Uhoh, we never converted Z's clip space to [0,1]
    return 0.5;
else
    return 1.0;

So, I suspect you want to compare to ProjCoords.z * 0.5 + 0.5:

if (Depth < (ProjCoords.z * 0.5 + 0.5 + 0.0001))
    return 0.5;
else
    return 1.0;

Also, that bias factor makes me nervous. Better yet, just take it out for now and deal with it once you get the shadows appearing in the right spots:

const float bias = 0.0;
if (Depth < (ProjCoords.z * 0.5 + 0.5 + bias))
    return 0.5;
else
    return 1.0;

I might not be entirely right about how to transform ProjCoords.z to match the sampled value, however this is likely the issue. Also, if you do move to cascaded shadow maps (I recommend world-aligned) I'd strongly recommend drawing frustums representing where each shadow map is viewing -- it makes debugging a whole lot easier.

Upvotes: 2

SigTerm
SigTerm

Reputation: 26429

NVidia OpenGL SDK has "cascaded shadow maps" example. You might want to check it out (haven't used it myself, though).

How to improve the quality of my shadows?

The problem could be caused by using incorrect matrix while rendering shadows. Your example doesn't demonstrate how light matrices are set. By murphy's law I'll have to assume that bug lies in this missing piece of code - since you decided that this part isn't important, it probably causes the problem. If matrix used while testing the shadow is different from matrix used to render the shadow, you'll get exactly this problem.

I suggest to forget about the whole minecraft thing for a moment, and play around with shadows in simple application. Make a standalone application with floor plane and rotating cube (or teapot or whatever you want), and debug shadow maps there, until you get hang of it. Since you're willing to throw +100 bounty onto the question, you might as well post the complete code of your standalone sample here - if you still get the problem in the sample. Trying to stick technology you aren't familiar with into the middle of working(?) engine isn't a good idea anyway. Take it slow, get used to the technique/technology/effect, then integrate it.

Upvotes: 1

datenwolf
datenwolf

Reputation: 162289

This is called the "deer in headlights" effect of buffer mapped shadows. There are a several ways to minimize this effect. Look for "light space shadow mapping".

Upvotes: 2

Related Questions