Jump to content
  • Advertisement
Sign in to follow this  
Fnord42

Shadow-map for multiple omnidirectional lights

This topic is 2636 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

Hi there,

I'm thinking about how I can implement shadow-mapping for multiple point-lights with cube-maps.

My current idea is to bind a cube-map with only a depth-component to an FBO.

Then for each light I want to do the following:
1) Draw to the shadow-map-FBO using simple shaders, which render all triangles in lightspace simply black to trigger writing to the depth-buffer.
2) Calculate the light for all pixels using the shadowmap and add it to the default FBO's Colormap.

Then I want to render my objects for real, combining their texture-color with the light-color that is already in the default FBO.

Now my question is: Is this going to work?
I've read about people creating shadow-cube-maps with 6 rendering passes. Is there something that I haven't thought about, that's preventing me from doing this in only 1 pass? How I've understood a cube-map-depth-buffer, for each vertex-direction the depth-value is stored, so this should work, right?

greets & thanks for your time!

edit: I forgot to mention that at step 1) I thought about just using the lights view matrix without the projection matrix.

Share this post


Link to post
Share on other sites
Advertisement
You need six projection matrices - one for each side of cube. And thus also 6 lights-pace matrices - each orienting negative Z to the direction of corresponding side.

You can still render shadow map in one pass using instancing or geometry shaders, but there is no way to avoid separate projection matrices. Except, of course, dual paraboloid shadow map, but this is quite different issue.

Which method (rendering in 6 passes or using geometry shaders) is actually faster depends AFAIK on GPU and driver versions. If you have frustum culling enabled the amount of geometry rendered is not too different. Doing 6 sides in one pass may actually be bad for memory locality, as all your triangles are each time guaranteed to touch all cubemap sides - but I do not know, how much this may be issue on modern GPU-s..

Share this post


Link to post
Share on other sites

You need six projection matrices - one for each side of cube. And thus also 6 lights-pace matrices - each orienting negative Z to the direction of corresponding side.

You can still render shadow map in one pass using instancing or geometry shaders, but there is no way to avoid separate projection matrices. Except, of course, dual paraboloid shadow map, but this is quite different issue.

Which method (rendering in 6 passes or using geometry shaders) is actually faster depends AFAIK on GPU and driver versions. If you have frustum culling enabled the amount of geometry rendered is not too different. Doing 6 sides in one pass may actually be bad for memory locality, as all your triangles are each time guaranteed to touch all cubemap sides - but I do not know, how much this may be issue on modern GPU-s..


Thanks for your answer. I've read about the conventional way with a geometry shader, the 6 passes method and also paraboloid-mapping. But isn't there a way to skip the projection?
I thought, if I transform the vertices in light-space, i could use their direction as a cubemap coordinate and their distance from the light-space null-vector as depth-value.
Is there really no way to write from fragment-shader to a cubemap with directional texture-coordinates?

I've tried to set gl_Position in the vertex-shader of the shadow-map-calculation to the normalized vertex-position in lightspace and to use the vertex's distance to the light-space null-vector as fragment-color. But if I use this shader with an FBO which has cubemaps as depth- and color-attachments only one face of the cube is used, the other faces are static but seem to be undefined.

edit: I've read in the opengl.org/wiki that I can only attach one face of a cubemap to a framebuffer. Can I write only to framebuffers from a shader or are there other locations I can adress?

Share this post


Link to post
Share on other sites

Thanks for your answer. I've read about the conventional way with a geometry shader, the 6 passes method and also paraboloid-mapping. But isn't there a way to skip the projection?
I thought, if I transform the vertices in light-space, i could use their direction as a cubemap coordinate and their distance from the light-space null-vector as depth-value.
Is there really no way to write from fragment-shader to a cubemap with directional texture-coordinates?

I've tried to set gl_Position in the vertex-shader of the shadow-map-calculation to the normalized vertex-position in lightspace and to use the vertex's distance to the light-space null-vector as fragment-color. But if I use this shader with an FBO which has cubemaps as depth- and color-attachments only one face of the cube is used, the other faces are static but seem to be undefined.

Okay, I probably understood what you meant.

Basically you want to replace standard projection (frustum) with custom calculation of gl_Position.

I think the biggest problem is, that your triangles will not be rasterized over cubemap. Rasterizer projects your triangle to XY viewport plane and then runs fragment shader for all pixels - if, for example, the triangle is parallel to XZ plane in clip coordinates (i.e. after perpective division) it will not be rasterized at all because it covers 0 viewport pixels.

Also do not forget, that single triangle can cover many sides of cubemap. The maximum is 5, although 2 and 3 are probably more common. Rasterization is bound to single layer/viewport I think. And I am pretty sure rasterizer will not select cube map side automatically - you have to do it in geometry shader.

Share this post


Link to post
Share on other sites

[quote name='Fnord42' timestamp='1316798099' post='4865207']
Thanks for your answer. I've read about the conventional way with a geometry shader, the 6 passes method and also paraboloid-mapping. But isn't there a way to skip the projection?
I thought, if I transform the vertices in light-space, i could use their direction as a cubemap coordinate and their distance from the light-space null-vector as depth-value.
Is there really no way to write from fragment-shader to a cubemap with directional texture-coordinates?

I've tried to set gl_Position in the vertex-shader of the shadow-map-calculation to the normalized vertex-position in lightspace and to use the vertex's distance to the light-space null-vector as fragment-color. But if I use this shader with an FBO which has cubemaps as depth- and color-attachments only one face of the cube is used, the other faces are static but seem to be undefined.

Okay, I probably understood what you meant.

Basically you want to replace standard projection (frustum) with custom calculation of gl_Position.

I think the biggest problem is, that your triangles will not be rasterized over cubemap. Rasterizer projects your triangle to XY viewport plane and then runs fragment shader for all pixels - if, for example, the triangle is parallel to XZ plane in clip coordinates (i.e. after perpective division) it will not be rasterized at all because it covers 0 viewport pixels.

Also do not forget, that single triangle can cover many sides of cubemap. The maximum is 5, although 2 and 3 are probably more common. Rasterization is bound to single layer/viewport I think. And I am pretty sure rasterizer will not select cube map side automatically - you have to do it in geometry shader.
[/quote]

Thanks for the answer. Then I'll probably go with the 6-pass shadow-cube-mapping.

Share this post


Link to post
Share on other sites
Hi again,

now my shadow-cube-map looks good but the shadows are looking really wrong (picture at the end of this post).
For test-reasons I'm currently rendering the shadow to a color-cube-map where all colors are the light-vertex-distance in worldspace scaled to a range between 0.0 and 1.0.
Then when I draw my scene, I'm calculating again the vector from light to vertex in worldspace and use this vector as the direction-vector of the texture-look-up of the shadow-cube-map and compare the shadow-texture-value with the scaled length of this vector. For scaling I divide through the far-plane of the projection which I use for creating the shadow-map.

I would really apreciate it if somebody could check what's wrong, I'm already messing with shadows for a few days and it just won't work ;)

Those are my shaders for rendering to a face of the shadow-map:
// ===== Vertex-Shader =============================================
in vec4 position; // In worldspace

uniform mat4 light_vp_matrix; // view-projection-matrix for the current face of the lights cubemap, far-plane: 40.0
uniform vec3 light_pos; // In worldspace

out vec4 var_light_to_vertex_ws;

void main(void)
{
var_light_to_vertex_ws = position - vec4(light_pos, 1.0);
gl_Position = light_vp_matrix * position;
}

// ===== Fragment-Shader =============================================
in vec4 var_light_to_vertex_ws;
out vec4 frag;

void main(void)
{
float dist_from_light = length(var_light_to_vertex_ws.xyz)/40.0;
frag = vec4(vec3(1.0, 1.0, 1.0) * dist_from_light, 1.0);
// gl_FragDepth = dist_from_light; // produces same strange results
}


Those are my shaders for drawing objects with light and shadow:
// ===== Vertex-Shader =============================================
in vec4 position;
uniform vec3 light_position_ws;
uniform mat4 mvp_matrix;
out vec4 var_light_to_vertex_ws;
...

void main(void)
{
var_light_to_vertex_ws = position - vec4(light_position_ws, 1.0);
gl_Position = mvp_matrix * position;
...
}

// ===== Fragment-Shader =============================================
in vec4 var_light_to_vertex_ws;
uniform samplerCube shadow_map;
out vec4 frag;
...

void main(void)
{
// Calculate shadow-map-depth of this fragment.
vec3 shadow_map_dir = normalize(var_light_to_vertex_ws).xyz;
shadow_map_dir.xz = -shadow_map_dir.xz; // For some reason it looks more right if I flip those.
float shadow_map_depth = texture(shadow_map, shadow_map_dir).r;

// Calculate depth of this fragment.
float frag_depth = length(var_light_to_vertex_ws.xyz)/40.0;

float is_in_light = 0.0;
if (shadow_map_depth < frag_depth + 0.0005)
is_in_light = 1.0;

...
}


Thanks for you time!

Light is flying over the terrains center @ (0.0, 6.0, 0.0)
terrain_buggy_shadows.png

The shadow-cube-map:
shadowcubemap.png

Share this post


Link to post
Share on other sites
The problem lies definetly in the cubemap-texture-lookup.
This happens if I set the fragment color to vec3(1.0, 1.0, 1.0)*shadow_map_depth:
terrain_buggy_shadows2.png

I just can't figure out whats wrong with the lookup :(

Share this post


Link to post
Share on other sites
When you write gl_Position, how does it know what face of the cubemap the triangle is going to write too?And the 1 pass sounds iffy to me, your going to try and draw 6 times the amount of models, effectively no culling then.

Share this post


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

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!