Jump to content

  • Log In with Google      Sign In   
  • Create Account


Computing view matrix to create shadow map


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
19 replies to this topic

#1 Medo3337   Members   -  Reputation: 665

Like
0Likes
Like

Posted 07 November 2013 - 12:05 AM

I'm trying to generate shadow map so I can pass it to the shader and create shadow

 

I have bunches of lights (including directional lights, point lights and spot lights)

 

How do I calculate the view matrix to create the shadow map?



Sponsor:

#2 Tispe   Members   -  Reputation: 1031

Like
1Likes
Like

Posted 07 November 2013 - 01:52 AM

You need one view matrix for each light, and 6 for point lights.

 

Use D3DXMatrixLookAtLH, D3DXMatrixOrthoLH for directional lights for projection.


Edited by Tispe, 07 November 2013 - 01:59 AM.


#3 Medo3337   Members   -  Reputation: 665

Like
0Likes
Like

Posted 07 November 2013 - 09:31 AM

@Tispe: I still don't understand, how should I work with multiple view matrices? Lets say I have 10 view matrices for the shadow map, should I multiply them with each others?



#4 N.I.B.   Members   -  Reputation: 1180

Like
0Likes
Like

Posted 07 November 2013 - 09:45 AM

@Tispe: I still don't understand, how should I work with multiple view matrices? Lets say I have 10 view matrices for the shadow map, should I multiply them with each others?

Each shadow map represent a single direction of light source, and so for each shaodw map you use a single ViewProjection matrix. Point lights have 6 matrices, because they reflect light in all directions, so you have a matrix for each direction.

 

Here is a tutorial about shadow mapping. It's OGL, but the same exact principles apply for DX. I suggest you start with shadow of a single directional light, once you understand it multiple light sources and point lights will become easier.



#5 Medo3337   Members   -  Reputation: 665

Like
0Likes
Like

Posted 07 November 2013 - 11:36 AM

@N.I.B.: This is my first time to implement shadows, so I have some questions

 

Does that means I have to send multiple shadow maps to the shader?

 

Lets say I have 2 point lights, you said that point light have 6 view/projection matrices, does that means I have to render the scene 12 times?



#6 Tom KQT   Members   -  Reputation: 1569

Like
1Likes
Like

Posted 07 November 2013 - 11:36 AM

@Tispe: I still don't understand, how should I work with multiple view matrices? Lets say I have 10 view matrices for the shadow map, should I multiply them with each others?

 

You mean multiple lights at the same time, all casting shadows? You need one shadow map per each light! But usually just one light is casting dynamic shadows at a time ;) So you need just one view matrix.

And how to construct the view matrix? Just imagine a camera instead of the light - a camera looking the same direction as the light is shining on the scene. And forget about point lights as shadow casters. It's not impossible of course, but definitely not good for somebody who's beginning with shadow mapping.

 

I'm quite confused about your question whether you should multiply the view matrices of different lights with each other. I don't mean any offense, but are you sure you understand the basics well enough to be able to start dealing with the kinda more advance technique of shadow mapping?



#7 N.I.B.   Members   -  Reputation: 1180

Like
0Likes
Like

Posted 07 November 2013 - 11:48 AM

@N.I.B.: This is my first time to implement shadows, so I have some questions
 
Does that means I have to send multiple shadow maps to the shader?
 
Lets say I have 2 point lights, you said that point light have 6 view/projection matrices, does that means I have to render the scene 12 times?


Theoretically, yes. But like Tom said, that's usually not necessary.

Shadows are a huge subject, and trickier than you might imagine. If you are just beginning, start with a single spot casting shadow. Get it to work, make sure you understand it well. Than you can start thinking about more advanced shadow techniques.

#8 Medo3337   Members   -  Reputation: 665

Like
0Likes
Like

Posted 07 November 2013 - 06:20 PM

@Tom KQT: How do I determine which light is CURRENTLY casting shadow? I'll mostly be having 2 directional lights out door, and several point lights indoor.

 

Another thing, to create shadow map for directional light I know how to create the projection matrix, however, how can I create the view matrix using D3DXMatrixLookAtLH()? I only have the light direction.


Edited by Medo3337, 07 November 2013 - 11:08 PM.


#9 Tispe   Members   -  Reputation: 1031

Like
0Likes
Like

Posted 08 November 2013 - 01:01 AM

For every light source you need to generate a shadow map. A shadow map is a a complete render of the scene where the camera is placed at the light source. Instead of saving the colors like a normal render pass, you save the depth Z.

 

When you have all the shadow maps you need there are several ways of applying shadows to the scene:

Option A, render the scene and check each pixel against each shadow map. This requires you to bind the shadow maps to samplers, and you are limited with how many samplers you have available.

 

Option B, you can also composition shadows ontop of the rendered scene. This is handy if you have many shadow maps. Render the scene to a texture for color and a depth buffer for Z. Then render a fullscreen quad with the scene. For each pixel check the depth buffer value against each shadow map.

 

 

Before you start applying shadows to your scene, try to render something like this first:

shadowmap-small.png

This is an inverted shadow map with a single channel. Instead of colors, the distance to the light determines the pixel value.


Edited by Tispe, 08 November 2013 - 01:09 AM.


#10 Tom KQT   Members   -  Reputation: 1569

Like
2Likes
Like

Posted 08 November 2013 - 02:13 AM

@Tom KQT: How do I determine which light is CURRENTLY casting shadow? I'll mostly be having 2 directional lights out door, and several point lights indoor.

That's also related to level design, usually designers decide which lights will cast shadows. And even in top games, shadows aren't fully realistic. They are important to be there, because they improve the visual quality and overall feeling from the game a lot, but it usually doesn't quite matter (players won't notice it) whether the shadows are cast from all lights, whether the shadows correspond perfectly with the light type etc.

 

2 directional lights outdoor? Isn't sun enough? Specifically outdoor scenes are perfect for shadows, because you can be absolutely fine with 1 directional light casting shadows (the sun or moon) and the scene will look realistic, because that's what people expect. Of course for a good appearance you will need more complex lighting than just one directinal light, because in real life also the skydome add a lot of scattered light and light bounces on objects. But as far as shadows are concerned, casting them from the sun is enough.

Indoor scenes are much more complicated. You can still be fine with quite "fake" shadows like in some FPS games (TF2 for example) because as I said - most players won't care as long as the shadow is there. But then there are games where lighting is very important part of the gameplay or feeling (survival horrors for example) and these games need better shadows. But I personaly have more experience with outdoor and faked indoor shadows, so I don't know exactly how to do it, how to seamlessly swap shadows when moving from one room (one light) to another.

So you should answer to yourself - how accurate shadows do you need? Does the game rely on them much? Is it worth the additional performance cost? The answers can be 'yes' of course.

 

Another thing, to create shadow map for directional light I know how to create the projection matrix, however, how can I create the view matrix using D3DXMatrixLookAtLH()? I only have the light direction.

Something like this should work:

// Variables you already know:
D3DXVECTOR3 lightDirectionVector = D3DXVECTOR3(....); // the light direction
// Variables you have to define somehow:
D3DXVECTOR3 lookAt = D3DXVECTOR3(...); // where the virtual light camera is looking, 
                                       // this doesn't come directly from the light because directional light has
                                       // just direction and not position, so you have to choose it somehow,
                                       // for example as the centre of you scene, for a start ;)
float distance = ....; // how far the virtual light camera is from the lookAt point (again, you have to choose this value)

// Then you go:
D3DXVECTOR3 front, left, up; // basic view matrix orientation vectors
D3DXVECTOR3 eyePosition; // where the virtual light camera is 

D3DXVec3Normalize(&front, &lightDirectionVector));
eyePosition = lookAt - front * distance;
D3DXVec3Cross(&right, &UNIT_Y, &front);
D3DXVec3Normalize(&right, &right);
// 'right' can be almost zero if 'front' was almost parallel with UNIT_Y, so check it and fix if needed:
if (D3DXVec3LengthSq(&right) < 0.5f)
	right = Vec3(1, 0, 0);
D3DXVec3Cross(&up, &front, &right);

// You can use D3DXMatrixLookAtLH, of fill the matrix manually like this (the function does it also, under the hood):
D3DXMATRIX view;
view._11 = right.x;        view._12 = up.x;    view._13 = front.x;        view._14 = 0;    
view._21 = right.y;        view._22 = up.y;    view._23 = front.y;        view._24 = 0;    
view._31 = right.z;        view._32 = up.z;    view._33 = front.z;        view._34 = 0;    
                                                                          view._44 = 1.f;    
view._41 = -D3DXVec3Dot(&right, &eyePosition);    
view._42 =-D3DXVec3Dot(&up, &eyePosition);    
view._43 = -D3DXVec3Dot(&front, &eyePosition);    




#11 Medo3337   Members   -  Reputation: 665

Like
0Likes
Like

Posted 08 November 2013 - 02:38 AM

I have created a shadow map

 

As I understand, I need to compare the current pixel depth with the shadow map, if the shadow map depth is LESS than the current pixel depth then I add shadow to the current pixel.

 

Now, I see half of the scene is darker (which is unexpected)

 

Shader

 

VS:

float4 oPosition = mul(float4(IN.Position, 1.0f), shadowWorldViewProj);
oPosition.x      = ((oPosition.x + oPosition.w) * ScreenParams.x + oPosition.w) * ScreenParams.z;
oPosition.y      = ((oPosition.w - oPosition.y) * ScreenParams.y + oPosition.w) * ScreenParams.w;
Out.screenPos    = oPosition;
Out.Z = mul(float4(IN.Position, 1.0f), shadowWorldView).z;

PS:

shadow = 0.0;
float scene_z = tex2Dproj(shadowMap, In.screenPos).x;
if (scene_z > 0)
{
    if (In.Z > scene_z)
        shadow = 1.0f;
}

Edited by Medo3337, 08 November 2013 - 02:41 AM.


#12 Tispe   Members   -  Reputation: 1031

Like
0Likes
Like

Posted 08 November 2013 - 03:00 AM

In the PS you should recieve the (x,y,z) position of the pixel in screen space (WorldViewProj). This position needs to be transformed to shadow map space using Proj-1*View-1*SMView*SMProj*SMTexBias matrices which can be calculated outside the shader and be set as a constant. Then you can do the comparison, I think :)



#13 Medo3337   Members   -  Reputation: 665

Like
0Likes
Like

Posted 08 November 2013 - 03:35 AM

@Tispe: I'm a little confused.

 

Can you show me any code example? I thought my above code is somehow correct.

 

BTW:

shadowWorldViewProj = The light worldViewProjection

shadowWorldView = The light worldView



#14 Tispe   Members   -  Reputation: 1031

Like
0Likes
Like

Posted 08 November 2013 - 03:55 AM

There are several ways of doing this.

 

You can calculate the vertex position in both shadow map space and screen space and then pass both positions to the PS. The PS would then recieve the interpolated position both in screen space and shadow map space. With this method you calculate first the vertex pos in world space. Then you calculate two positions from that world pos using ViewProj and SMViewSMProj.

 

Another method is the one I described above, it has less performance but more flexibility. I think :)



#15 Medo3337   Members   -  Reputation: 665

Like
0Likes
Like

Posted 08 November 2013 - 12:05 PM

@Tispe: I think I'm doing the same idea in my code

 

Comparing the depth in the shadow map with the current pixel depth to determine if the pixel should be darker or not

 

And it's still not working...

 

Here is my shader code:

...
...
VS_OUTPUT VS( VERTEX IN )
{
     VS_OUTPUT OUT;
     OUT = (VS_OUTPUT)0;
     OUT.Position  = mul(float4(IN.Position, 1.0f), WorldViewProjection);
     OUT.UV        = IN.UV;
     OUT.Normal    = mul(float4(IN.Normal, 0.0f), World).xyz;
     float4 oPosition = mul(float4(IN.Position, 1.0f), lightWorldViewProj);
     oPosition.x      = ((oPosition.x + oPosition.w) * ScreenParams.x + oPosition.w) * ScreenParams.z;
     oPosition.y      = ((oPosition.w - oPosition.y) * ScreenParams.y + oPosition.w) * ScreenParams.w;
     OUT.screenPos    = oPosition;

     OUT.Z = mul(float4(IN.Position, 1.0f), lightWorldView).z;
     return OUT;
}

float4 PS(VS_OUTPUT IN) : COLOR
{
    float scene_z = tex2Dproj(shadowMap, IN.screenPos).x;
    if (IN.Z > scene_z)
        // Pixel in shadow
        shadow = float4(0.4f, 0.4f, 0.4f);
    else
        shadow = float4(1.0f, 1.0f, 1.0f)
    return tex2D( colorTex, IN.UV ) * shadow;
}

Edited by Medo3337, 08 November 2013 - 01:08 PM.


#16 Tispe   Members   -  Reputation: 1031

Like
0Likes
Like

Posted 08 November 2013 - 01:09 PM


     oPosition.x      = ((oPosition.x + oPosition.w) * ScreenParams.x + oPosition.w) * ScreenParams.z;
     oPosition.y      = ((oPosition.w - oPosition.y) * ScreenParams.y + oPosition.w) * ScreenParams.w;

 

Where did you get this code? I have not seen it before.

 

Maybe you should remove it and apply SMTexBias to IN.screenPos in the PS.

float fTexOffs = 0.5 + (0.5 / (float)SHADOW_MAP_SIZE);
D3DXMATRIX SMTexBias( 0.5f,	 0.0f,	 0.0f, 0.0f,
					  0.0f,	 -0.5f,	0.0f, 0.0f,
					  0.0f,	 0.0f,	 1.0f, 0.0f,
					  fTexOffs, fTexOffs, 0.0f, 1.0f );


#17 Medo3337   Members   -  Reputation: 665

Like
0Likes
Like

Posted 08 November 2013 - 05:11 PM

@Tispe: The code I posted is somehow close to the one that I use for soft particles, but of course the depth map is different as well as I'm comparing the pixel depth with the scene depth to determine if the pixel is in shadow

 

I tried using the code you mentioned, but it's still not working... what I see is that half of scene is darker instead of shadows.

 

Here is what I have done:

// Shadow map size: 1024 x 1024

VS_OUTPUT VS( VERTEX IN )
{
     VS_OUTPUT OUT;
     OUT = (VS_OUTPUT)0;
     OUT.Position  = mul(float4(IN.Position, 1.0f), WorldViewProjection);
     OUT.UV        = IN.UV;
     OUT.Normal    = mul(float4(IN.Normal, 0.0f), World).xyz;
     OUT.vTexCoord = mul( IN.Position, SMTexBias );
     OUT.ShadowZ = mul(float4(IN.Position, 1.0f), lightViewProj).z;
     return OUT;
}

float4 PS(VS_OUTPUT IN) : COLOR
{
    // Generate the 9 texture co-ordinates for a 3x3 PCF kernel
    float4 vTexCoords[9];
    // Texel size
    float fTexelSize = 1.0f / 1024.0f;

    // Generate the tecture co-ordinates for the specified depth-map size
    // 4 3 5
    // 1 0 2
    // 7 6 8
    vTexCoords[0] = IN.vTexCoord;
    vTexCoords[1] = IN.vTexCoord + float4( -fTexelSize, 0.0f, 0.0f, 0.0f );
    vTexCoords[2] = IN.vTexCoord + float4( fTexelSize, 0.0f, 0.0f, 0.0f );
    vTexCoords[3] = IN.vTexCoord + float4( 0.0f, -fTexelSize, 0.0f, 0.0f );
    vTexCoords[6] = IN.vTexCoord + float4( 0.0f, fTexelSize, 0.0f, 0.0f );
    vTexCoords[4] = IN.vTexCoord + float4( -fTexelSize, -fTexelSize, 0.0f, 0.0f );
    vTexCoords[5] = IN.vTexCoord + float4( fTexelSize, -fTexelSize, 0.0f, 0.0f );
    vTexCoords[7] = IN.vTexCoord + float4( -fTexelSize, fTexelSize, 0.0f, 0.0f );
    vTexCoords[8] = IN.vTexCoord + float4( fTexelSize, fTexelSize, 0.0f, 0.0f );
    // Sample each of them checking whether the pixel under test is shadowed or not
    float fShadowTerms[9];
    float fShadowTerm = 0.0f;
    for( int i = 0; i < 9; i++ )
    {
        float A = tex2Dproj( shadowMap, vTexCoords[i] ).r;
        float B = (IN.ShadowZ - 0.1f);
        // Texel is shadowed
        fShadowTerms[i] = A < B ? 0.0f : 1.0f;
        fShadowTerm += fShadowTerms[i];
    }
    // Get the average
    fShadowTerm /= 9.0f;
    return fShadowTerm;
}

Edited by Medo3337, 08 November 2013 - 05:44 PM.


#18 Tispe   Members   -  Reputation: 1031

Like
0Likes
Like

Posted 09 November 2013 - 03:51 AM

I would not do any PCF before you atleast get shadows working.



#19 Medo3337   Members   -  Reputation: 665

Like
0Likes
Like

Posted 09 November 2013 - 07:36 AM

@Tispe: Unfortunately, I'm still having troubles getting shadow to work.

 

Please check my code and let me know if there is something wrong:

 

Shader for rendering the final scene:

...
texture shadowMapTexture;
sampler shadowMap = sampler_state
{
    Texture     = <shadowMapTexture>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
};

// Vertex shader
VS_OUTPUT VS( VERTEX IN )
{
     VS_OUTPUT OUT;
     OUT = (VS_OUTPUT)0;
     OUT.Position  = mul(float4(IN.Position, 1.0f), WorldViewProjection);
     OUT.UV        = IN.UV;
     OUT.Normal    = mul(float4(IN.Normal, 0.0f), World).xyz;
     OUT.vTexCoord = mul( IN.Position, g_matTexture[0] );
     OUT.Z = mul(float4(IN.Position, 1.0f), lightWorldView).z;
     return OUT;
}

// Pixel shader
float3 PS( VS_OUTPUT IN ) : COLOR
{
     float A = tex2Dproj( shadowMap, IN.vTexCoord ).r;
     float B = (IN.ShadowZ - 0.1f);
     float shadow = A < B ? 0.4f : 1.0f;
     return tex2D(colorMap, IN.UV) * shadow;
}
 
Shader for generating shadow map (depth texture):
...
VS_OUTPUT VS( VS_INPUT IN )
{
    VS_OUTPUT OUT;
    OUT = (VS_OUTPUT)0;
    OUT.Pos = mul(float4(IN.Pos, 1.0f), WorldViewProj);
    OUT.UV = IN.UV;
    OUT.Normal    = mul(float4(IN.Normal, 0.0f), World).xyz;
    OUT.Z = mul(float4(IN.Pos, 1.0f), WorldView).z;
    return OUT;
}

PS_OUTPUT PS( VS_OUTPUT IN )
{
    OUT.Depth = IN.Z;
    OUT.Color = ...;
}


#20 Tispe   Members   -  Reputation: 1031

Like
0Likes
Like

Posted 09 November 2013 - 12:29 PM

In the shadow map VS shader:

 


OUT.Z = mul(float4(IN.Pos, 1.0f), WorldView).z;

 

I think you need WorldViewProj here. But call it LightViewProj instead for proper naming convention for the second pass.

 

Also you need to pass two floats zw, not just z. Then in PS you need to divide z by w:

OUT.Depth = IN.z / IN.w;

Edited by Tispe, 09 November 2013 - 12:38 PM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS