OpenGL shadow map and two light sources

Started by
9 comments, last by ddlox 3 years, 5 months ago

I'm trying to add a little light to the back side of the object I'm drawing, and the shadow comes through. The full source is at: https://github.com/sjhalayka/opengl4_stlview_shadow_map

Is this a limitation of the shadow mapping?

Advertisement

It looks like you are drawing the shadow either with incorrect geometry (e.g. the second light source projects a bogus mirrored copy of the shadow) or incorrect backface culling (shadowing fragments that are on the wrong side of the sphere but aligned with shadowed ones on the correct side).

The terminator in the first screenshot shouldn't be soft; at least, not softer than the mapped shadow. It's similarly incorrect in the second screenshot.

Can you make a test with two close light sources on the same side in arbitrary positions (animated if possible), to demonstrate multiple shadow maps, correct blending and correct geometry? If it works, just move the second light source where you want it.

Omae Wa Mou Shindeiru

u can also render yr shadow map as a texture on a quad to see if it is what u expect , good as a debugging means

have fun ?

I totally faked it. The full code is at https://github.com/sjhalayka/opengl4_stlview_shadow_map

The C++ code is:

glDisable(GL_CULL_FACE);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(2.5f, 10.0f);

draw_meshes(shadow_map.get_program());
glFlush();
	
...
	
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
draw_meshes(shadow_map.get_program());

The shader is:

#version 430

uniform sampler2DShadow shadow_map;

in vec3 Position;
in vec3 Normal;
in vec4 ShadowCoord;

uniform vec4 LightPosition; // in view space
uniform vec4 LightPosition_Untransformed; // in world space

vec3 LightIntensity = vec3(1.0, 1.0, 1.0);
vec3 MaterialKa = vec3(0.1, 0.1, 0.1);
vec3 MaterialKd = vec3(1.0, 1.0, 1.0);
vec3 MaterialKs = vec3(1.0, 1.0, 1.0);
float MaterialShininess = 1000.0;

layout (location = 0) out vec4 FragColor;

vec3 phongModelDiffAndSpec()
{
    vec3 n = Normal;
    vec3 s = normalize(vec3(LightPosition.xyz) - Position);
    vec3 v = normalize(-Position.xyz);
    vec3 r = reflect( -s, n );
    float sDotN = max( dot(s,n), 0.0 );
    vec3 diffuse = LightIntensity * MaterialKd * sDotN;
    vec3 spec = vec3(0.0);

    if( sDotN > 0.0 )
    {
        spec.x = pow( max( dot(r,v), 0.0 ), MaterialShininess );
        spec.y = pow( max( dot(r,v), 0.0 ), MaterialShininess );
        spec.z = pow( max( dot(r,v), 0.0 ), MaterialShininess );
    }

    vec3 n2 = Normal;
    vec3 s2 = normalize(vec3(-LightPosition) - Position);
    vec3 v2 = normalize(-Position.xyz);
    vec3 r2 = reflect( -s2, n2 );
    float sDotN2 = max( dot(s2,n2)*0.5f, 0.0 );
    vec3 diffuse2 = LightIntensity*0.25 * MaterialKd * sDotN2;

    return diffuse + diffuse2 + spec;
}

subroutine void RenderPassType();
subroutine uniform RenderPassType RenderPass;

subroutine (RenderPassType)
void shadeWithShadow()
{
    vec3 diffAndSpec = phongModelDiffAndSpec();

    float shadow = 1.0;

    if( ShadowCoord.z >= 0.0 )
    {
        shadow = textureProj(shadow_map, ShadowCoord);

        vec3 n = normalize(Normal);
        vec3 n2 = normalize(LightPosition.xyz);
        float dp = dot(n, n2);

        if(dp < 0.0)
        {
            shadow = 1.0;
        }
        else
        {
            if(shadow == 0.0)
                shadow = 1.0 - dp;
        }
    }

    // If the fragment is in shadow, use ambient light only.
    FragColor = vec4(diffAndSpec * shadow + MaterialKa, 1.0);

    // Gamma correct
    FragColor = pow( FragColor, vec4(1.0 / 2.2) );
}

subroutine (RenderPassType)
void recordDepth()
{
    // Do nothing, depth will be written automatically
}

void main() {
    // This will call either shadeWithShadow or recordDepth
    RenderPass();
}

Even better, this code does not draw the specular highlight when the region of the sphere lies in shadow. The full code is at: https://github.com/sjhalayka/opengl4_stlview_shadow_map

#version 430

uniform sampler2DShadow shadow_map;

in vec3 Position;
in vec3 Normal;
in vec4 ShadowCoord;

uniform vec4 LightPosition; // in view space
uniform vec4 LightPosition_Untransformed; // in world space

vec3 LightIntensity = vec3(1.0, 1.0, 1.0);
vec3 MaterialKa = vec3(0.0, 0.0, 0.0);

uniform vec3 MaterialKd = vec3(1.0, 1.0, 1.0);

vec3 MaterialKs = vec3(1.0, 1.0, 1.0);
float MaterialShininess = 1000.0;

layout (location = 0) out vec4 FragColor;

vec3 phongModelDiffAndSpec(bool do_specular)
{
    vec3 n = Normal;
    vec3 s = normalize(vec3(LightPosition.xyz) - Position);
    vec3 v = normalize(-Position.xyz);
    vec3 r = reflect( -s, n );
    float sDotN = max( dot(s,n), 0.0 );
    vec3 diffuse = LightIntensity * MaterialKd * sDotN;
    vec3 spec = vec3(0.0);

    if( sDotN > 0.0 )
    {
        spec.x = pow( max( dot(r,v), 0.0 ), MaterialShininess );
        spec.y = pow( max( dot(r,v), 0.0 ), MaterialShininess );
        spec.z = pow( max( dot(r,v), 0.0 ), MaterialShininess );
    }

    vec3 n2 = Normal;
    vec3 s2 = normalize(vec3(-LightPosition) - Position);
    vec3 v2 = normalize(-Position.xyz);
    vec3 r2 = reflect( -s2, n2 );
    float sDotN2 = max( dot(s2,n2)*0.5f, 0.0 );
    vec3 diffuse2 = LightIntensity*0.25 * MaterialKd * sDotN2;

    vec3 ret = diffuse + diffuse2;

    if(do_specular)
        ret = ret + spec;
    
    return ret;
}

subroutine void RenderPassType();
subroutine uniform RenderPassType RenderPass;

subroutine (RenderPassType)
void shadeWithShadow()
{
    float shadow = 1.0;

    if( ShadowCoord.z >= 0.0 )
    {
        shadow = textureProj(shadow_map, ShadowCoord);

        vec3 n = normalize(Normal);
        vec3 n2 = normalize(LightPosition.xyz);
        float dp = dot(n, n2);

        if(dp < 0.0)
        {
            shadow = 1.0;
        }
        else
        {
            if(shadow == 0.0)
                shadow = 1.0 - dp;
        }
    }
    
    vec3 diffAndSpec;
    
    if(shadow == 1.0)
        diffAndSpec = phongModelDiffAndSpec(true);
    else
        diffAndSpec = phongModelDiffAndSpec(false);

    // If the fragment is in shadow, use ambient light only.
    FragColor = vec4(diffAndSpec * shadow + MaterialKa, 1.0);

    // Gamma correct
    FragColor = pow( FragColor, vec4(1.0 / 2.2) );
}

subroutine (RenderPassType)
void recordDepth()
{
    // Do nothing, depth will be written automatically
}

void main() {
    // This will call either shadeWithShadow or recordDepth
    RenderPass();
}

I added in ambient light, so that the shadows have a blue colouring, like in real life.

#version 430

uniform sampler2DShadow shadow_map;

in vec3 Position;
in vec3 Normal;
in vec4 ShadowCoord;

uniform vec4 LightPosition; // in view space
uniform vec4 LightPosition_Untransformed; // in world space

vec3 LightIntensity = vec3(1.0, 1.0, 1.0);

uniform vec3 MaterialKd = vec3(1.0, 1.0, 1.0);
vec3 MaterialKs = vec3(1.0, 1.0, 1.0);
vec3 MaterialKa = vec3(0.0, 0.025, 0.075);
float MaterialShininess = 100.0;

layout (location = 0) out vec4 FragColor;

vec3 phongModelDiffAndSpec(bool do_specular)
{
    vec3 n = Normal;
    vec3 s = normalize(vec3(LightPosition.xyz) - Position);
    vec3 v = normalize(-Position.xyz);
    vec3 r = reflect( -s, n );
    float sDotN = max( dot(s,n), 0.0 );
    vec3 diffuse = LightIntensity * MaterialKd * sDotN;
    vec3 spec = vec3(0.0);

    if( sDotN > 0.0 )
    {
        spec.x = pow( max( dot(r,v), 0.0 ), MaterialShininess );
        spec.y = pow( max( dot(r,v), 0.0 ), MaterialShininess );
        spec.z = pow( max( dot(r,v), 0.0 ), MaterialShininess );
    }

    vec3 n2 = Normal;
    vec3 s2 = normalize(vec3(-LightPosition) - Position);
    vec3 v2 = normalize(-Position.xyz);
    vec3 r2 = reflect( -s2, n2 );
    float sDotN2 = max( dot(s2,n2)*0.5f, 0.0 );
    vec3 diffuse2 = LightIntensity*0.25 * MaterialKd * sDotN2;

    // Only use ambient light on the backside of the object
    vec3 ret = diffuse + diffuse2 + MaterialKa*(1.0 - sDotN)/2.0;

    if(do_specular)
        ret = ret + spec;
    
    return ret;
}

subroutine void RenderPassType();
subroutine uniform RenderPassType RenderPass;

subroutine (RenderPassType)
void shadeWithShadow()
{
    float shadow = 1.0;

    if( ShadowCoord.z >= 0.0 )
    {
        shadow = textureProj(shadow_map, ShadowCoord);

        vec3 n = normalize(Normal);
        vec3 n2 = normalize(LightPosition.xyz);
        float dp = dot(n, n2);

        if(dp <= 0.0)
        {
            shadow = 1.0;
        }
        else
        {
            if(shadow == 0.0)
                shadow = 1.0 - dp;
        }
    }
    
    vec3 diffAndSpec;
    
    if(shadow == 1.0)
    {
        diffAndSpec = phongModelDiffAndSpec(true);
        FragColor = vec4(diffAndSpec, 1.0);

    }
    else
    {
        diffAndSpec = phongModelDiffAndSpec(false);
        FragColor = vec4(diffAndSpec * shadow + (1.0 - pow(shadow, 2.0))*MaterialKa*2.0, 1.0);
    }
  


    // Gamma correct
    FragColor = pow( FragColor, vec4(1.0 / 2.2) );
}

subroutine (RenderPassType)
void recordDepth()
{
    // Do nothing, depth will be written automatically
}

void main() {
    // This will call either shadeWithShadow or recordDepth
    RenderPass();
}
Gray shadow, gray lit side, gray back side.
Blue shadow, gray lit side, blue back side.

BTW… the shadow mapping code that I am using is from Opengl 4 Shading Language Cookbook by Wolff. Great book!

Hi @ddlox – Thanks for the debugging tip. Attached is a 1024x1024 shadow map. Is it what you were expecting?

well done on rendering it to a texture; so it looks like your scene is very lit ? is that what u expect?

In this shadow map picture here (seen from the viewer's perspective not from the light):

bunny asks dragon, ‘dear sir, which way to Game o’ Thrones?', but dragon was actually hungry…

the white colour is where the light is coming from and the black colour is where light isn't reaching, so the "grayscale"-like values are light attenuation and shadow values;

so when i look at your shadow map scene the white value is almost everywhere (seems like this could be your ambient values) and the gray objects in the middle appear to have only 1 gray value which means that these objects surfaces/triangles are all at the same distance from the light?… this seems a bit odd because your coloured screenshots show good attenuation of shades and colour?

so if this is what u expect then ok (maybe u changed something in your scene -lol-), if not then u need to check your render pipeline where u render your shadow values to texture (when u render to scene it's ok), but your shadow map texture seems to speak otherwise;

so maybe you should double-check again ?

Yep, well lit. The light is at camera.eye + camera.up + camera.left, so it's fixed with regard to the position and orientation of the camera… like a headlamp.

I wasn't sure what to expect. ? Thanks again for your time and patience.

Another shot:

lovely ?

This topic is closed to new replies.

Advertisement