Scalable way to generate Shadow maps for scene with multiple light sources

Started by
1 comment, last by Juliean 2 years, 7 months ago

I've been reading up on this tutorial: Tutorial 41: Multiple Light Shadow Mapping (rastertek.com) from RasterTek on how to do multiple light shadow mapping. They do it in a pretty brute force style. Ie. they decided to add a second shadow casting light so they're just adding everywhere a lightPos2, lightViewMatrix2, lightProjectionMatrix2, shadowTexture2, … 2.

I have already implemented shadow mapping for 1 light source in a way that is satisfactory to me. I was in search of a way to expand this, by using multiple shadow casting lights and just have them “add” their contributions to the finalColor of the final Pixel Shader. How do I do this? Particularly in the Shader side.. I believe I know how to do it in C++ side. But how to have the shader code, the vertex shader and pixel shaders in particular. How should they accumulate all these shadow & light contributions without having to hardcode how many lights, and textures, and constant buffers and parameters there would be (because I don't know beforehand - it depends on the scene and how many lights I add to it).

To elaborate further.

  1. The current state of my Vertex Shader outputs a posLightSpace variable, which is basically (as you'd have guessed) the vertex pos in the light source's space. But this is only 1 light source. How do I factor in multiple light sources?
  2. The current state of my Pixel Shader gets that posLightSpace variable which I use to calculateShadowLevel() and I use SamplerComparisonState SampleCmpLevelZero with hardware PCF to do the sampling from the single shadow map TextureCube. Later I proceed to use that single's Light source vectors to do my lighting calculations. So this is a single shadowMap, a single light source parameters/vectors. How do I factor in multiple light source contributions and multiple shadow maps?

I should note, if it hasn't been clear, that I'm using forward rendering. I haven't progressed into deferred territory yet.

I must be missing a critical step, I can't believe the messy RasterTek's article approach should be the only way here.

How do I make the shader code scale to multiple sources?

I hope I made myself clear, if not let me know. Help please. Thank you.

Using DirectX, HLSL, C++.

None

Advertisement

Key_C0de said:
I have already implemented shadow mapping for 1 light source in a way that is satisfactory to me. I was in search of a way to expand this, by using multiple shadow casting lights and just have them “add” their contributions to the finalColor of the final Pixel Shader. How do I do this? Particularly in the Shader side.. I believe I know how to do it in C++ side. But how to have the shader code, the vertex shader and pixel shaders in particular. How should they accumulate all these shadow & light contributions without having to hardcore how many lights, and textures, and constant buffers and parameters there would be (because I don't know beforehand - it depends on the scene and how many lights I add to it).

We have to talk about two different things here - arbitrary number of lights, and arbitrary number of lights with shadows.

For having a highly scalable number of lights, there are many techniques - i personally implemented “clustered" deferred shading from this article (https://www.humus.name/Articles/PracticalClusteredShading.pdf).​ This can practically support a few thousand lights, without having to hardcode the number (though you are still somewhat limited by texture-sizes and/or cbuffer-limits).

Now shadows is a whole different beast. The technical limitations aside, having 1000 shadow-casters is a very bad idea in general. Having to draw the scene 1000 times, one for each light is going to kill any card in any scene real fast. So thats the main reason why even big AAA game-engines ususally limit the number of dynamic shadow-casters; and use different shader-permutation for the number of supported shadowed lights.
If you still really want to do this, I quess you could try:

  1. Moving all shadow-related calculations into the pixel-shader, instead of passing data from the vertex-shader. Yup, thats going to make things slower but as I said, thats already what you are going to get.
  2. Do it the “old” forward/deferred way: Draw each object once for each light-source that affects it (forward); or draw each light-source with a primitive-shape over the g-buffer - with additive blending; that way you can have one shader always calculate one light+shadow, and still have as many as you want (though I personally don't like this approach due to the additional complexity, and performance-drawbacks.

This topic is closed to new replies.

Advertisement