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.
This commit is contained in:
Clément Foucault 2022-08-04 22:30:09 +02:00
parent 897aa777c5
commit 72cdb0ed2d
6 changed files with 78 additions and 29 deletions

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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");