HLSL Variance shadow mapping

Started by
1 comment, last by Cineska 15 years, 3 months ago
hi, our engine is using deferred shading atm and i am implementing variance shadow maps. At the moment i do the following: -> render depth map -> blur depth map -> set to the shader -> apply light calculations somehow the scene is not shadowing and i have no clue why. I have broken down the shader to only output the chevby's calculations, but they always result in 'lit' as it appears. I was wondering if you guys had any clue of what might go wrong. I removed the blur part for now since that only makes more possible error spots. the shader for depth map:
      
//Our shader's global variables. We have 2 different output structures, 
//one for the DepthMap technique, and one for the Scene technique, 
//this is because they both require different values (HLSL requires that all
//values of an output structure be set).

struct VS_INPUT
{
    float4 Position         : POSITION;
    float2 TexCoords        : TEXCOORD0;
};

struct DEPTH_VS_OUTPUT
{
    float4 Position         : POSITION;
    float4 WorldPosition    : TEXCOORD1;
};

struct SCENE_VS_OUTPUT
{
    float4 Position         : POSITION;
    float2 TexCoords        : TEXCOORD0;
    float4 WorldPosition    : TEXCOORD1;
    
    // ShadowProjection is similar to Position, except its the position 
    // from the light's view, not the camera. We need both to be able to project
    // our shadow map.
    float4 ShadowProjection : TEXCOORD2; 
                                         
                                         
};

shared float4x4 viewProjection;
shared float4x4 view;
float4x4 world;

// The position of our light
shared float3 cameraPosition;

// The far clip distance of our light's view matrix. 
//We use this value to normalise our distances 
//(so that they fit into the 0.0 - 1.0 colour range in HLSL)
float farClip = 1000;


//Depth Map Pass
//==============
//This part of the shader is used to render the scene from the view of our spot light.
//It's just like a normal shader, except instead of colouring the models with a texture,
//it gives the pixels a colour based on their distance from the light.


DEPTH_VS_OUTPUT Depth_VS (VS_INPUT Input)
{
    DEPTH_VS_OUTPUT Output;
    
    // Our standard WVP multiply, using the light's 
    //View and Projection matrices
    Output.Position = mul(float4(Input.Position.xyz, 1.0f), mul(world, viewProjection));

    // We also keep a copy of our position that is only 
    //multiplied by the world matrix. This is
    // because we do not want the camera's angle/position to 
    //affect our distance calculations
    Output.WorldPosition = mul(float4(Input.Position.xyz, 1.0f), world);
    //Output.WorldPosition /= Output.WorldPosition.w;
    
    return Output;
}

float4 Depth_PS (DEPTH_VS_OUTPUT Input) : COLOR0
{
    // Get the distance from this pixel to the light, 
    //we divide by the far clip of the light so that it 
    //will fit into the 0.0-1.0 range of pixel shader colours
    float depth = length(cameraPosition - Input.WorldPosition) / farClip;
    
    return float4(depth, depth*depth, 0, 0);
}

technique DepthMap
{
    pass P0
    {
        VertexShader = compile vs_2_0 Depth_VS();
        PixelShader  = compile ps_2_0 Depth_PS();
    }
}
where farclip = the range of the spotlight and the texture is R32G32B32A32. spotlight shader:

float4x4 world;
shared float4x4 view;
shared float4x4 projection;

float4x4 lightViewProjection;

//color of the light 
float3 diffuseColor; 

//position of the camera, for specular light
shared float3 cameraPosition;

//this is used to compute the world-position
shared float4x4 invertViewProjection; 

//this is the position of the light
float3 lightPosition;

//how far does this light reach
float lightRadius;

float3 lightDirection;

float lightIntensity;

float lightDecayExponent;

float lightAngleCosine;

// diffuse color, and specularIntensity in the alpha channel
texture colorMap : ColorMap;  
// normals, and specularPower in the alpha channel
texture normalMap : NormalMap;
//depth
texture depthMap : DepthTexture;

texture shadowMap : ShadowMap;

Texture shadowMapMask : ShadowMapMask;

sampler colorSampler = sampler_state
{
    Texture = (colorMap);
    AddressU = CLAMP;
    AddressV = CLAMP;
    MagFilter = LINEAR;
    MinFilter = LINEAR;
    Mipfilter = LINEAR;
};
sampler depthSampler = sampler_state
{
    Texture = (depthMap);
    AddressU = CLAMP;
    AddressV = CLAMP;
    MagFilter = POINT;
    MinFilter = POINT;
    Mipfilter = POINT;
};
sampler normalSampler = sampler_state
{
    Texture = (normalMap);
    AddressU = CLAMP;
    AddressV = CLAMP;
    MagFilter = POINT;
    MinFilter = POINT;
    Mipfilter = POINT;
};

sampler shadowSampler = sampler_state
{
    Texture = <shadowMap>;
    
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
    
    AddressU = CLAMP;
    AddressV = CLAMP;
};

sampler maskSampler = sampler_state
{
    Texture = <shadowMapMask>;
    
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
    
    AddressU = CLAMP;
    AddressV = CLAMP;
};

struct VertexShaderInput
{
    float3 Position : POSITION0;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float4 ScreenPosition : TEXCOORD0;
};

float    g_VSMMinVariance = 0.000001;         // Minimum variance for VSM

float ChebyshevUpperBound(float2 Moments, float Mean, float g_MinVariance)  
{  
    // One-tailed inequality valid if Mean > Moments.x
    float p = (Mean <= Moments.x);

    // Compute variance.
    float Variance = Moments.y - (Moments.x*Moments.x);
    Variance = max(Variance, g_MinVariance);

    //Compute probabilistic upper bound
    float d = Moments.x - Mean;
    float p_max = Variance / (Variance + d * d); 
    //return Mean * p;
    //return p_max;
    return max(p, p_max);
};
 
float ShadowContribution(float2 LightTexCoord, float DistanceToLight)  
{  
    // Read the moments from the variance shadow map.  
    //float2 Moments = texShadow.Sample(ShadowSampler, LightTexCoord).xy; 
    float2 moments = tex2D(shadowSampler, LightTexCoord).xy;
    // Compute the Chebyshev upper bound.  
    return ChebyshevUpperBound(moments, DistanceToLight, g_VSMMinVariance);  
};

float linstep(float min, float max, float v)  
{  
    return clamp((v - min) / (max - min), 0, 1);  
};

float ReduceLightBleeding(float p_max, float Amount)  
{  
    // Remove the [0, Amount] tail and linearly rescale (Amount, 1].  
    return linstep(Amount, 1, p_max);  
}; 

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;
    //processing geometry coordinates
    float4 worldPosition = mul(float4(input.Position,1), world);
    float4 viewPosition = mul(worldPosition, view);
    output.Position = mul(viewPosition, projection);

    //output.Normal = mul(input.Normal, World);

    output.ScreenPosition = output.Position;

    return output;
}

float2 halfPixel : HalfPixel;
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    //obtain screen position
    input.ScreenPosition.xy /= input.ScreenPosition.w;

    //obtain textureCoordinates corresponding to the current pixel
    //the screen coordinates are in [-1,1]*[1,-1]
    //the texture coordinates need to be in [0,1]*[0,1]
    float2 texCoord = 0.5f * (float2(input.ScreenPosition.x,-input.ScreenPosition.y) + 1);
    //allign texels to pixels
    texCoord -=halfPixel;

    //read depth
    float depthVal = tex2D(depthSampler,texCoord).r;

    //compute screen-space position
    float4 position;
    position.xy = input.ScreenPosition.xy;
    position.z = depthVal;
    position.w = 1.0f;
    //transform to world space
    position = mul(position, invertViewProjection);
    position /= position.w;


    //surface-to-light vector
    float3 lightVector = lightPosition - position;

    //compute attenuation based on distance - linear attenuation
    float attenuation = saturate(1.0f - length(lightVector)/lightRadius); 

    //normalize light vector
    lightVector = normalize(lightVector); 

    float SdL = dot(lightDirection, -lightVector);

    if(SdL > lightAngleCosine)
    {
        float2 projectedTexCoords;

        float4 ShadowProjection = mul(position, lightViewProjection);

        projectedTexCoords[0] = (ShadowProjection.x / 
                                 ShadowProjection.w / 2.0f) + 0.5f;
        projectedTexCoords[1] = (-ShadowProjection.y / 
                                  ShadowProjection.w / 2.0f) + 0.5f;

        //////// for shadow
        float len = length(lightPosition - position) / lightRadius;
   
        float shadow = ShadowContribution(projectedTexCoords, len);

            float spotIntensity = pow(SdL, lightDecayExponent);

            //get normal data from the normalMap
            float4 normalData = tex2D(normalSampler,texCoord);
            //tranform normal back into [-1,1] range
            float3 normal = 2.0f * normalData.xyz - 1.0f;
            //get specular power
            float specularPower = normalData.a * 255;
            //get specular intensity from the colorMap
            float specularIntensity = tex2D(colorSampler, texCoord).a;

            //compute diffuse light
            float NdL = max(0,dot(normal,lightVector));
            float3 diffuseLight = NdL * diffuseColor.rgb;

            //reflection vector
            float3 reflectionVector = normalize(reflect(-lightVector, normal));
            //camera-to-surface vector
            float3 directionToCamera = normalize(cameraPosition - position);
            //compute specular light

            float specularLight = specularIntensity * pow( saturate(dot(reflectionVector, directionToCamera)), specularPower);

            attenuation *= spotIntensity;

            //take into account attenuation and lightIntensity.
            //return (lightIntensity * float4(diffuseLight.rgb,specularLight) * attenuation) * shadow;
            return shadow; // * float4(diffuseLight.rgb, specularLight) * attenuation * lightIntensity;

        }

    return float4(0,0,0,0);
}

technique Technique1
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_a PixelShaderFunction();
    }
}

";
where radius = the same as the farclip in depth map rendering and the shadowSampler is the blurred depth map. does anyone have any clue? the shader codes are partly from GPU Gems 3 and an xna article on ziggyware.com
Advertisement
I just had a click look over your code and it looks okay...have you used PIX to look at your RenderTargets and debug your shaders? I would first check to make sure you're getting the right values in the depth buffer, and then if that's okay I'd step though your lighting shader to see where things are going wrong.
VSM was a pain to get it to work. (At least for me)

The problem was in the depth map. With proper rescaling it worked flawlessly, so give this a try.

This topic is closed to new replies.

Advertisement