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

[3.x] Add a project setting to enable 3D shadow dithering #53967

Open
wants to merge 1 commit into
base: 3.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1801,12 +1801,15 @@
Lower-end override for [member rendering/quality/shadow_atlas/size] on mobile devices, due to performance concerns or driver support.
</member>
<member name="rendering/quality/shadows/filter_mode" type="int" setter="" getter="" default="1">
Shadow filter mode. Higher-quality settings result in smoother shadows that flicker less when moving. "Disabled" is the fastest option, but also has the lowest quality. "PCF5" is smoother but is also slower. "PCF13" is the smoothest option, but is also the slowest.
Shadow filter mode in 3D. Higher-quality settings result in smoother shadows that flicker less when moving. "Disabled" is the fastest option, but also has the lowest quality. "PCF5" is smoother but is also slower. "PCF13" is the smoothest option, but is also the slowest. See also [member rendering/quality/shadows/use_dithering].
[b]Note:[/b] When using the GLES2 backend, the "PCF13" option actually uses 16 samples to emulate linear filtering in the shader. This results in a shadow appearance similar to the one produced by the GLES3 backend.
</member>
<member name="rendering/quality/shadows/filter_mode.mobile" type="int" setter="" getter="" default="0">
Lower-end override for [member rendering/quality/shadows/filter_mode] on mobile devices, due to performance concerns or driver support.
</member>
<member name="rendering/quality/shadows/use_dithering" type="bool" setter="" getter="" default="false">
If [code]true[/code], enables dithering for 3D shadows. Dithering improves overall shadow quality, but introduces a grainy effect that can be noticeable in scenes with large flat color areas. This dithering effect becomes less noticeable as textures become more complex and as the 3D rendering resolution increases. Disabling dithering can also slightly improve shadow rendering performance. See also [member rendering/quality/shadows/filter_mode].
</member>
<member name="rendering/quality/skinning/force_software_skinning" type="bool" setter="" getter="" default="false">
Forces [MeshInstance] to always perform skinning on the CPU (applies to both GLES2 and GLES3).
See also [member rendering/quality/skinning/software_skinning_fallback].
Expand Down
7 changes: 7 additions & 0 deletions drivers/gles2/rasterizer_scene_gles2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1911,6 +1911,7 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, false);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_5, false);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_13, false);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_USE_DITHERING, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_DIRECTIONAL, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_OMNI, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_SPOT, false);
Expand Down Expand Up @@ -1952,6 +1953,7 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas
}
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_5, shadow_filter_mode == SHADOW_FILTER_PCF5);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_13, shadow_filter_mode == SHADOW_FILTER_PCF13);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_USE_DITHERING, shadow_use_dithering);
}

} break;
Expand All @@ -1967,6 +1969,7 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas
}
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_5, shadow_filter_mode == SHADOW_FILTER_PCF5);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_13, shadow_filter_mode == SHADOW_FILTER_PCF13);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_USE_DITHERING, shadow_use_dithering);
}
} break;
case VS::LIGHT_SPOT: {
Expand All @@ -1981,6 +1984,7 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas
}
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_5, shadow_filter_mode == SHADOW_FILTER_PCF5);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_13, shadow_filter_mode == SHADOW_FILTER_PCF13);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_USE_DITHERING, shadow_use_dithering);
}
} break;
}
Expand Down Expand Up @@ -4103,6 +4107,7 @@ void RasterizerSceneGLES2::initialize() {
}

shadow_filter_mode = SHADOW_FILTER_NEAREST;
shadow_use_dithering = false;

glFrontFace(GL_CW);
}
Expand All @@ -4115,6 +4120,8 @@ void RasterizerSceneGLES2::iteration() {
directional_shadow_size = directional_shadow_size_new;
directional_shadow_create();
}

shadow_use_dithering = bool(GLOBAL_GET("rendering/quality/shadows/use_dithering"));
}

void RasterizerSceneGLES2::finalize() {
Expand Down
2 changes: 2 additions & 0 deletions drivers/gles2/rasterizer_scene_gles2.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class RasterizerSceneGLES2 : public RasterizerScene {

ShadowFilterMode shadow_filter_mode;

bool shadow_use_dithering;

RID default_material;
RID default_material_twosided;
RID default_shader;
Expand Down
14 changes: 14 additions & 0 deletions drivers/gles2/shaders/scene.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -1500,9 +1500,23 @@ LIGHT_SHADER_CODE

#define SAMPLE_SHADOW_TEXEL(p_shadow, p_pos, p_depth) step(p_depth, SHADOW_DEPTH(texture2D(p_shadow, p_pos)))

#ifdef SHADOW_USE_DITHERING
// Interleaved Gradient Noise
// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
float quick_hash(vec2 pos) {
const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f);
return fract(magic.z * fract(dot(pos, magic.xy)));
}
#endif

float sample_shadow(highp sampler2D shadow, highp vec4 spos) {
spos.xyz /= spos.w;
#ifdef SHADOW_USE_DITHERING
vec2 dither = (-vec2(0.5) + quick_hash(gl_FragCoord.xy)) * shadow_pixel_size;
vec2 pos = spos.xy + dither;
#else
vec2 pos = spos.xy;
#endif
float depth = spos.z;

#ifdef SHADOW_MODE_PCF_13
Expand Down
6 changes: 6 additions & 0 deletions drivers/gles3/rasterizer_scene_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2045,6 +2045,7 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
state.scene_shader.set_conditional(SceneShaderGLES3::LIGHT_USE_PSSM_BLEND, false);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_5, false);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_13, false);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_USE_DITHERING, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_CAPTURE, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP, false);
Expand All @@ -2071,6 +2072,7 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
state.scene_shader.set_conditional(SceneShaderGLES3::LIGHT_USE_PSSM_BLEND, false);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_5, shadow_filter_mode == SHADOW_FILTER_PCF5);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_13, shadow_filter_mode == SHADOW_FILTER_PCF13);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_USE_DITHERING, shadow_use_dithering);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP, use_radiance_map);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_CONTACT_SHADOWS, state.used_contact_shadows);

Expand Down Expand Up @@ -2232,6 +2234,7 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
state.scene_shader.set_conditional(SceneShaderGLES3::SHADELESS, false);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_5, false);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_13, false);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_USE_DITHERING, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_LAYERED, false);
Expand Down Expand Up @@ -5187,6 +5190,7 @@ void RasterizerSceneGLES3::initialize() {
}

shadow_filter_mode = SHADOW_FILTER_NEAREST;
shadow_use_dithering = false;

{ //reflection cubemaps
int max_reflection_cubemap_sampler_size = 512;
Expand Down Expand Up @@ -5334,6 +5338,8 @@ void RasterizerSceneGLES3::iteration() {
directional_shadow_create();
}

shadow_use_dithering = bool(GLOBAL_GET("rendering/quality/shadows/use_dithering"));

subsurface_scatter_follow_surface = GLOBAL_GET("rendering/quality/subsurface_scattering/follow_surface");
subsurface_scatter_weight_samples = GLOBAL_GET("rendering/quality/subsurface_scattering/weight_samples");
subsurface_scatter_quality = SubSurfaceScatterQuality(int(GLOBAL_GET("rendering/quality/subsurface_scattering/quality")));
Expand Down
2 changes: 2 additions & 0 deletions drivers/gles3/rasterizer_scene_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ class RasterizerSceneGLES3 : public RasterizerScene {

ShadowFilterMode shadow_filter_mode;

bool shadow_use_dithering;

uint64_t shadow_atlas_realloc_tolerance_msec;

enum SubSurfaceScatterQuality {
Expand Down
65 changes: 34 additions & 31 deletions drivers/gles3/shaders/scene.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -1260,49 +1260,52 @@ LIGHT_SHADER_CODE
#endif //defined(USE_LIGHT_SHADER_CODE)
}

float sample_shadow(highp sampler2DShadow shadow, vec2 shadow_pixel_size, vec2 pos, float depth, vec4 clamp_rect) {
#ifdef SHADOW_MODE_PCF_13 //ubershader-runtime
#ifdef SHADOW_USE_DITHERING
// Interleaved Gradient Noise
// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
float quick_hash(vec2 pos) {
const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f);
return fract(magic.z * fract(dot(pos, magic.xy)));
}
#endif

float avg = textureProj(shadow, vec4(pos + vec2(shadow_pixel_size.x * 2.0, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(-shadow_pixel_size.x * 2.0, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(0.0, shadow_pixel_size.y * 2.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(0.0, -shadow_pixel_size.y * 2.0), depth, 1.0));
// Early bail if distant samples are fully shaded (or none are shaded) to improve performance.
if (avg <= 0.000001) {
// None shaded at all.
return 0.0;
} else if (avg >= 3.999999) {
// All fully shaded.
return 1.0;
}
float sample_shadow(highp sampler2DShadow shadow, vec2 shadow_pixel_size, vec2 pos, float depth, vec4 clamp_rect) {
#ifdef SHADOW_USE_DITHERING
vec2 dither = (-vec2(0.5) + quick_hash(gl_FragCoord.xy)) * shadow_pixel_size;
#else
vec2 dither = vec2(0.0);
#endif

Copy link
Member

Choose a reason for hiding this comment

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

I don't know if you can do this with defines in GLSL, but it might be worth testing if something like this is possible:

#ifdef SHADOW_USE_DITHERING
#define PLUS_DITHER +((-vec2(0.5) + quick_hash(gl_FragCoord.xy)) * shadow_pixel_size)
#else
#define PLUS_DITHER 
#endif
...
avg += textureProj(shadow, vec4(pos PLUS_DITHER + vec2(-shadow_pixel_size.x, 0.0), depth, 1.0));

avg += textureProj(shadow, vec4(pos, depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(shadow_pixel_size.x, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(-shadow_pixel_size.x, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(0.0, shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(0.0, -shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(shadow_pixel_size.x, shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(-shadow_pixel_size.x, shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(shadow_pixel_size.x, -shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(-shadow_pixel_size.x, -shadow_pixel_size.y), depth, 1.0));
#ifdef SHADOW_MODE_PCF_13 //ubershader-runtime
float avg = textureProj(shadow, vec4(pos + dither, depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(shadow_pixel_size.x, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(-shadow_pixel_size.x, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(0.0, shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(0.0, -shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(shadow_pixel_size.x, shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(-shadow_pixel_size.x, shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(shadow_pixel_size.x, -shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(-shadow_pixel_size.x, -shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(shadow_pixel_size.x * 2.0, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(-shadow_pixel_size.x * 2.0, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(0.0, shadow_pixel_size.y * 2.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(0.0, -shadow_pixel_size.y * 2.0), depth, 1.0));
return avg * (1.0 / 13.0);
#endif //ubershader-runtime

#ifdef SHADOW_MODE_PCF_5 //ubershader-runtime

float avg = textureProj(shadow, vec4(pos, depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(shadow_pixel_size.x, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(-shadow_pixel_size.x, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(0.0, shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(0.0, -shadow_pixel_size.y), depth, 1.0));
float avg = textureProj(shadow, vec4(pos + dither, depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(shadow_pixel_size.x, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(-shadow_pixel_size.x, 0.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(0.0, shadow_pixel_size.y), depth, 1.0));
avg += textureProj(shadow, vec4(pos + dither + vec2(0.0, -shadow_pixel_size.y), depth, 1.0));
return avg * (1.0 / 5.0);

#endif //ubershader-runtime

#ifndef SHADOW_MODE_PCF_5 //ubershader-runtime
#ifndef SHADOW_MODE_PCF_13 //ubershader-runtime

return textureProj(shadow, vec4(pos, depth, 1.0));
return textureProj(shadow, vec4(pos + dither, depth, 1.0));

#endif //ubershader-runtime
#endif //ubershader-runtime
Expand Down
1 change: 1 addition & 0 deletions servers/visual_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2656,6 +2656,7 @@ VisualServer::VisualServer() {
GLOBAL_DEF("rendering/quality/shadows/filter_mode", 1);
GLOBAL_DEF("rendering/quality/shadows/filter_mode.mobile", 0);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/shadows/filter_mode", PropertyInfo(Variant::INT, "rendering/quality/shadows/filter_mode", PROPERTY_HINT_ENUM, "Disabled,PCF5,PCF13"));
GLOBAL_DEF("rendering/quality/shadows/use_dithering", false);

GLOBAL_DEF("rendering/quality/reflections/texture_array_reflections", true);
GLOBAL_DEF("rendering/quality/reflections/texture_array_reflections.mobile", false);
Expand Down