I am having an issue with shadow mapping with a spot light in XNA. The issue is, my shadow map area does not seem to coincide with the spot light area. I'm done a lot of internet searching. Found some tutorials and many forum topics, but am still unable to solve my issue. Perhaps an illustration will better show my problem =)

**SM_Error_0.jpg**

**45.88KB**23 downloads

The light (white blob in center) is positioned in the world and it's direction is (0, 0, -1). The error region (green square) seems incorrect, I would like the shadow map region to coincide with the (white square) region, "wrapping around" the spot-light.

Here is the 1024x1024 floating point 32-bit shadow-depth map in PNG format. White is furthest possible distance, blue is occluding objects.

**SM_Error_DepthMap_0.jpg**

**15.61KB**29 downloads

I will post some test CODE SNIPPETS to show my methods, keep in mind these are not finished or optimized yet :

**--------------**

**HLSL Effect file - Shadow Map Creation :**

// Vertex Shader

VSOut TransformVS( VSIn _vsin )

{

// Zero out our output structure

VSOut _output = (VSOut)0;

_output.pos = float4(_vsin.pos, 1.0f);

#if defined(SKINNED)

int IndexArray[4] = (int[4])_vsin.BoneIndices;

_output.pos = SkinningTransformPosition(gBones, _vsin.BoneWeights, IndexArray, _vsin.pos);

#endif

float4 vPosLightCast = GetPositionFromLightCast(_output.pos, gWorld, gViewLight, gProjLight);

.....

#elif defined(SPOT)

float4 vLightViewSpace = mul( mul( _output.pos, gWorld ), gViewLight );

_output.depth = vLightViewSpace.z;

#endif

_output.pos = vPosLightCast;

// Return output

return _output;

}

// Pixel Shader

PSOut TransformPS( PSIn _psin ) : COLOR

{

PSOut _output = (PSOut)0;

.....

#elif defined(SPOT)

float fDepthVal = -(_psin.depth / gFarClipPlaneLight); // Negate because we are in Right-Handed Coordinate System.

_output.color = float4(fDepthVal, 0, 0, 1);

#endif

return _output;

}

**--------------**

**HLSL Effect file - Spot Light Lighting - Depth Map already computed and being used here.**

....

float4 GetPositionFromLight(float4 pos, float4x4 viewLight, float4x4 projLight)

{

float4x4 ViewProjection = mul( viewLight, projLight );

return mul(pos, ViewProjection);

}

float GetShadowMapDepthValue(float4 lightingPosition)

{

// Get the shadow map depth value for this pixel

float2 ShadowTexC = 0.5 * (lightingPosition.xy / lightingPosition.w) + float2( 0.5, 0.5 );

ShadowTexC.y = 1.0f - ShadowTexC.y;

return tex2D(TextureShadowMapSampler, ShadowTexC).r;

}

PSOut TransformPS( PSIn _psin ) : COLOR

{

.......

// _psin.pos3D is in world space

// gLightPos is light position in world space

float3 vLightToVertex = _psin.pos3D.xyz - gLightPos; // Compute the vector from the light position to the vertex.

float3 vLightToVertexNormalized = normalize( vLightToVertex );

float fLightToVertexLength = distance( _psin.pos3D.xyz, gLightPos );

......

#elif defined(SPOT)

float4 lightingPosition = GetPositionFromLight(_psin.pos3D, gViewLight, gProjLight); // Get our position on the shadow map

float4 lightPositionViewSpace = mul( _psin.pos3D, gViewLight );

float shadowdepth = GetShadowMapDepthValue(lightingPosition);

float shadowsample = -(lightPositionViewSpace.z / gFarClipPlaneLight) - 0.03f;

float fInShadow = (shadowsample < shadowdepth) ? 1.0f : 0.0f;

float fInnerConeTest = 1.0f - saturate( dot(vLightToVertexNormalized, normalize(gLightDirection)) );

float fStepResult = 1.0f - smoothstep(gLightInnerConeAngle, gLightOuterConeAngle, fInnerConeTest);

diffuseLightingFactor = saturate( dot(-vLightToVertexNormalized, _psin.normal)) * fStepResult * fInShadow;

#endif

....

// diffuseLightingFactor is then used in a lighting equation...

}

**Next is some C# XNA code showing how the view and projection matrices are calculated for the depth map calculation phase, the depth map itself seems to be okay...I hope =)**

// Angle between light direction and cone edge.

// Normalized between 0 and 1 for half-cone angle, where 0.0f is no angle and 1.0f is 90 degrees (both sides 90 degrees = full 180 spot-light).

private float m_fInnerConeAngle = 0.0f;

private float m_fOuterConeAngle = 0.0f;

public override Matrix GetLightProjectionMatrix()

{

float fFOV = Math.Min(m_fOuterConeAngle * (float)Math.PI, (float)Math.PI);

return Matrix.CreatePerspectiveFieldOfView(

fFOV, // Between 0 and PI (180 degrees).

1,

m_fNearClipPlane,

m_fFarClipPlane);

}

public override Matrix GetLightViewMatrix()

{

Vector3 m_vLookAtPoint = m_vPosition + m_vDirection;

Vector3 vUp = new Vector3(0.0f, 1.0f, 0.0f);

// Special case : Change the up-vector if looking straight UP or DOWN.

// Direction should always be normalized for testing.

float fUpDownTest = Math.Abs(m_vDirection.Y);

float fEpsilon = 1e-10f;

if (fUpDownTest < (1.0f + fEpsilon) &&

fUpDownTest > (1.0f - fEpsilon))

{

if (m_vDirection.Y > 0.0f)

{

vUp = new Vector3(0.0f, 0.0f, -1.0f);

}

else

{

vUp = new Vector3(0.0f, 0.0f, 1.0f);

}

}

return Matrix.CreateLookAt(m_vPosition, m_vLookAtPoint, vUp);

}

**----------------**

Any help would be so so so much appreciated, I've been spinning my wheels, perhaps looking at this too long. I just can't seem to figure it out. I'm also finding it hard to find tutorials on the subject, without holes in them, in general. =)

- Michael