8
\$\begingroup\$

I've implemented VSM (and also ESM) in my engine but results are for me not as I expected and saw in many examples published on network.

I set filtering of shadow maps to GL_LINEAR but when I compare result to normal shadow map it is visibly worse.

Screen

I tried calculate moments directly in point light shader or get it from texture as it is in most tutorials but results are the same.

Code:

uniform samplerCubeShadow shadowMap;

...

vec4 worldSpace=inverse(ViewMatrix)*vec4(pos,1);

vec4 coord=LightViewMatrix*worldSpace;
vec4 abs_coord=abs(coord);

float fs_z=-max(abs_coord.x, max(abs_coord.y, abs_coord.z));

vec4 clip=LightProjectionMatrix*vec4(0.0,0.0,fs_z,1.0);
float d2=(clip.z / clip.w)*0.5+0.5; // clamp to [0..1]

...

float shadowTexel=texture(shadowMap,vec4(coord.xyz,d2));

// VSM (Variance Shadow Map)
// get partial derivatives
float dx = dFdx(d2);
float dy = dFdy(d2);
vec2 moments = vec2(d2, d2*d2+0.25*(dx*dx+dy*dy));
return chebychevInequality(moments, shadowTexel);

Using this code I get results as on image above. I tried also not to use samplerCubeShadow but samplerCube but results are even worse. First, I got hard shadows. Second, shadows don fill area as they should when getting moments from other texture. Look at second screen. Here is also look at generated cube map. It isn't similar to what is in depth map even if I put depth/moment1 in all 3 channels.

Shader for getting moments:

// Vartex shader
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;

// Fragment shader
float depth = v_position.z / v_position.w ;
depth = depth * 0.5 + 0.5;          //Don't forget to move away from unit cube ([-1,1]) to [0,1] coordinate system

float moment1 = depth;
float moment2 = depth * depth;

// Adjusting moments (this is sort of bias per pixel) using derivative
float dx = dFdx(depth);
float dy = dFdy(depth);
moment2 += 0.25*(dx*dx+dy*dy) ;

FragColor = vec4( moment1,moment2, 0.0, 0.0 );

Screen

I'm really stuck. I hope you will help mi to solve my problems.

EDIT:

I've found the solution to second problem. I had enabled blending and it gaves me wrong depth map.

I also get better result to first problem but now I'm fighting with proper depth to compare it with depth from shadow map.

In simple SM I use this code:

vec4 worldSpace=inverse(ViewMatrix)*vec4(pos,1);

vec4 coord=LightViewMatrix*worldSpace;
vec4 abs_coord=abs(coord);

float fs_z=-max(abs_coord.x, max(abs_coord.y, abs_coord.z));

vec4 clip=LightProjectionMatrix*vec4(0.0,0.0,fs_z,1.0);
float d=(clip.z / clip.w)*0.5+0.5; // clamp to [0..1]

where pos is position in View Space. Then I read values from shadow map using:

texture(shadowMap,vec4(coord.xyz,d))

In VSM I store depth in R channel in RG32F texture. Depth value is calculated in this way:

// VS
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;

// FS
float depth = v_position.z/v_position.w;
depth = depth * 0.5 + 0.5;

Then in shader for point light I use coord vector (as in standard SM) to read values from shadow map and this works ok. But problem is in this part:

// No shadow if depth of fragment is in front
if ( moments.x <= distance)
    return 1.0;

In what coordinates distance should be get? In what coordinates I have depth from shadow map? It should be linear? Could someone explain me that? I'm little confused right now, I tried many ways to get this and all results aren't as I expect.

EDIT2: Following JarkkoL tip and this tutorial I changed my code. Now I'm storing depth using this code:

// VS
v_position=ModelViewMatrix*Vertex;
gl_Position=ProjectionMatrix*v_position;

// FS
const float Near = 0.1;
const float Far = 90.0; // camera far plane
const float LinearDepthConstant = 1.0 / (Far - Near);

float depth = length(v_position)*LinearDepthConstant;

And I compare it with value which I obtain in this way:

float dist=length( vec3(inverse(ViewMatrix)*vec4(pos,1.0)) - PointLight.position )*LinearDepthConstant; // pos is read from depth buffer and is in view space so I want invert it to world space as it was in tutorial

And here is the result:

Screen3

In the red circle I marked visible borders between cube map faces. There is still something wrong. I think that it could be someting with inversing of View Matrix but I'm not sure.

\$\endgroup\$
  • \$\begingroup\$ I've added info about my current progress. \$\endgroup\$ – Harry Oct 24 '14 at 20:49
  • 1
    \$\begingroup\$ You shouldn't use non-linear depth that you are using currently \$\endgroup\$ – JarkkoL Oct 26 '14 at 3:22
  • \$\begingroup\$ Thanks for the tip. I tried to follow it and I edited post with my new results. \$\endgroup\$ – Harry Oct 27 '14 at 21:46
  • \$\begingroup\$ Try setting address mode to clamp for the cubemap \$\endgroup\$ – JarkkoL Oct 28 '14 at 2:11
  • \$\begingroup\$ This helps indeed. I set GL_REPEAT for cubemaps and completely forgot about that. Anyway vsm shadows still doesn't render like simple shadow map. \$\endgroup\$ – Harry Oct 28 '14 at 16:15
1
\$\begingroup\$

Regarding cubemap seams, you can just filter across edges.

See: glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);

As far as your actual shadow quality, the entire benefit of VSM and ESM techniques comes from the special form that the visibility test takes. You probably want to introduce a constant factor for over- or under-darkening the shadows, so that the edge isn't so hard.

For ESM data it's straightforward:

light = exp(factor * (occluder - receiver));
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.