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");