7
\$\begingroup\$

I'm trying to port an XNA example to OpenTK + OpenGL, I found it here http://graphicsrunner.blogspot.com.es/2008/07/dual-paraboloid-shadow-maps.html

But I'm having a really hard time, I think my maps are generated right, but then the shadowing of the objects gets really weird, it's like some axes are inverted, when the light is under the shadow receiver plane it shows the shadows but they're also displaced.

I believe the shader code is right and the problem is setting up the matrices but I'm really lost, I've tried everything that came to my mind but with no luck.

Here are the two maps, they will look dark or weird because I'm using RGBA textures and pack the depth in them, so the data is right and the position looks okay:

https://i.stack.imgur.com/PEtYV.png

https://i.stack.imgur.com/ISdMW.png

Let's start with the code.

First the light view, I set it up like this:

lightView = 
Matrix4.LookAt(light.lightPosition, light.lightPosition - Vector3.UnitZ, Vector3.UnitY);

To the map creation shader I pass the object's world multiplied with the light view.

This is the vertex shader for the map creation:

#version 120

uniform mat4 worldView;
uniform float direction;

attribute vec3 position; //usage:position
attribute vec3 normal; //usage:normal

varying highp vec2 depth;

varying float clip;

const float near = 0.1f;
const float far = 1000.0f;

void main()
{

    highp vec4 pos4 = worldView * vec4(position, 1);


    pos4 = pos4 / pos4.w;
    pos4.z = pos4.z * direction;

    highp float L = length(pos4.xyz);
    pos4 = pos4 / L;

    clip = pos4.z;
    pos4.z = pos4.z + 1;

    pos4.x = pos4.x / pos4.z;
    pos4.y = pos4.y / pos4.z;

    pos4.z = (L - near) / (far - near);
    pos4.w = 1;

    depth = pos4.zw;

    gl_Position = pos4;

}

And this is the fragment shader:

 #version 120

varying highp vec2 depth;
varying float clip;

vec4 mapDepthToARGB32(float value)
{
    const vec4 bitSh = vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0);
    const vec4 mask = vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
    highp vec4 res = fract(value * bitSh);
    res -= res.xxyz * mask;
    return res;
}


void main()
{

    if(clip < 0)
        discard;

    float finalDepth = depth.x / depth.y;
    gl_FragColor = mapDepthToARGB32(finalDepth); //vec4(depth.y, depth.y, depth.y, 1.0f);

}

Next, to render the shadows I pass the light view matrix and the object's world matrix to the shader unmultiplied.

Here is the vertex shader (it includes multitexturing as I'm expanding a terrain shader I already have working)

#version 120

uniform highp vec3 lightPosition;

uniform mat4 worldViewProjection;
uniform mat4 world;

attribute vec3 position; //usage:position
attribute vec2 uv; //usage:uv
attribute vec3 normal; //usage:normal

varying highp vec3 faceNormal;
varying highp vec3 lightNormal;
varying highp vec2 finalUv;
varying highp vec3 pos;

void main()
{

    vec4 pos4 = vec4(position, 1);
    pos = (world * pos4).xyz;

    gl_Position = worldViewProjection * pos4;

    lightNormal = lightPosition - pos;
    faceNormal = (world * vec4(normal , 0)).xyz;

    finalUv = uv;

}

And finally the fragment shader:

#version 120

uniform highp vec3 lightColor;
uniform highp vec3 ambientColor;

uniform mat4 lightView;

uniform highp float uvMultiplier;

uniform sampler2D colorTexture1;
uniform sampler2D colorTexture2;
uniform sampler2D colorTexture3;

uniform sampler2D weightTexture;

uniform sampler2D frontShadowMap;
uniform sampler2D backShadowMap;

varying highp vec3 faceNormal;
varying highp vec3 lightNormal;
varying highp vec2 finalUv;
varying highp vec3 pos;

const float SHADOW_EPSILON = 0.00005f;

float getDepthFromARGB32(vec4 value)
{
    const vec4 bitSh = vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0);
    return(dot(value, bitSh));
}

const float near = 0.1f;
const float far = 1000;

void main()
{

    highp float factor = clamp(dot(normalize(lightNormal), normalize(faceNormal)), 0.0, 1.0);   

    highp vec3 finalColor = vec3(0,0,0);

    lowp vec4 weights = texture2D(weightTexture, finalUv);

    finalColor += texture2D(colorTexture1, finalUv * uvMultiplier).xyz * weights.x;
    finalColor += texture2D(colorTexture2, finalUv * uvMultiplier).xyz * weights.y;
    finalColor += texture2D(colorTexture3, finalUv * uvMultiplier).xyz * weights.z;

    finalColor = (finalColor * lightColor * factor) + (finalColor * ambientColor);

    highp vec3 vPosDP = (vec4(pos, 1) * lightView).xyz;

    float fLength = length(vPosDP);

     vPosDP /= fLength;

     float fDPDepth;
     float fSceneDepth;

     if(vPosDP.z >= 0.0f)
     {      
         vec2 vTexFront;
         vTexFront.x =  (vPosDP.x /  (1.0f + vPosDP.z)) * 0.5f + 0.5f; 
         vTexFront.y =  1.0f - ((vPosDP.y /  (1.0f + vPosDP.z)) * 0.5f + 0.5f);     

         fSceneDepth = (fLength - near) / (far - near);

         fDPDepth = getDepthFromARGB32(texture2D(frontShadowMap, vTexFront));           
     }
     else
     {
         // for the back the z has to be inverted       
         vec2 vTexBack;
         vTexBack.x =  (vPosDP.x /  (1.0f - vPosDP.z)) * 0.5f + 0.5f; 
         vTexBack.y =  1.0f - ((vPosDP.y /  (1.0f - vPosDP.z)) * 0.5f + 0.5f); 

         fSceneDepth = (fLength - near) / (far - near);

         fDPDepth = getDepthFromARGB32(texture2D(backShadowMap, vTexBack));
     }

    if((fDPDepth + SHADOW_EPSILON) < fSceneDepth)
        finalColor = finalColor * 0.3f;

    gl_FragColor = vec4(finalColor, 1);

}

I must say I'm packing the depth values into an RGBA texture because the devices I'm targeting don't support R32f nor R16f textures.

Any help would be really apreciated, and also, if someone knows of a working example on GLSL of the dual paraboloid shadow map I wold be really grateful, I searched for a fully working example a lot of time and the only ones I found are for DirectX.

EDIT:

Well, found a bug and the root of the problem.

First, the light view matrix multiplication is reversed, instead of

highp vec3 vPosDP = (vec4(pos, 1) * lightView).xyz;

it must be

highp vec3 vPosDP = (lightView * vec4(pos, 1)).xyz;

And the root of the problem, this code assumes the light is ALWAYS at 0,0,0, so, placing the light at 0,0,0 at first did not solved the problem, but as this code comes from DirectX and the operations are reversed, if instead of using pos we use -pos and place the light at 0,0,0 everything works, a nice shadows como to lofe :).

Now I'm struggling on how to apply the light displacement, adding it to the render shader did not work, the shadows got displaced, so I suppose I must modify also the map generation.

Any help will be welcomed. If I get it working will post an answer.

EDIT 2:

More advances, in my test scene had 8 shadow casters distributed equally around a center, I have changed it to be uneven and the shadows are exactly reversed.

I have checked the maps and they are exact to the ones generated by te example, so my lightView matrix and the map generation code must be OK, this leaves just a place to check and is the fragment shader.

\$\endgroup\$

1 Answer 1

7
\$\begingroup\$

Finally got it working!!!

The problem indeed was in the fragment shader.

First of all, there was no need to invert the position, the error came from the UV coordinates.

Finally I remembered when I was using XNA the textures where flipped vertically respect to OpenGL, and that was the problem.

After flipping the V coordinate and correcting the reversed multiplication everything works as expected.

This is the final code for the fragment shader in case someone else needs it:

#version 120

uniform highp vec3 lightColor;
uniform highp vec3 ambientColor;
uniform highp mat4 lightView;
uniform highp float uvMultiplier;

uniform sampler2D colorTexture1;
uniform sampler2D colorTexture2;
uniform sampler2D colorTexture3;

uniform sampler2D weightTexture;

uniform sampler2D frontShadowMap;
uniform sampler2D backShadowMap;

varying highp vec3 faceNormal;
varying highp vec3 lightNormal;
varying highp vec2 finalUv;
varying highp vec3 pos;

const float SHADOW_EPSILON = 0.000005f;

float getDepthFromARGB32(vec4 value)
{
    const vec4 bitSh = vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0);
    return(dot(value, bitSh));
}

const float near = 0.1f;
const float far = 1000;

void main()
{

    highp float factor = clamp(dot(normalize(lightNormal), normalize(faceNormal)), 0.0, 1.0);   

    highp vec3 finalColor = vec3(0,0,0);

    lowp vec4 weights = texture2D(weightTexture, finalUv);

    finalColor += texture2D(colorTexture1, finalUv * uvMultiplier).xyz * weights.x;
    finalColor += texture2D(colorTexture2, finalUv * uvMultiplier).xyz * weights.y;
    finalColor += texture2D(colorTexture3, finalUv * uvMultiplier).xyz * weights.z;

    finalColor = (finalColor * lightColor * factor) + (finalColor * ambientColor);

    highp vec3 vPosDP = (lightView * vec4(pos , 1)).xyz;

    float fLength = length(vPosDP);

        vPosDP /= fLength;

        float fDPDepth;
        float fSceneDepth;

        if(vPosDP.z >= 0.0f)
        {       
            vec2 vTexFront;
            vTexFront.x =  ((vPosDP.x /  (1.0f + vPosDP.z)) * 0.5f + 0.5f); 
            vTexFront.y =  ((vPosDP.y /  (1.0f + vPosDP.z)) * 0.5f + 0.5f);     


            fSceneDepth = (fLength - near) / (far - near);

            fDPDepth = getDepthFromARGB32(texture2D(frontShadowMap, vTexFront));            
        }
        else
        {
            // for the back the z has to be inverted        
            vec2 vTexBack;
            vTexBack.x =  ((vPosDP.x /  (1.0f - vPosDP.z)) * 0.5f + 0.5f); 
            vTexBack.y =  ((vPosDP.y /  (1.0f - vPosDP.z)) * 0.5f + 0.5f); 

            fSceneDepth = (fLength - near) / (far - near);

            fDPDepth = getDepthFromARGB32(texture2D(backShadowMap, vTexBack));
        }

    if((fDPDepth + SHADOW_EPSILON) < fSceneDepth)
        finalColor = finalColor * 0.3f;

    gl_FragColor = vec4(finalColor, 1);

}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .