diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2022-08-04 23:30:09 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2022-08-05 15:45:09 +0300 |
commit | 72cdb0ed2d2cc4eebe9c756d2a87e2513b6bf1db (patch) | |
tree | af86ee2596082365bd048ec86f041307b8b59799 /source/blender/draw | |
parent | 897aa777c53439d67401b4ae3d567394a80f8be7 (diff) |
EEVEE-Next: Depth Of Field: Improve image stability
This adds anti-flicker pass to the slight focus region by using the
temporaly stable output from stabilize pass.
This also fixes the bilateral weight factor which was reversed.
Diffstat (limited to 'source/blender/draw')
6 files changed, 78 insertions, 29 deletions
diff --git a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc index e3db4a7f0ef..713fcf77dc8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc +++ b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc @@ -470,6 +470,7 @@ void DepthOfField::resolve_pass_sync() DRW_shgroup_uniform_block(grp, "dof_buf", data_); DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "stable_color_tx", &resolve_stable_color_tx_, no_filter); DRW_shgroup_uniform_texture_ref_ex(grp, "color_bg_tx", &color_bg_tx_.current(), with_filter); DRW_shgroup_uniform_texture_ref_ex(grp, "color_fg_tx", &color_fg_tx_.current(), with_filter); DRW_shgroup_uniform_image_ref(grp, "in_tiles_fg_img", &tiles_fg_tx_.current()); @@ -593,7 +594,7 @@ void DepthOfField::render(GPUTexture **input_tx, } { setup_color_tx_.acquire(half_res, GPU_RGBA16F); - setup_coc_tx_.acquire(half_res, GPU_RG16F); + setup_coc_tx_.acquire(half_res, GPU_R16F); DRW_draw_pass(setup_ps_); } @@ -741,6 +742,8 @@ void DepthOfField::render(GPUTexture **input_tx, { DRW_stats_group_start("Resolve"); + resolve_stable_color_tx_ = dof_buffer.stabilize_history_tx_; + DRW_draw_pass(resolve_ps_); color_bg_tx_.current().release(); diff --git a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh index a11924c3806..84134e94483 100644 --- a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh +++ b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh @@ -127,6 +127,7 @@ class DepthOfField { DRWPass *scatter_bg_ps_ = nullptr; /** Recombine the results and also perform a slight out of focus gather. */ + GPUTexture *resolve_stable_color_tx_ = nullptr; int3 dispatch_resolve_size_ = int3(-1); DRWPass *resolve_ps_ = nullptr; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl index 15c1073309a..97c53ce3692 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl @@ -6,6 +6,7 @@ **/ #pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl) #pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) #pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) @@ -339,10 +340,10 @@ void dof_gather_accumulate_resolve(int total_sample_count, out_weight = 0.0; } /* Same thing for alpha channel. */ - if (out_col.a > 0.99) { + if (out_col.a > 0.993) { out_col.a = 1.0; } - else if (out_col.a < 0.01) { + else if (out_col.a < 0.003) { out_col.a = 0.0; } } @@ -573,7 +574,8 @@ void dof_slight_focus_gather(sampler2D depth_tx, sampler2D bkh_lut_tx, /* Renamed because of ugly macro job. */ float radius, out vec4 out_color, - out float out_weight) + out float out_weight, + out float out_center_coc) { vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5; float noise_offset = sampling_rng_1D_get(SAMPLING_LENS_U); @@ -650,6 +652,8 @@ void dof_slight_focus_gather(sampler2D depth_tx, center_data.coc = clamp(center_data.coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max); center_data.dist = 0.0; + out_center_coc = center_data.coc; + /* Slide 38. */ float bordering_radius = 0.5; @@ -666,7 +670,7 @@ void dof_slight_focus_gather(sampler2D depth_tx, dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion); dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion); - /* Fix weighting issues on perfectly focus > slight focus transitionning areas. */ + /* Fix weighting issues on perfectly focus to slight focus transitionning areas. */ if (abs(center_data.coc) < 0.5) { bg_col = center_data.color; bg_weight = 1.0; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl index 2efcf43a0f6..a4b58f950a0 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl @@ -41,6 +41,45 @@ float dof_slight_focus_coc_tile_get(vec2 frag_coord) return uintBitsToFloat(shared_max_slight_focus_abs_coc); } +vec3 dof_neighborhood_clamp(vec2 frag_coord, vec3 color, float center_coc, float weight) +{ + /* Stabilize color by clamping with the stable half res neighboorhood. */ + vec3 neighbor_min, neighbor_max; + const vec2 corners[4] = vec2[4](vec2(-1, -1), vec2(1, -1), vec2(-1, 1), vec2(1, 1)); + for (int i = 0; i < 4; i++) { + /** + * Visit the 4 half-res texels around (and containing) the fullres texel. + * Here a diagram of a fullscreen texel (f) in the bottom left corner of a half res texel. + * We sample the stable half-resolution texture at the 4 location denoted by (h). + * ┌───────┬───────┐ + * │ h │ h │ + * │ │ │ + * │ │ f │ + * ├───────┼───────┤ + * │ h │ h │ + * │ │ │ + * │ │ │ + * └───────┴───────┘ + */ + vec2 uv_sample = ((frag_coord + corners[i]) * 0.5) / vec2(textureSize(stable_color_tx, 0)); + /* Reminder: The content of this buffer is YCoCg + CoC. */ + vec3 ycocg_sample = textureLod(stable_color_tx, uv_sample, 0.0).rgb; + neighbor_min = (i == 0) ? ycocg_sample : min(neighbor_min, ycocg_sample); + neighbor_max = (i == 0) ? ycocg_sample : max(neighbor_max, ycocg_sample); + } + /* Pad the bounds in the near in focus region to get back a bit of detail. */ + float padding = 0.125 * saturate(1.0 - sqr(center_coc) / sqr(8.0)); + neighbor_max += abs(neighbor_min) * padding; + neighbor_min -= abs(neighbor_min) * padding; + /* Progressively apply the clamp to avoid harsh transition. Also mask by weight. */ + float fac = saturate(sqr(center_coc) * 4.0) * weight; + /* Clamp in YCoCg space to avoid too much color drift. */ + color = colorspace_YCoCg_from_scene_linear(color); + color = mix(color, clamp(color, neighbor_min, neighbor_max), fac); + color = colorspace_scene_linear_from_YCoCg(color); + return color; +} + void main() { vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5; @@ -94,11 +133,20 @@ void main() } if (!no_slight_focus_pass && prediction.do_slight_focus) { - dof_slight_focus_gather( - depth_tx, color_tx, bokeh_lut_tx, slight_focus_max_coc, layer_color, layer_weight); + float center_coc; + dof_slight_focus_gather(depth_tx, + color_tx, + bokeh_lut_tx, + slight_focus_max_coc, + layer_color, + layer_weight, + center_coc); + /* Composite slight defocus. */ out_color = out_color * (1.0 - layer_weight) + layer_color; weight = weight * (1.0 - layer_weight) + layer_weight; + + out_color.rgb = dof_neighborhood_clamp(frag_coord, out_color.rgb, center_coc, layer_weight); } if (!no_focus_pass && prediction.do_focus) { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl index 254cacc45b7..b22af0e88f0 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl @@ -110,8 +110,9 @@ float dof_bilateral_weight(float reference_coc, float sample_coc) { /* NOTE: The difference between the cocs should be inside a abs() function, * but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). + * Effectively bleed background into foreground. * Compared to dof_bilateral_coc_weights() this saturates as 2x the reference CoC. */ - return saturate(1.0 - (reference_coc - sample_coc) / max(1.0, abs(reference_coc))); + return saturate(1.0 - (sample_coc - reference_coc) / max(1.0, abs(reference_coc))); } DofSample dof_spatial_filtering() @@ -218,7 +219,7 @@ DofSample dof_sample_history(vec2 input_texel) vec2 uv = vec2(input_texel + 0.5) / textureSize(in_history_tx, 0); vec4 color = textureLod(in_history_tx, uv, 0.0); -#elif 0 /* Catmull Rom interpolation. 5 Bilinear Taps. */ +#else /* Catmull Rom interpolation. 5 Bilinear Taps. */ vec2 center_texel; vec2 inter_texel = modf(input_texel, center_texel); vec2 weights[4]; @@ -254,31 +255,22 @@ DofSample dof_sample_history(vec2 input_texel) return DofSample(color.xyzz, color.w); } -/* 1D equivalent of line_aabb_clipping_dist(). */ -float dof_aabb_clipping_dist_coc(float origin, float direction, float aabb_min, float aabb_max) -{ - if (abs(direction) < 1e-5) { - return 0.0; - } - float nearest_plane = (direction > 0.0) ? aabb_min : aabb_max; - return (nearest_plane - origin) / direction; -} - /* Modulate the history color to avoid ghosting artifact. */ DofSample dof_amend_history(DofNeighborhoodMinMax bbox, DofSample history, DofSample src) { +#if 0 /* Clip instead of clamping to avoid color accumulating in the AABB corners. */ - DofSample clip_dir; - clip_dir.color = src.color - history.color; - clip_dir.coc = src.coc - history.coc; + vec3 clip_dir = src.color.rgb - history.color.rgb; float t = line_aabb_clipping_dist( - history.color.rgb, clip_dir.color.rgb, bbox.min.color.rgb, bbox.max.color.rgb); - history.color.rgb += clip_dir.color.rgb * saturate(t); - - /* Clip CoC on its own to avoid interference with other chanels. */ - float t_a = dof_aabb_clipping_dist_coc(history.coc, clip_dir.coc, bbox.min.coc, bbox.max.coc); - history.coc += clip_dir.coc * saturate(t_a); + history.color.rgb, clip_dir, bbox.min.color.rgb, bbox.max.color.rgb); + history.color.rgb += clip_dir * saturate(t); +#else + /* More responsive. */ + history.color = clamp(history.color, bbox.min.color, bbox.max.color); +#endif + /* Clamp CoC to reduce convergence time. Otherwise the result is laggy. */ + history.coc = clamp(history.coc, bbox.min.coc, bbox.max.coc); return history; } diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh index 1dd9178ae84..54f8ca3e61b 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh @@ -24,7 +24,7 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_setup) .sampler(0, ImageType::FLOAT_2D, "color_tx") .sampler(1, ImageType::DEPTH_2D, "depth_tx") .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") - .image(1, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_img") + .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_img") .compute_source("eevee_depth_of_field_setup_comp.glsl"); GPU_SHADER_CREATE_INFO(eevee_depth_of_field_stabilize) @@ -243,6 +243,7 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_resolve) .sampler(7, ImageType::FLOAT_2D, "weight_bg_tx") .sampler(8, ImageType::FLOAT_2D, "weight_fg_tx") .sampler(9, ImageType::FLOAT_2D, "weight_hole_fill_tx") + .sampler(10, ImageType::FLOAT_2D, "stable_color_tx") .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") .compute_source("eevee_depth_of_field_resolve_comp.glsl"); |