• Advertisement
Sign in to follow this  

shadow maps question

This topic is 4793 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 been reading alot of different tutorials and stuff here on this forum, but one thing I havn't understand yet is if I need one depthmap for each light that cast shadows? but then I need to activate all of them at the same time when doing the test against the depthmap?

Share this post


Link to post
Share on other sites
Advertisement
Yes, you need a separate depth map for each lightsource. When rendering the shadowed geometry, you perform as many tests as there are lights, and somehow combine the results. I said "somehow", because different people use different methods, it's more often than not an artistic decision. The physically most correct way is to add the light contribution of all sources together, each one multiplied with the shadow comparison factor of its depth map.

1) Start by rendering each light to its individual depth surface

2) Bind each surface to a separate texture (imaging) unit.

3) Set up a depth compare for each unit, making sure that the generated projected texture coordinates for each unit match the one of the respective lightsource.

4) Use the following equation per pixel:

c(final_light) = c(light0) * depthCompareResult(0) + c(light1) * depthCompareResult(1) + ...

5) Multiply the c(final_light) with whatever diffuse texture or colour you might need.

Share this post


Link to post
Share on other sites
I have a question relating to Shadow maps.. I think I sort of understand how the process of setting up the depth map works, but what I dont understand is the rendering process.. (basically) how does the texture create these shadows on the screen, and do you use a textured quad in front of the camera? I know a quad is involved somehow, but.. I just don't see how it all fits together at the moment. Maybe someone could direct me to a nice explanation of shadow mapping.

Share this post


Link to post
Share on other sites
A couple of shadow map links:

nVidia paper

Paul's Projects tutorial

To provide a bit of answer to your question Mikey, shadow mapping makes use of projective texturing, to project the shadow map onto the geometry into the scene. Using this you can easily figure out which texel in the shadow map applies to the particular part of the geometry you are rendering, and thus know if it is in shadow or not WRT the light source.

-Mezz

Share this post


Link to post
Share on other sites
The depth map is projected onto the shadow receivers, so the uv coords are generated. The nVidia paper that Mezz posted is a very good reference, especially when you first start to learn about shadow maps.

Share this post


Link to post
Share on other sites
ok, I'll look into the nVidia paper... but so far I can't really understand how the shadow maps can be valid at the same time.. this will cost alot of texunits? or do I create an extra pass that project all of them into one big depthmap and use that one only?

Share this post


Link to post
Share on other sites
Quote:
Original post by McZ
I can't really understand how the shadow maps can be valid at the same time..


I'm not sure I understand this question. If you have one shadow map for each light, then render the scene from each light's POV into their shadow maps, then all the shadow maps contain valid information. After you've done this, you can use these shadow maps when drawing your actual scene from the viewer's perspective, to tell whether certain objects are in shadow or not.

Quote:
Original post by McZ
this will cost alot of texunits?


It will if you do multiple lights in one pass, but you can do a pass per light and only bind one of your shadow map textures at a time, hence only using one texture unit.

Quote:
Original post by McZ
or do I create an extra pass that project all of them into one big depthmap and use that one only?


I've never heard of this as a technique, so I couldn't recommend it.

-Mezz

Share this post


Link to post
Share on other sites
ahh thank you! now I understand :)

I use up all tex units I have free for a pass.. but what if my texunits isn't enough for one pass? how do I split it into several passes? do the extra passes also need depthmap testing?

and what to do if I have more depthmaps then tex units?

Share this post


Link to post
Share on other sites
If you don't have enough texture units to do one light in a single pass... well, that's some complex lighting equation you have going on there :)

If you were able to split one light into multiple passes, then yes you'd still need a shadow test for each pass, remember that in the end all your shadow test does is effectively multiply the light's value either by 1.0 (i.e. full brightness) if it's not in shadow, or 0.0 (i.e. no brightness at all) if it is. Sometimes you might multiply it somewhere in between if it's on a boundary but that's a more advanced case.

If you have more depth maps than texture units... well, like has been said previously, you need to do a pass per light, or whatever you can afford. You only need the depth map for the light you are rendering, so if you do a single pass per light, you would use at most 1 texture unit with the depth map.

Hope that clears some stuff up.

-Mezz

Share this post


Link to post
Share on other sites
I tried to implement Nvidia's HardwareShadowMap sample, but I am definitely not seeing the right results.
(http://download.nvidia.com/developer/SDK/Individual_Samples/DEMOS/Direct3D9/HardwareShadowMap.zip)

-set the render target to the color surface
-set the depth stencil surface to the new one
-clear the target & depth buffer
-set the new viewport
-set up matrices from lights POV.
-draw each mesh with the 2nd technique (GenHardwareShadowMap)
-reset the viewport
-reset the render target/depth buffers
-clear the target/depthbuffer
-set up texScaleBias Matrix
-set "ShadowMap" to the depth texture
-set "spotlight" texture
-set view's worldviewproj and worldIT for each mesh
-Draw each mesh with 1st technique (UseHardwareShadowMap)
-Rinse, lather, repeat

And this is what it looks like: (Top in wireframe, bottom solid, gray = color render target rendered to a sprite)
So anybody know what many things I might be doing wrong?

Share this post


Link to post
Share on other sites
Heres what it should look like (this is with stencil volumes though):

Share this post


Link to post
Share on other sites

I know a quad is involved somehow
[\quote]
I think you were meaning a volume shadows bad version, in wich you use a black quad over the whole screen to create the shadows, using the stencil generated mask to render it.

Share this post


Link to post
Share on other sites
No, I mean shadow mapping.. I realized some time ago that the quad they(nvidia) are creating in their sample is only used for the shadow casting object's shadow to be projected onto (:D).

Share this post


Link to post
Share on other sites
Well there isn't really anything wrong with the algorithm you've written there MikeyO, but I can't tell what the problem is just from those shots. Are you sure you're doing the texture projection correctly in the UseHardWareShadowMap phase?
I assume you're just using nVidia's shaders, in which case they will be correct.
Hmm... dunno.

-Mezz

Share this post


Link to post
Share on other sites
Ok, here's the relevent source code... I'm sure it's either some fundamental error, something I'm doing completely wrong, or its a few small things I'm forgetting. - Captain Obvious


///////INITIALIZATION CODE//////
shadowFX.SetValue("LightVec", Vector4.Scale(Vector4.Normalize(new Vector4(lightVector.X, lightVector.Y, lightVector.Z, 1.0f)), 100));

backBuffer = device.GetRenderTarget(0); //I assume this is a reference..
zBuffer = device.DepthStencilSurface;

colShadowTex = new Texture(device, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 1, Usage.RenderTarget, Format.A8R8G8B8, Pool.Default); //Color texture
zShadowTex = new Texture(device, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 1, Usage.DepthStencil, Format.D24S8, Pool.Default); //Z depth texture

shadowColSurface = colShadowTex.GetSurfaceLevel(0); //Color surface
shadowZSurface = zShadowTex.GetSurfaceLevel(0); //Z depth surface

lightPosition = new Vector3(1000, 1000, 0);


///////MAKE SHADOW CODE//////////
Matrix World;

Vector3 lookAt = Vector3.Subtract(lightPosition, lightVector);
Matrix lightView, lightProj;
lightView = Matrix.LookAtLH(lightPosition, lookAt, new Vector3(0, 1, 0));
lightProj = Matrix.PerspectiveFovLH((float)Math.PI / 3, 1, 1, 1000);
lightViewProj = lightView * lightProj;

device.SetRenderTarget(0, shadowColSurface);
device.DepthStencilSurface = shadowZSurface;

Viewport oldViewport = device.Viewport;
Viewport newViewport = new Viewport();
newViewport.X = 0;
newViewport.Y = 0;
newViewport.Width = SHADOW_MAP_SIZE;
newViewport.Height = SHADOW_MAP_SIZE;
newViewport.MinZ = 0.0f;
newViewport.MaxZ = 1.0f;
device.Viewport = newViewport;

shadowFX.Technique = shadowFX.GetTechnique(1);
device.RenderState.DepthBias = 0.0002f;
device.RenderState.SlopeScaleDepthBias = 2.0f;

device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Gray/*0x00FFFFFF*/, 1.0f, 0);

device.VertexDeclaration = defDeclare;
Matrix tempIdent = Matrix.Identity;
int passes;
for(int i = 0; i < balls.Count; i++)
{
Ball b = (Ball)balls;
///////RENDER CODE HERE /////
World = device.Transform.World = Matrix.Scaling(1, 1, 1) * Matrix.RotationYawPitchRoll(0,0,0) * Matrix.Translation(b.position);
device.Transform.World = World;

//Set up matrices
Matrix WorldViewProj = World * lightViewProj;
Matrix WorldITMat = Matrix.TransposeMatrix(Matrix.Invert(World));

shadowFX.SetValue("WorldViewProj", WorldViewProj);
shadowFX.SetValue("WorldIT", WorldITMat);
shadowFX.SetValue("TexTransform", tempIdent);

passes = shadowFX.Begin(0);
for(int j = 0; j < passes; j++)
{
shadowFX.BeginPass(j);
ballMesh.DrawSubset(0);
shadowFX.EndPass();
}
shadowFX.End();
}

device.Transform.World = World = Matrix.Translation(0, -4, 0);
shadowFX.SetValue("WorldViewProj", World * lightViewProj);
shadowFX.SetValue("WorldIT", Matrix.TransposeMatrix(Matrix.Invert(World)));

passes = shadowFX.Begin(0);
for(int j = 0; j < passes; j++)
{
shadowFX.BeginPass(j);
plane.DrawSubset(0);
shadowFX.EndPass();
}
shadowFX.End();

//Reset render settings
device.Viewport = oldViewport;
device.RenderState.DepthBias = 0.0f;
device.RenderState.SlopeScaleDepthBias = 0.0f;


//////////USE SHADOW CODE/////////////////
//Generate Shadow Map
RenderShadowMap(device, View, Proj); //Calls above code (MAKE SHADOW CODE)

//Render Scene
shadowFX.Technique = shadowFX.GetTechnique(0); //Use shadow technique
device.SetRenderTarget(0, backBuffer); //reset to original render target
device.DepthStencilSurface = zBuffer;

device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Blue, 1.0f, 0);

shadowFX.SetValue("ShadowMap", zShadowTex);
shadowFX.SetValue("SpotLight", decalShadowModel);

float fOffset = 0.5f + (0.5f / (float)SHADOW_MAP_SIZE);
int range = 1;
float fbias = 0f;

Matrix texScaleBiasMat = new Matrix();
texScaleBiasMat.M11 = 0.5f;
texScaleBiasMat.M22 = -0.5f;
texScaleBiasMat.M33 = (float)range;
texScaleBiasMat.M12 = texScaleBiasMat.M13 = texScaleBiasMat.M14 =
texScaleBiasMat.M21 = texScaleBiasMat.M23 = texScaleBiasMat.M24 =
texScaleBiasMat.M31 = texScaleBiasMat.M32 = texScaleBiasMat.M34 = 0.0f;
texScaleBiasMat.M41 = texScaleBiasMat.M42 = fOffset;
texScaleBiasMat.M43 = fbias;
texScaleBiasMat.M44 = 1.0f;

int passes;
for(int i = 0; i < balls.Count; i++)
{
Ball b = (Ball)balls;
///////RENDER CODE HERE /////
World = device.Transform.World = Matrix.Scaling(1, 1, 1) * Matrix.RotationYawPitchRoll(0,0,0) * Matrix.Translation(b.position);
device.Transform.World = World;

//Set up Matrices
Matrix WorldViewProj = World * View * Proj;
Matrix WorldIT = Matrix.TransposeMatrix(Matrix.Invert(World));

shadowFX.SetValue("WorldViewProj", WorldViewProj);
shadowFX.SetValue("WorldIT", WorldIT);
shadowFX.SetValue("TexTransform", World * lightViewProj * texScaleBiasMat);

passes = shadowFX.Begin(0);
for(int j = 0; j < passes; j++)
{
shadowFX.BeginPass(j);
ballMesh.DrawSubset(0);
shadowFX.EndPass();
}
shadowFX.End();
}

shadowFX.SetValue("SpotLight", decalShadowSurf);
device.Transform.World = World = Matrix.Translation(0, -4, 0);
shadowFX.SetValue("WorldViewProj", World * View * Proj);
shadowFX.SetValue("WorldIT", Matrix.TransposeMatrix(Matrix.Invert(World)));

passes = shadowFX.Begin(0);
for(int j = 0; j < passes; j++)
{
shadowFX.BeginPass(j);
plane.DrawSubset(0);
shadowFX.EndPass();
}
shadowFX.End();


The shader can be found here: http://pk.dras.us/users/Orion/ShadowMap.fx

Anything that is just glare-in-your-face obvious? subtle errors? Help thus far has been greatly appreciated.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mezz
If you don't have enough texture units to do one light in a single pass... well, that's some complex lighting equation you have going on there :)

If you were able to split one light into multiple passes, then yes you'd still need a shadow test for each pass, remember that in the end all your shadow test does is effectively multiply the light's value either by 1.0 (i.e. full brightness) if it's not in shadow, or 0.0 (i.e. no brightness at all) if it is. Sometimes you might multiply it somewhere in between if it's on a boundary but that's a more advanced case.

If you have more depth maps than texture units... well, like has been said previously, you need to do a pass per light, or whatever you can afford. You only need the depth map for the light you are rendering, so if you do a single pass per light, you would use at most 1 texture unit with the depth map.

Hope that clears some stuff up.

-Mezz


lets say I have 4 texture units and 2 of them is texture but I have 4 shadowcasting lights(depthmaps) so then I need to do it in 2 passes?

first make sure the depthmaps is updated.

bind the textures for the mesh
bind the first 2 depthmaps
draw

blend scene with the new pass
bind the last 2 depthmaps
draw

or is it some other way?

Share this post


Link to post
Share on other sites
Not that I'm aware of (well, you could split it into 4 passes if you wanted) but that 2-pass approach looks fine McZ.

-Mezz

Share this post


Link to post
Share on other sites
just another thing, do I need the textures for the mesh in all passes? or can I blend it someway so that I only need to draw the textures for the mesh in one pass and skip them in the others?

Share this post


Link to post
Share on other sites
You need the textures for you mesh in each pass, in every case I can currently think of.

-Mezz

Share this post


Link to post
Share on other sites
So.. would you add normal mapping/texture mapping into the useshadowmap pass?

Share this post


Link to post
Share on other sites
Yes, I would - what you have to understand is that the shadow map is simply part of your lighting equation. Everything you do with normal maps, diffuse textures, specular highlights etc. is all part of the lighting equation - so is the shadow map, it just tells you what to multiply your final light result by - either a 1 or a 0 in simple terms (although with some PCF it can change to a fractional value for something partially in shadow).

-Mezz

Share this post


Link to post
Share on other sites
so for all passes I do I will need all textures and stuff as if it was the first pass? but with some blending I guess so I add it to the pass before..

like this?

pass 1
bind textures/normal maps etc.
bind the depthmaps for this pass
draw mesh

pass 2...n
enable blending
bind textures/normal maps etc.
bind depthmaps that didn't fit in pass 1
draw mesh


how do I know which depthmap to use? do I add some kind of range on the lights so I can check if the object is in the lights range, if so then it will be affected by the lights depthmap else it will not?

Share this post


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

  • Advertisement