2
\$\begingroup\$

I'm trying to implement shadow mapping to my engine. I started with directional lights because they seemed to be the easiest one, but I was wrong :) I have implemented deferred shading and I retrieve position from depth. I think that there is the biggest problem but code looks ok for me.

Now more about problem:

Shadow map projected onto meshes looks bad scaled and translated and also some information from shadow map texture aren't visible.

Yellow frustum is light frustum and I have mixed shadow map preview and actual scene. As you can see shadows are in wrong place and shadow of cone and sphere aren't visible.

Could you look at my codes and tell me where I have a mistake?

// create shadow map
if(!_shd)glGenTextures(1, &_shd);
glBindTexture(GL_TEXTURE_2D, _shd);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_FLOAT,NULL); // shadow map size
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _shd, 0);

glDrawBuffer(GL_NONE);

// setting camera
Vector dire=Vector(0,0,1);
ACamera.setLookAt(dire,Vector(0));
ACamera.setPerspectiveView(60.0f,1,0.1f,10.0f); // currently needed for proper frustum corners calculation

Vector min(ACamera._point[0]),max(ACamera._point[0]);
for(int i=0;i<8;i++){
    max=Max(max,ACamera._point[i]);
    min=Min(min,ACamera._point[i]);
}

ACamera.setOrthogonalView(min.x,max.x,min.y,max.y,-max.z,-min.z);

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _s_buffer); // framebuffer for shadow map

// rendering to depth buffer

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _g_buffer);

Shaders["DirLight"].set(true);

Matrix4 bias;
bias.x.set(0.5,0.0,0.0,0.0);
bias.y.set(0.0,0.5,0.0,0.0);
bias.z.set(0.0,0.0,0.5,0.0);
bias.w.set(0.5,0.5,0.5,1.0);

Shaders["DirLight"].set("textureMatrix",ACamera.matrix*Projection3D*bias); // order of multiplications are 100% correct, everything gives mi the same result as using glm

glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_2D,_shd);

lightDir(dir); // light calculations

Vertex Shader makes nothing related to shadow calculatons

Pixel shader function which calculates if pixel is in shadow or not:

float readShadowMap(vec3 eyeDir)
{
// retrieve depth of pixel  
float z = texture2D(depth, gl_FragCoord.xy/screen).z;  

vec3 pos = vec3(gl_FragCoord.xy/screen, z); 

// transform by the projection and view inverse 
vec4 worldSpace = inverse(View)*inverse(ProjectionMatrix)*vec4(pos*2-1,1);

worldSpace /= worldSpace.w;

vec4 coord=textureMatrix*worldSpace;

float vis=1.0f;
if(texture2D(shadow, coord.xy).z < coord.z-0.001)vis=0.2f;

return vis;
}

I also have question about shadows specifically for directional light. Currently I always look at 0,0,0 position and in further implementation I have to move light frustum along to camera frustum. I've found how to do this here: http://www.gamedev.net/topic/505893-orthographic-projection-for-shadow-mapping/ but it doesn't give me what I want. Maybe because of problems mentioned above, but I want know your opinion.

EDIT:

vec4 worldSpace is position read from depht of the scene (not shadow map). Maybe I wasn't precise so I'll try quick explain what is what: View is camera view matrix, ProjectionMatrix is camera projection,. First I try to get world space position from depth map and then multiply it by textureMatrix which is light view light projectionbias. Rest of code is the same as in many tutorials. I can't use vertex shader to make something like gl_Position=textureMatrix*gl_Vertex and get it interpolated in fragment shader because of deffered rendering use so I want get it from depht buffer.

EDIT2:

I also tried make it as in Coding Labs tutorial about Shadow Mapping with Deferred Rendering but unfortunately this either works wrong.

\$\endgroup\$
5
  • \$\begingroup\$ Are you using different projection matrices between shadowmap and screen rendering, but then using the same one for both tasks in the fragment shader? \$\endgroup\$
    – MickLH
    Commented Sep 22, 2013 at 20:43
  • \$\begingroup\$ Projection3D in the expression of your textureMatrix should be the same as ACamera.setOrthogonalView. Is it? \$\endgroup\$
    – darius
    Commented Sep 23, 2013 at 0:08
  • \$\begingroup\$ darius - Yes, ACamera.setOrthogonalView set Projection2D matrix, but because rendering functions require Projection3D matrix, I overwrite it with Projection2D values. I have to reorganize here some things because code isn't clear right now. @MickLH Shadow map has diferent projection matrix than screen rendering. And I use the second one to get world space position (I get it from depth buffer and it's screen size, shadow map has 1024x1024 resolution). Should I use here light projection matrix? \$\endgroup\$
    – Harry
    Commented Sep 23, 2013 at 6:04
  • \$\begingroup\$ Yes, you must reverse exactly the lights transformation when reading the shadowmap. \$\endgroup\$
    – MickLH
    Commented Sep 23, 2013 at 12:50
  • \$\begingroup\$ @MickLH I'm a little bit confused right now. When I use inversed light projection effect is even worse than previous attemp. I wrote some extra informations in first post. \$\endgroup\$
    – Harry
    Commented Sep 23, 2013 at 16:33

1 Answer 1

3
\$\begingroup\$

I've already found some time and solution to my problems and I want share it with you. Maybe it will help someone:

float distFromCentroid=ACamera.far();
camera.setLookAt(frustum_centroid+dir->direction*distFromCentroid,frustum_centroid);

for(int i=0;i<8;i++){
    point[i]=ACamera._point[i]*camera.matrix;
}

min=point[0]; max=point[0];
for(int i=0;i<8;i++){
    max=Max(max,point[i]);
    min=Min(min,point[i]);
}

camera.setOrthogonalView(min.x,max.x,min.y,max.y,-max.z-distFromCentroid,-min.z);
camera.set();

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _s_buffer);

Shaders["Null"].set(true);

glViewport(0, 0, D.shadowMapSize(), D.shadowMapSize()); // this helps me to solve first problem
// rendering to depth buffer, I used here glPolygonOffset to solve problems with bias
glViewport(0, 0, D.x(), D.y()); // reset viewport

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _g_buffer);

// rest of code is almost the same as in first post

Shadow shader method:

float readShadowMap()
{
vec4 worldSpace=inverse(View)*vec4(posFromDepth(gl_FragCoord.xy/screen),1); // posFromDepth get position in view space from depth texture

vec4 coord=textureMatrix*worldSpace;

float visible=1.0f;

if(texture2D(shadow, coord.xy).z < coord.z)
    visible=0.2f;

return visible;
}

Hope it will be useful :)

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .