Sign in to follow this  
Mercenarey

ShadowMap: How do I localize its effect?

Recommended Posts

Let us start with some fun and watch some pictures :) http://www.mercenaries.ws/shadowplane.jpg As can be seen from picture 1, everything looks normal when the light is coming directly from above. But as soon as the angle decreases, an effect pops up on the entire horizon - the area outside the shadowmap. At picture 4-5, artifacts begin to show, when the angle gets really low (however, that isn't the main point with this post). I altered the DX sample "ShadowMap" to use directional light instead of spotlight. With the spotlight, the shader would just paint anything outside the spotlight theta with the standard ambient color (pretty dark). I did, however, disable this code. But when I had got everything else to work, I tried to get rid of this problem - I simply measured the distance from a point to the light point (just some point I defined, though directional light doesn't have a point as such), and then I only drew what the distance could "reach". The effect is much the same as what the spotlight did: It only rendered what was inside the theta. Isn't there a way in which I can test what point lies inside the textures area of influence? I made some feeble attempts at making a conditional around the variable "texelpos" (see shader code below), but with no success. I know that I could be better at this shadowing stuff, and Im sure it will come. But please help me for now :) Pixel Shader (modified from the DX sample):
float4 PixScene(	float2 Tex : TEXCOORD0,
					float4 vPos : TEXCOORD1,
					float3 vNormal : TEXCOORD2,
					float4 vPosLight : TEXCOORD3 ) : COLOR
{
	float4 Diffuse;

	// vLight is the unit vector from the light to this pixel
//	float3 vLight = normalize( float3( vPos - g_vLightPos ) );
	float3 vLight = normalize(g_vLightDir);

	// Compute diffuse from the light
/*	if( dot( vLight, g_vLightDir ) > g_fCosTheta ) // Light must face the pixel (within Theta)
	{
*/
//if(distance(g_vLightPos, vPos) < 30.0f) // this if/else code will limit the drawing on the horizon
//{
		// Pixel is in lit area. Find out if it's
		// in shadow using 2x2 percentage closest filtering

		//transform from RT space to texture space.
		float2 ShadowTexC = 0.5 * vPosLight.xy / vPosLight.w + float2( 0.5, 0.5 );
		ShadowTexC.y = 1.0f - ShadowTexC.y;

// ************
// would it be possible to maybe use this variable (texelpos) to test if the vPos is in the area of influence?
// ************
		// transform to texel space
		float2 texelpos = SMAP_SIZE * ShadowTexC;
// ************
	    
		// Determine the lerp amounts           
		float2 lerps = frac( texelpos );

		//read in bilerp stamp, doing the shadow checks
		float sourcevals[4];
		sourcevals[0] = (tex2D( g_samShadow, ShadowTexC ) + SHADOW_EPSILON < vPosLight.z / vPosLight.w)? 0.0f: 1.0f;  
		sourcevals[1] = (tex2D( g_samShadow, ShadowTexC + float2(1.0/SMAP_SIZE, 0) ) + SHADOW_EPSILON < vPosLight.z / vPosLight.w)? 0.0f: 1.0f;  
		sourcevals[2] = (tex2D( g_samShadow, ShadowTexC + float2(0, 1.0/SMAP_SIZE) ) + SHADOW_EPSILON < vPosLight.z / vPosLight.w)? 0.0f: 1.0f;  
		sourcevals[3] = (tex2D( g_samShadow, ShadowTexC + float2(1.0/SMAP_SIZE, 1.0/SMAP_SIZE) ) + SHADOW_EPSILON < vPosLight.z / vPosLight.w)? 0.0f: 1.0f;  
	    
		// lerp between the shadow values to calculate our light amount
		float LightAmount = lerp( lerp( sourcevals[0], sourcevals[1], lerps.x ),
									lerp( sourcevals[2], sourcevals[3], lerps.x ),
									lerps.y );
		// Light it
		Diffuse = ( saturate( dot( -vLight, normalize( vNormal ) ) ) * LightAmount * ( 1 - g_vLightAmbient ) + g_vLightAmbient )
					* g_vMaterial;
//}
//else // part of the "if(distance(..." above
//{
//	Diffuse = ( saturate( dot( -vLight, normalize( vNormal ) ) ) * 1.0f * ( 1 - g_vLightAmbient ) + g_vLightAmbient ) * g_vMaterial;
//}

	return tex2D( g_samScene, Tex ) * Diffuse;
}


Share this post


Link to post
Share on other sites
You could try to set the texture addressing states for your shadow map sampler g_samShadow to clamp. And when you create your shadowmap texture, make an empty border so that everything outside the shadow map will be seen as not shadowed.

pixel shader code:

sampler g_samShadow = sampler_state
{
Texture = <ShadowMap>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = None;
AddressU = Clamp;
AddressV = Clamp;
};



shadow map setup code snippet:


//set render target and depth stencil surface
if(FAILED(pD3DDevice->SetRenderTarget(0, m_pShadowMapColorSurface)))
{
return E_FAIL;
}
if(FAILED(pD3DDevice->SetDepthStencilSurface(m_pShadowMapZSurface)))
{
return E_FAIL;
}

// set viewport for shadow map
D3DVIEWPORT9 shadowmapViewport;
shadowmapViewport.X = 1;
shadowmapViewport.Y = 1;
shadowmapViewport.Width = SHADOWMAPSIZE-2;
shadowmapViewport.Height = SHADOWMAPSIZE-2;
shadowmapViewport.MinZ = 0.0f;
shadowmapViewport.MaxZ = 1.0f;
pD3DDevice->SetViewport(&shadowmapViewport);

// clear shadow map, draw shadow casters etc..




Good luck!

Share this post


Link to post
Share on other sites
hoho, I solved it!

The problem arose when the far view plane of my orthogonal matrix lost partial contact with the ground, and hung in the air. The depth would then be recorded as very close, and when that value was compared to the far away points, the program evaluates the "shadow" to be closer, and then renders the percieved shadow.

The solution was to make the far view much bigger. It is important to make sure that the lightcamera's orthogonal matrix has a far view that will be as far as the camera's.
Or so I think :)

I changed:
D3DXMatrixOrthoLH(&g_mShadowProj, 50.0f, 50.0f, 0.3f, 50.0f);

Into:
D3DXMatrixOrthoLH(&g_mShadowProj, 50.0f, 50.0f, 0.3f, 350.0f);

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