Percentage-Closer Soft Shadows #1107
Replies: 41 comments 91 replies
-
Thanks Chris for posting this work. Once the implementation is further along I'll merge with VSG and vsgExamples master, as a step in this direction I have merged the changes as a VSG separate-shadow-samplers branch and a vsgExamples soft-shadows branch: https://github.com/vsg-dev/VulkanSceneGraph/tree/separate-shadow-samplers I've done a first past review of the changes to the VSG, it looks like we should be able to use the new createDescriptorImage convenience functions. I want to test the changes against the original cascaded shadow map implementation, if that works fine then I'll get these changes to the VSG merged with master, hopefully wrapped up today. Looking at vsgExample changes to shaders suggest that the phong shader is the most mature and as there is an overlap between the phong and pbr shaders w.r.t shadows I think it's probably time we created a dedicate shadow.frag/shadow.glsl file that is included by phong or pbr shaders to reduce the amount of duplication. I would also like look at controlling whether Percentage-Closer Soft Shadows is enabled in the shaders and the settings used. Once I have spent more time with the new shader and C++ code I have a better idea of what changes to vsg::ViewDependentState and vsg::Light might be required. One change I have been considering is moving the vsg::Light::shadowMaps value out of vsg::Light and leaving this to vsg::ViewDependentState. Adding extra light size property to vsg::Light to help guide the size of penumbras seems like something we might consider as well. |
Beta Was this translation helpful? Give feedback.
-
I have been testing the soft-shadows branch of vsgExamples and found that the modified standard_phong.frag that defaults to num of samples to 8 I get obvious under sampling issues, but upping this to 16 I get significantly better results and obviously going all the way up to 64 produces very nice results. To harder to judge performance impact without creating a set of test models and animation paths. From my initial tests with just the simple vsgshadow test model I'm seeing 2750fps with 8 samples, 2131fps for 32 samples. I'm thinking that ViewDependentState should pass in the number of samples to use. The angleSubtended is something I would put into vsg::DirectionalLight and pass to the shader via the LightData. Defaulting to sun and earth makes sense. In the shader I see that the tan(angleSubtended/2) is used so perhaps ViewDependentState can compute this. The use of the inverse of the shadow map matrix is something that could be done by ViewDependentState. |
Beta Was this translation helpful? Give feedback.
-
I used the convenience functions to create the sampler descriptors, but the sampled image descriptors needed The prior shadow implementation should work just fine with the separate sampler and image provided it's adapted to combine them in the shader. That should just mean changing layout(set = VIEW_DESCRIPTOR_SET, binding = 2) uniform sampler2DArrayShadow shadowMaps;
...
texture(shadowMaps, vec4(sm_tc.st, shadowMapIndex, sm_tc.z)).r to layout(set = VIEW_DESCRIPTOR_SET, binding = 2) uniform texture2DArray shadowMaps;
layout(set = VIEW_DESCRIPTOR_SET, binding = 4) uniform sampler shadowMapShadowSampler;
...
texture(sampler2DArrayShadow(shadowMaps, shadowMapShadowSampler), vec4(sm_tc.st, shadowMapIndex, sm_tc.z)).r As an aside, this made me realise I'd forgotten to put some of the shadow implementation in the PBR shader, so I've just pushed another commit that copies and pastes the rest.
Some of the difference will be the aforementioned stuff I forgot to copy and paste until just now. I definitely agree that splitting it our into a separate reusable file is a good idea. This also makes switching implementations easier - even if multiple implementations end up in the same file, it prevents filling all the lighting shaders with several shadow implementations, but there's also the option of putting each shadow implementation in its own file.
Eight was essentially chosen arbitrarily, but with the sampling pattern I ended up on, the undersampling artefacts are a lot less bad than they were with much higher sample counts and all the other ones I tried first. I'm definitely in favour of it being parameterised, but I'm not sure
The precomputable part of the trigonometry's currently left in the shader because while its inputs are The inverse light space matrix is in the shader because I wanted to gauge your appetite for using up more of the light data buffer on something like that before making things more complicated. |
Beta Was this translation helpful? Give feedback.
-
Something that I've alluded to on one of our calls, but I've only just confirmed is a real problem is the depth clamping. If all you care about is whether something occludes light, it makes sense to enable depth clamping for the shadow map RTT and restrict light space to only cover the frustum as it means there's more depth precision available where it matters. However, when PCSS is used, you also care about how far away occluders are, and depth clamping squashes a bunch of potential occluders onto the shadow camera's near plane. This means that any occluders outside the view frustum potentially end up with much narrower penumbras than they're supposed to have, and this can look particularly bad when zooming in etc. as they grow and shrink before your eyes. The solution would be to disable depth clamping and extend light space to encompass all potential occluders rather than just all potential receivers. Currently, disabling depth clamping in the vsgShadow example only makes the first change, which just means that a bunch of occluders are left out and some shadows are totally missing, or even worse, totally missing from just one shadow map, causing an abrupt cutoff. I'm under the impression we don't necessarily know the bounds of the set of potential occluders for a shadow map, so would need an arbitrary large amount of depth precision to be wasted, but at the worst, a bound could be set based on the blocker search radius and angle subtended by the light. With the current default of 1m, we'd 'only' need 220m of shadow map depth range to be nearer the near plane than the nearest shadow receiver. As a side note, while calculating that I realised I'd got the angle subtended by the Sun on Earth wrong by a factor of ten. When I use the correct value, the soft shadows are a lot less impressive in the example scenes than the screenshots above. Reducing the penumbras from 36cm to 3.6cm obviously has a big impact, not least because lots of shadow map texels aren't much smaller, so end up visible. On the plus side, if a new planet gets discovered much, much closer to the sun than Mercury, we know that what I've created would work pretty well for simulating the shadows there. |
Beta Was this translation helpful? Give feedback.
-
W.r.t depth clamp, currently this is enable for the whole scene graph, my plan is to specialize the shadow map rendering so only depth clamp is used for it. This doesn't change the issue you raise but as a heads up that I may do some work in this area. |
Beta Was this translation helpful? Give feedback.
-
W.r.t blocker search requiring unclamped shadow/depth values, we don't know the extents of that the shadow map requires unless we run a compute bounds traversal, something that is expensive so not something we'd want to do on the fly. So we could pick a conservative value for the near plane of the shadow map, pushing it back towards the light to make sure it captures all the geometry that we think is relevant. I think you are suggesting that do this but limit how far we push the shadow map near plane back. When originally implementing the shadow map rendering I did experiment with not clipping the depth value with the intention of just having unbounded floating values rendered to the shadow map. I didn't get this to work, but I may well have just been doing it wrong. If it's possible then it might be worth seeing if this is possible rather than resorting to using the depth clamp. |
Beta Was this translation helpful? Give feedback.
-
@robertosfield Does vsg-dev/vsgExamples@31f288f seem like the right approach? |
Beta Was this translation helpful? Give feedback.
-
I've got a performance comparison from yesterday's change with made it so the inverse shadow matrix is computed on the CPU instead of in the fragment shader: 64 samples
8 samples
So I think it's fair to say it barely helps, if at all. I don't have an explanation for why it ends up slower for the single-light single-shadow-map 64-sample case, but it's consistent when I come back to it, so doesn't appear to be down to something like a background task running on my machine while I was doing that one test. |
Beta Was this translation helpful? Give feedback.
-
Some more investigation about the impact of the stochastic rotation of the poisson disk. Obviously, the most obvious impact of this is that it's going to convert artefacts with patterns into noise, which should be less visually impactful. When there are only a few samples, the most obvious problem is fairly severe banding, and that gets pretty effectively turned into much less severe noise. However, it would make intuitive sense if the banding wasn't a problem in the first place when the sample count was turned up high, but it's still clear that there's some noise under certain conditions when you look closely at the shadows even with sixty-four samples, so there must be some kind of signal that it's scrambling to create that noise. Disabling the rotation makes its causes pretty clear:
From a little local testing, I've determined that most of the noise visible through the rotation is caused by 64 samples not being enough to totally eliminate banding, unless the resolutions really bad. That's irritating, as it's not like simply throwing more samples at the problem is an entirely viable solution. The results from the previous post demonstrate that going from eight samples to sixty-four halves the framerate, so going from 64 to 128 might well halve it again, but will only halve the intensity of the noise. However, I don't think this justifies abandoning the current approach and switching to moment shadow maps or anything like that. I imagine that a big part of why I'm noticing this is that I've spent a long time staring at these shadows so am hyper-aware of any defect that most people wouldn't notice. Additionally, the noise is very attenuated by a gentle blur or small amount of scaling of the final image (in fact, you might not be able to see there's noise at all in GitHub's preview until you click the image to see it at its original resolution), so I'd expect that any application using shadows in addition to bloom, depth of field, or temporal anti-aliasing won't have visible noise. Also, most applications will be using textured meshes instead of solid colours like this test scene, and the noise will be far less significant than colour variations from textures. 8 samples, no rotation8 samples, rotation64 samples, no rotation64 samples, rotation |
Beta Was this translation helpful? Give feedback.
-
Also, I just did a quick test of the various techniques that now exist on my branch.
Eight samples (for soft techniques)
|
technique | average framerate |
---|---|
--pcss |
557.26 |
--pcf |
646.871 |
--hard |
711.961 |
--none |
708.328 |
Beta Was this translation helpful? Give feedback.
-
Thanks for all the details. It sounds like you've close to exhausting what you can do with this type of PCSS algorithm. Do you feel the implementation is close to ready to merge with the main VSG? It's a curious finding that the CPU inverse is not noticeably faster, this suggests that read memory bandwidth is a significant cost vs cost of computing an inverse. This doesn't sound right but your observations are hard to explain away otherwise. Trying different hardware might be informative. What hardware are you working on for these tests? Have you tried looking at the results with multisampling enabled? |
Beta Was this translation helpful? Give feedback.
-
Another bit of data:
The result for 0.01 units with eight samples is particularly interesting as it's faster than fixed-radius PCF at 0.05 units, despite doing extra maths and twice as many texture accesses. Together, these numbers are a strong sign that the biggest problem is simply waiting to sample bits of the texture that aren't in the cache, and there might be decent gains to be had by reordering the sample points in the poisson-like disk so bits of the sampled texture near to each other get sampled in quick succession, reducing the chance relevant cache lines have been evicted. The tool I used to generate the sampling disk as a feature intended for this that I haven't used yet, so I'll see if I can generate something that runs faster without sacrificing the progressive property that allows the same disk to be used for small and large sample counts. |
Beta Was this translation helpful? Give feedback.
-
It looks like the tool either does the cache optimisation or maintains the progressive property, but can't do both (and also the cache optimisation seems a little naive as it's just sorting points by their y coordinate). However, it definitely impacted performance:
|
Beta Was this translation helpful? Give feedback.
-
I tried merging your vsgExamples soft-shadows branch with main repo's vsgExamples soft-shadows branch but I didn't get any shadows exact for when using --technique hard, using pccs I don't see anything shadow. WIth soft I do see shadows but not if I use more than one shadow map. When I use the --technique soft I only see to get shadows for the what is probably the nearest shadow map. I tried use your repos soft-shadows branch directly as well but get the same result. Could there a check-in missing? |
Beta Was this translation helpful? Give feedback.
-
I have merged the soft shadows work into VSG and vsgExamples as soft-shadows branches: https://github.com/vsg-dev/VulkanSceneGraph/tree/soft-shadows Let me know if I've made any mistakes in the merge. To test things I tried: vsgshadow --technique hard --sm 3 --dc --direction 1 1 -1.0
vsgshadow --technique pcf --sm 3 --dc --direction 1 1 -1.0
vsgshadow --technique pcss --sm 3 --dc --direction 1 1 -1.0 In testing I found that all of the techniques are hitting up clipping of the shadow map, even with depth clamp on. Unfortunately Github doesn't allow me to attach .vsgt files so I've renamed them to .txt: problem_small.txt To test it out for the large dataset I use this command line: vsgshadow --technique hard --sm 3 --dc --samples 8 --shadow-samples 64 --direction 1 1 -0.5 -n 2 --large --sd 1000 -p problem_large.vsgt I tested this same path and command line with VSG + vsgExamples master and see the same problem so this clearly isn't soft shadows specific issue. It seems to be low angle light direction that seems to provoke it. |
Beta Was this translation helpful? Give feedback.
-
As another simplification step I have modified the vsgshadow example so that it sets up the shader hints just once then uses this for the phong and pbr ShaderSets: This change is checked into vsgExamples soft-shadows-simplfied branch. There is still more stuff I'd like to get things ready for merging with master, but will be to return to this tomorrow. |
Beta Was this translation helpful? Give feedback.
-
I have refactored the way vsgshadow sets up the ShadowSettings and shaderHints so it's all done more locally: I'm now wondering about how we might combine the defines required into the ShadowSettings subclasses. |
Beta Was this translation helpful? Give feedback.
-
I checked in an shader optimization to the soft-shadows-simplfied branch of vsgExamples that changes how the loops are testing and exited: With the huge_medieval_battle_scene test model:
The PCF cases is a bit skewed because it's using a penumbra radius of 0.1 on model that is so small, I've also changed the example to enable the radius to be passed in I get 64fps, I've left the same values as the original test though so we can see like for like. A structuring of the PCSS shader a bit further so the that occluder search use the same approach I've done for the final sampling might net additional improvement in fps. As part of the changes I've made today I've changed vsgshadow so that it takes --hard, --pcss and --pcf radius command line options for toggling the technique rather than the previous --technique command line option. This allows vsgshadow to be a bit more centralized and easier to follow. I'm seeing a regression vs VSG/vsgExample master in vsganimation with hard shadows but I haven't yet got to the bottom of this. |
Beta Was this translation helpful? Give feedback.
-
I have add the ability to override the ShadowSettings per Light, or as a catch all in ViewDependentState: Changes to VSG: vsg-dev/vsgExamples@888070c This change should make it possible to have different View's with different ShadowSettings such as reducing/increasing visual quality. |
Beta Was this translation helpful? Give feedback.
-
@AnyOldName3 I'm now close to being ready to merge the soft-shadows-simplified branch with VSG master. Are there issue you can think of that I should wait for? I am thinking about renaming the shadow_pcf.glsl and associated VSG_SHADOW_PCF to shadow_soft.glsl and VSG_SHADOW_SOFT respectively as PCF is a bit cryptic. We could add doxygen and shader comments explaining the algorithm details. |
Beta Was this translation helpful? Give feedback.
-
During testing I found the vsgtextureprojection example was no longer creation textures so I've updated the vsgExamples/data/shaders/textureprojection_phong.frag to be consistent with the new standard_phong.frag that utilizes the new shadow*.glsl shaders: The following change also had to be made to enable shadows: auto shaderHints = shaderSet->defaultShaderHints = vsg::ShaderCompileSettings::create();
if (numShadowMapsPerLight>0)
{
shaderHints->defines.insert("VSG_SHADOWS_HARD");
} Previously hard shadows were supported out of the box without the need for any additional defines, so the soft-shadows branch work will break them as things currently stand. We could possibly just have VSG_SHADOWS_HARD always be built into the shaders. This would at least help existing application mostly work as before when they update to VulkanSceneGraph-1.1.3. I am thinking about adding a ShadowSettings std::string define member variable that could be used to pass on to the ShaderSet configuration. The HardShadow::define would default to VSG_SHADOWS_HARD, SoftShadow::define to VSG_SHADOWS_SOFT and PercentageCloserSoftShadows::define to VSG_SHADOWS_PCSS. ShaderSet's don't know about ShadowSettings so figuring out which optional code paths to enable would still need to be done explicitly by applications even if it could be made less hardwired than passing a specific "VSG_SHADOWS_*" string. |
Beta Was this translation helpful? Give feedback.
-
I have decided to return the phong and pbr ShaderSets to enable VSG_SHADOWS_HARD code path by default so compatibility with older versions of the VSG is a bit more seamless: Users will still need to change light->shadowMaps = numShadowMaps; to light->shadowSettings = vsg::HardShadows::create(numShadowMaps); But this necessary to provide a coherent and extensible means of defining the desired shadow rendering technique so key part of evolution of the VSG's API to handle increasingly sophisticated types of rendering out of the box. I have now tackled all the issues I've spotted in today's review and testing so I'm ready to merge with VSG/vsgExamples master. I'll do one more round of review & testing then merge. |
Beta Was this translation helpful? Give feedback.
-
I have now merged the soft-shadows-simplified branches of the VSG and vsgExamples with the respective masters: |
Beta Was this translation helpful? Give feedback.
-
@AnyOldName3 I'm replying here as trying to navigate nested replies is painfully out of chronically order. W.r.t optimizations, we are working on different OS, hardware and of course drivers and test models so I'd expect some variations. The performance regression with storage buffer usage is troubling. For an variable sized data structure a storage buffer is better fit, but if it's hammering performance then we may need to think about making it fixed sized. If it's cache optimization issue that your hardware/driver is hitting up against then then storing the inverse matrix may part of the problem. |
Beta Was this translation helpful? Give feedback.
-
It might be worth taking vsg-dev/vsgExamples@master...AnyOldName3:vsgExamples:speedy-bodge for a spin on hardware other than mine before I put effort into doing it properly. It seems to give me maybe a 10% boost, but I cut corners (e.g. hardcoding things) and don't know how much of the benefit will stay once everything's done right. |
Beta Was this translation helpful? Give feedback.
-
I didn't do before and after performance test from the storage buffer change. I made this change for both jointMatrices used for skinning and the lightData. Both I changed to avoid the hardwiring of sizes in the shaders. I will need to create a branch to test uniform vs buffer so it can be tested on a range of system. I have an Intel LInux lapto, and Intel Windows desktop both with integrated graphics, a AMD 5700G Linux destop but it has my Geforce 2060 plugged in. I will need to formularize a set of performance tests. |
Beta Was this translation helpful? Give feedback.
-
I have updated the StorageVsUniform branches of VSG and vsgExamples to enable testing of storage vs uniform buffers for the lightData. This allows us to do tests with storage buffer (default) or uniform buffer (enabled with --ubo) such as: vsgshadow --large -p saved_animation.vsgt -t --duration 5.0 --sm 3 --pcss
vsgshadow --large -p saved_animation.vsgt -t --duration 5.0 --sm 3 --pcss --ubo The animation path I used is attached but renamed to have .txt extension, this just needs to be removed to use it as above: Results on my AMD 5700G + Geforce 2060 linux system shows uniform buffer is faster by 22% for hard shadows, 12% for PCSS and 6% for Soft shadows (with a penumbra of 1.0 for the above test.) I did see a noticeable difference in vsganimation with a skinned model so it looks like use of storage buffer in the vertex shader has far less impact than use in the fragment shader so it may be that we can split the ticket. I will now test on my Intel Linux laptop and my Intel Windows 11 desktop both with integrated graphics. |
Beta Was this translation helpful? Give feedback.
-
On Thu, 4 Apr 2024 at 16:52, Chris Djali ***@***.***> wrote:
I did try using specialization constants to set the lightData[] arrays
size but GLSL/SPIR-V didn't allow this.
One of the examples for specialization constants in the GL_KHR_vulkan_glsl
spec is setting an array size, so I'm surprised to hear that, unless it's a
bug in glslang which might have been fixed. I just tried
layout(constant_id = 17) const int arraySize = 12;layout(set = 7, binding = 0) uniform TestUniform
{
mat4 matrices[arraySize];
} mats;
and it at least compiles.
It could have been a bug/limitation in an older version of glslang. It's
something I tried last year and just couldn't get it to work.
|
Beta Was this translation helpful? Give feedback.
-
@AnyOldName3 This morning I made a series of changes to the VSG to help optimize rendering performance, merging changes to use Uniform Buffer for LightData and then introduction of VSG_ALPHA_TEST #define into the build-in ShaderSets. Performance tests I've done showed the value of these changes, so the defaults will now perform better. I have also created a branch to test whether computing the inverse shadow map matrix on the CPU and passing to the GPU in the LightData uniform buffer or computing the inverse shadow map matrix on the GPU when required was better. The branches that tests this out are:
Running vsgshadow with a new --smi command line option moves the compute of the inverse shadow matrix into the PCSS shader and out of the LightData. Performance test of hard, soft and PCSS shadows on my Linux desktop AMD5700G, Linux Intel i7 Laptop, and Windos 11 Intel i7+Gefore2060 desktop failed to show any performance differences between the two configurations except for a 5.7% difference on my AMD5700G where --smi (computing in PCSS shader) was slower than what is in VSG master/computing the shadow matrix on the CPU. I am surprised that there was no measurable penalty for having the extra LightData usage for the hard and soft shadow code paths as neither use the inverse shadow matrix. I am also surprised there there is no measurable cost in computing the inverse shadow matrix on the Intel i7 onboard GPU on my laptop or the Geforce 2060. I like the simplicity of doing the inverse when required on the GPU, but making PCSS a little ~6% on an AMD5700G and similar hardware is not an easy trade. So for now I think I'll now merge changes to VSG master to move the inverse computation back into the PCSS shader. Perhaps this is something to revisit in the future. |
Beta Was this translation helpful? Give feedback.
-
@AnyOldName3 I have changed the fragment shaders to use the suggest specialization constant approach for setting the lightData[] size: #1169 These changes are now part of VSG and vsgExamples master. |
Beta Was this translation helpful? Give feedback.
-
I've done some work to implement Percentage-Closer Soft Shadows into the VulkanSceneGraph. It's available on this branch of vsgExamples and requires this branch of the VSG. So far, I've not updated the precompiled shaders as the VSG branch isn't technically dependent on the vsgExamples one, so to see them in action, you'll need to use options that force the shaders to be compiled from source at runtime, or precompile them yourself.
Here are a few screenshots:
Basic scene with five blocker samples and eight shadow samples
This angle actually has several cascade boundaries, but they're well-hidden, which can be a problem when combining PCSS with cascaded shadow maps.
Basic scene with lots of samples
Large scene
Nearly everything's just been changes in the shader - the only change to the VSG itself is having
ViewDependentData
pass the shadow map array and samplers as separate uniforms instead of using a combined image sampler, which allows the blocker search to access depth values, but the shadow samples to still benefit from hardware PCF.The basic approach (after a decent amount of prototyping - there's a description of each decision in the commit history) is:
I've kept the current VSG approach of selecting shadow cascades based on whether a sample point lies within that cascade's light-space bounds. This has made things more complicated than some implementations, which would instead pass the view-space depth range for each cascade to the shader and select based on that, but avoids ignoring a better-quality shadow map in the regions where it spills beyond its intended boundary. There'll be some performance impact of this, although I can't quantify it, but I didn't want to override decisions that had already been made just to make this a bit simpler - it can always be changed later.
Blocker search
The blocker search is conducted across each cascade before any shadow sampling is done - if another approach is taken, there's a risk of missing potential blockers beyond the bounds of a given shadow map, and calculating an inappropriate penumbra radius because of that. The sample points use the same rotated poisson-like disk as the actual shadow sampling, but with a fixed radius - I intend to turn this into a specialisation constant, define or uniform as the most sensible value depends on the scene, its scale and the number of sample points used. If the blocker search radius is too large, and sample points too sparse, it becomes likely that the most important occluders directly between the centre of the light and the shadowed object are missed, and holes appear in the shadow. There are ways to calculate the maximum necessary blocker search radius, but typically it's large and would require many, many blocker samples to ensure nothing important was missed, so most of the time, knowing the nature of the scene, a more appropriate value can be selected manually.
At the moment, the blocker distances are averaged in light-space, but the average blocker for a particular shadow map is converted back to eye space. The first reason for this is that it allows the distances from different cascades with different variants of light space to be combined.
Penumbra radius
The second reason the average blocker is converted to eye space is that not all versions of light space preserve angles between lines. That means that if a technique like Light-Space Perspective Shadow Maps, or even worse plain Perspective Shadow Maps (neither of which I recommend as one of the maniacs who's previously implemented one), are used, we can't know the angle subtended by the light source in light space. We need to know that angle to know how big the penumbra from the average blocker would be. Currently, it's another constant set based on how big the Sun appears on Earth, but I'll eventually move it to the directional light data as not everything people want to render is on Earth and non-fictional. The actual radius calculation is just super-simple trigonometry.
Shadow sampling
As mentioned already, sampling is done using a rotated poisson-like disk scaled to the penumbra radius. Being poisson-like, it avoids moiré patterns and similar problems caused by having sample positions be too ordered, but unlike an actual poisson disk, it has the additional properties that:
The literature I've historically found about shadow map sampling didn't make it clear how much a difference a good poisson-like sampling disk would make over a bad poisson disk, so I thought this was interesting to discuss here.
The rotation for the poisson disk is calculated based on the same maths as Godot (also MIT-licenced) uses - it takes a hash of the pixel position using a formula originally from Crytek. Other techniques I tried led to obvious banding, significant moving noise patterns or noise that changed so much from frame to frame that surfaces looked like an untuned analogue CRT screen. I didn't keep screenshots, but the Godot approach just worked far better than anything else, especially with low sample counts.
Like with the blocker search, each shadow map in turn is tried with all the sample points until the total amount that landed within valid light space reaches the target threshold.
The result of having both the blocker search and shadow sampling work this way is really consistent penumbras across cascade boundaries - I can't find them even in screenshots where I know where they should be.
Beta Was this translation helpful? Give feedback.
All reactions