Jump to content
  • Advertisement
Sign in to follow this  
d h k

[D3D9] Shadow Maps for Point Lights

This topic is 2557 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

I have implemented a deferred renderer with support for point lights. Now I'm working towards implementing shadow mapping for the lights. I've never done this, and I'm aware that shadow maps for directional lights are much easier point to start with, but my application has very specific needs and, as far as shadows goes, I need them for point lights.

I'm dealing with a single point light at the moment. My first concern is getting that one point light's shadow map to be rendered correctly. My shadow map is a cube map where each face is a 1024x1024 large R32F render target.

Before I do any further scene rendering, I try and fill my shadow map in this fashion:

[source lang="cpp"]
if ( FAILED ( shader_shadowmap_fill->effect->Begin ( &num_passes, 0 ) ) )
Error ( "Failed to begin effect..." );

for ( unsigned int pass = 0; pass < num_passes; pass++ )
// loop through all passes
{
if ( FAILED ( shader_shadowmap_fill->effect->BeginPass ( pass ) ) )
Error ( "Failed to begin pass..." );

for ( int i = 0; i < 6; i++ )
{
D3DCUBEMAP_FACES face;
D3DXVECTOR3 light_position, face_direction, face_up;

light_position = D3DXVECTOR3 ( lights[0].position.x, lights[0].position.y, lights[0].position.z );

if ( i == 0 )
{
face = D3DCUBEMAP_FACE_POSITIVE_X;
face_direction = D3DXVECTOR3 ( 1.0f, 0.0f, 0.0f );
face_up = D3DXVECTOR3 ( 0.0f, 1.0f, 0.0f );
}
else if ( i == 1 )
{
face = D3DCUBEMAP_FACE_NEGATIVE_X;
face_direction = D3DXVECTOR3 ( -1.0f, 0.0f, 0.0f );
face_up = D3DXVECTOR3 ( 0.0f, 1.0f, 0.0f );
}
else if ( i == 2 )
{
face = D3DCUBEMAP_FACE_POSITIVE_Y;
face_direction = D3DXVECTOR3 ( 0.0f, 1.0f, 0.0f );
face_up = D3DXVECTOR3 ( 0.0f, 0.0f, -1.0f );
}
else if ( i == 3 )
{
face = D3DCUBEMAP_FACE_NEGATIVE_Y;
face_direction = D3DXVECTOR3 ( 0.0f, -1.0f, 0.0f );
face_up = D3DXVECTOR3 ( 0.0f, 0.0f, 1.0f );
}
else if ( i == 4 )
{
face = D3DCUBEMAP_FACE_POSITIVE_Z;
face_direction = D3DXVECTOR3 ( 0.0f, 0.0f, 1.0f );
face_up = D3DXVECTOR3 ( 0.0f, 1.0f, 0.0f );
}
else if ( i == 5 )
{
face = D3DCUBEMAP_FACE_NEGATIVE_Z;
face_direction = D3DXVECTOR3 ( 0.0f, 0.0f, -1.0f );
face_up = D3DXVECTOR3 ( 0.0f, 1.0f, 0.0f );
}

IDirect3DSurface9 *tmp;
shadow_map->GetCubeMapSurface ( face, 0, &tmp );
window->GetDevice ( )->SetRenderTarget ( 0, tmp );

std::vector<Model*>::iterator model;
for ( model = deferred_models.begin ( ); model < deferred_models.end ( ); model++ )
{
D3DXMATRIX w = (*model)->GetMatrix ( );
D3DXMATRIX v;
D3DXMATRIX p;

D3DXMatrixLookAtLH ( &v, &light_position, &( light_position + face_direction ), &face_up );
D3DXMatrixPerspectiveFovLH ( &p, D3DX_PI / 2, 1.0f, 0.1f, lights[0].radius );

D3DXMATRIX wvp = w * v * p;

shader_shadowmap_fill->effect->SetMatrix ( "mat_worldviewproj", &wvp );
shader_shadowmap_fill->effect->SetFloat ( "render_distance", lights[0].radius );

if ( FAILED ( shader_shadowmap_fill->effect->CommitChanges ( ) ) )
// make sure all values send to the shader are updated
Error ( "Failed to commit changes in the shader..." );

RenderModel ( &model );
}
}

shader_shadowmap_fill->effect->EndPass ( );
}

shader_shadowmap_fill->effect->End ( );
[/source]

You can see how I'm going through all six sides of the cube map, creating view and projection matrices for each, setting the render target to the appropriate face in the shadow (cube) map and then render each model. This is the shader (shader_shadowmap_fill):

[source lang="cpp"]
float4x4 mat_worldviewproj;
float render_distance;

struct vs_input
{
float4 position : POSITION;
};

struct vs_output
{
float4 position : POSITION;
float depth : TEXCOORD0;
};

vs_output MyVertexShader ( vs_input input )
{
vs_output output;

// transform vertex into projection/screen space for rendering
output.position = mul ( float4 ( input.position.xyz, 1.0f ), mat_worldviewproj );

// get the position of the vertex in projection-space, retrieve depth and pass it on
output.depth = output.position.z;

return output;
}

struct ps_input
{
float depth : TEXCOORD0;
};

struct ps_output
{
float4 color : COLOR0;
};

ps_output MyPixelShader ( ps_input input )
{
ps_output output;

// debug
output.color.rgba = 0.5f;

// actual line
//output.color.rgba = input.depth / render_distance;

return output;
}
[/source]

This is how I create my shadow map at load time:


if ( FAILED ( D3DXCreateCubeTexture ( window->GetDevice ( ), 1024, 0, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &shadow_map ) ) )
{
Error ( "Failed to create Shadow Map..." );
return;
}


My current problem is that all six sides of my shadow map end up at 0.0f. I'm checking with NVidia's PerfHUD and it returns (0.0f, 1.0f, 1.0f, 1.0f) for each pixel in the R32F cube map. Since my pixelshader in the shader that is used to fill the shadow map returns 0.5f for every pixel, this tells me that somehow no geometry actually is rendered into the shadow map. I have double and triple checked my code, however, and the matrices are all as they should be! The world matrix comes from the model (created by the model's scale * rotation * translation, of course) and is also used when actually rendering the geometry to the g-buffer for deferred rendering purposes. The view and projection matrices are created in the way I should for the light.

I can't debug these shaders in PIX as they don't show up in any on-screen pixel history.

What could be going wrong here? Any help is greatly appreciated. Let me know if you need more information and I'll gladly provide it!

Share this post


Link to post
Share on other sites
Advertisement
Just after a quick glance it looks like your face_direction might be opposite what you want when you create the look-at matrix. If you want to project onto the negative z face of the cube map and the point light is inside the cube, don't you want a face direction of (0,0,1) instead of (0,0,-1)?

Share this post


Link to post
Share on other sites
According to this post by MJP, I was doing it correctly before hand. But I tried out flipping all face_directions, to no avail unfortunately. This is still what I'm getting in PerfHUD:

defrend_shadow1.png

It's definitely trying to render the scene to the cube map (ie. render it six times to each face), I can tell from the slider at the bottom of that tab in PerfHUD. The draw calls are definitely there, my cube map just stays at 0.0f in the R channel throughout, as you can see above.

Any further ideas?

Share this post


Link to post
Share on other sites
Bumping this one single time as it has helped my threads get 'started' plenty of times in the past and it fell off the first page. I won't bump it again if there's still no reaction.

I can't tell what the silence in the thread means... Please let me know if I need to provide any further information - or is the problem so odd that nobody knows anything I could do?

I'm really hoping to get this issue sorted out, so PLEASE, if you have anything, even if it's a far-fetched idea, please don't hesitate to just post it up.

Share this post


Link to post
Share on other sites
even if it's a far-fetched idea, please don't hesitate to just post it up.


I assume you have enabled the dx debug runtimes to make sure there are no technical problems.

Comparing to my implementation (old but working) I don't see huge differences.
You are passing 0 to D3DXCreateCubeTexture for MipLevels which creates a full mip chain. Try passing 1 to create the bottom level only.
I'm also using the ID3DXRenderToEnvMap interface with depth stencil. Other than that I can only suggest to do some debug testing, like
using a regular render target instead of cubemap etc.

Share this post


Link to post
Share on other sites
After a long, long while of not getting anywhere I figured out what the problem was. I had my debug slider not all the way up where it should have been so I never figured out that my calls to RenderIndexedPrimitive failed. And they failed because before doing anything with the shadow map I was clearing the various buffers - including the G-Buffer. And when clearing the G-Buffer I set three render targets to be cleared at the same time. I never called SetRenderTarget ( 1, NULL ); and SetRenderTarget ( 2, NULL );

Now I reset the unused render targets 1 and 2 when rendering my shadow map and it's working just fine.

Thanks!

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!