Sign in to follow this  
Keba

Shadow map problem

Recommended Posts

So, i implemeted a shadow map algortihm (variance) but are not quite satisfied as you can se in this picture. in the left corner one could se the shadow map which at the moment is not blured/filtered In the picture, the box nearest the plane should have a "stronger" shadow, and the above box should have a weaker one. the light is placed at origio of the plane pointing straight down. Here is my hlsl shader(its just a rewrite of the "famous" one from the papper):
[Source]

float4x4 g_fWorldViewProjection			: MATRIX_WORLDVIEWPROJECTION;
float4x4 g_fWorldView				: MATRIX_WORLDVIEW;
float4x4 g_fWorld				: MATRIX_WORLD;
float4x4 g_fLightView				: MATRIX_LIGHTVIEW;
float4x4 g_fLightViewInverse			: MATRIX_LIGHTVIEWINVERSE;
float4x4 g_fLightProjection			: MATRIX_LIGHTPROJECTION;

float3  g_fLightPosition			: LIGHT_POSITION;		
float4	g_fLightDirection			: LIGHT_DIRECTION;
float	g_fLightAngleAttenBegin 		: LIGHT_ANGLE_ATTENBEGIN; 
float	g_fLightAngleAttenEnd			: LIGHT_ANGLE_ATTENEND;	
float	g_fLightAttenBegin			: LIGHT_ATTEN_BEGIN;	
float	g_fLightAttenEnd			: LIGHT_ATTEN_END;	
float	g_fLightVSMEpsilon			: LIGHT_VSMEPSILON;	// = 0.0001;
float	g_fLightShadowBias			: LIGHT_SHADOWBIAS;		//= 0.001f;

float4	g_fLightColor = {1.0f,1.0f,1.0f,1.0f};
float4  g_fDiffuseColor = {1.0f, 1.0f, 1.0f,1.0f};


static float2 g_fLightAngleAtten = float2(g_fLightAngleAttenBegin, g_fLightAngleAttenEnd) * 0.5;
static float2 g_fCosLightAngleAtten = cos(g_fLightAngleAtten);

///////////////////////////////////////////////////////////////////////////////
// Textures/samplers and Render Targets
///////////////////////////////////////////////////////////////////////////////

// texture ShadowRenderTarget

sampler2D Sampler0: s0;



//////////////////////////////////////////////////////////////////////////////
// Depth Pass Shaders
//////////////////////////////////////////////////////////////////////////////


struct VsInput_VSMDepth
{
	float4 fPosition : POSITION;	
	
};

struct VsOutput_VSMDepth
{
	float4 fScreenPosition	 : POSITION;
	float3 fLightVector		 : TEXCOORD0;
};

VsOutput_VSMDepth Vs_VSMDepth(VsInput_VSMDepth InData,	uniform float4x4 ShadowView,uniform float4x4 ShadowViewProjection)
{
    VsOutput_VSMDepth Out   = (VsOutput_VSMDepth)0;
	
    float4 WorldPos		= mul(InData.fPosition, g_fWorld);
    Out.fScreenPosition		= mul(WorldPos, ShadowViewProjection);
    Out.fLightVector		= mul(WorldPos, ShadowView).xyz;
    
    return Out;
}

float4 Ps_VSMDepth(VsOutput_VSMDepth InData): COLOR
{
	// Work out the depth of this fragment from the light, normalized to 0->1
	float2 fDepth;
	fDepth.x = length(InData.fLightVector) / g_fLightAttenEnd;
    	fDepth.y = fDepth.x * fDepth.x;
    
	return fDepth.xyxy;
}


//////////////////////////////////////////////////////////////////////////////
// Lighting Pass Shaders
//////////////////////////////////////////////////////////////////////////////


struct VsInput_VSMLight
{
	float4 fPosition : POSITION;
	float3 fNormal   : NORMAL;
};

struct VsOutput_VSMLight
{
	float4 fScreenPosition : POSITION;
	float3 fWorldPosition  : TEXCOORD0;
	float3 fWorldNormal    : TEXCOORD1;
};

VsOutput_VSMLight Vs_VSMLight(VsInput_VSMLight InData,uniform float4x4 Shadow)
{      
    VsOutput_VSMLight Out = (VsOutput_VSMLight)0;
    
    Out.fScreenPosition  = mul(InData.fPosition, g_fWorldViewProjection);
    Out.fWorldPosition   = mul(InData.fPosition, g_fWorld).xyz;
    Out.fWorldNormal     = mul(float4(InData.fNormal, 0), g_fWorld).xyz;
    
    return Out;
}


float4 Ps_VSMLight(VsOutput_VSMLight InData,uniform float4x4 LightViewProjection): COLOR
{

  
  	// Sum the contributions from all lights
   	float3 LitColor = float3(0, 0, 0);
      
  	// Light Shader:
  	float3 fLightContribution	= {0,0,0};
  	float3 fDirToLight			= {0,0,0};
  	float  fDistToLight			= 0;
  	float  NdotL				= 0;
  	
  	{  		  	
    	// Unnormalized light vector
    	fDirToLight	 = g_fLightPosition - InData.fWorldPosition;
		fDistToLight = length(fDirToLight);
		float		   fAttenAmount = clamp((fDistToLight   - g_fLightAttenBegin) / (g_fLightAttenEnd - g_fLightAttenBegin), 0.0, 1.0);
        							
    	// Radial attenuation	
    	fDirToLight = normalize(fDirToLight);    	
    	float2		fCosAngleAtten		= g_fCosLightAngleAtten;
    	float		CosAngle			= dot(-fDirToLight, g_fLightDirection);
    	float		fAngleAttenAmount	= clamp((CosAngle - fCosAngleAtten.x) / (fCosAngleAtten.y - fCosAngleAtten.x), 0.0, 1.0);
            	   
    	// Compose the light shader outputs
    	fLightContribution = (1.0 - fAttenAmount) * (1.0 - fAngleAttenAmount) * g_fLightColor;
    	NdotL			   = dot(InData.fWorldNormal, fDirToLight);
  	}
  	
  	// Variance Shadow Mapping:
  	{
  		// Transform the surface into light space and project
  		// NB: Could be done in the vertex shader, but doing it here keeps the "light
  		// shader" abstraction and doesn't limit # of shadowed lights.
  		float4 fSurfTex = mul(float4(InData.fWorldPosition, 1.0), LightViewProjection);
    	fSurfTex = fSurfTex / fSurfTex.w;
    	
    	// Rescale viewport to be [0,1] (texture coordinate space)
    	float2 fShadowTex = fSurfTex.xy * float2(0.5, -0.5) + 0.5;
	
    	float4 fMoments;
      	fMoments = tex2D(Sampler0, fShadowTex);
	
    	// Rescale light distance and check if we're in shadow
    	float fRescaledDistToLight = fDistToLight / g_fLightAttenEnd;
    	
    	fRescaledDistToLight = fRescaledDistToLight - g_fLightShadowBias;
    	
    	float fLitFactor = (fRescaledDistToLight <= fMoments.x);

    	// Variance shadow mapping
     	float E_x2 = fMoments.y;
    	float Ex_2 = fMoments.x * fMoments.x;
    	float fVariance = min(max(E_x2 - Ex_2, 0.0) + g_fLightVSMEpsilon, 1.0);
    	float m_d = (fMoments.x - fRescaledDistToLight);
    	float p = fVariance / (fVariance + m_d * m_d);
    	
    	// Adjust the light color based on the shadow attenuation
    	fLightContribution *= max(fLitFactor,p);
  	}
      
  	// Evaluate basic diffuse lighting
  	float fSelfshadow = clamp(NdotL * 2.0, 0.0, 1.0);
  	
  	float3 DirectContribution = g_fDiffuseColor * NdotL;
  	
  	LitColor += fLightContribution * fSelfshadow * DirectContribution;

        return float4(LitColor, 1.0);	
}


//////////////////////////////////////////////////////////////////////////////
// Techniques
//////////////////////////////////////////////////////////////////////////////

technique ShadowMapRender
{
	pass p0
	{
		CullMode = None;
		
		ZEnable = true;
		ZWriteEnable = true;
		ZFunc = LessEqual;				
		VertexShader = compile vs_3_0 Vs_VSMDepth(g_fLightView, mul(g_fLightView, g_fLightProjection));
		PixelShader = compile ps_3_0 Ps_VSMDepth();
	}
}

technique SceneRender
{
    pass p0 
    {
  
                ZEnable = true;
		ZWriteEnable = true;
		ZFunc = LessEqual;
		CullMode = None;
		
		AddressU[0] = CLAMP;
                AddressV[0] = CLAMP; 
         
		  
	        VertexShader = compile vs_3_0 Vs_VSMLight(g_fLightViewInverse);
		PixelShader = compile ps_3_0 Ps_VSMLight(mul(g_fLightView, g_fLightProjection));
    }
}


[/Source]
its seems like the 'p' var is gettin the "wrong" value, it looks the same as the shadowmap, which IMO is wrong... and another thing:
[Source]
     	float E_x2 = fMoments.y;
    	float Ex_2 = fMoments.x * fMoments.x;
[/Source]
fMoments is sampled from the shadow map, and in that shadow map the x value is the distance from the light to a fragment in world space(scaled by some nice value), and the y value is squared x value. wouldn't the above code be the same as:
[Source]
	float E_x2 = fMoments.y;
    	float Ex_2 = fMoments.y;
[/Source]
E_x2 and Ex_2 is nearly the same when debugging the shader. Which dosen't really make sense to me....could someone explain this for me?

Share this post


Link to post
Share on other sites
No one can answer this? come on....it can't be that hard?
anyway, The creation of the shadow map:



VsOutput_VSMDepth Vs_VSMDepth(VsInput_VSMDepth InData, uniform float4x4 ShadowView,uniform float4x4 ShadowViewProjection)
{
VsOutput_VSMDepth Out = (VsOutput_VSMDepth)0;

float4 WorldPos = mul(InData.fPosition, g_fWorld);
Out.fScreenPosition = mul(WorldPos, ShadowViewProjection);
Out.fLightVector = mul(WorldPos, ShadowView).xyz;

return Out;
}

float4 Ps_VSMDepth(VsOutput_VSMDepth InData): COLOR
{
// Work out the depth of this fragment from the light, normalized to 0->1
float2 fDepth;
fDepth.x = length(InData.fLightVector) / g_fLightAttenEnd;
fDepth.y = fDepth.x * fDepth.x;

return fDepth.xyxy;
}





seems fine, one could se that on the above picture, the cube which is closets to the source got a stronger purple color, since the distance is smaller, and the cube with a longer distance got a smaller purple value, and so on.
The projection of the current rendered pixel into the shadow map is also correct(i think) since the shadows is appearing where they should. if the projection was wrong the shadows would start to shown on random places....
The spotlight code also works, as one can se on the picture, the plane is lightened up with a round "spotlightish light"....

so i have concentrated on this part of the code:



float4 fMoments;
fMoments = tex2D(Sampler0, fShadowTex);

// Rescale light distance and check if we're in shadow
float fRescaledDistToLight = fDistToLight / g_fLightAttenEnd;

fRescaledDistToLight = fRescaledDistToLight - g_fLightShadowBias;

float fLitFactor = (fRescaledDistToLight <= fMoments.x);

// Variance shadow mapping
float E_x2 = fMoments.y;
float Ex_2 = fMoments.x * fMoments.x;
float fVariance = min(max(E_x2 - Ex_2, 0.0) + g_fLightVSMEpsilon, 1.0);
float m_d = (fMoments.x - fRescaledDistToLight);
float p = fVariance / (fVariance + m_d * m_d);

// Adjust the light color based on the shadow attenuation
fLightContribution *= max(fLitFactor,p);
}

// Evaluate basic diffuse lighting
float fSelfshadow = clamp(NdotL * 2.0, 0.0, 1.0);

float3 DirectContribution = g_fDiffuseColor * NdotL;

LitColor += fLightContribution * fSelfshadow * DirectContribution;






when the distance from the current rendering pixel in world space is larger than the moments(or distance and square distance) read from the shadow map, the current pixel is in shadows(fLitFactor), and the value of 'p' is calculated as the "shadow value", something gets wrong in this calculation, i think, the strength of the shadow is somehow inverted.
It looks exactly as the shadow map, like the shadow that was calculated in 'p' is the same as the distances values in the shadow map, and the geometry with the lowest distance to the source gets the strongest shadow on the plane, and the geometry with the shortest distances to the source gets the weaker shadow on the plane.
I found this incorrect, what should i do to fix this? and what i'm i doing wrong?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this