EEVEE-Next: Depth Of Field: Use random sampling in slight focus gather

This replace the previous square rings approach by sampling a disk the
footprint of the search area. This avoids sampling in areas in corners
where there isn't any weight.

This results in much less samples needed to acheive a good enough result.
The max number of samples for an area of 11x11 px is hard coded to 16 and
still gives good results with the final clamp.

The number of samples is adaptative and is scaled by the search area (max
CoC).

The High Quality Slight Defocus is not required anymore. If there is a
quality parameter to add, it would be sample count option. But I consider
the temporal stability enough for viewport work and render can still
render many full scene samples. So I don't see a need for that yet.
This commit is contained in:
Clément Foucault 2022-08-05 13:51:29 +02:00
parent 3690dad40a
commit 2a4cc0c81c
8 changed files with 53 additions and 75 deletions

View File

@ -267,7 +267,6 @@ class RENDER_PT_eevee_next_depth_of_field(RenderButtonsPanel, Panel):
col.prop(props, "bokeh_max_size")
col.prop(props, "bokeh_threshold")
col.prop(props, "bokeh_neighbor_max")
col.prop(props, "use_bokeh_high_quality_slight_defocus")
col.prop(props, "use_bokeh_jittered")
col = layout.column()

View File

@ -57,6 +57,7 @@
#define DOF_TILES_DILATE_GROUP_SIZE 8
#define DOF_BOKEH_LUT_SIZE 32
#define DOF_MAX_SLIGHT_FOCUS_RADIUS 5
#define DOF_SLIGHT_FOCUS_SAMPLE_MAX 16
#define DOF_MIP_COUNT 4
#define DOF_REDUCE_GROUP_SIZE (1 << (DOF_MIP_COUNT - 1))
#define DOF_DEFAULT_GROUP_SIZE 32

View File

@ -55,8 +55,6 @@ void DepthOfField::init()
/* Reminder: These are parameters not interpolated by motion blur. */
int update = 0;
int sce_flag = sce_eevee.flag;
update += assign_if_different(do_hq_slight_focus_,
(sce_flag & SCE_EEVEE_DOF_HQ_SLIGHT_FOCUS) != 0);
update += assign_if_different(do_jitter_, (sce_flag & SCE_EEVEE_DOF_JITTER) != 0);
update += assign_if_different(user_overblur_, sce_eevee.bokeh_overblur / 100.0f);
update += assign_if_different(fx_max_coc_, sce_eevee.bokeh_max_size);
@ -462,8 +460,7 @@ void DepthOfField::resolve_pass_sync()
resolve_ps_ = DRW_pass_create("Dof.resolve_ps_", DRW_STATE_NO_DRAW);
bool use_lut = bokeh_lut_ps_ != nullptr;
eShaderType sh_type = do_hq_slight_focus_ ? (use_lut ? DOF_RESOLVE_LUT_HQ : DOF_RESOLVE_HQ) :
(use_lut ? DOF_RESOLVE_LUT : DOF_RESOLVE);
eShaderType sh_type = use_lut ? DOF_RESOLVE_LUT : DOF_RESOLVE;
GPUShader *sh = inst_.shaders.static_shader_get(sh_type);
DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
inst_.sampling.bind_resources(grp);

View File

@ -136,8 +136,6 @@ class DepthOfField {
/** Scene settings that are immutable. */
float user_overblur_;
float fx_max_coc_;
/** Use High Quality (expensive) in-focus gather pass. */
bool do_hq_slight_focus_;
/** Use jittered depth of field where we randomize camera location. */
bool do_jitter_;

View File

@ -99,23 +99,19 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
case DOF_GATHER_FOREGROUND_LUT:
return "eevee_depth_of_field_gather_foreground_lut";
case DOF_GATHER_FOREGROUND:
return "eevee_depth_of_field_gather_foreground";
return "eevee_depth_of_field_gather_foreground_no_lut";
case DOF_GATHER_BACKGROUND_LUT:
return "eevee_depth_of_field_gather_background_lut";
case DOF_GATHER_BACKGROUND:
return "eevee_depth_of_field_gather_background";
return "eevee_depth_of_field_gather_background_no_lut";
case DOF_GATHER_HOLE_FILL:
return "eevee_depth_of_field_hole_fill";
case DOF_REDUCE:
return "eevee_depth_of_field_reduce";
case DOF_RESOLVE:
return "eevee_depth_of_field_resolve_lq";
case DOF_RESOLVE_HQ:
return "eevee_depth_of_field_resolve_hq";
return "eevee_depth_of_field_resolve_no_lut";
case DOF_RESOLVE_LUT:
return "eevee_depth_of_field_resolve_lq_lut";
case DOF_RESOLVE_LUT_HQ:
return "eevee_depth_of_field_resolve_hq_lut";
return "eevee_depth_of_field_resolve_lut";
case DOF_SETUP:
return "eevee_depth_of_field_setup";
case DOF_SCATTER:

View File

@ -38,8 +38,6 @@ enum eShaderType {
DOF_GATHER_FOREGROUND,
DOF_GATHER_HOLE_FILL,
DOF_REDUCE,
DOF_RESOLVE_HQ,
DOF_RESOLVE_LUT_HQ,
DOF_RESOLVE_LUT,
DOF_RESOLVE,
DOF_SCATTER,

View File

@ -578,68 +578,63 @@ void dof_slight_focus_gather(sampler2D depth_tx,
out float out_center_coc)
{
vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5;
float noise_offset = sampling_rng_1D_get(SAMPLING_LENS_U);
float noise = no_gather_random ? 0.0 : interlieved_gradient_noise(frag_coord, 3, noise_offset);
vec2 noise_offset = sampling_rng_2D_get(SAMPLING_LENS_U);
vec2 noise = no_gather_random ? vec2(0.0) :
vec2(interlieved_gradient_noise(frag_coord, 3, noise_offset.x),
interlieved_gradient_noise(frag_coord, 5, noise_offset.y));
DofGatherData fg_accum = GATHER_DATA_INIT;
DofGatherData bg_accum = GATHER_DATA_INIT;
int i_radius = clamp(int(radius), 0, int(dof_layer_threshold));
const int resolve_ring_density = DOF_SLIGHT_FOCUS_DENSITY;
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
const float sample_count_max = float(DOF_SLIGHT_FOCUS_SAMPLE_MAX);
/* Scale by search area. */
float sample_count = sample_count_max * saturate(sqr(radius) / sqr(dof_layer_threshold));
bool first_ring = true;
for (int ring = i_radius; ring > 0; ring--) {
DofGatherData fg_ring = GATHER_DATA_INIT;
DofGatherData bg_ring = GATHER_DATA_INIT;
int ring_distance = ring;
int ring_sample_count = resolve_ring_density * ring_distance;
for (int sample_id = 0; sample_id < ring_sample_count; sample_id++) {
int s = sample_id * (4 / resolve_ring_density) +
int(noise * float((4 - resolve_ring_density) * ring_distance));
ivec2 offset = dof_square_ring_sample_offset(ring_distance, s);
float ring_dist = length(vec2(offset));
DofGatherData pair_data[2];
for (int i = 0; i < 2; i++) {
ivec2 sample_offset = ((i == 0) ? offset : -offset);
ivec2 sample_texel = texel + sample_offset;
/* OPTI: could precompute the factor. */
vec2 sample_uv = (vec2(sample_texel) + 0.5) / vec2(textureSize(depth_tx, 0));
float depth = textureLod(depth_tx, sample_uv, 0.0).r;
pair_data[i].coc = dof_coc_from_depth(dof_buf, sample_uv, depth);
pair_data[i].color = safe_color(textureLod(color_tx, sample_uv, 0.0));
pair_data[i].dist = ring_dist;
if (DOF_BOKEH_TEXTURE) {
/* Contains subpixel distance to bokeh shape. */
sample_offset += dof_max_slight_focus_radius;
pair_data[i].dist = texelFetch(bkh_lut_tx, sample_offset, 0).r;
}
pair_data[i].coc = clamp(pair_data[i].coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max);
}
float bordering_radius = ring_dist + 0.5;
const float isect_mul = 1.0;
dof_gather_accumulate_sample_pair(
pair_data, bordering_radius, isect_mul, first_ring, false, false, bg_ring, bg_accum);
for (float s = 0.0; s < sample_count; s++) {
vec2 rand2 = fract(hammersley_2d(s, sample_count) + noise);
vec2 offset = sample_disk(rand2);
float ring_dist = sqrt(rand2.y);
DofGatherData pair_data[2];
for (int i = 0; i < 2; i++) {
vec2 sample_offset = ((i == 0) ? offset : -offset);
/* OPTI: could precompute the factor. */
vec2 sample_uv = (frag_coord + sample_offset) / vec2(textureSize(depth_tx, 0));
float depth = textureLod(depth_tx, sample_uv, 0.0).r;
pair_data[i].coc = dof_coc_from_depth(dof_buf, sample_uv, depth);
pair_data[i].color = safe_color(textureLod(color_tx, sample_uv, 0.0));
pair_data[i].dist = ring_dist;
if (DOF_BOKEH_TEXTURE) {
/* Swap distances in order to flip bokeh shape for foreground. */
float tmp = pair_data[0].dist;
pair_data[0].dist = pair_data[1].dist;
pair_data[1].dist = tmp;
/* Contains subpixel distance to bokeh shape. */
ivec2 lut_texel = ivec2(round(sample_offset)) + dof_max_slight_focus_radius;
pair_data[i].dist = texelFetch(bkh_lut_tx, lut_texel, 0).r;
}
dof_gather_accumulate_sample_pair(
pair_data, bordering_radius, isect_mul, first_ring, false, true, fg_ring, fg_accum);
pair_data[i].coc = clamp(pair_data[i].coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max);
}
dof_gather_accumulate_sample_ring(
bg_ring, ring_sample_count * 2, first_ring, false, false, bg_accum);
dof_gather_accumulate_sample_ring(
fg_ring, ring_sample_count * 2, first_ring, false, true, fg_accum);
float bordering_radius = ring_dist + 0.5;
const float isect_mul = 1.0;
DofGatherData bg_ring = GATHER_DATA_INIT;
dof_gather_accumulate_sample_pair(
pair_data, bordering_radius, isect_mul, first_ring, false, false, bg_ring, bg_accum);
/* Treat each sample as a ring. */
dof_gather_accumulate_sample_ring(bg_ring, 2, first_ring, false, false, bg_accum);
if (DOF_BOKEH_TEXTURE) {
/* Swap distances in order to flip bokeh shape for foreground. */
float tmp = pair_data[0].dist;
pair_data[0].dist = pair_data[1].dist;
pair_data[1].dist = tmp;
}
DofGatherData fg_ring = GATHER_DATA_INIT;
dof_gather_accumulate_sample_pair(
pair_data, bordering_radius, isect_mul, first_ring, false, true, fg_ring, fg_accum);
/* Treat each sample as a ring. */
dof_gather_accumulate_sample_ring(fg_ring, 2, first_ring, false, true, fg_accum);
first_ring = false;
}
@ -666,7 +661,7 @@ void dof_slight_focus_gather(sampler2D depth_tx,
float bg_weight, fg_weight;
vec2 unused_occlusion;
int total_sample_count = dof_gather_total_sample_count(i_radius + 1, resolve_ring_density);
int total_sample_count = int(sample_count) * 2 + 1;
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);

View File

@ -130,24 +130,18 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_lut)
GPU_SHADER_CREATE_INFO(eevee_depth_of_field_background).define("DOF_FOREGROUND_PASS", "false");
GPU_SHADER_CREATE_INFO(eevee_depth_of_field_foreground).define("DOF_FOREGROUND_PASS", "true");
GPU_SHADER_CREATE_INFO(eevee_depth_of_field_hq).define("DOF_SLIGHT_FOCUS_DENSITY", "4");
GPU_SHADER_CREATE_INFO(eevee_depth_of_field_lq).define("DOF_SLIGHT_FOCUS_DENSITY", "2");
#define EEVEE_DOF_FINAL_VARIATION(name, ...) \
GPU_SHADER_CREATE_INFO(name).additional_info(__VA_ARGS__).do_static_compilation(true);
#define EEVEE_DOF_LUT_VARIATIONS(prefix, ...) \
EEVEE_DOF_FINAL_VARIATION(prefix##_lut, "eevee_depth_of_field_lut", __VA_ARGS__) \
EEVEE_DOF_FINAL_VARIATION(prefix, "eevee_depth_of_field_no_lut", __VA_ARGS__)
EEVEE_DOF_FINAL_VARIATION(prefix##_no_lut, "eevee_depth_of_field_no_lut", __VA_ARGS__)
#define EEVEE_DOF_GROUND_VARIATIONS(name, ...) \
EEVEE_DOF_LUT_VARIATIONS(name##_background, "eevee_depth_of_field_background", __VA_ARGS__) \
EEVEE_DOF_LUT_VARIATIONS(name##_foreground, "eevee_depth_of_field_foreground", __VA_ARGS__)
#define EEVEE_DOF_HQ_VARIATIONS(name, ...) \
EEVEE_DOF_LUT_VARIATIONS(name##_hq, "eevee_depth_of_field_hq", __VA_ARGS__) \
EEVEE_DOF_LUT_VARIATIONS(name##_lq, "eevee_depth_of_field_lq", __VA_ARGS__)
/** \} */
/* -------------------------------------------------------------------- */
@ -247,6 +241,6 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_resolve)
.image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img")
.compute_source("eevee_depth_of_field_resolve_comp.glsl");
EEVEE_DOF_HQ_VARIATIONS(eevee_depth_of_field_resolve, "eevee_depth_of_field_resolve")
EEVEE_DOF_LUT_VARIATIONS(eevee_depth_of_field_resolve, "eevee_depth_of_field_resolve")
/** \} */