From d44ee7bf11bbcdd238badd197e119b01d78f693f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Mon, 22 Apr 2024 21:19:00 +0200 Subject: [PATCH] EEVEE-Next: Make light clamping consistent This adds clamping at the light combine stage for both direct and indirect light. This allows for clamping direct and indirect light separately. While direct light clamping might not be very desirable in EEVEE, it might be wanted to reduce the flickering from distant shiny bumpy surfaces, or for artistic reason. This happens after applying the BSDF throughput just like cycles. This is done in order to minimize the performance impact and allows to split the clamp for direct light and indirect light. The indirect light clamp value is still used in the ray-tracing pipeline to clamp the ray intensity. But this differs from cycles as we clamp the ray without the BSDF throughput here. Sphere probe have the same issues. Some more energy loss is expected compared to the direct light clamp. Note that we still clamp the indirect light after applying BSDF in case the BSDF is scaling the energy up above the threshold. This also corrects the clamping for volume that now clamps after applying the scattering term. Also adds clamping to volume indirect lighting. Since we use light probe volumes for both surface and volume indirect lighting, we need to clamp them at sampling time. Pull Request: https://projects.blender.org/blender/blender/pulls/120866 --- scripts/startup/bl_ui/properties_render.py | 15 +- .../blenloader/intern/versioning_400.cc | 5 +- .../draw/engines/eevee_next/eevee_instance.hh | 2 +- .../eevee_next/eevee_irradiance_cache.cc | 2 + .../engines/eevee_next/eevee_lightprobe.cc | 8 - .../draw/engines/eevee_next/eevee_pipeline.cc | 171 +++++++++--------- .../draw/engines/eevee_next/eevee_pipeline.hh | 59 ++++-- .../draw/engines/eevee_next/eevee_raytrace.cc | 71 +++++--- .../draw/engines/eevee_next/eevee_raytrace.hh | 57 ++++-- .../eevee_next/eevee_reflection_probes.cc | 6 +- .../draw/engines/eevee_next/eevee_sampling.cc | 7 + .../draw/engines/eevee_next/eevee_sampling.hh | 4 +- .../engines/eevee_next/eevee_shader_shared.hh | 20 +- .../draw/engines/eevee_next/eevee_world.cc | 7 +- .../shaders/eevee_deferred_combine_frag.glsl | 59 ++++-- .../shaders/eevee_deferred_light_frag.glsl | 22 ++- .../eevee_next/shaders/eevee_forward_lib.glsl | 25 ++- .../shaders/eevee_horizon_resolve_comp.glsl | 3 + .../shaders/eevee_horizon_setup_comp.glsl | 2 +- .../shaders/eevee_lightprobe_eval_lib.glsl | 27 +-- .../eevee_ray_trace_fallback_comp.glsl | 7 +- .../shaders/eevee_ray_trace_planar_comp.glsl | 7 +- .../shaders/eevee_ray_trace_screen_comp.glsl | 7 +- .../shaders/eevee_reflection_probe_lib.glsl | 8 +- .../eevee_reflection_probe_remap_comp.glsl | 7 +- .../eevee_reflection_probe_select_comp.glsl | 3 + .../eevee_spherical_harmonics_lib.glsl | 26 +++ .../shaders/eevee_volume_scatter_comp.glsl | 59 ++++-- .../shaders/infos/eevee_deferred_info.hh | 9 +- .../infos/eevee_irradiance_cache_info.hh | 4 +- .../infos/eevee_reflection_probe_info.hh | 8 +- source/blender/makesdna/DNA_scene_defaults.h | 3 +- source/blender/makesdna/DNA_scene_types.h | 9 +- source/blender/makesrna/intern/rna_scene.cc | 68 ++++++- 34 files changed, 530 insertions(+), 267 deletions(-) diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index 9dc122a9caf..1a89fab6f67 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -721,11 +721,9 @@ class RENDER_PT_eevee_next_clamping_surface(RenderButtonsPanel, Panel): scene = context.scene props = scene.eevee - # TODO(fclem): Add clamp properties - options = props.ray_tracing_options - layout.prop(options, "sample_clamp", text="Indirect Light") - # layout.prop(props, "clamp_surface_direct", text="Direct Light") - # layout.prop(props, "clamp_surface_indirect", text="Indirect Light") + col = layout.column(align=True) + col.prop(props, "clamp_surface_direct", text="Direct Light") + col.prop(props, "clamp_surface_indirect", text="Indirect Light") class RENDER_PT_eevee_next_clamping_volume(RenderButtonsPanel, Panel): @@ -743,9 +741,10 @@ class RENDER_PT_eevee_next_clamping_volume(RenderButtonsPanel, Panel): layout.use_property_decorate = False scene = context.scene props = scene.eevee - layout.prop(props, "volumetric_light_clamp", text="Direct Light") - # layout.prop(props, "clamp_volumetric_direct", text="Direct Light") - # layout.prop(props, "clamp_volumetric_indirect", text="Indirect Light") + + col = layout.column(align=True) + col.prop(props, "clamp_volume_direct", text="Direct Light") + col.prop(props, "clamp_volume_indirect", text="Indirect Light") class RENDER_PT_eevee_next_sampling_shadows(RenderButtonsPanel, Panel): diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 87288d66d64..d751ba4a0f8 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -2829,7 +2829,6 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) scene->eevee.ray_tracing_options.screen_trace_quality = 0.25f; scene->eevee.ray_tracing_options.screen_trace_thickness = 0.2f; scene->eevee.ray_tracing_options.trace_max_roughness = 0.5f; - scene->eevee.ray_tracing_options.sample_clamp = 10.0f; scene->eevee.ray_tracing_options.resolution_scale = 2; } } @@ -3180,6 +3179,10 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) /* Keep legacy EEVEE old behavior. */ scene->eevee.flag |= SCE_EEVEE_VOLUME_CUSTOM_RANGE; } + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + scene->eevee.clamp_surface_indirect = 10.0f; + } } /** diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index e4683a799e5..4ca309e690f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -154,7 +154,7 @@ class Instance { depth_of_field(*this), cryptomatte(*this), hiz_buffer(*this, uniform_data.data.hiz), - sampling(*this), + sampling(*this, uniform_data.data.clamp), camera(*this, uniform_data.data.camera), film(*this, uniform_data.data.film), render_buffers(*this, uniform_data.data.render_pass), 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 3e1e512e85b..4b7458b5238 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -248,6 +248,7 @@ void VolumeProbeModule::set_view(View & /*view*/) if (do_update_world_) { grid_upload_ps_.init(); grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_WORLD)); + grid_upload_ps_.bind_resources(inst_.uniform_data); grid_upload_ps_.bind_ssbo("harmonic_buf", &inst_.sphere_probes.spherical_harmonics_buf()); grid_upload_ps_.bind_ubo("grids_infos_buf", &grids_infos_buf_); grid_upload_ps_.bind_ssbo("bricks_infos_buf", &bricks_infos_buf_); @@ -364,6 +365,7 @@ void VolumeProbeModule::set_view(View & /*view*/) grid_upload_ps_.init(); grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_LOAD)); + grid_upload_ps_.bind_resources(inst_.uniform_data); grid_upload_ps_.push_constant("validity_threshold", grid->validity_threshold); grid_upload_ps_.push_constant("dilation_threshold", grid->dilation_threshold); grid_upload_ps_.push_constant("dilation_radius", grid->dilation_radius); diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc index 7b93d1625b0..c19c1a62b32 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc @@ -86,14 +86,6 @@ void LightProbeModule::sync_volume(const Object *ob, ObjectHandle &handle) grid.object_to_world = ob->object_to_world(); grid.cache = ob->lightprobe_cache; - /* Needed for display. */ - // LightProbeGridCacheFrame *cache = grid.cache ? grid.cache->grid_static_cache : nullptr; - // if (cache != nullptr) { - // int3 grid_size = int3(cache->size); - // /* Offset sample placement so that border texels are on the edges of the volume. */ - // float3 grid_scale = float3(grid_size) / float3(grid_size + 1); - // grid.object_to_world *= math::from_scale(grid_scale); - // } grid.world_to_object = float4x4( math::normalize(math::transpose(float3x3(grid.object_to_world)))); diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index eebada260ea..f969c0769e0 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -528,9 +528,24 @@ void DeferredLayer::begin_sync() this->gbuffer_pass_sync(inst_); } -void DeferredLayer::end_sync() +void DeferredLayer::end_sync(bool is_first_pass, bool is_last_pass) { - use_combined_lightprobe_eval = inst_.pipelines.data.use_combined_lightprobe_eval; + const SceneEEVEE &sce_eevee = inst_.scene->eevee; + const bool has_transmit_closure = (closure_bits_ & (CLOSURE_REFRACTION | CLOSURE_TRANSLUCENT)); + const bool has_reflect_closure = (closure_bits_ & (CLOSURE_REFLECTION | CLOSURE_DIFFUSE)); + use_raytracing_ = (has_transmit_closure || has_reflect_closure) && + (sce_eevee.flag & SCE_EEVEE_SSR_ENABLED) != 0; + + use_clamp_direct_ = sce_eevee.clamp_surface_direct != 0.0f; + use_clamp_indirect_ = sce_eevee.clamp_surface_indirect != 0.0f; + + /* The first pass will never have any surfaces behind it. Nothing is refracted except the + * environment. So in this case, disable tracing and fallback to probe. */ + use_screen_transmission_ = use_raytracing_ && has_transmit_closure && !is_first_pass; + use_screen_reflection_ = use_raytracing_ && has_reflect_closure; + + use_split_radiance_ = use_raytracing_ || (use_clamp_direct_ || use_clamp_indirect_); + use_feedback_output_ = use_raytracing_ && (!is_last_pass || use_screen_reflection_); { RenderBuffersInfoData &rbuf_data = inst_.render_buffers.data; @@ -569,6 +584,7 @@ void DeferredLayer::end_sync() pass.init(); { + const bool use_split_indirect = !use_raytracing_ && use_split_radiance_; PassSimple::Sub &sub = pass.sub("Eval.Light"); /* Use depth test to reject background pixels which have not been stencil cleared. */ /* WORKAROUND: Avoid rasterizer discard by enabling stencil write, but the shaders actually @@ -585,7 +601,8 @@ void DeferredLayer::end_sync() * OpenGL and Vulkan implementation which aren't fully supporting the specialize * constant. */ sub.specialize_constant(sh, "render_pass_shadow_enabled", rbuf_data.shadow_id != -1); - sub.specialize_constant(sh, "use_lightprobe_eval", use_combined_lightprobe_eval); + sub.specialize_constant(sh, "use_split_indirect", use_split_indirect); + sub.specialize_constant(sh, "use_lightprobe_eval", !use_raytracing_); const ShadowSceneData &shadow_scene = inst_.shadows.get_data(); sub.specialize_constant(sh, "shadow_ray_count", &shadow_scene.ray_count); sub.specialize_constant(sh, "shadow_ray_step_count", &shadow_scene.step_count); @@ -593,6 +610,9 @@ void DeferredLayer::end_sync() sub.bind_image("direct_radiance_1_img", &direct_radiance_txs_[0]); sub.bind_image("direct_radiance_2_img", &direct_radiance_txs_[1]); sub.bind_image("direct_radiance_3_img", &direct_radiance_txs_[2]); + sub.bind_image("indirect_radiance_1_img", &indirect_result_.closures[0]); + sub.bind_image("indirect_radiance_2_img", &indirect_result_.closures[1]); + sub.bind_image("indirect_radiance_3_img", &indirect_result_.closures[2]); sub.bind_resources(inst_.uniform_data); sub.bind_resources(inst_.gbuffer); sub.bind_resources(inst_.lights); @@ -621,8 +641,10 @@ void DeferredLayer::end_sync() "render_pass_specular_light_enabled", (rbuf_data.specular_light_id != -1) || (rbuf_data.specular_color_id != -1)); + pass.specialize_constant(sh, "use_split_radiance", use_split_radiance_); + pass.specialize_constant( + sh, "use_radiance_feedback", use_feedback_output_ && use_clamp_direct_); pass.specialize_constant(sh, "render_pass_normal_enabled", rbuf_data.normal_id != -1); - pass.specialize_constant(sh, "use_combined_lightprobe_eval", use_combined_lightprobe_eval); pass.shader_set(sh); /* Use stencil test to reject pixels not written by this layer. */ pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL | DRW_STATE_STENCIL_NEQUAL); @@ -631,11 +653,12 @@ void DeferredLayer::end_sync() pass.bind_texture("direct_radiance_1_tx", &direct_radiance_txs_[0]); pass.bind_texture("direct_radiance_2_tx", &direct_radiance_txs_[1]); pass.bind_texture("direct_radiance_3_tx", &direct_radiance_txs_[2]); - pass.bind_texture("indirect_radiance_1_tx", &indirect_radiance_txs_[0]); - pass.bind_texture("indirect_radiance_2_tx", &indirect_radiance_txs_[1]); - pass.bind_texture("indirect_radiance_3_tx", &indirect_radiance_txs_[2]); + pass.bind_texture("indirect_radiance_1_tx", &indirect_result_.closures[0]); + pass.bind_texture("indirect_radiance_2_tx", &indirect_result_.closures[1]); + pass.bind_texture("indirect_radiance_3_tx", &indirect_result_.closures[2]); pass.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx); pass.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx); + pass.bind_image("radiance_feedback_img", &radiance_feedback_tx_); pass.bind_resources(inst_.gbuffer); pass.bind_resources(inst_.uniform_data); pass.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); @@ -675,50 +698,28 @@ PassMain::Sub *DeferredLayer::material_add(::Material *blender_mat, GPUMaterial return &pass->sub(GPU_material_get_name(gpumat)); } -void DeferredLayer::render(View &main_view, - View &render_view, - Framebuffer &prepass_fb, - Framebuffer &combined_fb, - Framebuffer &gbuffer_fb, - int2 extent, - RayTraceBuffer &rt_buffer, - bool is_first_pass) +GPUTexture *DeferredLayer::render(View &main_view, + View &render_view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, + Framebuffer &gbuffer_fb, + int2 extent, + RayTraceBuffer &rt_buffer, + GPUTexture *radiance_behind_tx) { if (closure_count_ == 0) { - return; + return nullptr; } RenderBuffers &rb = inst_.render_buffers; - /* The first pass will never have any surfaces behind it. Nothing is refracted except the - * environment. So in this case, disable tracing and fallback to probe. */ - bool do_screen_space_refraction = !is_first_pass && - (closure_bits_ & (CLOSURE_REFRACTION | CLOSURE_TRANSLUCENT)); - bool do_screen_space_reflection = (closure_bits_ & (CLOSURE_REFLECTION | CLOSURE_DIFFUSE)); constexpr eGPUTextureUsage usage_read = GPU_TEXTURE_USAGE_SHADER_READ; constexpr eGPUTextureUsage usage_write = GPU_TEXTURE_USAGE_SHADER_WRITE; constexpr eGPUTextureUsage usage_rw = usage_read | usage_write; - if (do_screen_space_reflection) { - if (radiance_feedback_tx_.ensure_2d(rb.color_format, extent, usage_read)) { - radiance_feedback_tx_.clear(float4(0.0)); - radiance_feedback_persmat_ = render_view.persmat(); - } - } - else { - /* Dummy texture. Will not be used. */ - radiance_feedback_tx_.ensure_2d(rb.color_format, int2(1), GPU_TEXTURE_USAGE_SHADER_READ); - } - - if (do_screen_space_refraction) { + if (use_screen_transmission_) { /* Update for refraction. */ inst_.hiz_buffer.update(); - radiance_behind_tx_.ensure_2d(rb.color_format, extent, usage_read); - GPU_texture_copy(radiance_behind_tx_, rb.combined_tx); - } - else { - /* Dummy texture. Will not be used. */ - radiance_behind_tx_.ensure_2d(rb.color_format, int2(1), GPU_TEXTURE_USAGE_SHADER_READ); } GPU_framebuffer_bind(prepass_fb); @@ -739,53 +740,48 @@ void DeferredLayer::render(View &main_view, (closure_count_ > i) ? extent : int2(1), DEFERRED_RADIANCE_FORMAT, usage_rw); } - RayTraceResult indirect_result; - - if (use_combined_lightprobe_eval) { - float4 data(0.0f); - /* Subsurface writes (black) to that texture. */ - dummy_black_tx.ensure_2d(RAYTRACE_RADIANCE_FORMAT, int2(1), usage_rw, data); - for (int i = 0; i < 3; i++) { - indirect_radiance_txs_[i] = dummy_black_tx; - } + if (use_raytracing_) { + indirect_result_ = inst_.raytracing.render( + rt_buffer, radiance_behind_tx, closure_bits_, main_view, render_view); + } + else if (use_split_radiance_) { + indirect_result_ = inst_.raytracing.alloc_only(rt_buffer); } else { - indirect_result = inst_.raytracing.render(rt_buffer, - radiance_behind_tx_, - radiance_feedback_tx_, - radiance_feedback_persmat_, - closure_bits_, - main_view, - render_view, - do_screen_space_refraction); - for (int i = 0; i < 3; i++) { - indirect_radiance_txs_[i] = indirect_result.closures[i].get(); - } + indirect_result_ = inst_.raytracing.alloc_dummy(rt_buffer); } GPU_framebuffer_bind(combined_fb); inst_.manager->submit(eval_light_ps_, render_view); inst_.subsurface.render( - direct_radiance_txs_[0], indirect_radiance_txs_[0], closure_bits_, render_view); + direct_radiance_txs_[0], indirect_result_.closures[0], closure_bits_, render_view); + + radiance_feedback_tx_ = rt_buffer.feedback_ensure(!use_feedback_output_, extent); + + if (use_feedback_output_ && use_clamp_direct_) { + /* We need to do a copy before the combine pass (otherwise we have a dependency issue) to save + * the emission and the previous layer's radiance. */ + GPU_texture_copy(radiance_feedback_tx_, rb.combined_tx); + } GPU_framebuffer_bind(combined_fb); inst_.manager->submit(combine_ps_); - if (!use_combined_lightprobe_eval) { - indirect_result.release(); + if (use_feedback_output_ && !use_clamp_direct_) { + /* We skip writting the radiance during the combine pass. Do a simple fast copy. */ + GPU_texture_copy(radiance_feedback_tx_, rb.combined_tx); } + indirect_result_.release(); + for (int i = 0; i < ARRAY_SIZE(direct_radiance_txs_); i++) { direct_radiance_txs_[i].release(); } - if (do_screen_space_reflection) { - GPU_texture_copy(radiance_feedback_tx_, rb.combined_tx); - radiance_feedback_persmat_ = render_view.persmat(); - } - inst_.pipelines.deferred.debug_draw(render_view, combined_fb); + + return use_feedback_output_ ? radiance_feedback_tx_ : nullptr; } /** \} */ @@ -800,8 +796,7 @@ void DeferredPipeline::begin_sync() { Instance &inst = opaque_layer_.inst_; - const bool use_raytracing = (inst.scene->eevee.flag & SCE_EEVEE_SSR_ENABLED); - inst.pipelines.data.use_combined_lightprobe_eval = !use_raytracing; + const bool use_raytracing = (inst.scene->eevee.flag & SCE_EEVEE_SSR_ENABLED) != 0; use_combined_lightprobe_eval = !use_raytracing; opaque_layer_.begin_sync(); @@ -810,8 +805,8 @@ void DeferredPipeline::begin_sync() void DeferredPipeline::end_sync() { - opaque_layer_.end_sync(); - refraction_layer_.end_sync(); + opaque_layer_.end_sync(true, refraction_layer_.is_empty()); + refraction_layer_.end_sync(opaque_layer_.is_empty(), true); debug_pass_sync(); } @@ -892,26 +887,28 @@ void DeferredPipeline::render(View &main_view, RayTraceBuffer &rt_buffer_opaque_layer, RayTraceBuffer &rt_buffer_refract_layer) { + GPUTexture *feedback_tx = nullptr; + DRW_stats_group_start("Deferred.Opaque"); - opaque_layer_.render(main_view, - render_view, - prepass_fb, - combined_fb, - gbuffer_fb, - extent, - rt_buffer_opaque_layer, - true); + feedback_tx = opaque_layer_.render(main_view, + render_view, + prepass_fb, + combined_fb, + gbuffer_fb, + extent, + rt_buffer_opaque_layer, + feedback_tx); DRW_stats_group_end(); DRW_stats_group_start("Deferred.Refract"); - refraction_layer_.render(main_view, - render_view, - prepass_fb, - combined_fb, - gbuffer_fb, - extent, - rt_buffer_refract_layer, - false); + feedback_tx = refraction_layer_.render(main_view, + render_view, + prepass_fb, + combined_fb, + gbuffer_fb, + extent, + rt_buffer_refract_layer, + feedback_tx); DRW_stats_group_end(); } diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index 36254498611..99c4095ae8f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -18,6 +18,7 @@ #include "draw_shader_shared.hh" #include "eevee_lut.hh" +#include "eevee_raytrace.hh" #include "eevee_subsurface.hh" namespace blender::eevee { @@ -248,9 +249,13 @@ class DeferredLayer : DeferredLayerBase { */ TextureFromPool direct_radiance_txs_[3] = { {"direct_radiance_1"}, {"direct_radiance_2"}, {"direct_radiance_3"}}; - Texture dummy_black_tx = {"dummy_black_tx"}; + /* NOTE: Only used when `use_split_radiance` is true. */ + TextureFromPool indirect_radiance_txs_[3] = { + {"indirect_radiance_1"}, {"indirect_radiance_2"}, {"indirect_radiance_3"}}; + /* Used when there is no indirect radiance buffer. */ + Texture dummy_black = {"dummy_black"}; /* Reference to ray-tracing results. */ - GPUTexture *indirect_radiance_txs_[3] = {nullptr}; + GPUTexture *radiance_feedback_tx_ = nullptr; /** * Tile texture containing several bool per tile indicating presence of feature. @@ -258,34 +263,52 @@ class DeferredLayer : DeferredLayerBase { */ Texture tile_mask_tx_ = {"tile_mask_tx_"}; - /* TODO(fclem): This should be a TextureFromPool. */ - Texture radiance_behind_tx_ = {"radiance_behind_tx"}; - /* TODO(fclem): This shouldn't be part of the pipeline but of the view. */ - Texture radiance_feedback_tx_ = {"radiance_feedback_tx"}; - float4x4 radiance_feedback_persmat_; + RayTraceResult indirect_result_; - bool use_combined_lightprobe_eval = true; + bool use_split_radiance_ = true; + /* Output radiance from the combine shader instead of copy. Allow passing unclamped result. */ + bool use_feedback_output_ = false; + bool use_raytracing_ = false; + bool use_screen_transmission_ = false; + bool use_screen_reflection_ = false; + bool use_clamp_direct_ = false; + bool use_clamp_indirect_ = false; public: - DeferredLayer(Instance &inst) : inst_(inst){}; + DeferredLayer(Instance &inst) : inst_(inst) + { + float4 data(0.0f); + dummy_black.ensure_2d(RAYTRACE_RADIANCE_FORMAT, + int2(1), + GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE, + data); + } void begin_sync(); - void end_sync(); + void end_sync(bool is_first_pass, bool is_last_pass); PassMain::Sub *prepass_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion); PassMain::Sub *material_add(::Material *blender_mat, GPUMaterial *gpumat); - void render(View &main_view, - View &render_view, - Framebuffer &prepass_fb, - Framebuffer &combined_fb, - Framebuffer &gbuffer_fb, - int2 extent, - RayTraceBuffer &rt_buffer, - bool is_first_pass); + bool is_empty() const + { + return closure_count_ != 0; + } + + /* Returns the radiance buffer to feed the next layer. */ + GPUTexture *render(View &main_view, + View &render_view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, + Framebuffer &gbuffer_fb, + int2 extent, + RayTraceBuffer &rt_buffer, + GPUTexture *radiance_behind_tx); }; class DeferredPipeline { + friend DeferredLayer; + private: /* Gbuffer filling passes. We could have an arbitrary number of them but for now we just have * a hardcoded number of them. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_raytrace.cc b/source/blender/draw/engines/eevee_next/eevee_raytrace.cc index 6e2415fd3c8..3d4c95d5a7e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_raytrace.cc +++ b/source/blender/draw/engines/eevee_next/eevee_raytrace.cc @@ -30,6 +30,10 @@ void RayTraceModule::init() ray_tracing_options_ = sce_eevee.ray_tracing_options; tracing_method_ = RaytraceEEVEE_Method(sce_eevee.ray_tracing_method); + + float4 data(0.0f); + radiance_dummy_black_tx_.ensure_2d( + RAYTRACE_RADIANCE_FORMAT, int2(1), GPU_TEXTURE_USAGE_SHADER_READ, data); } void RayTraceModule::sync() @@ -323,16 +327,19 @@ void RayTraceModule::debug_draw(View & /*view*/, GPUFrameBuffer * /*view_fb*/) { RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer, GPUTexture *screen_radiance_back_tx, - GPUTexture *screen_radiance_front_tx, - const float4x4 &screen_radiance_persmat, eClosureBits active_closures, /* TODO(fclem): Maybe wrap these two in some other class. */ View &main_view, - View &render_view, - bool do_refraction_tracing) + View &render_view) { using namespace blender::math; + screen_radiance_front_tx_ = rt_buffer.radiance_feedback_tx.is_valid() ? + rt_buffer.radiance_feedback_tx : + radiance_dummy_black_tx_; + screen_radiance_back_tx_ = screen_radiance_back_tx ? screen_radiance_back_tx : + screen_radiance_front_tx_; + RaytraceEEVEE options = ray_tracing_options_; bool use_horizon_scan = options.trace_max_roughness < 1.0f; @@ -386,10 +393,9 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer, data_.roughness_mask_bias = data_.roughness_mask_scale * roughness_mask_start; /* Data for the radiance setup. */ - data_.brightness_clamp = (options.sample_clamp > 0.0) ? options.sample_clamp : 1e20; data_.resolution_scale = resolution_scale; data_.resolution_bias = int2(inst_.sampling.rng_2d_get(SAMPLING_RAYTRACE_V) * resolution_scale); - data_.radiance_persmat = screen_radiance_persmat; + data_.radiance_persmat = render_view.persmat(); data_.full_resolution = extent; data_.full_resolution_inv = 1.0f / float2(extent); @@ -410,26 +416,16 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer, inst_.manager->submit(tile_classify_ps_); } - data_.trace_refraction = do_refraction_tracing; + data_.trace_refraction = screen_radiance_back_tx != nullptr; for (int i = 0; i < 3; i++) { - result.closures[i] = trace(i, - (closure_count > i), - options, - rt_buffer, - screen_radiance_back_tx, - screen_radiance_front_tx, - screen_radiance_persmat, - main_view, - render_view); + result.closures[i] = trace(i, (closure_count > i), options, rt_buffer, main_view, render_view); } if (has_active_closure) { if (use_horizon_scan) { DRW_stats_group_start("Horizon Scan"); - screen_radiance_front_tx_ = screen_radiance_front_tx; - downsampled_in_radiance_tx_.acquire(tracing_res_horizon, RAYTRACE_RADIANCE_FORMAT, usage_rw); downsampled_in_normal_tx_.acquire(tracing_res_horizon, GPU_RGB10_A2, usage_rw); @@ -440,7 +436,7 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer, horizon_radiance_denoised_tx_[i].acquire(tracing_res_horizon, GPU_RGBA8, usage_rw); } for (int i : IndexRange(3)) { - horizon_scan_output_tx_[i] = result.closures[i].get(); + horizon_scan_output_tx_[i] = result.closures[i]; } horizon_tracing_dispatch_buf_.clear_to_zero(); @@ -465,6 +461,8 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer, DRW_stats_group_end(); + rt_buffer.history_persmat = render_view.persmat(); + return result; } @@ -473,9 +471,6 @@ RayTraceResultTexture RayTraceModule::trace( bool active_layer, RaytraceEEVEE options, RayTraceBuffer &rt_buffer, - GPUTexture *screen_radiance_back_tx, - GPUTexture *screen_radiance_front_tx, - const float4x4 &screen_radiance_persmat, /* TODO(fclem): Maybe wrap these two in some other class. */ View &main_view, View &render_view) @@ -512,7 +507,6 @@ RayTraceResultTexture RayTraceModule::trace( data_.thickness = options.screen_trace_thickness; data_.quality = 1.0f - 0.95f * options.screen_trace_quality; - data_.brightness_clamp = (options.sample_clamp > 0.0) ? options.sample_clamp : 1e20; float roughness_mask_start = options.trace_max_roughness; float roughness_mask_fade = 0.2f; @@ -522,7 +516,7 @@ RayTraceResultTexture RayTraceModule::trace( data_.resolution_scale = resolution_scale; data_.resolution_bias = int2(inst_.sampling.rng_2d_get(SAMPLING_RAYTRACE_V) * resolution_scale); data_.history_persmat = denoise_buf->history_persmat; - data_.radiance_persmat = screen_radiance_persmat; + data_.radiance_persmat = render_view.persmat(); data_.full_resolution = extent; data_.full_resolution_inv = 1.0f / float2(extent); data_.skip_denoise = !use_spatial_denoise; @@ -540,9 +534,6 @@ RayTraceResultTexture RayTraceModule::trace( ray_time_tx_.acquire(tracing_res, RAYTRACE_RAYTIME_FORMAT); ray_radiance_tx_.acquire(tracing_res, RAYTRACE_RADIANCE_FORMAT); - screen_radiance_front_tx_ = screen_radiance_front_tx; - screen_radiance_back_tx_ = screen_radiance_back_tx; - inst_.manager->submit(generate_ps_, render_view); if (tracing_method_ == RAYTRACE_EEVEE_METHOD_SCREEN) { if (inst_.planar_probes.enabled()) { @@ -638,6 +629,32 @@ RayTraceResultTexture RayTraceModule::trace( return result; } +RayTraceResult RayTraceModule::alloc_only(RayTraceBuffer &rt_buffer) +{ + const int2 extent = inst_.film.render_extent_get(); + eGPUTextureUsage usage_rw = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE; + + RayTraceResult result; + for (int i = 0; i < 3; i++) { + RayTraceBuffer::DenoiseBuffer *denoise_buf = &rt_buffer.closures[i]; + denoise_buf->denoised_bilateral_tx.acquire(extent, RAYTRACE_RADIANCE_FORMAT, usage_rw); + result.closures[i] = {denoise_buf->denoised_bilateral_tx}; + } + return result; +} + +RayTraceResult RayTraceModule::alloc_dummy(RayTraceBuffer &rt_buffer) +{ + eGPUTextureUsage usage_rw = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE; + + RayTraceResult result; + for (int i = 0; i < 3; i++) { + RayTraceBuffer::DenoiseBuffer *denoise_buf = &rt_buffer.closures[i]; + denoise_buf->denoised_bilateral_tx.acquire(int2(1), RAYTRACE_RADIANCE_FORMAT, usage_rw); + result.closures[i] = {denoise_buf->denoised_bilateral_tx}; + } + return result; +} /** \} */ } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_raytrace.hh b/source/blender/draw/engines/eevee_next/eevee_raytrace.hh index d7345b5bb4b..a935cd8c7c9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_raytrace.hh +++ b/source/blender/draw/engines/eevee_next/eevee_raytrace.hh @@ -28,7 +28,7 @@ class Instance; * \{ */ /** - * Contain persistent buffer that need to be stored per view, per layer. + * Contain persistent buffer that need to be stored per view, per deferred layer. */ struct RayTraceBuffer { /** Set of buffers that need to be allocated for each ray type. */ @@ -54,6 +54,26 @@ struct RayTraceBuffer { * One for each closure. Not to be mistaken with deferred layer type. */ DenoiseBuffer closures[3]; + + /** + * Radiance feedback of the deferred layer for next sample's reflection or next layer's + * transmission. + */ + Texture radiance_feedback_tx = {"radiance_feedback_tx"}; + /** + * Perspective matrix for which the radiance feedback buffer was recorded. + * Can be different from de-noise buffer's history matrix. + */ + float4x4 history_persmat; + + GPUTexture *feedback_ensure(bool is_dummy, int2 extent) + { + eGPUTextureUsage usage_rw = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE; + if (radiance_feedback_tx.ensure_2d(GPU_RGBA16F, is_dummy ? int2(1) : extent, usage_rw)) { + radiance_feedback_tx.clear(float4(0.0f)); + } + return radiance_feedback_tx; + } }; /** @@ -65,18 +85,26 @@ class RayTraceResultTexture { private: /** Result is in a temporary texture that needs to be released. */ TextureFromPool *result_ = nullptr; + /** Value of `result_->tx_` that can be referenced in advance. */ + GPUTexture *tx_ = nullptr; /** History buffer to swap the temporary texture that does not need to be released. */ Texture *history_ = nullptr; public: RayTraceResultTexture() = default; - RayTraceResultTexture(TextureFromPool &result) : result_(result.ptr()){}; + RayTraceResultTexture(TextureFromPool &result) : result_(result.ptr()), tx_(result){}; RayTraceResultTexture(TextureFromPool &result, Texture &history) - : result_(result.ptr()), history_(history.ptr()){}; + : result_(result.ptr()), tx_(result), history_(history.ptr()){}; - GPUTexture *get() + operator GPUTexture *() const { - return *result_; + BLI_assert(tx_ != nullptr); + return tx_; + } + + GPUTexture **operator&() + { + return &tx_; } void release() @@ -186,6 +214,7 @@ class RayTraceModule { GPUTexture *screen_radiance_front_tx_ = nullptr; GPUTexture *screen_radiance_back_tx_ = nullptr; + Texture radiance_dummy_black_tx_ = {"radiance_dummy_black_tx"}; /** Dummy texture when the tracing is disabled. */ TextureFromPool dummy_result_tx_ = {"dummy_result_tx"}; /** Pointer to `inst_.render_buffers.depth_tx` updated before submission. */ @@ -223,13 +252,20 @@ class RayTraceModule { */ RayTraceResult render(RayTraceBuffer &rt_buffer, GPUTexture *screen_radiance_back_tx, - GPUTexture *screen_radiance_front_tx, - const float4x4 &screen_radiance_persmat, eClosureBits active_closures, /* TODO(fclem): Maybe wrap these two in some other class. */ View &main_view, - View &render_view, - bool do_refraction_tracing); + View &render_view); + + /** + * Only allocate the RayTraceResult results buffers to be used by other passes. + */ + RayTraceResult alloc_only(RayTraceBuffer &rt_buffer); + + /** + * Only allocate the RayTraceResult results buffers as dummy texture to ensure correct bindings. + */ + RayTraceResult alloc_dummy(RayTraceBuffer &rt_buffer); void debug_pass_sync(); void debug_draw(View &view, GPUFrameBuffer *view_fb); @@ -239,9 +275,6 @@ class RayTraceModule { bool active_layer, RaytraceEEVEE options, RayTraceBuffer &rt_buffer, - GPUTexture *screen_radiance_back_tx, - GPUTexture *screen_radiance_front_tx, - const float4x4 &screen_radiance_persmat, /* TODO(fclem): Maybe wrap these two in some other class. */ View &main_view, View &render_view); diff --git a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc index 87aae71ecb6..f0d569ea124 100644 --- a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc @@ -34,9 +34,6 @@ void SphereProbeModule::begin_sync() LightProbeModule &light_probes = instance_.light_probes; SphereProbeData &world_data = *static_cast(&light_probes.world_sphere_); { - const RaytraceEEVEE &options = instance_.scene->eevee.ray_tracing_options; - float probe_brightness_clamp = (options.sample_clamp > 0.0) ? options.sample_clamp : 1e20; - GPUShader *shader = instance_.shaders.static_shader_get(SPHERE_PROBE_REMAP); PassSimple &pass = remap_ps_; @@ -50,7 +47,7 @@ void SphereProbeModule::begin_sync() pass.push_constant("probe_coord_packed", reinterpret_cast(&probe_sampling_coord_)); pass.push_constant("write_coord_packed", reinterpret_cast(&probe_write_coord_)); pass.push_constant("world_coord_packed", reinterpret_cast(&world_data.atlas_coord)); - pass.push_constant("probe_brightness_clamp", probe_brightness_clamp); + pass.bind_resources(instance_.uniform_data); pass.dispatch(&dispatch_probe_pack_); } { @@ -85,6 +82,7 @@ void SphereProbeModule::begin_sync() pass.bind_ssbo("reflection_probe_buf", &data_buf_); instance_.volume_probes.bind_resources(pass); instance_.sampling.bind_resources(pass); + pass.bind_resources(instance_.uniform_data); pass.dispatch(&dispatch_probe_select_); pass.barrier(GPU_BARRIER_UNIFORM); } diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc index 3356a2b8500..841ae25579e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc @@ -60,6 +60,13 @@ void Sampling::init(const Scene *scene) /* Only multiply after to have full the full DoF web pattern for each time steps. */ sample_count_ *= motion_blur_steps_; + + auto clamp_value_load = [](float value) { return (value > 0.0) ? value : 1e20; }; + + clamp_data_.surface_direct = clamp_value_load(scene->eevee.clamp_surface_direct); + clamp_data_.surface_indirect = clamp_value_load(scene->eevee.clamp_surface_indirect); + clamp_data_.volume_direct = clamp_value_load(scene->eevee.clamp_volume_direct); + clamp_data_.volume_indirect = clamp_value_load(scene->eevee.clamp_volume_indirect); } void Sampling::init(const Object &probe_object) diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.hh b/source/blender/draw/engines/eevee_next/eevee_sampling.hh index 0fa0a26cf2f..066e5dcd37b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.hh @@ -65,8 +65,10 @@ class Sampling { SamplingDataBuf data_; + ClampData &clamp_data_; + public: - Sampling(Instance &inst) : inst_(inst){}; + Sampling(Instance &inst, ClampData &clamp_data) : inst_(inst), clamp_data_(clamp_data){}; ~Sampling(){}; void init(const Scene *scene); 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 a4c8cc6ab8d..726de21805b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -1622,6 +1622,20 @@ BLI_STATIC_ASSERT_ALIGN(HiZData, 16) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Light Clamping + * \{ */ + +struct ClampData { + float surface_direct; + float surface_indirect; + float volume_direct; + float volume_indirect; +}; +BLI_STATIC_ASSERT_ALIGN(ClampData, 16) + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Ray-Tracing * \{ */ @@ -1685,8 +1699,6 @@ struct RayTraceData { int horizon_resolution_scale; /** Determine how fast the sample steps are getting bigger. */ float quality; - /** Maximum brightness during lighting evaluation. */ - float brightness_clamp; /** Maximum roughness for which we will trace a ray. */ float roughness_mask_scale; float roughness_mask_bias; @@ -1697,6 +1709,7 @@ struct RayTraceData { /** Closure being ray-traced. */ int closure_index; int _pad0; + int _pad1; }; BLI_STATIC_ASSERT_ALIGN(RayTraceData, 16) @@ -1821,7 +1834,7 @@ BLI_STATIC_ASSERT_ALIGN(PlanarProbeDisplayData, 16) struct PipelineInfoData { float alpha_hash_scale; bool32_t is_probe_reflection; - bool32_t use_combined_lightprobe_eval; + float _pad1; float _pad2; }; BLI_STATIC_ASSERT_ALIGN(PipelineInfoData, 16) @@ -1836,6 +1849,7 @@ BLI_STATIC_ASSERT_ALIGN(PipelineInfoData, 16) struct UniformData { AOData ao; CameraData camera; + ClampData clamp; FilmData film; HiZData hiz; RayTraceData raytrace; diff --git a/source/blender/draw/engines/eevee_next/eevee_world.cc b/source/blender/draw/engines/eevee_next/eevee_world.cc index f4cbac9aaa1..3bf52c8d83e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.cc +++ b/source/blender/draw/engines/eevee_next/eevee_world.cc @@ -78,11 +78,9 @@ World::~World() void World::sync() { - ::World *bl_world = inst_.use_studio_light() ? nullptr : inst_.scene->world; - bool has_update = false; - if (bl_world) { + if (inst_.scene->world != nullptr) { /* Detect world update before overriding it. */ WorldHandle wo_handle = inst_.sync.sync_world(); has_update = wo_handle.recalc != 0; @@ -91,8 +89,9 @@ void World::sync() /* Sync volume first since its result can override the surface world. */ sync_volume(); + ::World *bl_world; if (inst_.use_studio_light()) { - has_update = lookdev_world_.sync(LookdevParameters(inst_.v3d)); + has_update |= lookdev_world_.sync(LookdevParameters(inst_.v3d)); bl_world = lookdev_world_.world_get(); } else if ((inst_.view_layer->layflag & SCE_LAY_SKY) == 0) { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl index 9d0362f35eb..d6427059751 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl @@ -48,40 +48,43 @@ void main() GBufferReader gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel); vec3 diffuse_color = vec3(0.0); - vec3 diffuse_light = vec3(0.0); + vec3 diffuse_direct = vec3(0.0); + vec3 diffuse_indirect = vec3(0.0); vec3 specular_color = vec3(0.0); - vec3 specular_light = vec3(0.0); + vec3 specular_direct = vec3(0.0); + vec3 specular_indirect = vec3(0.0); + vec3 out_direct = vec3(0.0); + vec3 out_indirect = vec3(0.0); vec3 average_normal = vec3(0.0); - out_combined = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < GBUFFER_LAYER_MAX && i < gbuf.closure_count; i++) { ClosureUndetermined cl = gbuffer_closure_get(gbuf, i); if (cl.type == CLOSURE_NONE_ID) { continue; } int layer_index = gbuffer_closure_get_bin_index(gbuf, i); - vec3 closure_light = load_radiance_direct(texel, layer_index); + vec3 closure_direct_light = load_radiance_direct(texel, layer_index); + vec3 closure_indirect_light = vec3(0.0); - if (!use_combined_lightprobe_eval) { - closure_light += load_radiance_indirect(texel, layer_index); + if (use_split_radiance) { + closure_indirect_light = load_radiance_indirect(texel, layer_index); } + average_normal += cl.N * reduce_add(cl.color); + switch (cl.type) { case CLOSURE_BSDF_TRANSLUCENT_ID: case CLOSURE_BSSRDF_BURLEY_ID: case CLOSURE_BSDF_DIFFUSE_ID: diffuse_color += cl.color; - diffuse_light += closure_light; - average_normal += cl.N * reduce_add(cl.color); + diffuse_direct += closure_direct_light; + diffuse_indirect += closure_indirect_light; break; case CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID: case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: specular_color += cl.color; - specular_light += closure_light; - average_normal += cl.N * reduce_add(cl.color); - break; - case CLOSURE_NONE_ID: - /* TODO(fclem): Assert. */ + specular_direct += closure_direct_light; + specular_indirect += closure_indirect_light; break; } @@ -93,16 +96,36 @@ void main() cl.color *= cl.color; } - closure_light *= cl.color; - out_combined.rgb += closure_light; + out_direct += closure_direct_light * cl.color; + out_indirect += closure_indirect_light * cl.color; } + if (use_radiance_feedback) { + /* Output unmodified radiance for indirect lighting. */ + vec3 out_radiance = imageLoad(radiance_feedback_img, texel).rgb; + out_radiance += out_direct + out_indirect; + imageStore(radiance_feedback_img, texel, vec4(out_radiance, 0.0)); + } + + /* Light clamping. */ + float clamp_direct = uniform_buf.clamp.surface_direct; + float clamp_indirect = uniform_buf.clamp.surface_indirect; + out_direct = colorspace_brightness_clamp_max(out_direct, clamp_direct); + out_indirect = colorspace_brightness_clamp_max(out_indirect, clamp_indirect); + /* TODO(fcleù): Shouldn't we clamp these relative the main clamp? */ + diffuse_direct = colorspace_brightness_clamp_max(diffuse_direct, clamp_direct); + diffuse_indirect = colorspace_brightness_clamp_max(diffuse_indirect, clamp_indirect); + specular_direct = colorspace_brightness_clamp_max(specular_direct, clamp_direct); + specular_indirect = colorspace_brightness_clamp_max(specular_indirect, clamp_indirect); + /* Light passes. */ if (render_pass_diffuse_light_enabled) { + vec3 diffuse_light = diffuse_direct + diffuse_indirect; output_renderpass_color(uniform_buf.render_pass.diffuse_color_id, vec4(diffuse_color, 1.0)); output_renderpass_color(uniform_buf.render_pass.diffuse_light_id, vec4(diffuse_light, 1.0)); } if (render_pass_specular_light_enabled) { + vec3 specular_light = specular_direct + specular_indirect; output_renderpass_color(uniform_buf.render_pass.specular_color_id, vec4(specular_color, 1.0)); output_renderpass_color(uniform_buf.render_pass.specular_light_id, vec4(specular_light, 1.0)); } @@ -113,9 +136,7 @@ void main() output_renderpass_color(uniform_buf.render_pass.normal_id, vec4(average_normal, 1.0)); } - if (any(isnan(out_combined))) { - out_combined = vec4(1.0, 0.0, 1.0, 0.0); - } - + out_combined = vec4(out_direct + out_indirect, 0.0); + out_combined = any(isnan(out_combined)) ? vec4(1.0, 0.0, 1.0, 0.0) : out_combined; out_combined = colorspace_safe_color(out_combined); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl index b69506793f3..3864b87bb52 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl @@ -91,9 +91,29 @@ void main() if (use_lightprobe_eval) { LightProbeSample samp = lightprobe_load(P, Ng, V); + float clamp_indirect = uniform_buf.clamp.surface_indirect; + samp.volume_irradiance = spherical_harmonics_clamp(samp.volume_irradiance, clamp_indirect); + for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT && i < gbuf.closure_count; i++) { ClosureUndetermined cl = gbuffer_closure_get(gbuf, i); - lightprobe_eval(samp, cl, P, V, gbuf.thickness, stack.cl[i].light_shadowed); + vec3 indirect_light = lightprobe_eval(samp, cl, P, V, gbuf.thickness); + + if (use_split_indirect) { + int layer_index = gbuffer_closure_get_bin_index(gbuf, i); + /* TODO(fclem): Layered texture. */ + if (layer_index == 0) { + imageStore(indirect_radiance_1_img, texel, vec4(indirect_light, 1.0)); + } + else if (layer_index == 1) { + imageStore(indirect_radiance_2_img, texel, vec4(indirect_light, 1.0)); + } + else if (layer_index == 2) { + imageStore(indirect_radiance_3_img, texel, vec4(indirect_light, 1.0)); + } + } + else { + stack.cl[i].light_shadowed += indirect_light; + } } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl index d55b0592ab2..768bb77fedf 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl @@ -12,6 +12,7 @@ #pragma BLENDER_REQUIRE(eevee_subsurface_lib.glsl) #pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl) #pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl) #if CLOSURE_BIN_COUNT != LIGHT_CLOSURE_EVAL_COUNT # error Closure data count and eval count must match @@ -64,21 +65,37 @@ void forward_lighting_eval(float thickness, out vec3 radiance, out vec3 transmit LightProbeSample samp = lightprobe_load(g_data.P, g_data.Ng, V); + float clamp_indirect_sh = uniform_buf.clamp.surface_indirect; + samp.volume_irradiance = spherical_harmonics_clamp(samp.volume_irradiance, clamp_indirect_sh); + /* Combine all radiance. */ - radiance = g_emission; + vec3 radiance_direct = vec3(0.0); + vec3 radiance_indirect = vec3(0.0); for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT; i++) { ClosureUndetermined cl = g_closure_get(i); - lightprobe_eval(samp, cl, g_data.P, V, thickness, stack.cl[i].light_shadowed); if (cl.weight > 1e-5) { + vec3 direct_light = stack.cl[i].light_shadowed; + vec3 indirect_light = lightprobe_eval(samp, cl, g_data.P, V, thickness); + if ((cl.type == CLOSURE_BSDF_TRANSLUCENT_ID || cl.type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID) && (thickness > 0.0)) { /* We model two transmission event, so the surface color need to be applied twice. */ - stack.cl[i].light_shadowed *= cl.color; + cl.color *= cl.color; } - radiance += stack.cl[i].light_shadowed * cl.color * cl.weight; + cl.color *= cl.weight; + + radiance_direct += direct_light * cl.color; + radiance_indirect += indirect_light * cl.color; } } + /* Light clamping. */ + float clamp_direct = uniform_buf.clamp.surface_direct; + float clamp_indirect = uniform_buf.clamp.surface_indirect; + radiance_direct = colorspace_brightness_clamp_max(radiance_direct, clamp_direct); + radiance_indirect = colorspace_brightness_clamp_max(radiance_indirect, clamp_indirect); + + radiance = radiance_direct + radiance_indirect + g_emission; transmittance = g_transmittance; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_resolve_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_resolve_comp.glsl index 96372ece43d..c829d4ed2ea 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_resolve_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_resolve_comp.glsl @@ -122,6 +122,9 @@ void main() LightProbeSample samp = lightprobe_load(P, Ng, V); + float clamp_indirect = uniform_buf.clamp.surface_indirect; + samp.volume_irradiance = spherical_harmonics_clamp(samp.volume_irradiance, clamp_indirect); + for (int i = 0; i < GBUFFER_LAYER_MAX && i < gbuf.closure_count; i++) { ClosureUndetermined cl = gbuffer_closure_get(gbuf, i); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_setup_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_setup_comp.glsl index 439234fee29..58eb5b5dd49 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_setup_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_setup_comp.glsl @@ -53,7 +53,7 @@ void main() vec3 ssP_prev = drw_ndc_to_screen(project_point(uniform_buf.raytrace.radiance_persmat, P)); vec4 radiance = texture(in_radiance_tx, ssP_prev.xy); - radiance = colorspace_brightness_clamp_max(radiance, uniform_buf.raytrace.brightness_clamp); + radiance = colorspace_brightness_clamp_max(radiance, uniform_buf.clamp.surface_indirect); imageStore(out_radiance_img, texel, radiance); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl index 4073d65aea0..f24998e1c86 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl @@ -68,7 +68,7 @@ vec3 lightprobe_spherical_sample_normalized_with_parallax( { SphereProbeData probe = reflection_probe_buf[probe_index]; ReflectionProbeLowFreqLight shading_sh = reflection_probes_extract_low_freq(P_sh); - vec3 normalization_factor = reflection_probes_normalization_eval( + float normalization_factor = reflection_probes_normalization_eval( L, shading_sh, probe.low_freq_light); L = lightprobe_sphere_parallax(probe, P, L); return normalization_factor * reflection_probes_sample(L, lod, probe.atlas_coord).rgb; @@ -139,32 +139,23 @@ vec3 lightprobe_eval(LightProbeSample samp, ClosureRefraction cl, vec3 P, vec3 V return mix(radiance_cube, radiance_sh, fac); } -void lightprobe_eval(LightProbeSample samp, - ClosureUndetermined cl, - vec3 P, - vec3 V, - float thickness, - inout vec3 radiance) +vec3 lightprobe_eval( + LightProbeSample samp, ClosureUndetermined cl, vec3 P, vec3 V, float thickness) { switch (cl.type) { case CLOSURE_BSDF_TRANSLUCENT_ID: - radiance += lightprobe_eval(samp, to_closure_translucent(cl), P, V, thickness); - break; + return lightprobe_eval(samp, to_closure_translucent(cl), P, V, thickness); case CLOSURE_BSSRDF_BURLEY_ID: /* TODO: Support translucency in ray tracing first. Otherwise we have a discrepancy. */ + return vec3(0.0); case CLOSURE_BSDF_DIFFUSE_ID: - radiance += lightprobe_eval(samp, to_closure_diffuse(cl), P, V); - break; + return lightprobe_eval(samp, to_closure_diffuse(cl), P, V); case CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID: - radiance += lightprobe_eval(samp, to_closure_reflection(cl), P, V); - break; + return lightprobe_eval(samp, to_closure_reflection(cl), P, V); case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: - radiance += lightprobe_eval(samp, to_closure_refraction(cl), P, V, thickness); - break; - case CLOSURE_NONE_ID: - /* TODO(fclem): Assert. */ - break; + return lightprobe_eval(samp, to_closure_refraction(cl), P, V, thickness); } + return vec3(0.0); } #endif /* SPHERE_PROBE */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_fallback_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_fallback_comp.glsl index d7097e46c71..e355b677bf7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_fallback_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_fallback_comp.glsl @@ -13,6 +13,7 @@ #pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) #pragma BLENDER_REQUIRE(eevee_ray_types_lib.glsl) #pragma BLENDER_REQUIRE(eevee_ray_trace_screen_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) void main() { @@ -60,12 +61,16 @@ void main() * direction over many rays. */ vec3 Ng = ray.direction; LightProbeSample samp = lightprobe_load(ray.origin, Ng, V); + /* Clamp SH to have parity with forward evaluation. */ + float clamp_indirect = uniform_buf.clamp.surface_indirect; + samp.volume_irradiance = spherical_harmonics_clamp(samp.volume_irradiance, clamp_indirect); + vec3 radiance = lightprobe_eval_direction( samp, ray.origin, ray.direction, safe_rcp(ray_pdf_inv)); /* Set point really far for correct reprojection of background. */ float hit_time = 1000.0; - radiance = colorspace_brightness_clamp_max(radiance, uniform_buf.raytrace.brightness_clamp); + radiance = colorspace_brightness_clamp_max(radiance, uniform_buf.clamp.surface_indirect); imageStore(ray_time_img, texel, vec4(hit_time)); imageStore(ray_radiance_img, texel, vec4(radiance, 0.0)); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_planar_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_planar_comp.glsl index 0255af2f70a..e883bff6d9c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_planar_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_planar_comp.glsl @@ -87,11 +87,6 @@ void main() if (hit.valid) { /* Evaluate radiance at hit-point. */ radiance = textureLod(planar_radiance_tx, vec3(hit.ss_hit_P.xy, planar_id), 0.0).rgb; - - /* Transmit twice if thickness is set and ray is longer than thickness. */ - // if (thickness > 0.0 && length(ray_data.xyz) > thickness) { - // ray_radiance.rgb *= color; - // } } else { /* Using ray direction as geometric normal to bias the sampling position. @@ -105,7 +100,7 @@ void main() hit.time = 10000.0; } - radiance = colorspace_brightness_clamp_max(radiance, uniform_buf.raytrace.brightness_clamp); + radiance = colorspace_brightness_clamp_max(radiance, uniform_buf.clamp.surface_indirect); imageStore(ray_time_img, texel, vec4(hit.time)); imageStore(ray_radiance_img, texel, vec4(radiance, 0.0)); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_comp.glsl index 49aebea3f95..1aed1446730 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_comp.glsl @@ -13,6 +13,7 @@ #pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) #pragma BLENDER_REQUIRE(eevee_ray_types_lib.glsl) #pragma BLENDER_REQUIRE(eevee_ray_trace_screen_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) void main() { @@ -131,12 +132,16 @@ void main() vec3 Ng = ray.direction; /* Fallback to nearest light-probe. */ LightProbeSample samp = lightprobe_load(ray.origin, Ng, V); + /* Clamp SH to have parity with forward evaluation. */ + float clamp_indirect = uniform_buf.clamp.surface_indirect; + samp.volume_irradiance = spherical_harmonics_clamp(samp.volume_irradiance, clamp_indirect); + radiance = lightprobe_eval_direction(samp, ray.origin, ray.direction, safe_rcp(ray_pdf_inv)); /* Set point really far for correct reprojection of background. */ hit.time = 10000.0; } - radiance = colorspace_brightness_clamp_max(radiance, uniform_buf.raytrace.brightness_clamp); + radiance = colorspace_brightness_clamp_max(radiance, uniform_buf.clamp.surface_indirect); imageStore(ray_time_img, texel, vec4(hit.time)); imageStore(ray_radiance_img, texel, vec4(radiance, 0.0)); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl index 34469b7cc11..6a9b58f0e72 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl @@ -40,11 +40,11 @@ ReflectionProbeLowFreqLight reflection_probes_extract_low_freq(SphericalHarmonic return result; } -vec3 reflection_probes_normalization_eval(vec3 L, - ReflectionProbeLowFreqLight numerator, - ReflectionProbeLowFreqLight denominator) +float reflection_probes_normalization_eval(vec3 L, + ReflectionProbeLowFreqLight numerator, + ReflectionProbeLowFreqLight denominator) { /* TODO(fclem): Adjusting directionality is tricky. * Needs to be revisited later on. For now only use the ambient term. */ - return vec3(numerator.ambient * safe_rcp(denominator.ambient)); + return (numerator.ambient * safe_rcp(denominator.ambient)); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl index 584a573cf2c..c658a79b51e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl @@ -89,11 +89,12 @@ void main() radiance.rgb = mix(world_radiance.rgb, radiance.rgb, opacity); } - radiance = colorspace_brightness_clamp_max(radiance, probe_brightness_clamp); - if (!any(greaterThanEqual(local_texel, ivec2(write_coord.extent)))) { + float clamp_indirect = uniform_buf.clamp.surface_indirect; + vec3 out_radiance = colorspace_brightness_clamp_max(radiance, clamp_indirect); + ivec3 texel = ivec3(local_texel + write_coord.offset, write_coord.layer); - imageStore(atlas_img, texel, vec4(radiance, 1.0)); + imageStore(atlas_img, texel, vec4(out_radiance, 1.0)); } if (extract_sh) { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl index b70fc8c16e4..14858097dd8 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl @@ -26,5 +26,8 @@ void main() sh = lightprobe_irradiance_sample(probe_center); } + float clamp_indirect_sh = uniform_buf.clamp.surface_indirect; + sh = spherical_harmonics_clamp(sh, clamp_indirect_sh); + reflection_probe_buf[idx].low_freq_light = reflection_probes_extract_low_freq(sh); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl index 85ca4e93748..673a4155e3e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl @@ -707,3 +707,29 @@ SphericalHarmonicL1 spherical_harmonics_decompress(SphericalHarmonicL1 sh) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clamping + * + * Clamp the total power of the SH function. + * \{ */ + +SphericalHarmonicL1 spherical_harmonics_clamp(SphericalHarmonicL1 sh, float clamp_value) +{ + /* Convert coefficients to per channel column. */ + mat4x4 per_channel = transpose(mat4x4(sh.L0.M0, sh.L1.Mn1, sh.L1.M0, sh.L1.Mp1)); + /* Maximum per channel. */ + vec3 max_L1 = vec3(reduce_max(abs(per_channel[0].yzw)), + reduce_max(abs(per_channel[1].yzw)), + reduce_max(abs(per_channel[2].yzw))); + /* Find maximum of the sh function over all chanels. */ + vec3 max_sh = abs(sh.L0.M0.rgb) * 0.282094792 + max_L1 * 0.488602512; + + float fac = clamp_value * safe_rcp(reduce_max(max_sh)); + if (fac > 1.0) { + return sh; + } + return spherical_harmonics_mul(sh, fac); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_volume_scatter_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_volume_scatter_comp.glsl index 7068e2e0ca8..9c4de2537a5 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_volume_scatter_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_volume_scatter_comp.glsl @@ -21,8 +21,7 @@ #ifdef VOLUME_LIGHTING -vec3 volume_scatter_light_eval( - const bool is_directional, vec3 P, vec3 V, uint l_idx, float s_anisotropy) +vec3 volume_light_eval(const bool is_directional, vec3 P, vec3 V, uint l_idx, float s_anisotropy) { LightData light = light_buf[l_idx]; @@ -48,10 +47,24 @@ vec3 volume_scatter_light_eval( return vec3(0); } - vec3 Li = volume_light(light, is_directional, lv) * visibility * - volume_shadow(light, is_directional, P, lv, extinction_tx); + vec3 Li = volume_light(light, is_directional, lv) * visibility; - return colorspace_brightness_clamp_max(Li, uniform_buf.volumes.light_clamp); + if (light.tilemap_index != LIGHT_NO_SHADOW) { + Li *= volume_shadow(light, is_directional, P, lv, extinction_tx); + } + + return Li; +} + +vec3 volume_lightprobe_eval(vec3 P, vec3 V, float s_anisotropy) +{ + SphericalHarmonicL1 phase_sh = volume_phase_function_as_sh_L1(V, s_anisotropy); + SphericalHarmonicL1 volume_radiance_sh = lightprobe_irradiance_sample(P); + + float clamp_indirect = uniform_buf.clamp.volume_indirect; + volume_radiance_sh = spherical_harmonics_clamp(volume_radiance_sh, clamp_indirect); + + return spherical_harmonics_dot(volume_radiance_sh, phase_sh).xyz; } #endif @@ -81,25 +94,35 @@ void main() float s_anisotropy = phase.x / max(1.0, phase.y); #ifdef VOLUME_LIGHTING - SphericalHarmonicL1 phase_sh = volume_phase_function_as_sh_L1(V, s_anisotropy); - SphericalHarmonicL1 volume_radiance_sh = lightprobe_irradiance_sample(P); + vec3 direct_radiance = vec3(0.0); - vec3 light_scattering = spherical_harmonics_dot(volume_radiance_sh, phase_sh).xyz; + if (reduce_max(s_scattering) > 0.0) { + LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) { + direct_radiance += volume_light_eval(true, P, V, l_idx, s_anisotropy); + } + LIGHT_FOREACH_END - LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) { - light_scattering += volume_scatter_light_eval(true, P, V, l_idx, s_anisotropy); + vec2 pixel = ((vec2(froxel.xy) + 0.5) * uniform_buf.volumes.inv_tex_size.xy) * + uniform_buf.volumes.main_view_extent; + + LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, pixel, vP.z, l_idx) + { + direct_radiance += volume_light_eval(false, P, V, l_idx, s_anisotropy); + } + LIGHT_FOREACH_END } - LIGHT_FOREACH_END - vec2 pixel = ((vec2(froxel.xy) + 0.5) * uniform_buf.volumes.inv_tex_size.xy) * - uniform_buf.volumes.main_view_extent; + vec3 indirect_radiance = volume_lightprobe_eval(P, V, s_anisotropy).xyz; - LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, pixel, vP.z, l_idx) { - light_scattering += volume_scatter_light_eval(false, P, V, l_idx, s_anisotropy); - } - LIGHT_FOREACH_END + direct_radiance *= s_scattering; + indirect_radiance *= s_scattering; - scattering += light_scattering * s_scattering; + float clamp_direct = uniform_buf.clamp.volume_direct; + float clamp_indirect = uniform_buf.clamp.volume_indirect; + direct_radiance = colorspace_brightness_clamp_max(direct_radiance, clamp_direct); + indirect_radiance = colorspace_brightness_clamp_max(indirect_radiance, clamp_indirect); + + scattering += direct_radiance + indirect_radiance; #endif if (uniform_buf.volumes.history_opacity > 0.0) { diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh index 3f120c9ce52..95e653c6c86 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh @@ -61,6 +61,11 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_light) .image_out(2, DEFERRED_RADIANCE_FORMAT, "direct_radiance_1_img") .image_out(3, DEFERRED_RADIANCE_FORMAT, "direct_radiance_2_img") .image_out(4, DEFERRED_RADIANCE_FORMAT, "direct_radiance_3_img") + /* Optimized out if use_split_indirect is false. */ + .image_out(5, DEFERRED_RADIANCE_FORMAT, "indirect_radiance_1_img") + .image_out(6, DEFERRED_RADIANCE_FORMAT, "indirect_radiance_2_img") + .image_out(7, DEFERRED_RADIANCE_FORMAT, "indirect_radiance_3_img") + .specialization_constant(Type::BOOL, "use_split_indirect", false) .specialization_constant(Type::BOOL, "use_lightprobe_eval", false) .specialization_constant(Type::BOOL, "render_pass_shadow_enabled", true) .define("SPECIALIZED_SHADOW_PARAMS") @@ -103,6 +108,7 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_combine) .sampler(5, ImageType::FLOAT_2D, "indirect_radiance_1_tx") .sampler(6, ImageType::FLOAT_2D, "indirect_radiance_2_tx") .sampler(7, ImageType::FLOAT_2D, "indirect_radiance_3_tx") + .image(5, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "radiance_feedback_img") .fragment_out(0, Type::VEC4, "out_combined") .additional_info("eevee_shared", "eevee_gbuffer_data", @@ -114,7 +120,8 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_combine) .specialization_constant(Type::BOOL, "render_pass_diffuse_light_enabled", true) .specialization_constant(Type::BOOL, "render_pass_specular_light_enabled", true) .specialization_constant(Type::BOOL, "render_pass_normal_enabled", true) - .specialization_constant(Type::BOOL, "use_combined_lightprobe_eval", false) + .specialization_constant(Type::BOOL, "use_radiance_feedback", false) + .specialization_constant(Type::BOOL, "use_split_radiance", false) .do_static_compilation(true); GPU_SHADER_CREATE_INFO(eevee_deferred_capture_eval) 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 6380d407151..e40e43382c7 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 @@ -174,7 +174,7 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_world) IRRADIANCE_GRID_BRICK_SIZE, IRRADIANCE_GRID_BRICK_SIZE) .define("IRRADIANCE_GRID_UPLOAD") - .additional_info("eevee_shared") + .additional_info("eevee_shared", "eevee_global_ubo") .push_constant(Type::INT, "grid_index") .storage_buf(0, Qualifier::READ, "uint", "bricks_infos_buf[]") .storage_buf(1, Qualifier::READ, "SphereProbeHarmonic", "harmonic_buf") @@ -188,7 +188,7 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_load) IRRADIANCE_GRID_BRICK_SIZE, IRRADIANCE_GRID_BRICK_SIZE) .define("IRRADIANCE_GRID_UPLOAD") - .additional_info("eevee_shared") + .additional_info("eevee_shared", "eevee_global_ubo") .push_constant(Type::MAT4, "grid_local_to_world") .push_constant(Type::INT, "grid_index") .push_constant(Type::INT, "grid_start_index") diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh index 4857cc4574c..e2cf2e82409 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh @@ -23,13 +23,12 @@ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_remap) .push_constant(Type::IVEC4, "probe_coord_packed") .push_constant(Type::IVEC4, "write_coord_packed") .push_constant(Type::IVEC4, "world_coord_packed") - .push_constant(Type::FLOAT, "probe_brightness_clamp") .sampler(0, ImageType::FLOAT_CUBE, "cubemap_tx") .sampler(1, ImageType::FLOAT_2D_ARRAY, "atlas_tx") .storage_buf(0, Qualifier::WRITE, "SphereProbeHarmonic", "out_sh[SPHERE_PROBE_MAX_HARMONIC]") .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "atlas_img") .compute_source("eevee_reflection_probe_remap_comp.glsl") - .additional_info("eevee_shared") + .additional_info("eevee_shared", "eevee_global_ubo") .do_static_compilation(true); GPU_SHADER_CREATE_INFO(eevee_reflection_probe_irradiance) @@ -48,7 +47,10 @@ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_select) "SphereProbeData", "reflection_probe_buf[SPHERE_PROBE_MAX]") .push_constant(Type::INT, "reflection_probe_count") - .additional_info("eevee_shared", "eevee_sampling_data", "eevee_volume_probe_data") + .additional_info("eevee_shared", + "eevee_sampling_data", + "eevee_global_ubo", + "eevee_volume_probe_data") .compute_source("eevee_reflection_probe_select_comp.glsl") .do_static_compilation(true); diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index a4fa7ea947f..60c75b0d765 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -165,7 +165,6 @@ .screen_trace_quality = 0.25f, \ .screen_trace_thickness = 0.2f, \ .trace_max_roughness = 0.5f, \ - .sample_clamp = 10.0f, \ .resolution_scale = 2, \ } @@ -231,6 +230,8 @@ .motion_blur_max = 32, \ .motion_blur_steps = 1, \ \ + .clamp_surface_indirect = 10.0f, \ +\ .shadow_cube_size = 512, \ .shadow_cascade_size = 1024, \ .shadow_ray_count = 1, \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 86906d3e642..0b427da3ead 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1803,14 +1803,10 @@ typedef struct RaytraceEEVEE { float trace_max_roughness; /** Resolution downscale factor. */ int resolution_scale; - /** Maximum intensity a ray can have. */ - float sample_clamp; /** #RaytraceEEVEE_Flag. */ int flag; /** #RaytraceEEVEE_DenoiseStages. */ int denoise_stages; - - char _pad0[4]; } RaytraceEEVEE; typedef struct SceneEEVEE { @@ -1881,6 +1877,11 @@ typedef struct SceneEEVEE { int shadow_step_count; float shadow_normal_bias; + float clamp_surface_direct; + float clamp_surface_indirect; + float clamp_volume_direct; + float clamp_volume_indirect; + int ray_tracing_method; struct RaytraceEEVEE ray_tracing_options; diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 182679f1c0c..04dc239ebef 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -1947,6 +1947,22 @@ static void rna_SceneEEVEE_gi_cubemap_resolution_update(Main * /*main*/, FOREACH_SCENE_OBJECT_END; } +static void rna_SceneEEVEE_clamp_surface_indirect_update(Main * /*main*/, + Scene *scene, + PointerRNA * /*ptr*/) +{ + /* Tag all light probes to recalc transform. This signals EEVEE to update the light probes. */ + FOREACH_SCENE_OBJECT_BEGIN (scene, ob) { + if (ob->type == OB_LIGHTPROBE) { + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + } + FOREACH_SCENE_OBJECT_END; + + /* Also tag the world. */ + DEG_id_tag_update(&scene->world->id, ID_RECALC_SHADING); +} + static std::optional rna_SceneRenderView_path(const PointerRNA *ptr) { const SceneRenderView *srv = (SceneRenderView *)ptr->data; @@ -7742,12 +7758,6 @@ static void rna_def_raytrace_eevee(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); - prop = RNA_def_property(srna, "sample_clamp", PROP_FLOAT, PROP_NONE); - RNA_def_property_ui_text(prop, "Clamp", "Clamp ray intensity to reduce noise (0 to disable)"); - RNA_def_property_range(prop, 0.0f, FLT_MAX); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); - prop = RNA_def_property(srna, "screen_trace_thickness", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_ui_text( prop, @@ -8032,6 +8042,52 @@ static void rna_def_scene_eevee(BlenderRNA *brna) "enabled for final renders)"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + /* Clamping */ + prop = RNA_def_property(srna, "clamp_surface_direct", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, + "Clamp Surface Direct", + "If non-zero, the maximum value for lights contribution on a surface. " + "Higher values will be scaled down to avoid too " + "much noise and slow convergence at the cost of accuracy. " + "Used by light objects"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + + prop = RNA_def_property(srna, "clamp_surface_indirect", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, + "Clamp Surface Indirect", + "If non-zero, the maximum value for indirect lighting on surface. " + "Higher values will be scaled down to avoid too " + "much noise and slow convergence at the cost of accuracy. " + "Used by ray-tracing and light-probes"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update( + prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_SceneEEVEE_clamp_surface_indirect_update"); + + prop = RNA_def_property(srna, "clamp_volume_direct", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, + "Clamp Volume Direct", + "If non-zero, the maximum value for lights contribution in volumes. " + "Higher values will be scaled down to avoid too " + "much noise and slow convergence at the cost of accuracy. " + "Used by light objects"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + + prop = RNA_def_property(srna, "clamp_volume_indirect", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, + "Clamp Volume Indirect", + "If non-zero, the maximum value for indirect lighting in volumes. " + "Higher values will be scaled down to avoid too " + "much noise and slow convergence at the cost of accuracy. " + "Used by light-probes"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + /* Volumetrics */ prop = RNA_def_property(srna, "volumetric_start", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_ui_text(prop, "Start", "Start distance of the volumetric effect");