From 117bfc870d2cb5e823da93e67afb60eb512fa316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 28 May 2024 16:00:38 +0200 Subject: [PATCH] EEVEE-Next: Fast GI: Add angular thickness While easier to understand, the conventionnal global scene thickness parameter have some downside: - It doesn't scale with larger scenes since the distant samples have small thickness - It doesn't handle fine geometric variation in foreground. The proposed angular thicknes makes all sample have the same angular span. This makes it distance independant and capture different occluder thickness with less artifacts. The downside is that the occluders have the same angular span at any distance which makes the same occluder inflate with distance. A downside is that the geometry near the shading point is under-represented. Leaving light leaking or lack of AO at contact points. To fix this, we introduce back a geometric thickness parameter. Pull Request: https://projects.blender.org/blender/blender/pulls/122334 --- scripts/startup/bl_ui/properties_render.py | 9 ++- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_400.cc | 8 +++ .../eevee_next/eevee_ambient_occlusion.cc | 3 +- .../engines/eevee_next/eevee_shader_shared.hh | 4 +- .../eevee_ambient_occlusion_pass_comp.glsl | 3 +- .../shaders/eevee_horizon_scan_comp.glsl | 3 +- .../shaders/eevee_horizon_scan_eval_lib.glsl | 65 ++++--------------- .../shaders/eevee_nodetree_lib.glsl | 3 +- source/blender/makesdna/DNA_scene_defaults.h | 2 + source/blender/makesdna/DNA_scene_types.h | 2 + source/blender/makesrna/intern/rna_scene.cc | 29 ++++++--- 12 files changed, 62 insertions(+), 71 deletions(-) diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index fdc749a3efe..75b61cba3ff 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -628,10 +628,13 @@ class RENDER_PT_eevee_next_gi_approximation(RenderButtonsPanel, Panel): col = layout.column(align=True) col.prop(props, "fast_gi_ray_count", text="Rays") col.prop(props, "fast_gi_step_count", text="Steps") + col.prop(props, "horizon_quality", text="Precision") + + col = layout.column(align=True) + col.prop(props, "fast_gi_distance") + col.prop(props, "fast_gi_thickness_near", text="Thickness Near") + col.prop(props, "fast_gi_thickness_far", text="Far") - layout.prop(props, "horizon_quality", text="Precision") - layout.prop(props, "fast_gi_distance") - layout.prop(props, "horizon_thickness", text="Thickness") layout.prop(props, "horizon_bias", text="Bias") diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 58cc474a772..7e8598cb7a1 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 45 +#define BLENDER_FILE_SUBVERSION 46 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 3caee3eabb0..6cd2f7f347a 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -3664,6 +3664,14 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 46)) { + const Scene *default_scene = DNA_struct_default_get(Scene); + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + scene->eevee.fast_gi_thickness_near = default_scene->eevee.fast_gi_thickness_near; + scene->eevee.fast_gi_thickness_far = default_scene->eevee.fast_gi_thickness_far; + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.cc b/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.cc index 73ef72c9e2d..d20db01ab25 100644 --- a/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.cc +++ b/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.cc @@ -43,8 +43,9 @@ void AmbientOcclusion::init() data_.distance = sce_eevee.gtao_distance; data_.gi_distance = (sce_eevee.fast_gi_distance > 0.0f) ? sce_eevee.fast_gi_distance : 1e16f; data_.lod_factor = 1.0f / (1.0f + sce_eevee.gtao_quality * 4.0f); - data_.thickness = sce_eevee.gtao_thickness; data_.angle_bias = 1.0 / max_ff(1e-8f, 1.0 - sce_eevee.gtao_focus); + data_.thickness_near = sce_eevee.fast_gi_thickness_near; + data_.thickness_far = sce_eevee.fast_gi_thickness_far; /* Size is multiplied by 2 because it is applied in NDC [-1..1] range. */ data_.pixel_size = float2(2.0f) / float2(inst_.film.render_extent_get()); } diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 48efab4f16d..d7117d06777 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -1934,10 +1934,10 @@ struct AOData { float distance; float lod_factor; - float thickness; + float thickness_near; + float thickness_far; float angle_bias; float gi_distance; - float _pad2; }; BLI_STATIC_ASSERT_ALIGN(AOData, 16) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl index 45537ec67b7..e4a5df94a53 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl @@ -36,7 +36,8 @@ void main() noise, uniform_buf.ao.pixel_size, uniform_buf.ao.distance, - uniform_buf.ao.thickness, + uniform_buf.ao.thickness_near, + uniform_buf.ao.thickness_far, uniform_buf.ao.angle_bias, 2, 10, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl index 0ef1c1b045a..dc2e6d833dc 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl @@ -51,7 +51,8 @@ void main() noise, uniform_buf.ao.pixel_size, uniform_buf.ao.gi_distance, - uniform_buf.ao.thickness, + uniform_buf.ao.thickness_near, + uniform_buf.ao.thickness_far, uniform_buf.ao.angle_bias, fast_gi_slice_count, fast_gi_step_count, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl index 4e5484cb2b5..ded3bec739a 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl @@ -49,36 +49,6 @@ vec3 horizon_scan_sample_normal(vec2 uv) #endif } -/** - * Returns the start and end point of a ray clipped to its intersection - * with a sphere. - */ -void horizon_scan_occluder_intersection_ray_sphere_clip(Ray ray, - Sphere sphere, - out vec3 P_entry, - out vec3 P_exit) -{ - vec3 f = ray.origin - sphere.center; - float a = length_squared(ray.direction); - float b = 2.0 * dot(ray.direction, f); - float c = length_squared(f) - square(sphere.radius); - float determinant = b * b - 4.0 * a * c; - if (determinant <= 0.0) { - /* No intersection. Return null segment. */ - P_entry = P_exit = ray.origin; - return; - } - /* Using fast sqrt_fast doesn't seem to cause artifact here. */ - float t_min = (-b - sqrt_fast(determinant)) / (2.0 * a); - float t_max = (-b + sqrt_fast(determinant)) / (2.0 * a); - /* Clip segment to the intersection range. */ - float t_entry = clamp(0.0, t_min, t_max); - float t_exit = clamp(ray.max_time, t_min, t_max); - - P_entry = ray.origin + ray.direction * t_entry; - P_exit = ray.origin + ray.direction * t_exit; -} - struct HorizonScanResult { #ifdef HORIZON_OCCLUSION float result; @@ -98,7 +68,8 @@ HorizonScanResult horizon_scan_eval(vec3 vP, vec2 noise, vec2 pixel_size, float search_distance, - float global_thickness, + float thickness_near, + float thickness_far, float angle_bias, const int slice_count, const int sample_count, @@ -184,31 +155,23 @@ HorizonScanResult horizon_scan_eval(vec3 vP, sample_depth += reversed ? -bias : bias; vec3 vP_sample = drw_point_screen_to_view(vec3(sample_uv, sample_depth)); - vec3 vV_sample = drw_view_incident_vector(vP_sample); - Ray ray; - ray.origin = vP_sample; - ray.direction = -vV_sample; - ray.max_time = global_thickness; - - if (reversed) { - /* Make the ray start above the surface and end exactly at the surface. */ - ray.max_time = 2.0 * distance(vP, vP_sample); - ray.origin = vP_sample + vV_sample * ray.max_time; - ray.direction = -vV_sample; + float sample_distance; + vec3 vL_front = normalize_and_get_length(vP_sample - vP, sample_distance); + if (sample_distance > search_distance) { + continue; } - Sphere sphere = shape_sphere(vP, search_distance); - - vec3 vP_front = ray.origin, vP_back = ray.origin + ray.direction * ray.max_time; - horizon_scan_occluder_intersection_ray_sphere_clip(ray, sphere, vP_front, vP_back); - - vec3 vL_front = normalize(vP_front - vP); - vec3 vL_back = normalize(vP_back - vP); + vec3 vL_back = normalize_and_get_length((vP_sample - vV * thickness_near) - vP, + sample_distance); + if (sample_distance > search_distance) { + continue; + } /* Ordered pair of angle. Minimum in X, Maximum in Y. * Front will always have the smallest angle here since it is the closest to the view. */ vec2 theta = acos_fast(vec2(dot(vL_front, vV), dot(vL_back, vV))); + theta.y = max(theta.x + thickness_far, theta.y); /* If we are tracing backward, the angles are negative. Swizzle to keep correct order. */ theta = (side == 0) ? theta.xy : -theta.yx; @@ -216,7 +179,7 @@ HorizonScanResult horizon_scan_eval(vec3 vP, /* Take emitter surface normal into consideration. */ vec3 sample_normal = horizon_scan_sample_normal(sample_uv); /* Discard back-facing samples. - * The 2 factor is to avoid loosing too much energy (which is something not + * The 2 factor is to avoid loosing too much energy v(which is something not * explained in the paper...). Likely to be wrong, but we need a soft falloff. */ float facing_weight = saturate(-dot(sample_normal, vL_front) * 2.0); @@ -227,8 +190,6 @@ HorizonScanResult horizon_scan_eval(vec3 vP, ~slice_bitmask); sample_radiance *= facing_weight * weight_bitmask; - /* Encoding using front sample direction gives better result than - * `normalize(vL_front + vL_back)` */ spherical_harmonics_encode_signal_sample( vL_front, vec4(sample_radiance, weight_bitmask), sh_slice); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index 22c71b305ea..72905e25fcd 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -370,7 +370,8 @@ float ambient_occlusion_eval(vec3 normal, noise, uniform_buf.ao.pixel_size, max_distance, - uniform_buf.ao.thickness, + uniform_buf.ao.thickness_near, + uniform_buf.ao.thickness_far, uniform_buf.ao.angle_bias, 2, 10, diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index bbb1cc36e7e..3508583ee33 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -216,6 +216,8 @@ .fast_gi_step_count = 8, \ .fast_gi_ray_count = 2, \ .fast_gi_distance = 0.0f, \ + .fast_gi_thickness_near = 0.25f, \ + .fast_gi_thickness_far = DEG2RAD(45), \ .fast_gi_method = FAST_GI_FULL, \ \ .bokeh_overblur = 5.0f, \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 84ef3815ce5..eaca57f800c 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1867,6 +1867,8 @@ typedef struct SceneEEVEE { int fast_gi_step_count; int fast_gi_ray_count; float fast_gi_distance; + float fast_gi_thickness_near; + float fast_gi_thickness_far; char fast_gi_method; char _pad0[3]; diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 1412093a946..3b3b75663b8 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -8294,16 +8294,27 @@ static void rna_def_scene_eevee(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); - /* Horizon Scan */ + /* Fast GI approximation */ - prop = RNA_def_property(srna, "horizon_thickness", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_float_sdna(prop, nullptr, "gtao_thickness"); - RNA_def_property_ui_text(prop, - "Thickness", - "Constant thickness of the surfaces considered when doing horizon scan " - "and by extension ambient occlusion"); - RNA_def_property_range(prop, 0.0f, FLT_MAX); - RNA_def_property_ui_range(prop, 0.0f, 100.0f, 1, 3); + prop = RNA_def_property(srna, "fast_gi_thickness_near", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_ui_text( + prop, + "Near Thickness", + "Geometric thickness of the surfaces when computing fast GI and ambient occlusion. " + "Reduces light leaking and missing contact occlusion"); + RNA_def_property_range(prop, 0.0f, 100000.0f); + RNA_def_property_ui_range(prop, 0.0f, 100.0f, 1.0f, 3); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + + prop = RNA_def_property(srna, "fast_gi_thickness_far", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_ui_text( + prop, + "Far Thickness", + "Angular thickness of the surfaces when computing fast GI and ambient occlusion. " + "Reduces energy loss and missing occlusion of far geometry"); + RNA_def_property_range(prop, DEG2RADF(1.0f), DEG2RADF(180.0f)); + RNA_def_property_ui_range(prop, DEG2RADF(1.0f), DEG2RADF(180.0f), 10.0f, 3); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);