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,