Dev:2.8/Source/Viewport/Soft shadows failure
Soft Shadow Map Faillure
In this document, we are only talking about realtime viewport shadow map blurring. The goal was to have some realistic soft shadows for spherical shadow maps (in our case octahedron maps) as this is what we use for shadowing point, spots and area lights.
- The shadowing problem:
What we need to find is what percentage of the light is visible from the receiving surface. This is true only if we are dealing with constant light source (not spatialy variying). If done precisely, the shadow's penumbra should depend on the distance to the occluders and the size of the lamp.
Traditionally for real-time rendering we record the distance to the first occluder from the light source in a shadow map. But this only gives the right occlusion for the exact position from which the shadow map was recorded. Also shadow maps suffer from a low precision since the recorded distance is quantized.
- Try 1
The first idea was to sample the shadow map based on the projected area of the light shape onto the cubemap near "sphere". This would ensure all the possible occluders inside the visibility cone could be found. The problem with this is that searching occluders from the light is not equal to searching from the shading point. Sampling was terrible and math overly complex for nothing. Shadows were disappearing after some short distance if the search area was too big, due to undersampling.
- Try 2
To fix the initial approach I tried to generate random rays from the shading point to the light shape. Theses would be used for raytracing against the shadow map. This lead to quite decent results (see images). But this had big flaws. Since we only have first occluder distance recorded in the shadow map we cannot account for ocluder thickness. And infinitly thick occluders only gives half of the real penumbra (because they completly triggers 100% ray hit if an occluder is in front of the shading point) So I had to resort to something similar to the SSR and added a thickness attribute to detect intersections. But this introduced another problems. Now the linear raymarch could go through thin occluders. Also performance was a huge problem since this technique required a huge amount of shadow map samples (32 per rays to be decent).
- Try 3
I tried to optimize the raymarching process of these shadow rays by grouping them and advancing them with the same one sample. Also I would fix the ray going trough occluders for facing rays. But that ended up behind way too complex to implement. Also I realized that this technique (without resorting to depth peeling) was creating "floating" shadow texels due to the thickness problem.
- Try 4
After reading a bit on the subject, I found out that I was doing something stupid. Tracing the rays is very inefficient and one rather equivalent technique is called occluder reprojection. I wont go over the detail of it but in short, you compute the occluded area for each occluder inside the visibility cone. This is a great idea but you need a way to not count occluded area twice. This is done with a technique called bitfield shadow mapping. I tried to implement a prototype of it in shadertoy but (even if not finished / buggy) it suffers from the same 1 layer problem which is light leaking ([ShaderToy Prototype]). Also without complex acceleration structures, the performance are not there.
For comparison here is [the ground truth]
- Try 5
My last hope was to implement Percentage Closer Soft Shadows. Unfortunately, it's a hack that tries to mimic the real soft shadows. Although it does not suffer from light leaking, I could not find/do a correct implementation of it for spherical shadowmaps. Also getting it to work with Exponential/Variance Shadow map would require using Summed Area Tables that are not practical to generate without compute shaders, and it would only works for blurring using Box filter kernels (kinda low quality, and also would not work for spherical maps!).
- Fixing the faillure
So what I think I'll be doing is this: - Keep fixed penumbra size filtering. - Add a filtering angle option per lamp. - Add an option to preview area shadow in the viewport by having multiple shadowmaps generated from it's light shape. - Jitter shadowmap capture position for every AA frame when rendering offline. - The PCSS could be tried again for sun shadows as it seems to be really efficient in theses case.
We could jitter the shadow map position for every TAA sample, but that would not create noise that TAA could fix. It would create well defined shadows that would smear. Only way to have these jittered temporal shadows is to have perfectly still image (no animation).
I think that coupled with the filter radius it should give some decent result. Unfortunatly it might get difficult to have a perfect shadow.
- In the Future
I don't have time at the moment to investigate theses techniques. Also they seem to use GPGPU (CUDA) for their implementation. Maybe this could fit into a compute shader (require ogl 4.0).
Backprojection seems to be the most correct solution. Yet it's really slow in practice, hard to implement.
Another approach is Visibility sampling (see paper) which seems to be even more accurate. But its implementation is also complex and it does not work for transparent objects. Also its view dependancy require it to be recomputed each frame.