Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Soft Very Low shadow quality mode for 3D #53992

Merged

Conversation

Calinou
Copy link
Member

@Calinou Calinou commented Oct 19, 2021

This can be used to improve 3D shadow rendering quality at little performance cost. Unlike the existing Hard setting which is limited to variable shadow blur only, it works with both fixed blur and variable blur.

Preview

Hard

2021-10-20_19 33 17

Soft Very Low (new)

Performs nearly as well as Hard with better quality. It's more grainy than Soft Low, so using lower blur values is recommended.

2021-10-20_19 34 13

Soft Low

2021-10-20_19 35 34

Editor comparison

Performance estimations are included in the top-right corner of every image.
Edit: These estimations are inaccurate because the CPU time measurement is also taken into account. In practice, I can get a more significant performance boost with shadow quality set to Soft Very Low.

DirectionalLight3D (fixed blur)

Soft Ultra Soft High Soft Medium Soft Low Soft Very Low Soft Hard
2021-10-20_18 59 45 2021-10-20_18 59 56 2021-10-20_19 00 21 2021-10-20_19 00 33 2021-10-20_19 00 46 2021-10-20_19 00 59

DirectionalLight3D (variable blur)

Soft Ultra Soft High Soft Medium Soft Low Soft Very Low Soft Hard
2021-10-20_19 02 18 2021-10-20_19 02 28 2021-10-20_19 02 39 2021-10-20_19 02 48 2021-10-20_19 02 58 2021-10-20_19 03 16

SpotLight3D (fixed blur)

Soft Ultra Soft High Soft Medium Soft Low Soft Very Low Soft Hard
2021-10-20_19 07 57 2021-10-20_19 08 11 2021-10-20_19 08 23 2021-10-20_19 08 33 2021-10-20_19 08 49 2021-10-20_19 09 00

OmniLight3D (variable blur)

Soft Ultra Soft High Soft Medium Soft Low Soft Very Low Soft Hard
2021-10-20_19 11 27 2021-10-20_19 11 37 2021-10-20_19 11 47 2021-10-20_19 11 56 2021-10-20_19 12 06 2021-10-20_19 12 15

@clayjohn
Copy link
Member

What is the rationale behind offsetting the texture lookup by +-PI pixels?

To me this just looks like a jittered single pixel lookup.

Dithering is usually implemented with a threshold function where the pixel is set to 0 if under the threshold and 1 if over.

@Calinou
Copy link
Member Author

Calinou commented Oct 19, 2021

What is the rationale behind offsetting the texture lookup by +-PI pixels?

To me this just looks like a jittered single pixel lookup.

Dithering is usually implemented with a threshold function where the pixel is set to 0 if under the threshold and 1 if over.

To be fair, I copied the existing code and tried to adapt it the best I could. It looks a little strange with DirectionalLights (one axis seems more dithered than the other), but point lights look fine to me.

There is probably a better way to do it, but I don't know how.

@clayjohn
Copy link
Member

The existing code uses a spiral-shaped kernel to offset the shadow texture lookups.quick_hash to create a 2D rotation matrix which rotates the spiral-kernel a different amount for each pixel (see this article for a nice description about how this works).

Importantly, the length of the offset is determined by the kernel and the blur amount, not by quick_hash (i.e. the per-pixel randomness does not change the length of the offset, just the direction). However, in your code, the distance (and position) can be anyway in the range of -PI to PI meaning there will be stretching towards the corners of the shadowmap (I think this is the issue you are seeing).

Also, if you want to add a jitter to the single sample mode, it should be added as another enum to the shadow sample quality setting

ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/directional_shadow/soft_shadow_quality", PropertyInfo(Variant::INT, "rendering/shadows/directional_shadow/soft_shadow_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)"));

Right now, the lowest setting is Hard (Fastest) If you want to continue with this PR, I suggest adding an enum named Soft Very Low (Fast) that also uses only 1 texture sample. You will also have to add a new quality selection here:
switch (shadows_quality) {
case RS::SHADOW_QUALITY_HARD: {
penumbra_shadow_samples = 4;
soft_shadow_samples = 1;
shadows_quality_radius = 1.0;
} break;

I suggest changing hard shadows to 0 samples, and then in the shader check if 0 samples are used, then use the non-offset version of the code:
if (sc_soft_shadow_samples == 1) {
return textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos, depth, 1.0));
}

e.g. change this to

if (sc_soft_shadow_samples == 0) {
	return textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos, depth, 1.0));
}

This way, when only 1 sample is selected, then the proper offsetting code will automatically be used.

Being honest though, I doubt the performance benefit of using a single texture lookup vs 4 will be worthwhile on any hardware. I think for low end hardware it would be best to use Hard shadows.

@Calinou
Copy link
Member Author

Calinou commented Oct 19, 2021

Also, if you want to add a jitter to the single sample mode, it should be added as another enum to the shadow sample quality setting

Is that still required if #53961 is merged? That PR adds a global toggle to disable shadow dithering, which will make it possible to achieve hard shadows again.

@clayjohn
Copy link
Member

#53961 doesn't produce hard shadows, it just removes the per-pixel rotation of the vogel disk. I'm not really sure what the benefit of doing that would be. Note especially, that the calculations for rotating the vogel disk still take place, so there will be no performance benefit.

I'm not sure that a global override to produce hard shadows makes sense. In other words, why would we take more than one sample when calculating hard shadows?

@Calinou
Copy link
Member Author

Calinou commented Oct 19, 2021

I'm not sure that a global override to produce hard shadows makes sense. In other words, why would we take more than one sample when calculating hard shadows?

Indeed, there is no benefit in doing so. I can look at adding a separate setting for hard + jittered shadows, but I'd prefer having #53961 settled first since I'm not sure how they'll interact together.

I'm not really sure what the benefit of doing that would be. Note especially, that the calculations for rotating the vogel disk still take place, so there will be no performance benefit.

Several people requested a way to disable dithering while still using soft shadows, which is currently not possible in 3.x. Dithering doesn't always go well with games that use a flat color/lowpoly aesthetic, since you want to avoid having a noisy look in that kind of game. Perceptually, disabling dithering will look worse. It's the only thing you can do if you want to avoid noise though (without increasing performance demands too much).

The issue I linked to isn't particularly a new concern. I remember seeing people wanting to disable dithering as soon as the new soft shadows were pushed to master.

@clayjohn
Copy link
Member

Several people requested a way to disable dithering while still using soft shadows, which is currently not possible in 3.x. Dithering doesn't always go well with games that use a flat color/lowpoly aesthetic, since you want to avoid having a noisy look in that kind of game. Perceptually, disabling dithering will look worse. It's the only thing you can do if you want to avoid noise though (without increasing performance demands too much).

For clarity, there is no dithering involved in soft shadows. At any rate, #53961 doesn't eliminate noise either, it removes the per-pixel rotation which decreases the "grainy" noise, but it leaves the noise created by using vogel disk sampling.

Further, in the issue you linked, the user was asking for a way to blur the shadows further to get a smooth gradient. In the example screenshots in #53961 the visual noise from the "disabled" version is much more jarring then having per-pixel rotations enabled.

I'm not sure there is any value in disabling per-pixel rotations while leaving the vogel disk sampling intact. But if we do, we should do it in a way that does not substantially increase visual noise. Perhaps allowing PCF as an option would do the trick?

@Calinou Calinou force-pushed the hard-shadow-mapping-allow-dithering branch from 25f67f6 to 016fcca Compare October 20, 2021 17:15
@Calinou Calinou requested a review from a team as a code owner October 20, 2021 17:15
@Calinou
Copy link
Member Author

Calinou commented Oct 20, 2021

I agree your suggestion makes more sense in the end. I reworked this PR to add a Soft Very Low quality setting as requested.

@Calinou Calinou changed the title Allow using dithering with hard shadow mapping Add Soft Very Low shadow quality mode for 3D Oct 20, 2021
@Calinou Calinou force-pushed the hard-shadow-mapping-allow-dithering branch 2 times, most recently from 0da6381 to 06913c3 Compare October 20, 2021 21:13
doc/classes/ProjectSettings.xml Outdated Show resolved Hide resolved
servers/rendering/renderer_rd/renderer_scene_render_rd.cpp Outdated Show resolved Hide resolved
doc/classes/ProjectSettings.xml Outdated Show resolved Hide resolved
doc/classes/ProjectSettings.xml Outdated Show resolved Hide resolved
doc/classes/RenderingServer.xml Outdated Show resolved Hide resolved
This can be used to improve 3D shadow rendering quality at little
performance cost. Unlike the existing Hard setting which is limited
to variable shadow blur only, it works with both fixed blur and
variable blur.
@Calinou Calinou force-pushed the hard-shadow-mapping-allow-dithering branch from 06913c3 to e87ec8e Compare October 21, 2021 16:34
Copy link
Member

@clayjohn clayjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thanks for sticking to it and responding to feedback!

@akien-mga akien-mga merged commit 3bebbca into godotengine:master Oct 22, 2021
@akien-mga
Copy link
Member

Thanks!

@Calinou Calinou deleted the hard-shadow-mapping-allow-dithering branch October 22, 2021 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants