Jump to content
  • Advertisement
Sign in to follow this  
Michael Smith_49683

Spotlight Shadow Mapping Issue, please help =)

This topic is 2519 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello everyone,

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 =)

[attachment=6859:SM_Error_0.jpg]

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.

[attachment=6860:SM_Error_DepthMap_0.jpg]

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

Share this post


Link to post
Share on other sites
Advertisement
I think I have it figured out. I will post more soon if I get it working. That way, if others have problems with omni-directional shadow maps, or spot lights in general, it will hopefully help them. =)

EDIT : It works now! =D

[attachment=6864:SM_error_fixed_0.png]

Basically, the problem was pretty isolated to a trigonometry problem, which was good. But, it was a silly mistake and I guess being over tired and rushing, I wasn't even thinking. The following code (and previous samples) are working for a basic spot-light effect with shadow mapping in XNA. There still needs to be changes to include range-distance decay, and obviously the code can be optimized and condensed, but it's a good starter point. =)

Code changes :

(1) Added m_fInnerConeCos and m_fOuterConeCos variables, which are calculated whenever m_fOuterConeAngle and m_fInnerConeAngle change. I simply find m_fOuterConeAngle and m_fInnerConeAngle normalized values easier to type in for an editor and visualize, but the shader needs the new variables.


// Angle between light direction and cone edge.
// Normalized between 0 and 1 for half-cone angle, where 0 is no angle and 1 is 90 degrees (full 180 spot-light).
private float m_fInnerConeAngle = 0.0f;
private float m_fOuterConeAngle = 0.0f;

private float m_fInnerConeCos = 0.0f; // Cached value. To compare to a dot product value in a spot light shader.
private float m_fOuterConeCos = 0.0f;

new variables are calculated like so, if/whenever the cone's change, on the CPU in XNA:

m_fOuterConeCos = (float)Math.Cos(m_fOuterConeAngle * (float)MathHelper.PiOver2);
m_fInnerConeCos = (float)Math.Cos(m_fInnerConeAngle * (float)MathHelper.PiOver2);

These are given to the shader, which calculates the lighting shown in the first post, in replacement of the original inner-outer values.

(2) Changes to the HLSL Effect shader - For calculating the lit-shadowed areas, shadow map already computed at this point and being used:


#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(1.0f - gLightInnerConeAngle, 1.0f - gLightOuterConeAngle, fInnerConeTest);

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

#endif


That's it, have a great day!


- Michael

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!