From 0dc3318ffddaec3f4d34291512a161f76ce3957a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 3 Aug 2023 16:47:05 +0200 Subject: [PATCH] EEVEE-Next: Irradiance Bake: Virtual Offset Implement capture point bias. This offsets the capture points to reduce the amount bad capture locations (i.e.: inside objects, near walls etc...). Two new parameters are added: - Capture Surface Bias: Ensure a minimum distance between capture points and surrounding geometry. This is expressed as the relative distance between two capture point. Requires re-bake to take effect. - Capture Escape Bias: Moves capture points enclosed inside objects above the nearest surface. This bias defines how far a capture point can be moved for escaping the object. This is expressed as the relative distance between two capture point. Requires re-bake to take effect. This is called virtual offset in the reference material. A quick prepass runs before the baking to offset the samples away from any surface that could potentially make bad samples. In order to speedup the process, we create cluster list of surfels near each irradiance grid point. This allow access to neighboring surfels that can contribute to the virtual offset which should never be more than half a cell wide. Pull Request: https://projects.blender.org/blender/blender/pulls/110355 --- .../bl_ui/properties_data_lightprobe.py | 5 + .../blender/blenkernel/intern/lightprobe.cc | 2 + .../blenloader/intern/versioning_400.cc | 7 + source/blender/draw/CMakeLists.txt | 4 + .../draw/engines/eevee_next/eevee_instance.cc | 3 + .../eevee_next/eevee_irradiance_cache.cc | 179 +++++++++++++--- .../eevee_next/eevee_irradiance_cache.hh | 40 +++- .../engines/eevee_next/eevee_lightprobe.cc | 1 + .../engines/eevee_next/eevee_lightprobe.hh | 2 + .../draw/engines/eevee_next/eevee_shader.cc | 6 + .../draw/engines/eevee_next/eevee_shader.hh | 3 + .../engines/eevee_next/eevee_shader_shared.hh | 19 +- .../eevee_debug_irradiance_grid_frag.glsl | 5 + .../eevee_debug_irradiance_grid_vert.glsl | 33 +++ .../shaders/eevee_debug_surfels_frag.glsl | 12 ++ ...vee_lightprobe_irradiance_offset_comp.glsl | 192 ++++++++++++++++++ .../eevee_lightprobe_irradiance_ray_comp.glsl | 6 +- .../eevee_surfel_cluster_build_comp.glsl | 27 +++ .../shaders/eevee_surfel_list_build_comp.glsl | 3 +- .../shaders/eevee_surfel_list_lib.glsl | 25 ++- .../infos/eevee_irradiance_cache_info.hh | 33 ++- .../makesdna/DNA_lightprobe_defaults.h | 2 + .../blender/makesdna/DNA_lightprobe_types.h | 5 + .../blender/makesrna/intern/rna_lightprobe.cc | 12 ++ 24 files changed, 582 insertions(+), 44 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_frag.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_vert.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_offset_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_surfel_cluster_build_comp.glsl diff --git a/scripts/startup/bl_ui/properties_data_lightprobe.py b/scripts/startup/bl_ui/properties_data_lightprobe.py index ebfe67792f4..46e732009f3 100644 --- a/scripts/startup/bl_ui/properties_data_lightprobe.py +++ b/scripts/startup/bl_ui/properties_data_lightprobe.py @@ -119,6 +119,11 @@ class DATA_PT_lightprobe_eevee_next(DataButtonsPanel, Panel): col.separator() + col.prop(probe, "grid_surface_bias") + col.prop(probe, "grid_escape_bias") + + col.separator() + col.prop(probe, "grid_dilation_threshold") col.prop(probe, "grid_dilation_radius") diff --git a/source/blender/blenkernel/intern/lightprobe.cc b/source/blender/blenkernel/intern/lightprobe.cc index 4a52b7e8b83..04d63ef3a83 100644 --- a/source/blender/blenkernel/intern/lightprobe.cc +++ b/source/blender/blenkernel/intern/lightprobe.cc @@ -173,6 +173,7 @@ static void lightprobe_grid_cache_frame_blend_read(BlendDataReader *reader, cache->baking.L1_a = nullptr; cache->baking.L1_b = nullptr; cache->baking.L1_c = nullptr; + cache->baking.virtual_offset = nullptr; cache->baking.validity = nullptr; cache->surfels = nullptr; cache->surfels_len = 0; @@ -230,6 +231,7 @@ void BKE_lightprobe_grid_cache_frame_free(LightProbeGridCacheFrame *cache) MEM_SAFE_FREE(cache->baking.validity); MEM_SAFE_FREE(cache->connectivity.validity); MEM_SAFE_FREE(cache->surfels); + MEM_SAFE_FREE(cache->baking.virtual_offset); MEM_SAFE_FREE(cache); } diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index fd31824e9f5..19c30cca09a 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -463,6 +463,13 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!DNA_struct_elem_find(fd->filesdna, "LightProbe", "float", "grid_surface_bias")) { + LISTBASE_FOREACH (LightProbe *, lightprobe, &bmain->lightprobes) { + lightprobe->grid_surface_bias = 0.05f; + lightprobe->grid_escape_bias = 0.1f; + } + } + /* Clear removed "Z Buffer" flag. */ { const int R_IMF_FLAG_ZBUF_LEGACY = 1 << 0; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 6bda159ff39..3793225be04 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -473,6 +473,8 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_cubemap_lib.glsl engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl + engines/eevee_next/shaders/eevee_debug_irradiance_grid_vert.glsl + engines/eevee_next/shaders/eevee_debug_irradiance_grid_frag.glsl engines/eevee_next/shaders/eevee_deferred_light_frag.glsl engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl @@ -514,6 +516,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl engines/eevee_next/shaders/eevee_lightprobe_irradiance_bounds_comp.glsl engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl + engines/eevee_next/shaders/eevee_lightprobe_irradiance_offset_comp.glsl engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl engines/eevee_next/shaders/eevee_lightprobe_lib.glsl engines/eevee_next/shaders/eevee_ltc_lib.glsl @@ -567,6 +570,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_surf_lib.glsl engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl engines/eevee_next/shaders/eevee_surf_world_frag.glsl + engines/eevee_next/shaders/eevee_surfel_cluster_build_comp.glsl engines/eevee_next/shaders/eevee_surfel_light_comp.glsl engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl engines/eevee_next/shaders/eevee_surfel_list_lib.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 0c2a3fdedc4..5dee4aff7e3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -554,6 +554,9 @@ void Instance::light_bake_irradiance( irradiance_cache.bake.surfels_create(probe); irradiance_cache.bake.surfels_lights_eval(); + + irradiance_cache.bake.clusters_build(); + irradiance_cache.bake.irradiance_offset(); }); sampling.init(probe); diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index a1caf7379c9..2f8ef8b59a1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -320,9 +320,15 @@ void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb) case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: inst_.info = "Debug Mode: Surfels Normal"; break; + case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_CLUSTER: + inst_.info = "Debug Mode: Surfels Cluster"; + break; case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: inst_.info = "Debug Mode: Surfels Irradiance"; break; + case eDebugMode::DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET: + inst_.info = "Debug Mode: Virtual Offset"; + break; default: /* Nothing to display. */ return; @@ -335,29 +341,64 @@ void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb) LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache; - if (cache->surfels == nullptr || cache->surfels_len == 0) { - continue; + switch (inst_.debug_mode) { + case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: + case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_CLUSTER: + case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: { + if (cache->surfels == nullptr || cache->surfels_len == 0) { + continue; + } + debug_ps_.init(); + debug_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | + DRW_STATE_DEPTH_LESS_EQUAL); + debug_ps_.framebuffer_set(&view_fb); + debug_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS)); + debug_ps_.push_constant("surfel_radius", 0.5f / grid.surfel_density); + debug_ps_.push_constant("debug_mode", static_cast(inst_.debug_mode)); + + debug_surfels_buf_.resize(cache->surfels_len); + /* TODO(fclem): Cleanup: Could have a function in draw::StorageArrayBuffer that takes an + * input data. */ + Span grid_surfels(static_cast(cache->surfels), cache->surfels_len); + MutableSpan(debug_surfels_buf_.data(), cache->surfels_len).copy_from(grid_surfels); + debug_surfels_buf_.push_update(); + + debug_ps_.bind_ssbo("surfels_buf", debug_surfels_buf_); + debug_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, cache->surfels_len, 4); + + inst_.manager->submit(debug_ps_, view); + break; + } + + case eDebugMode::DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET: { + if (cache->baking.virtual_offset == nullptr) { + continue; + } + int3 grid_size = int3(cache->size); + debug_ps_.init(); + debug_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | + DRW_STATE_DEPTH_LESS_EQUAL); + debug_ps_.framebuffer_set(&view_fb); + debug_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_IRRADIANCE_GRID)); + debug_ps_.push_constant("debug_mode", static_cast(inst_.debug_mode)); + debug_ps_.push_constant("grid_mat", grid.object_to_world); + + float4 *data = (float4 *)cache->baking.virtual_offset; + + Texture debug_data_tx = {"debug_data_tx"}; + debug_data_tx.ensure_3d( + GPU_RGBA16F, grid_size, GPU_TEXTURE_USAGE_SHADER_READ, (float *)data); + + debug_ps_.bind_texture("debug_data_tx", debug_data_tx); + debug_ps_.draw_procedural(GPU_PRIM_LINES, 1, grid_size.x * grid_size.y * grid_size.z * 2); + + inst_.manager->submit(debug_ps_, view); + break; + } + + default: + break; } - - debug_surfels_ps_.init(); - debug_surfels_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | - DRW_STATE_DEPTH_LESS_EQUAL); - display_grids_ps_.framebuffer_set(&view_fb); - debug_surfels_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS)); - debug_surfels_ps_.push_constant("surfel_radius", 1.5f / 4.0f); - debug_surfels_ps_.push_constant("debug_mode", int(inst_.debug_mode)); - - debug_surfels_buf_.resize(cache->surfels_len); - /* TODO(fclem): Cleanup: Could have a function in draw::StorageArrayBuffer that takes an input - * data. */ - Span grid_surfels(static_cast(cache->surfels), cache->surfels_len); - MutableSpan(debug_surfels_buf_.data(), cache->surfels_len).copy_from(grid_surfels); - debug_surfels_buf_.push_update(); - - debug_surfels_ps_.bind_ssbo("surfels_buf", debug_surfels_buf_); - debug_surfels_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, cache->surfels_len, 4); - - inst_.manager->submit(debug_surfels_ps_, view); } } @@ -465,6 +506,8 @@ void IrradianceBake::init(const Object &probe_object) { const ::LightProbe *lightprobe = static_cast<::LightProbe *>(probe_object.data); surfel_density_ = lightprobe->surfel_density; + min_distance_to_surface_ = lightprobe->grid_surface_bias; + max_virtual_offset_ = lightprobe->grid_escape_bias; } void IrradianceBake::sync() @@ -485,6 +528,17 @@ void IrradianceBake::sync() pass.barrier(GPU_BARRIER_TEXTURE_FETCH); pass.dispatch(&dispatch_per_surfel_); } + { + PassSimple &pass = surfel_cluster_build_ps_; + pass.init(); + pass.shader_set(inst_.shaders.static_shader_get(SURFEL_CLUSTER_BUILD)); + pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); + pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); + pass.bind_image("cluster_list_img", &cluster_list_tx_); + pass.barrier(GPU_BARRIER_SHADER_STORAGE); + pass.dispatch(&dispatch_per_surfel_); + pass.barrier(GPU_BARRIER_SHADER_STORAGE | GPU_BARRIER_TEXTURE_FETCH); + } { PassSimple &pass = surfel_ray_build_ps_; pass.init(); @@ -539,6 +593,19 @@ void IrradianceBake::sync() pass.bind_image("irradiance_L1_b_img", &irradiance_L1_b_tx_); pass.bind_image("irradiance_L1_c_img", &irradiance_L1_c_tx_); pass.bind_image("validity_img", &validity_tx_); + pass.bind_image("virtual_offset_img", &virtual_offset_tx_); + pass.barrier(GPU_BARRIER_SHADER_STORAGE | GPU_BARRIER_SHADER_IMAGE_ACCESS); + pass.dispatch(&dispatch_per_grid_sample_); + } + { + PassSimple &pass = irradiance_offset_ps_; + pass.init(); + pass.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_OFFSET)); + pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); + pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); + pass.bind_ssbo("list_info_buf", &list_info_buf_); + pass.bind_image("cluster_list_img", &cluster_list_tx_); + pass.bind_image("virtual_offset_img", &virtual_offset_tx_); pass.barrier(GPU_BARRIER_SHADER_STORAGE | GPU_BARRIER_SHADER_IMAGE_ACCESS); pass.dispatch(&dispatch_per_grid_sample_); } @@ -586,11 +653,21 @@ void IrradianceBake::surfels_create(const Object &probe_object) dispatch_per_grid_sample_ = math::divide_ceil(grid_resolution, int3(IRRADIANCE_GRID_GROUP_SIZE)); capture_info_buf_.irradiance_grid_size = grid_resolution; capture_info_buf_.irradiance_grid_local_to_world = grid_local_to_world; + capture_info_buf_.irradiance_grid_world_to_local = float4x4(probe_object.world_to_object); capture_info_buf_.irradiance_grid_world_to_local_rotation = float4x4( invert(normalize(float3x3(grid_local_to_world)))); + capture_info_buf_.min_distance_to_surface = min_distance_to_surface_; + capture_info_buf_.max_virtual_offset = max_virtual_offset_; + capture_info_buf_.surfel_radius = 0.5f / lightprobe->surfel_density; + /* Make virtual offset distances scale relative. */ + float3 scale = math::to_scale(grid_local_to_world) / float3(grid_resolution); + float min_distance_between_grid_samples = min_fff(UNPACK3(scale)); + capture_info_buf_.min_distance_to_surface *= min_distance_between_grid_samples; + capture_info_buf_.max_virtual_offset *= min_distance_between_grid_samples; + eGPUTextureUsage texture_usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE | - GPU_TEXTURE_USAGE_HOST_READ | GPU_TEXTURE_USAGE_ATTACHMENT; + GPU_TEXTURE_USAGE_HOST_READ; /* 32bit float is needed here otherwise we loose too much energy from rounding error during the * accumulation when the sample count is above 500. */ @@ -605,6 +682,9 @@ void IrradianceBake::surfels_create(const Object &probe_object) irradiance_L1_c_tx_.clear(float4(0.0f)); validity_tx_.clear(float4(0.0f)); + virtual_offset_tx_.ensure_3d(GPU_RGBA16F, grid_resolution, texture_usage); + virtual_offset_tx_.clear(float4(0.0f)); + DRW_stats_group_start("IrradianceBake.SceneBounds"); { @@ -672,11 +752,11 @@ void IrradianceBake::surfels_create(const Object &probe_object) capture_info_buf_.surfel_len = 0u; capture_info_buf_.push_update(); - empty_raster_fb_.ensure(transform_point(invert(basis_x_), grid_pixel_extent_).xy()); + empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_x_), grid_pixel_extent_).xy())); inst_.pipelines.capture.render(view_x_); - empty_raster_fb_.ensure(transform_point(invert(basis_y_), grid_pixel_extent_).xy()); + empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_y_), grid_pixel_extent_).xy())); inst_.pipelines.capture.render(view_y_); - empty_raster_fb_.ensure(transform_point(invert(basis_z_), grid_pixel_extent_).xy()); + empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_z_), grid_pixel_extent_).xy())); inst_.pipelines.capture.render(view_z_); DRW_stats_group_end(); @@ -703,11 +783,11 @@ void IrradianceBake::surfels_create(const Object &probe_object) capture_info_buf_.surfel_len = 0u; capture_info_buf_.push_update(); - empty_raster_fb_.ensure(transform_point(invert(basis_x_), grid_pixel_extent_).xy()); + empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_x_), grid_pixel_extent_).xy())); inst_.pipelines.capture.render(view_x_); - empty_raster_fb_.ensure(transform_point(invert(basis_y_), grid_pixel_extent_).xy()); + empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_y_), grid_pixel_extent_).xy())); inst_.pipelines.capture.render(view_y_); - empty_raster_fb_.ensure(transform_point(invert(basis_z_), grid_pixel_extent_).xy()); + empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_z_), grid_pixel_extent_).xy())); inst_.pipelines.capture.render(view_z_); /* Sync with any other following pass using the surfel buffer. */ @@ -730,6 +810,32 @@ void IrradianceBake::surfels_lights_eval() inst_.manager->submit(surfel_light_eval_ps_, view_z_); } +void IrradianceBake::clusters_build() +{ + if (max_virtual_offset_ == 0.0f) { + return; + } + eGPUTextureUsage texture_usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE; + + cluster_list_tx_.ensure_3d(GPU_R32I, capture_info_buf_.irradiance_grid_size, texture_usage); + cluster_list_tx_.clear(int4(-1)); + /* View is not important here. It is only for validation. */ + inst_.manager->submit(surfel_cluster_build_ps_, view_z_); +} + +void IrradianceBake::irradiance_offset() +{ + if (max_virtual_offset_ == 0.0f) { + /* NOTE: Virtual offset texture should already have been cleared to 0. */ + return; + } + + inst_.manager->submit(irradiance_offset_ps_, view_z_); + + /* Not needed after this point. */ + cluster_list_tx_.free(); +} + void IrradianceBake::raylists_build() { using namespace blender::math; @@ -815,6 +921,7 @@ void IrradianceBake::irradiance_capture() void IrradianceBake::read_surfels(LightProbeGridCacheFrame *cache_frame) { if (!ELEM(inst_.debug_mode, + eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_CLUSTER, eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL, eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE)) { @@ -833,11 +940,24 @@ void IrradianceBake::read_surfels(LightProbeGridCacheFrame *cache_frame) surfels_dst.copy_from(surfels_src); } +void IrradianceBake::read_virtual_offset(LightProbeGridCacheFrame *cache_frame) +{ + if (!ELEM(inst_.debug_mode, eDebugMode::DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET)) { + return; + } + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + cache_frame->baking.virtual_offset = (float(*)[4])virtual_offset_tx_.read( + GPU_DATA_FLOAT); +} + LightProbeGridCacheFrame *IrradianceBake::read_result_unpacked() { LightProbeGridCacheFrame *cache_frame = BKE_lightprobe_grid_cache_frame_create(); read_surfels(cache_frame); + read_virtual_offset(cache_frame); cache_frame->size[0] = irradiance_L0_tx_.width(); cache_frame->size[1] = irradiance_L0_tx_.height(); @@ -859,6 +979,7 @@ LightProbeGridCacheFrame *IrradianceBake::read_result_packed() LightProbeGridCacheFrame *cache_frame = BKE_lightprobe_grid_cache_frame_create(); read_surfels(cache_frame); + read_virtual_offset(cache_frame); cache_frame->size[0] = irradiance_L0_tx_.width(); cache_frame->size[1] = irradiance_L0_tx_.height(); diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 52a69671c32..0418fa19e5d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -49,10 +49,14 @@ class IrradianceBake { PassSimple surfel_light_eval_ps_ = {"LightEval"}; /** Create linked list of surfel to emulated ray-cast. */ PassSimple surfel_ray_build_ps_ = {"RayBuild"}; + /** Create linked list of surfel to cluster them in the 3D irradiance grid. */ + PassSimple surfel_cluster_build_ps_ = {"RayBuild"}; /** Propagate light from surfel to surfel. */ PassSimple surfel_light_propagate_ps_ = {"LightPropagate"}; /** Capture surfel lighting to irradiance samples. */ PassSimple irradiance_capture_ps_ = {"IrradianceCapture"}; + /** Compute virtual offset for each irradiance samples. */ + PassSimple irradiance_offset_ps_ = {"IrradianceOffset"}; /** Compute scene bounding box. */ PassSimple irradiance_bounds_ps_ = {"IrradianceBounds"}; /** Index of source and destination radiance in radiance double-buffer. */ @@ -91,6 +95,10 @@ class IrradianceBake { Texture irradiance_L1_a_tx_ = {"irradiance_L1_a_tx_"}; Texture irradiance_L1_b_tx_ = {"irradiance_L1_b_tx_"}; Texture irradiance_L1_c_tx_ = {"irradiance_L1_c_tx_"}; + /** Offset per irradiance point to apply to the baking location. */ + Texture virtual_offset_tx_ = {"virtual_offset_tx_"}; + /** List of closest surfels per irradiance sample. */ + Texture cluster_list_tx_ = {"cluster_list_tx_"}; /** Contains ratio of back-face hits. Allows to get rid of invalid probes. */ Texture validity_tx_ = {"validity_tx_"}; @@ -98,6 +106,18 @@ class IrradianceBake { float4 scene_bound_sphere_; /* Surfel per unit distance. */ float surfel_density_ = 1.0f; + /** + * Minimum distance a grid sample point should have with a surface. + * In minimum grid sample spacing. + * Avoids samples to be too close to surface even if they are valid. + */ + float min_distance_to_surface_ = 0.05f; + /** + * Maximum distance from the grid sample point to the baking location. + * In minimum grid sample spacing. + * Avoids samples to be too far from their actual origin. + */ + float max_virtual_offset_ = 0.1f; public: IrradianceBake(Instance &inst) : inst_(inst){}; @@ -111,10 +131,22 @@ class IrradianceBake { void surfels_create(const Object &probe_object); /** Evaluate direct lighting (and also clear the surfels radiance). */ void surfels_lights_eval(); - /** Create a surfel lists to emulate ray-casts for the current sample random direction. */ + /** + * Create a surfel lists per irradiance probe in order to compute the virtual baking offset. + * NOTE: The resulting lists are only valid until `clusters_build()` or `raylists_build()` are + * called since they share the same links inside the Surfel struct. + */ + void clusters_build(); + /** + * Create a surfel lists to emulate ray-casts for the current sample random direction. + * NOTE: The resulting lists are only valid until `clusters_build()` or `raylists_build()` are + * called since they share the same links inside the Surfel struct. + */ void raylists_build(); /** Propagate light from surfel to surfel in a random direction over the sphere. */ void propagate_light(); + /** Compute offset to bias irradiance capture location. */ + void irradiance_offset(); /** Store surfel irradiance inside the irradiance grid samples. */ void irradiance_capture(); @@ -126,6 +158,8 @@ class IrradianceBake { private: /** Read surfel data back to CPU into \a cache_frame . */ void read_surfels(LightProbeGridCacheFrame *cache_frame); + /** Read virtual offset back to CPU into \a cache_frame . */ + void read_virtual_offset(LightProbeGridCacheFrame *cache_frame); }; /** @@ -153,8 +187,8 @@ class IrradianceCache { /** If true, will trigger the reupload of all grid data instead of just streaming new ones. */ bool do_full_update_ = true; - /** Display surfel debug data. */ - PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"}; + /** Display debug data. */ + PassSimple debug_ps_ = {"IrradianceCache.Debug"}; /** Debug surfel elements copied from the light cache. */ draw::StorageArrayBuffer debug_surfels_buf_; diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc index 4c590aa17f0..a0f1e12fd87 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc @@ -34,6 +34,7 @@ void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle) grid.initialized = true; grid.updated = true; + grid.surfel_density = static_cast(ob->data)->surfel_density; grid.object_to_world = float4x4(ob->object_to_world); grid.world_to_object = float4x4( math::normalize(math::transpose(float3x3(grid.object_to_world)))); diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh index f3065887b8f..fa8c8223930 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh @@ -42,6 +42,8 @@ struct IrradianceGrid : public LightProbe, IrradianceGridData { Vector bricks; /** Index of the grid inside the grid UBO. */ int grid_index; + /** Copy of surfel density for debugging purpose. */ + float surfel_density; /** Copy of DNA members. */ float dilation_threshold; float dilation_radius; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 31db6fe0e7d..041a4c8d408 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -106,6 +106,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_motion_blur_tiles_flatten_rg"; case DEBUG_SURFELS: return "eevee_debug_surfels"; + case DEBUG_IRRADIANCE_GRID: + return "eevee_debug_irradiance_grid"; case DISPLAY_PROBE_GRID: return "eevee_display_probe_grid"; case DOF_BOKEH_LUT: @@ -176,6 +178,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_ray_tile_compact"; case LIGHTPROBE_IRRADIANCE_BOUNDS: return "eevee_lightprobe_irradiance_bounds"; + case LIGHTPROBE_IRRADIANCE_OFFSET: + return "eevee_lightprobe_irradiance_offset"; case LIGHTPROBE_IRRADIANCE_RAY: return "eevee_lightprobe_irradiance_ray"; case LIGHTPROBE_IRRADIANCE_LOAD: @@ -214,6 +218,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_shadow_tag_usage_transparent"; case SUBSURFACE_EVAL: return "eevee_subsurface_eval"; + case SURFEL_CLUSTER_BUILD: + return "eevee_surfel_cluster_build"; case SURFEL_LIGHT: return "eevee_surfel_light"; case SURFEL_LIST_BUILD: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index bea50281700..e069c2aa38c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -36,6 +36,7 @@ enum eShaderType { DEFERRED_LIGHT_DIFFUSE_ONLY, DEBUG_SURFELS, + DEBUG_IRRADIANCE_GRID, DISPLAY_PROBE_GRID, @@ -67,6 +68,7 @@ enum eShaderType { LIGHT_CULLING_ZBIN, LIGHTPROBE_IRRADIANCE_BOUNDS, + LIGHTPROBE_IRRADIANCE_OFFSET, LIGHTPROBE_IRRADIANCE_RAY, LIGHTPROBE_IRRADIANCE_LOAD, @@ -107,6 +109,7 @@ enum eShaderType { SUBSURFACE_EVAL, + SURFEL_CLUSTER_BUILD, SURFEL_LIGHT, SURFEL_LIST_BUILD, SURFEL_LIST_SORT, 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 58f3a0979de..b47fbb39c76 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -55,6 +55,11 @@ enum eDebugMode : uint32_t { */ DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL = 3u, DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE = 4u, + DEBUG_IRRADIANCE_CACHE_SURFELS_CLUSTER = 5u, + /** + * Display IrradianceCache virtual offset. + */ + DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET = 6u, /** * Show tiles depending on their status. */ @@ -866,7 +871,8 @@ struct Surfel { float ray_distance; /** Surface albedo to apply to incoming radiance. */ packed_float3 albedo_back; - int _pad3; + /** Cluster this surfel is assigned to. */ + int cluster_id; /** Surface radiance: Emission + Direct Lighting. */ SurfelRadiance radiance_direct; /** Surface radiance: Indirect Lighting. Double buffered to avoid race conditions. */ @@ -889,6 +895,8 @@ struct CaptureInfoData { float sample_index; /** Transform of the light-probe object. */ float4x4 irradiance_grid_local_to_world; + /** Transform of the light-probe object. */ + float4x4 irradiance_grid_world_to_local; /** Transform vectors from world space to local space. Does not have location component. */ /** TODO(fclem): This could be a float3x4 or a float3x3 if padded correctly. */ float4x4 irradiance_grid_world_to_local_rotation; @@ -899,9 +907,12 @@ struct CaptureInfoData { int scene_bound_x_max; int scene_bound_y_max; int scene_bound_z_max; - int _pad0; - int _pad1; - int _pad2; + /** Minimum distance between a grid sample and a surface. Used to compute virtual offset. */ + float min_distance_to_surface; + /** Maximum world scale offset an irradiance grid sample can be baked with. */ + float max_virtual_offset; + /** Radius of surfels. */ + float surfel_radius; }; BLI_STATIC_ASSERT_ALIGN(CaptureInfoData, 16) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_frag.glsl new file mode 100644 index 00000000000..f25e120b786 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_frag.glsl @@ -0,0 +1,5 @@ + +void main() +{ + out_color = vec4(0.0, 1.0, 1.0, 0.0); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_vert.glsl new file mode 100644 index 00000000000..cb8ea839e3e --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_vert.glsl @@ -0,0 +1,33 @@ + +#pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + ivec3 grid_resolution = textureSize(debug_data_tx, 0); + ivec3 grid_sample; + int sample_id = gl_VertexID / 2; + grid_sample.x = (sample_id % grid_resolution.x); + grid_sample.y = (sample_id / grid_resolution.x) % grid_resolution.y; + grid_sample.z = (sample_id / (grid_resolution.x * grid_resolution.y)); + + vec3 P = lightprobe_irradiance_grid_sample_position(grid_mat, grid_resolution, grid_sample); + + vec4 debug_data = texelFetch(debug_data_tx, grid_sample, 0); + if (debug_mode == DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET) { + if (is_zero(debug_data.xyz)) { + /* Only render points that have offset. */ + gl_Position = vec4(0.0); + gl_PointSize = 0.0; + return; + } + + if ((gl_VertexID & 1) == 1) { + P += debug_data.xyz; + } + } + + gl_Position = point_world_to_ndc(P); + gl_Position.z -= 2.5e-5; + gl_PointSize = 3.0; +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl index cb7bfafa955..1248f8fb462 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl @@ -1,4 +1,13 @@ +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +vec3 debug_random_color(int v) +{ + float r = interlieved_gradient_noise(vec2(v, 0), 0.0, 0.0); + return hue_gradient(r); +} + void main() { Surfel surfel = surfels_buf[surfel_index]; @@ -13,6 +22,9 @@ void main() case DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: out_color = vec4(pow(surfel.normal * 0.5 + 0.5, vec3(2.2)), 0.0); break; + case DEBUG_IRRADIANCE_CACHE_SURFELS_CLUSTER: + out_color = vec4(pow(debug_random_color(surfel.cluster_id), vec3(2.2)), 0.0); + break; case DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: out_color = vec4(radiance, 0.0); break; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_offset_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_offset_comp.glsl new file mode 100644 index 00000000000..2754c2c6b94 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_offset_comp.glsl @@ -0,0 +1,192 @@ + +/** + * For every irradiance probe sample, check if close to a surounding surfel and try to offset the + * irradiance sample position. This is similar to the surfel ray but we do not actually transport + * the light. + * + * Dispatched as 1 thread per irradiance probe sample. + */ + +#pragma BLENDER_REQUIRE(eevee_surfel_list_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl) + +int find_closest_surfel(ivec3 grid_coord, vec3 P) +{ + int surfel_first = imageLoad(cluster_list_img, grid_coord).r; + float search_radius_sqr = square_f(capture_info_buf.max_virtual_offset + + capture_info_buf.min_distance_to_surface); + + int closest_surfel = -1; + float closest_distance_sqr = 1e10; + for (int surfel_id = surfel_first; surfel_id > -1; surfel_id = surfel_buf[surfel_id].next) { + Surfel surfel = surfel_buf[surfel_id]; + + vec3 probe_to_surfel = surfel.position - P; + float surfel_dist_sqr = length_squared(probe_to_surfel); + /* Do not consider surfels that are outside of search radius. */ + if (surfel_dist_sqr > search_radius_sqr) { + continue; + } + + if (closest_distance_sqr > surfel_dist_sqr) { + closest_distance_sqr = surfel_dist_sqr; + closest_surfel = surfel_id; + } + } + return closest_surfel; +} + +float front_facing_offset(float surfel_distance) +{ + if (abs(surfel_distance) > capture_info_buf.min_distance_to_surface) { + return 0.0; + } + /* NOTE: distance can be negative. */ + return surfel_distance - ((surfel_distance > 0.0) ? capture_info_buf.min_distance_to_surface : + -capture_info_buf.min_distance_to_surface); +} + +float back_facing_offset(float surfel_distance) +{ + if (surfel_distance > capture_info_buf.max_virtual_offset) { + return 0.0; + } + /* NOTE: distance can be negative. */ + return surfel_distance + ((surfel_distance > 0.0) ? capture_info_buf.min_distance_to_surface : + -capture_info_buf.min_distance_to_surface); +} + +float compute_offset_length(ivec3 grid_coord, vec3 P, vec3 offset_direction) +{ + int surfel_first = imageLoad(cluster_list_img, grid_coord).r; + float search_radius = max(capture_info_buf.max_virtual_offset, + capture_info_buf.min_distance_to_surface); + /* Scale it a bit to avoid missing surfaces. */ + float ray_radius = capture_info_buf.surfel_radius * M_SQRT2; + + /* Nearest and farthest surfels in offset direction on both sides. */ + int surfel_pos = -1; + int surfel_neg = -1; + float surfel_distance_pos = +1e10; + float surfel_distance_neg = -1e10; + for (int surfel_id = surfel_first; surfel_id > -1; surfel_id = surfel_buf[surfel_id].next) { + Surfel surfel = surfel_buf[surfel_id]; + + vec3 probe_to_surfel = surfel.position - P; + float surf_dist_signed = dot(offset_direction, probe_to_surfel); + /* Do not consider surfels that are outside of search radius. */ + if (abs(surf_dist_signed) > search_radius) { + continue; + } + /* Emulate ray cast with any hit shader by discarding surfels outside of the ray radius. */ + float ray_dist = distance(surf_dist_signed * offset_direction, probe_to_surfel); + if (ray_dist > ray_radius) { + continue; + } + + if (surf_dist_signed > 0.0) { + if (surfel_distance_pos > surf_dist_signed) { + surfel_distance_pos = surf_dist_signed; + surfel_pos = surfel_id; + } + } + else { + if (surfel_distance_neg < surf_dist_signed) { + surfel_distance_neg = surf_dist_signed; + surfel_neg = surfel_id; + } + } + } + + bool has_neighbor_pos = surfel_pos != -1; + bool has_neighbor_neg = surfel_neg != -1; + + if (has_neighbor_pos && has_neighbor_neg) { + /* If both sides have neighbors. */ + bool is_front_facing_pos = dot(offset_direction, surfel_buf[surfel_pos].normal) < 0.0; + bool is_front_facing_neg = dot(-offset_direction, surfel_buf[surfel_neg].normal) < 0.0; + if (is_front_facing_pos && is_front_facing_neg) { + /* If both sides have same facing. */ + if (is_front_facing_pos) { + /* If both sides are front facing. */ + float distance_between_neighbors = surfel_distance_pos - surfel_distance_neg; + if (distance_between_neighbors < capture_info_buf.min_distance_to_surface * 2.0) { + /* Choose the middle point. */ + return (surfel_distance_pos + surfel_distance_neg) / 2.0; + } + else { + /* Choose the maximum offset. */ + /* NOTE: The maximum offset is always from positive side since it's the closest. */ + return front_facing_offset(surfel_distance_pos); + } + } + else { + /* If both sides are back facing. */ + /* Choose the minimum offset. */ + /* NOTE: The minimum offset is always from positive side since it's the closest. */ + return back_facing_offset(surfel_distance_pos); + } + } + else { + /* If both sides have different facing. */ + float front_distance = is_front_facing_pos ? surfel_distance_pos : surfel_distance_neg; + float back_distance = !is_front_facing_pos ? surfel_distance_pos : surfel_distance_neg; + float front_offset = front_facing_offset(front_distance); + float back_offset = back_facing_offset(back_distance); + /* Choose the minimum offset. */ + return (abs(front_offset) < abs(back_offset)) ? front_offset : back_offset; + } + } + + if (has_neighbor_pos || has_neighbor_neg) { + /* Only one side have neighbor. */ + int nearest_surfel_id = has_neighbor_pos ? surfel_pos : surfel_neg; + float surfel_distance = has_neighbor_pos ? surfel_distance_pos : surfel_distance_neg; + bool is_front_facing = dot(has_neighbor_pos ? offset_direction : -offset_direction, + surfel_buf[nearest_surfel_id].normal) < 0.0; + if (is_front_facing) { + return front_facing_offset(surfel_distance); + } + else { + return back_facing_offset(surfel_distance); + } + } + /* If no sides has neighbor (should never happen here since we already bailed out). */ + return 0.0; +} + +void main() +{ + ivec3 grid_coord = ivec3(gl_GlobalInvocationID); + + if (any(greaterThanEqual(grid_coord, capture_info_buf.irradiance_grid_size))) { + return; + } + + vec3 P = lightprobe_irradiance_grid_sample_position( + capture_info_buf.irradiance_grid_local_to_world, + capture_info_buf.irradiance_grid_size, + grid_coord); + + int closest_surfel_id = find_closest_surfel(grid_coord, P); + if (closest_surfel_id == -1) { + imageStore(virtual_offset_img, grid_coord, vec4(0.0)); + return; + } + + /* Offset direction towards the sampling point. */ + // vec3 offset_direction = safe_normalize(surfel_buf[closest_surfel_id].position - P); + /* NOTE: Use normal direction of the surfel instead for stability reasons. */ + vec3 offset_direction = surfel_buf[closest_surfel_id].normal; + bool is_front_facing = dot(surfel_buf[closest_surfel_id].position - P, + surfel_buf[closest_surfel_id].normal) < 0.0; + if (is_front_facing) { + offset_direction = -offset_direction; + } + + float offset_length = compute_offset_length(grid_coord, P, offset_direction); + + vec3 virtual_offset = offset_direction * offset_length; + + imageStore(virtual_offset_img, grid_coord, vec4(virtual_offset, 0.0)); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl index 93522898788..4cd4803310f 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl @@ -71,9 +71,13 @@ void main() capture_info_buf.irradiance_grid_size, grid_coord); + /* Add virtual offset to avoid baking inside of geometry as much as possible. */ + P += imageLoad(virtual_offset_img, grid_coord).xyz; + /* Project to get ray linked list. */ float irradiance_sample_ray_distance; - int list_index = surfel_list_index_get(P, irradiance_sample_ray_distance); + int list_index = surfel_list_index_get( + list_info_buf.ray_grid_size, P, irradiance_sample_ray_distance); /* Walk the ray to get which surfels the irradiance sample is between. */ int surfel_prev = -1; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_cluster_build_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_cluster_build_comp.glsl new file mode 100644 index 00000000000..fcbe36d3f08 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_cluster_build_comp.glsl @@ -0,0 +1,27 @@ + +/** + * Takes scene surfel representation and build list of surfels inside 3D cells. + * + * The lists head are allocated to fit the probe samples granularity. + * + * Dispatch 1 thread per surfel. + */ + +#pragma BLENDER_REQUIRE(eevee_surfel_list_lib.glsl) + +void main() +{ + int surfel_index = int(gl_GlobalInvocationID.x); + if (surfel_index >= capture_info_buf.surfel_len) { + return; + } + + ivec3 cluster = surfel_cluster_index_get(imageSize(cluster_list_img), + capture_info_buf.irradiance_grid_world_to_local, + surfel_buf[surfel_index].position); + /* For debugging. */ + surfel_buf[surfel_index].cluster_id = cluster.x + cluster.y * 1000 + cluster.z * 1000000; + /* NOTE: We only need to init the `cluster_list_img` to -1 for the whole list to be valid since + * every surfel will load its `next` value from the list head. */ + surfel_buf[surfel_index].next = imageAtomicExchange(cluster_list_img, cluster, surfel_index); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl index e6dbae33339..20a3815f6d1 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl @@ -22,7 +22,8 @@ void main() } float ray_distance; - int list_index = surfel_list_index_get(surfel_buf[surfel_index].position, ray_distance); + int list_index = surfel_list_index_get( + list_info_buf.ray_grid_size, surfel_buf[surfel_index].position, ray_distance); /* Do separate assignement to avoid reference to buffer in arguments which is tricky to cross * compile. */ surfel_buf[surfel_index].ray_distance = ray_distance; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_lib.glsl index 11b6b9bdf53..111873da90c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_lib.glsl @@ -1,19 +1,34 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl) /** - * Return the coresponding list index in the `list_start_buf` for a given world position. + * Return the corresponding list index in the `list_start_buf` for a given world position. * It will clamp any coordinate outside valid bounds to nearest list. * Also return the surfel sorting value as `r_ray_distance`. */ -int surfel_list_index_get(vec3 P, out float r_ray_distance) +int surfel_list_index_get(ivec2 ray_grid_size, vec3 P, out float r_ray_distance) { vec4 hP = point_world_to_ndc(P); r_ray_distance = -hP.z; vec2 ssP = hP.xy * 0.5 + 0.5; - ivec2 ray_coord_on_grid = ivec2(ssP * vec2(list_info_buf.ray_grid_size)); - ray_coord_on_grid = clamp(ray_coord_on_grid, ivec2(0), list_info_buf.ray_grid_size - 1); + ivec2 ray_coord_on_grid = ivec2(ssP * vec2(ray_grid_size)); + ray_coord_on_grid = clamp(ray_coord_on_grid, ivec2(0), ray_grid_size - 1); - int list_index = ray_coord_on_grid.y * list_info_buf.ray_grid_size.x + ray_coord_on_grid.x; + int list_index = ray_coord_on_grid.y * ray_grid_size.x + ray_coord_on_grid.x; return list_index; } + +/** + * Return the corresponding cluster index in the `cluster_list_tx` for a given world position. + * It will clamp any coordinate outside valid bounds to nearest cluster. + */ +ivec3 surfel_cluster_index_get(ivec3 cluster_grid_size, + float4x4 irradiance_grid_world_to_local, + vec3 P) +{ + vec3 lP = transform_point(irradiance_grid_world_to_local, P) * 0.5 + 0.5; + ivec3 cluster_index = ivec3(lP * vec3(cluster_grid_size)); + cluster_index = clamp(cluster_index, ivec3(0), cluster_grid_size - 1); + return cluster_index; +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index 650de1ce295..d4beb240e6f 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -24,6 +24,16 @@ GPU_SHADER_CREATE_INFO(eevee_debug_surfels) .push_constant(Type::INT, "debug_mode") .do_static_compilation(true); +GPU_SHADER_CREATE_INFO(eevee_debug_irradiance_grid) + .additional_info("eevee_shared", "draw_view") + .fragment_out(0, Type::VEC4, "out_color") + .sampler(0, ImageType::FLOAT_3D, "debug_data_tx") + .push_constant(Type::MAT4, "grid_mat") + .push_constant(Type::INT, "debug_mode") + .vertex_source("eevee_debug_irradiance_grid_vert.glsl") + .fragment_source("eevee_debug_irradiance_grid_frag.glsl") + .do_static_compilation(true); + GPU_SHADER_INTERFACE_INFO(eevee_display_probe_grid_iface, "") .smooth(Type::VEC2, "lP") .flat(Type::IVEC3, "cell"); @@ -67,6 +77,14 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_light) .compute_source("eevee_surfel_light_comp.glsl") .do_static_compilation(true); +GPU_SHADER_CREATE_INFO(eevee_surfel_cluster_build) + .local_group_size(SURFEL_GROUP_SIZE) + .additional_info("eevee_shared", "eevee_surfel_common", "draw_view") + .storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf") + .image(0, GPU_R32I, Qualifier::READ_WRITE, ImageType::INT_3D, "cluster_list_img") + .compute_source("eevee_surfel_cluster_build_comp.glsl") + .do_static_compilation(true); + GPU_SHADER_CREATE_INFO(eevee_surfel_list_build) .local_group_size(SURFEL_GROUP_SIZE) .additional_info("eevee_shared", "eevee_surfel_common", "draw_view") @@ -119,10 +137,23 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray) .image(1, GPU_RGBA32F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_a_img") .image(2, GPU_RGBA32F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_b_img") .image(3, GPU_RGBA32F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_c_img") - .image(4, GPU_R32F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "validity_img") + .image(4, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_3D, "virtual_offset_img") + .image(5, GPU_R32F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "validity_img") .compute_source("eevee_lightprobe_irradiance_ray_comp.glsl") .do_static_compilation(true); +GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_offset) + .local_group_size(IRRADIANCE_GRID_GROUP_SIZE, + IRRADIANCE_GRID_GROUP_SIZE, + IRRADIANCE_GRID_GROUP_SIZE) + .additional_info("eevee_shared", "eevee_surfel_common", "draw_view") + .storage_buf(0, Qualifier::READ, "int", "list_start_buf[]") + .storage_buf(6, Qualifier::READ, "SurfelListInfoData", "list_info_buf") + .image(0, GPU_R32I, Qualifier::READ, ImageType::INT_3D, "cluster_list_img") + .image(1, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "virtual_offset_img") + .compute_source("eevee_lightprobe_irradiance_offset_comp.glsl") + .do_static_compilation(true); + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/makesdna/DNA_lightprobe_defaults.h b/source/blender/makesdna/DNA_lightprobe_defaults.h index 67a5d2fb7a2..6598a342070 100644 --- a/source/blender/makesdna/DNA_lightprobe_defaults.h +++ b/source/blender/makesdna/DNA_lightprobe_defaults.h @@ -21,6 +21,8 @@ .grid_resolution_y = 4, \ .grid_resolution_z = 4, \ .grid_bake_samples = 2048, \ + .grid_surface_bias = 0.05, \ + .grid_escape_bias = 0.1, \ .grid_normal_bias = 0.3f, \ .grid_view_bias = 0.0f, \ .grid_facing_bias = 0.5f, \ diff --git a/source/blender/makesdna/DNA_lightprobe_types.h b/source/blender/makesdna/DNA_lightprobe_types.h index d4e5283e642..6d85774670a 100644 --- a/source/blender/makesdna/DNA_lightprobe_types.h +++ b/source/blender/makesdna/DNA_lightprobe_types.h @@ -57,6 +57,9 @@ typedef struct LightProbe { int grid_resolution_z; /** Irradiance grid: number of directions to evaluate light transfer in. */ int grid_bake_samples; + /** Irradiance grid: Virtual offset parameters. */ + float grid_surface_bias; + float grid_escape_bias; /** Irradiance grid: Sampling biases. */ float grid_normal_bias; float grid_view_bias; @@ -245,6 +248,8 @@ typedef struct LightProbeBakingData { float (*L1_b)[4]; float (*L1_c)[4]; float *validity; + /* Capture offset. Only for debugging. */ + float (*virtual_offset)[4]; } LightProbeBakingData; /** diff --git a/source/blender/makesrna/intern/rna_lightprobe.cc b/source/blender/makesrna/intern/rna_lightprobe.cc index f069de5baf5..ed4d1fe6be8 100644 --- a/source/blender/makesrna/intern/rna_lightprobe.cc +++ b/source/blender/makesrna/intern/rna_lightprobe.cc @@ -198,6 +198,18 @@ static void rna_def_lightprobe(BlenderRNA *brna) RNA_def_property_range(prop, 1, INT_MAX); RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING, "rna_LightProbe_recalc"); + prop = RNA_def_property(srna, "grid_surface_bias", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_text(prop, + "Capture Surface Bias", + "Moves capture points position away from surfaces to avoid artifacts"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING, "rna_LightProbe_recalc"); + + prop = RNA_def_property(srna, "grid_escape_bias", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_text(prop, "Capture Escape Bias", "Moves capture points outside objects"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING, "rna_LightProbe_recalc"); + prop = RNA_def_property(srna, "surfel_density", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text(prop,