• Create Account

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.

27 replies to this topic

### #1Silverlan  Members

Posted 16 August 2014 - 07:17 AM

I'm trying to implement cascaded shadow mapping in my engine, but I'm stuck at the last step.

The rendering of the shadows works as it should (From what I can tell):

The shadow matrix is composed of the cropped projection matrix (Encompassing the scene) and the view (lookAt) matrix pointing straight down.

The shadow cascades are stored in a texture array and passed to the main scene shader as a sampler2DArrayShadow. All that's left to do is the shadow texture lookup, but I'm not sure how to transform the vertex shader position into the shadow space.

Any nudge in the right direction would be appreciated.

### #2kauna  Members

Posted 16 August 2014 - 01:04 PM

So to render the shadows you have a orthographic (shadow) matrix which transforms from world space to shadow texture space.

When rendering the scene and applying shadows in the pixel shader, you'll use the pixels position to choose the good cascade.

To get a texture coordinate in the shadow texture depends - for example, if the position you have in the pixel shader is in the view space, you'll need to transform the position with the inverse view matrix to get the world position and then use the shadow matrix to get the texture coordinate in the shadow map.

If the position is in the clip space, then you'd apply inverse view-projection matrix and then the shadow matrix.

Cheers!

 you may need to apply some translation and scale to get the correct texture coordinates in the shadow map. Typically you'd calculate the final matrix on the CPU, so in the pixel shader you'll need just one vertex matrix multiplication.

Edited by kauna, 16 August 2014 - 01:07 PM.

### #3Silverlan  Members

Posted 17 August 2014 - 06:43 AM

The lookup position is calculated in the vertex shader like so:

csmPos1 = csmMatrices[0] *(M *vpos);
csmPos2 = csmMatrices[1] *(M *vpos);
csmPos3 = csmMatrices[2] *(M *vpos);


Where vpos is the vertex position, M is the model matrix to transform it into world space and the respective matrix from csmMatrices is the view-projection matrix I used to render the shadows. This is then passed to the fragment shader, where I select the cascade and use shadow2DArray to grab the shadow value:

float GetShadowCoefficient()
{
int index = 2;
vec4 csmPos;
if(gl_FragCoord.z < far_d.x)
{
index = 0;
csmPos = csmPos1;
}
else if(gl_FragCoord.z < far_d.y)
{
index = 1;
csmPos = csmPos2;
}
else if(gl_FragCoord.z < far_d.z)
{
index = 2;
csmPos = csmPos3;
}
}


The code is just for testing purposes and not optimized yet.

Either way, the result is somewhat of a mess:

I don't think my shadow matrices are wrong, considering the shadow maps look more or less as they should, so I'm not sure what's going on?

### #4kauna  Members

Posted 17 August 2014 - 12:34 PM

I think that you'll need to offset and scale your texture coordinates since the projection space is from -1 to 1, but the texture space is from 0 to 1. So, multiply by 0.5 and add 0.5 to your texture coordinate.

Cheers!

ps. I'm not sure if it will resolve the problem you are having. On the other hand the shadows seem to move more and less correctly.

Edited by kauna, 17 August 2014 - 12:38 PM.

### #5Silverlan  Members

Posted 17 August 2014 - 01:49 PM

Thanks, that actually did the trick!

There are still some issues, but I'm a major step closer now!

What puzzles me a bit is how the shadow maps darken the further I look downwards, that doesn't seem right. Any idea what could be causing this?

### #6kauna  Members

Posted 17 August 2014 - 02:13 PM

Cheers!

### #7Silverlan  Members

Posted 17 August 2014 - 02:41 PM

Well, the only CSM-related part in the fragment shader is basically what I already posted before:

in vec4 csmPos[3];

uniform vec4 far_d;
uniform mat4 csmMatrices[3];
{
int index = 3;
for(int i=0;i<4;i++)
{
if(gl_FragCoord.z < far_d[i])
{
index = i;
break;
}
}
}


The result from this function is then multiplied by the output color, that's about it. As for the vertex shader:

layout(std140) uniform ViewProjection
{
mat4 M; // Model Matrix
mat4 V; // View Matrix
mat4 P; // Projection Matrix
mat4 MVP;
};

layout(location = 0) in vec4 vertPos;

// CSM Test
uniform mat4 csmMatrices[3];
out vec4 csmPos[3];

[...] Inside the main function:
csmPos[0] = csmMatrices[0] *(M *vertPos);
csmPos[1] = csmMatrices[1] *(M *vertPos);
csmPos[2] = csmMatrices[2] *(M *vertPos);


These are the only parts that use the CSM-related data.

### #8kauna  Members

Posted 17 August 2014 - 03:01 PM

I'd like to see the whole fragment shader. Are you performing a comparison between the fragments position and the shadowmap value somewhere?

Cheers!

### #9L. Spiro  Members

Posted 17 August 2014 - 07:41 PM

When generating the shadow maps you are enclosing only the “visible” area (created by the view frustum).  This doesn’t make sense, as things above the player, even if not directly visible to the player, can still cast shadows visible to the player.

Lock the near plane of the shadow-map projections to include any objects above the player that can cast shadows visible to the player.

L. Spiro

### #10Silverlan  Members

Posted 18 August 2014 - 02:51 PM

When generating the shadow maps you are enclosing only the “visible” area (created by the view frustum).  This doesn’t make sense, as things above the player, even if not directly visible to the player, can still cast shadows visible to the player.

Lock the near plane of the shadow-map projections to include any objects above the player that can cast shadows visible to the player.

Right, thanks. I've changed the bounds for each cascade to be large enough to encompass the entire scene, just for testing purposes, and that does give me better results:

The other problems (self-shadowing and different intensity) are directly related with the other problem I believe:

I'd like to see the whole fragment shader. Are you performing a comparison between the fragments position and the shadowmap value somewhere?

The return value from GetShadowCoefficient is literally just multiplied with the diffuse color. I've tried a comparison like so:

	[...]
return 0.25f;
return 1;


However that only gives me proper results for the first cascade, after that it's just cut off:

### #11Silverlan  Members

Posted 23 September 2014 - 02:58 PM

Still haven't figured this out. I've went through the Nvidia CSM Tutorial again, which I based my code mostly on, but they are doing pretty much exactly what I am. Their sample program results in similar cascades as my code does, so I'm pretty sure it's the final shader (For the main scene) which is messing things up.

uniform sampler2D tex; // terrain texture
uniform vec4 far_d; // far distances of
// every split
varying vec4 vPos; // fragment’s position in
// view space
uniform sampler2DArrayShadow stex; // depth textures
{
int index = 3;
// find the appropriate depth map to look up in
// based on the depth of this fragment
if(gl_FragCoord.z < far_d.x)
index = 0;
else if(gl_FragCoord.z < far_d.y)
index = 1;
else if(gl_FragCoord.z < far_d.z)
index = 2;

// transform this fragment's position from view space to
// scaled light clip space such that the xy coordinates
// lie in [0;1]. Note that there is no need to divide by w
// for othogonal light sources
// set the current depth to compare with

// tell glsl in which layer to do the look up

// let the hardware do the comparison for us
}
void main()
{
vec4 color_tex = texture2D(tex, gl_TexCoord[0].st);
float fog_coef = clamp(gl_Fog.scale*(gl_Fog.end + vPos.z), 0.0, 1.0);
gl_FragColor = mix(gl_Fog.color, (0.9 * shadow_coef * gl_Color * color_tex + 0.1), fog_coef);
}


Which is essentially the same as my shader above.

I'm at a complete loss...

### #12vlj  Members

Posted 23 September 2014 - 03:22 PM

Are you sure you properly set uniform values for gl_TextureMatrix[1] and [2] ? I would rather use explicitly declared matrixes for this.

### #13Silverlan  Members

Posted 23 September 2014 - 11:44 PM

Are you sure you properly set uniform values for gl_TextureMatrix[1] and [2] ? I would rather use explicitly declared matrixes for this.

I'm using my own uniforms, in my case "csmMatrices" represents the "gl_TextureMatrix" that's being used in the Nvidia shader.

// Extract from my vertex shader
layout(std140) uniform ViewProjection
{
mat4 M; // Model Matrix
mat4 V; // View Matrix
mat4 P; // Projection Matrix
mat4 MVP;
};

layout(location = 0) in vec4 vertPos;

// CSM Test
uniform mat4 csmMatrices[3];
out vec4 csmPos[3];

[...] Inside the main function:
csmPos[0] = csmMatrices[0] *(M *vertPos);
csmPos[1] = csmMatrices[1] *(M *vertPos);
csmPos[2] = csmMatrices[2] *(M *vertPos);


Edited by Silverlan, 23 September 2014 - 11:45 PM.

### #14vlj  Members

Posted 24 September 2014 - 08:40 AM

I think the issue is here :

The return value from GetShadowCoefficient is literally just multiplied with the diffuse color. I've tried a comparison like so:
[...]
return 0.25f;
return 1;
However that only gives me proper results for the first cascade, after that it's just cut off:

Assuming the sampler mode is GL_NEAREST, shadow2DArray returns either 0. ("false") if the comparaison test fails or 1. ("true") if the comparaison test succeed.

If the sampler mode is GL_LINEAR then the 0./1. from neighbour texels are interpolated linearly.

I don't understand the "if(z < shadowCoord.w)" statement since you're comparing a "bool" (the result of a comparison) with a depth value.

I think you should use "return z;" directly.

### #15Silverlan  Members

Posted 24 September 2014 - 09:43 AM

Assuming the sampler mode is GL_NEAREST, shadow2DArray returns either 0. ("false") if the comparaison test fails or 1. ("true") if the comparaison test succeed.

If the sampler mode is GL_LINEAR then the 0./1. from neighbour texels are interpolated linearly.

I don't understand the "if(z < shadowCoord.w)" statement since you're comparing a "bool" (the result of a comparison) with a depth value.

I think you should use "return z;" directly.

That's what I did before, which essentially had the same results:

The texture parameters for the cascades are:

glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_COMPARE_FUNC,GL_LEQUAL);
glDrawBuffer(GL_NONE);

### #16vlj  Members

Posted 24 September 2014 - 10:00 AM

What is wrong with this picture ?

To have better result with self shadowing you need to use a bias or polygon offset.

### #17Silverlan  Members

Posted 24 September 2014 - 10:13 AM

What is wrong with this picture ?

To have better result with self shadowing you need to use a bias or polygon offset.

The different 'intensity' of the shadow between the cascades is what I'm trying to get rid of. You can see that the shadows on each cascade are darker than in the previous one.

### #18vlj  Members

Posted 24 September 2014 - 10:33 AM

I suspect there is some z fighting here.
What happens if you add a small offset to shadowcoord.w before sampling ?

### #19Silverlan  Members

Posted 24 September 2014 - 11:02 AM

I suspect there is some z fighting here.
What happens if you add a small offset to shadowcoord.w before sampling ?

That's... odd. I can change the 'w' component to anything, 1, 0, any value inbetween, negative values - It literally changes nothing, the result is always the same as in the screenshot I posted.

I assume that means something's wrong with the actual shadow map textures?

### #20vlj  Members

Posted 24 September 2014 - 01:56 PM

Did you set GL_TEXTURE_COMPARE_MODE too ? eg

glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE)

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.