diff --git a/scripts/startup/bl_ui/properties_data_lightprobe.py b/scripts/startup/bl_ui/properties_data_lightprobe.py index 833a5d40a6e..60f71cab48f 100644 --- a/scripts/startup/bl_ui/properties_data_lightprobe.py +++ b/scripts/startup/bl_ui/properties_data_lightprobe.py @@ -155,6 +155,7 @@ class DATA_PT_lightprobe_eevee_next(DataButtonsPanel, Panel): col = layout.column() row = col.row() col.prop(probe, "clip_start", text="Clipping Offset") + col.prop(probe, "influence_distance", text="Distance") pass else: # Currently unsupported diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 04724461c4e..86ea7076acb 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -473,6 +473,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_deferred_light_frag.glsl engines/eevee_next/shaders/eevee_deferred_capture_frag.glsl engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl + engines/eevee_next/shaders/eevee_deferred_planar_frag.glsl engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl @@ -532,6 +533,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl engines/eevee_next/shaders/eevee_ray_tile_compact_comp.glsl engines/eevee_next/shaders/eevee_ray_trace_fallback_comp.glsl + engines/eevee_next/shaders/eevee_ray_trace_planar_comp.glsl engines/eevee_next/shaders/eevee_ray_trace_screen_comp.glsl engines/eevee_next/shaders/eevee_ray_trace_screen_lib.glsl engines/eevee_next/shaders/eevee_ray_types_lib.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index d09045686f2..ce81bf072bd 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -40,6 +40,8 @@ #define REFLECTION_PROBE_SH_GROUP_SIZE 512 #define REFLECTION_PROBE_SH_SAMPLES_PER_GROUP 64 +#define PLANAR_PROBES_MAX 16 + /** * IMPORTANT: Some data packing are tweaked for these values. * Be sure to update them accordingly. @@ -166,6 +168,9 @@ #define REFLECTION_PROBE_TEX_SLOT 7 #define VOLUME_SCATTERING_TEX_SLOT 8 #define VOLUME_TRANSMITTANCE_TEX_SLOT 9 +/* Currently only used by ray-tracing, but might become used by forward too. */ +#define PLANAR_PROBE_DEPTH_TEX_SLOT 10 +#define PLANAR_PROBE_RADIANCE_TEX_SLOT 11 /* Images. */ #define RBUFS_COLOR_SLOT 0 @@ -188,6 +193,7 @@ /* Only during surface shading (forward and deferred eval). */ #define IRRADIANCE_GRID_BUF_SLOT 2 #define REFLECTION_PROBE_BUF_SLOT 3 +#define PLANAR_PROBE_BUF_SLOT 4 /* Only during pre-pass. */ #define VELOCITY_CAMERA_PREV_BUF 2 #define VELOCITY_CAMERA_CURR_BUF 3 diff --git a/source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc index eb2775525e8..8968fce7d9d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc +++ b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc @@ -17,8 +17,6 @@ namespace blender::eevee { void HiZBuffer::sync() { - RenderBuffers &render_buffers = inst_.render_buffers; - int2 render_extent = inst_.film.render_extent_get(); /* Padding to avoid complexity during down-sampling and screen tracing. */ int2 hiz_extent = math::ceil_to_multiple(render_extent, int2(1u << (HIZ_MIP_COUNT - 1))); @@ -33,22 +31,45 @@ void HiZBuffer::sync() data_.uv_scale = float2(render_extent) / float2(hiz_extent); { - hiz_update_ps_.init(); - hiz_update_ps_.shader_set(inst_.shaders.static_shader_get(HIZ_UPDATE)); - hiz_update_ps_.bind_ssbo("finished_tile_counter", atomic_tile_counter_); - hiz_update_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, with_filter); - hiz_update_ps_.bind_image("out_mip_0", hiz_tx_.mip_view(0)); - hiz_update_ps_.bind_image("out_mip_1", hiz_tx_.mip_view(1)); - hiz_update_ps_.bind_image("out_mip_2", hiz_tx_.mip_view(2)); - hiz_update_ps_.bind_image("out_mip_3", hiz_tx_.mip_view(3)); - hiz_update_ps_.bind_image("out_mip_4", hiz_tx_.mip_view(4)); - hiz_update_ps_.bind_image("out_mip_5", hiz_tx_.mip_view(5)); - hiz_update_ps_.bind_image("out_mip_6", hiz_tx_.mip_view(6)); + PassSimple &pass = hiz_update_ps_; + pass.init(); + pass.shader_set(inst_.shaders.static_shader_get(HIZ_UPDATE)); + pass.bind_ssbo("finished_tile_counter", atomic_tile_counter_); + /* TODO(fclem): Should be a parameter to avoid confusion. */ + pass.bind_texture("depth_tx", &src_tx_, with_filter); + pass.bind_image("out_mip_0", hiz_tx_.mip_view(0)); + pass.bind_image("out_mip_1", hiz_tx_.mip_view(1)); + pass.bind_image("out_mip_2", hiz_tx_.mip_view(2)); + pass.bind_image("out_mip_3", hiz_tx_.mip_view(3)); + pass.bind_image("out_mip_4", hiz_tx_.mip_view(4)); + pass.bind_image("out_mip_5", hiz_tx_.mip_view(5)); + pass.bind_image("out_mip_6", hiz_tx_.mip_view(6)); /* TODO(@fclem): There might be occasions where we might not want to * copy mip 0 for performance reasons if there is no need for it. */ - hiz_update_ps_.push_constant("update_mip_0", true); - hiz_update_ps_.dispatch(int3(dispatch_size, 1)); - hiz_update_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); + pass.push_constant("update_mip_0", true); + pass.dispatch(int3(dispatch_size, 1)); + pass.barrier(GPU_BARRIER_TEXTURE_FETCH); + } + { + PassSimple &pass = hiz_update_layer_ps_; + pass.init(); + pass.shader_set(inst_.shaders.static_shader_get(HIZ_UPDATE_LAYER)); + pass.bind_ssbo("finished_tile_counter", atomic_tile_counter_); + /* TODO(fclem): Should be a parameter to avoid confusion. */ + pass.bind_texture("depth_layered_tx", &src_tx_, with_filter); + pass.bind_image("out_mip_0", hiz_tx_.mip_view(0)); + pass.bind_image("out_mip_1", hiz_tx_.mip_view(1)); + pass.bind_image("out_mip_2", hiz_tx_.mip_view(2)); + pass.bind_image("out_mip_3", hiz_tx_.mip_view(3)); + pass.bind_image("out_mip_4", hiz_tx_.mip_view(4)); + pass.bind_image("out_mip_5", hiz_tx_.mip_view(5)); + pass.bind_image("out_mip_6", hiz_tx_.mip_view(6)); + /* TODO(@fclem): There might be occasions where we might not want to + * copy mip 0 for performance reasons if there is no need for it. */ + pass.push_constant("update_mip_0", true); + pass.push_constant("layer_id", &layer_id_); + pass.dispatch(int3(dispatch_size, 1)); + pass.barrier(GPU_BARRIER_TEXTURE_FETCH); } if (inst_.debug_mode == eDebugMode::DEBUG_HIZ_VALIDATION) { @@ -66,18 +87,13 @@ void HiZBuffer::update() return; } - /* Bind another framebuffer in order to avoid triggering the feedback loop check. - * This is safe because we only use compute shaders in this section of the code. - * Ideally the check should be smarter. */ - GPUFrameBuffer *fb = GPU_framebuffer_active_get(); - if (G.debug & G_DEBUG_GPU) { - GPU_framebuffer_restore(); + src_tx_ = *src_tx_ptr_; + + if (layer_id_ == -1) { + inst_.manager->submit(hiz_update_ps_); } - - inst_.manager->submit(hiz_update_ps_); - - if (G.debug & G_DEBUG_GPU) { - GPU_framebuffer_bind(fb); + else { + inst_.manager->submit(hiz_update_layer_ps_); } } diff --git a/source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh index e11e423d762..40e3bdaa0f3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh +++ b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh @@ -37,10 +37,16 @@ class HiZBuffer { draw::StorageBuffer atomic_tile_counter_ = {"atomic_tile_counter"}; /** Single pass recursive down-sample. */ PassSimple hiz_update_ps_ = {"HizUpdate"}; + /** Single pass recursive down-sample for layered depth buffer. Only downsample 1 layer. */ + PassSimple hiz_update_layer_ps_ = {"HizUpdate.Layer"}; + int layer_id_ = -1; /** Debug pass. */ PassSimple debug_draw_ps_ = {"HizUpdate.Debug"}; /** Dirty flag to check if the update is necessary. */ bool is_dirty_ = true; + /** Reference to the depth texture to downsample. */ + GPUTexture *src_tx_; + GPUTexture **src_tx_ptr_; HiZData &data_; @@ -52,6 +58,15 @@ class HiZBuffer { void sync(); + /** + * Set source texture for the hiz downsampling. + */ + void set_source(GPUTexture **texture, int layer = -1) + { + src_tx_ptr_ = texture; + layer_id_ = layer; + } + /** * Tag the buffer for update if needed. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 59807cb0109..4932e496d06 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -895,6 +895,7 @@ void DeferredProbeLayer::render(View &view, GPU_framebuffer_bind(prepass_fb); inst_.manager->submit(prepass_ps_, view); + inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx); inst_.hiz_buffer.set_dirty(); inst_.lights.set_view(view, extent); inst_.shadows.set_view(view); @@ -981,6 +982,11 @@ void PlanarProbePipeline::begin_sync() inst_.bind_uniform_data(&gbuffer_ps_); inst_.sampling.bind_resources(&gbuffer_ps_); inst_.hiz_buffer.bind_resources(&gbuffer_ps_); + /* Cryptomatte. */ + gbuffer_ps_.bind_image(RBUFS_CRYPTOMATTE_SLOT, &inst_.render_buffers.cryptomatte_tx); + /* RenderPasses & AOVs. */ + gbuffer_ps_.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx); + gbuffer_ps_.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx); inst_.cryptomatte.bind_resources(&gbuffer_ps_); DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM | DRW_STATE_DEPTH_EQUAL; @@ -994,13 +1000,16 @@ void PlanarProbePipeline::begin_sync() { PassSimple &pass = eval_light_ps_; pass.init(); - pass.shader_set(inst_.shaders.static_shader_get(DEFERRED_CAPTURE_EVAL)); + pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL); + pass.shader_set(inst_.shaders.static_shader_get(DEFERRED_PLANAR_EVAL)); + pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); inst_.bind_uniform_data(&pass); inst_.gbuffer.bind_resources(&pass); inst_.lights.bind_resources(&pass); inst_.shadows.bind_resources(&pass); inst_.sampling.bind_resources(&pass); inst_.hiz_buffer.bind_resources(&pass); + inst_.reflection_probes.bind_resources(&pass); inst_.irradiance_cache.bind_resources(&pass); pass.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); pass.draw_procedural(GPU_PRIM_TRIS, 1, 3); @@ -1033,10 +1042,13 @@ PassMain::Sub *PlanarProbePipeline::material_add(::Material *blender_mat, GPUMat return &pass->sub(GPU_material_get_name(gpumat)); } -void PlanarProbePipeline::render(View &view, Framebuffer &combined_fb, int2 extent) +void PlanarProbePipeline::render(View &view, Framebuffer &combined_fb, int layer_id, int2 extent) { GPU_debug_group_begin("Planar.Capture"); + inst_.hiz_buffer.set_source(&inst_.planar_probes.depth_tx_, layer_id); + inst_.hiz_buffer.set_dirty(); + GPU_framebuffer_bind(combined_fb); GPU_framebuffer_clear_depth(combined_fb, 1.0f); inst_.manager->submit(prepass_ps_, view); @@ -1047,6 +1059,7 @@ void PlanarProbePipeline::render(View &view, Framebuffer &combined_fb, int2 exte inst_.gbuffer.acquire(extent, closure_bits_); + inst_.hiz_buffer.update(); GPU_framebuffer_bind(combined_fb); GPU_framebuffer_clear_color(combined_fb, float4(0.0f, 0.0f, 0.0f, 1.0f)); inst_.manager->submit(gbuffer_ps_, view); diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index 2b316d2a8d5..27390a90900 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -352,7 +352,7 @@ class PlanarProbePipeline : DeferredLayerBase { PassMain::Sub *prepass_add(::Material *material, GPUMaterial *gpumat); PassMain::Sub *material_add(::Material *material, GPUMaterial *gpumat); - void render(View &view, Framebuffer &combined_fb, int2 extent); + void render(View &view, Framebuffer &combined_fb, int layer_id, int2 extent); }; /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_planar_probes.cc b/source/blender/draw/engines/eevee_next/eevee_planar_probes.cc index 5c76124dd29..9d6cafd6102 100644 --- a/source/blender/draw/engines/eevee_next/eevee_planar_probes.cc +++ b/source/blender/draw/engines/eevee_next/eevee_planar_probes.cc @@ -7,6 +7,49 @@ namespace blender::eevee { +using namespace blender::math; + +/* -------------------------------------------------------------------- */ +/** \name Planar Probe + * \{ */ + +void PlanarProbe::sync(const float4x4 &world_to_object, + float clipping_offset, + float influence_distance) +{ + this->plane_to_world = float4x4(world_to_object); + this->plane_to_world.z_axis() = normalize(this->plane_to_world.z_axis()) * influence_distance; + this->world_to_plane = invert(this->plane_to_world); + this->clipping_offset = clipping_offset; +} + +void PlanarProbe::set_view(const draw::View &view, int layer_id) +{ + this->viewmat = view.viewmat() * reflection_matrix_get(); + this->winmat = view.winmat(); + this->world_to_object_transposed = float3x4(transpose(world_to_plane)); + this->normal = normalize(plane_to_world.z_axis()); + + bool view_is_below_plane = dot(view.location() - plane_to_world.location(), + plane_to_world.z_axis()) < 0.0; + if (view_is_below_plane) { + this->normal = -this->normal; + } + this->layer_id = layer_id; +} + +float4x4 PlanarProbe::reflection_matrix_get() +{ + return plane_to_world * from_scale(float3(1, 1, -1)) * world_to_plane; +} + +float4 PlanarProbe::reflection_clip_plane_get() +{ + return float4(-normal, dot(normal, plane_to_world.location()) - clipping_offset); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Planar Probe Module * \{ */ @@ -20,8 +63,8 @@ void PlanarProbeModule::init() void PlanarProbeModule::begin_sync() { - for (PlanarProbe &planar_probe : probes_.values()) { - planar_probe.is_probe_used = false; + for (PlanarProbe &probe : probes_.values()) { + probe.is_probe_used = false; } } @@ -32,18 +75,15 @@ void PlanarProbeModule::sync_object(Object *ob, ObjectHandle &ob_handle) return; } - /* TODO Cull out of view planars. */ - PlanarProbe &probe = find_or_insert(ob_handle); - probe.plane_to_world = float4x4(ob->object_to_world); - probe.world_to_plane = float4x4(ob->world_to_object); - probe.clipping_offset = light_probe->clipsta; + probe.sync(float4x4(ob->object_to_world), light_probe->clipsta, light_probe->distinf); probe.is_probe_used = true; } void PlanarProbeModule::end_sync() { - remove_unused_probes(); + probes_.remove_if( + [](const PlanarProbes::MutableItem &item) { return !item.value.is_probe_used; }); /* When first planar probes are enabled it can happen that the first sample is off. */ if (!update_probes_ && !probes_.is_empty()) { @@ -51,60 +91,55 @@ void PlanarProbeModule::end_sync() } } -float4x4 PlanarProbeModule::reflection_matrix_get(const float4x4 &plane_to_world, - const float4x4 &world_to_plane) -{ - return math::normalize(plane_to_world) * math::from_scale(float3(1, 1, -1)) * - math::normalize(world_to_plane); -} - -float4 PlanarProbeModule::reflection_clip_plane_get(const float4x4 &plane_to_world, - float clip_offset) -{ - /* Compute clip plane equation / normal. */ - float4 plane_equation = float4(-math::normalize(plane_to_world.z_axis())); - plane_equation.w = -math::dot(plane_equation.xyz(), plane_to_world.location()); - plane_equation.w -= clip_offset; - return plane_equation; -} - void PlanarProbeModule::set_view(const draw::View &main_view, int2 main_view_extent) { const int64_t num_probes = probes_.size(); - if (resources_.size() != num_probes) { - resources_.reinitialize(num_probes); - } - - eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ; - if (num_probes == 0) { - color_tx_.ensure_2d_array(GPU_R11F_G11F_B10F, int2(1), 1, usage); - depth_tx_.ensure_2d_array(GPU_DEPTH_COMPONENT32F, int2(1), 1, usage); - return; - } /* TODO resolution percentage. */ int2 extent = main_view_extent; - color_tx_.ensure_2d_array(GPU_R11F_G11F_B10F, extent, num_probes, usage); - depth_tx_.ensure_2d_array(GPU_DEPTH_COMPONENT32F, extent, num_probes, usage); + int layer_count = num_probes; + + if (num_probes == 0) { + /* Create valid dummy texture. */ + extent = int2(1); + layer_count = 1; + } + + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ; + radiance_tx_.ensure_2d_array(GPU_R11F_G11F_B10F, extent, layer_count, usage); + depth_tx_.ensure_2d_array(GPU_DEPTH_COMPONENT32F, extent, layer_count, usage); int resource_index = 0; for (PlanarProbe &probe : probes_.values()) { + if (resource_index == PLANAR_PROBES_MAX) { + break; + } + PlanarProbeResources &res = resources_[resource_index]; - float4x4 winmat = main_view.winmat(); - float4x4 viewmat = main_view.viewmat(); - viewmat = viewmat * reflection_matrix_get(probe.plane_to_world, probe.world_to_plane); - res.view.sync(viewmat, winmat); - res.view.visibility_test(false); + /* TODO Cull out of view planars. */ - world_clip_buf_.plane = reflection_clip_plane_get(probe.plane_to_world, probe.clipping_offset); + probe.set_view(main_view, resource_index); + probe_planar_buf_[resource_index] = probe; + + res.view.sync(probe.viewmat, probe.winmat); + + world_clip_buf_.plane = probe.reflection_clip_plane_get(); world_clip_buf_.push_update(); res.combined_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx_, resource_index), - GPU_ATTACHMENT_TEXTURE_LAYER(color_tx_, resource_index)); + GPU_ATTACHMENT_TEXTURE_LAYER(radiance_tx_, resource_index)); - instance_.pipelines.planar.render(res.view, res.combined_fb, main_view_extent); + instance_.pipelines.planar.render(res.view, res.combined_fb, resource_index, extent); + + resource_index++; } + + if (resource_index < PLANAR_PROBES_MAX) { + /* Tag the end of the array. */ + probe_planar_buf_[resource_index].layer_id = -1; + } + probe_planar_buf_.push_update(); } PlanarProbe &PlanarProbeModule::find_or_insert(ObjectHandle &ob_handle) @@ -113,12 +148,6 @@ PlanarProbe &PlanarProbeModule::find_or_insert(ObjectHandle &ob_handle) return planar_probe; } -void PlanarProbeModule::remove_unused_probes() -{ - probes_.remove_if( - [](const PlanarProbes::MutableItem &item) { return !item.value.is_probe_used; }); -} - /** \} */ } // namespace blender::eevee \ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/eevee_planar_probes.hh b/source/blender/draw/engines/eevee_next/eevee_planar_probes.hh index 215ee1bd795..b63fe69b0a3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_planar_probes.hh +++ b/source/blender/draw/engines/eevee_next/eevee_planar_probes.hh @@ -19,13 +19,14 @@ struct Material; namespace blender::eevee { class Instance; +class HiZBuffer; struct ObjectHandle; /* -------------------------------------------------------------------- */ /** \name Planar Probe * \{ */ -struct PlanarProbe { +struct PlanarProbe : ProbePlanarData { /* Copy of object matrices. */ float4x4 plane_to_world; float4x4 world_to_plane; @@ -35,6 +36,30 @@ struct PlanarProbe { int resource_index; /* Pruning flag. */ bool is_probe_used = false; + + public: + void sync(const float4x4 &world_to_object, float clipping_offset, float influence_distance); + + /** + * Update the ProbePlanarData part of the struct. + * `view` is the view we want to render this probe with. + */ + void set_view(const draw::View &view, int layer_id); + + /** + * Create the reflection clip plane equation that clips along the XY plane of the given + * transform. The `clip_offset` will push the clip plane a bit further to avoid missing pixels in + * reflections. The transform does not need to be normalized but is expected to be orthogonal. + * \note Only works after `set_view` was called. + */ + float4 reflection_clip_plane_get(); + + private: + /** + * Create the reflection matrix that reflect along the XY plane of the given transform. + * The transform does not need to be normalized but is expected to be orthogonal. + */ + float4x4 reflection_matrix_get(); }; struct PlanarProbeResources : NonCopyable { @@ -50,18 +75,18 @@ struct PlanarProbeResources : NonCopyable { class PlanarProbeModule { using PlanarProbes = Map; - using Resources = Array; private: Instance &instance_; PlanarProbes probes_; - Resources resources_; + std::array resources_; - Texture color_tx_ = {"planar.color_tx"}; + Texture radiance_tx_ = {"planar.radiance_tx"}; Texture depth_tx_ = {"planar.depth_tx"}; ClipPlaneBuf world_clip_buf_ = {"world_clip_buf"}; + ProbePlanarDataBuf probe_planar_buf_ = {"probe_planar_buf"}; bool update_probes_ = false; @@ -75,26 +100,25 @@ class PlanarProbeModule { void set_view(const draw::View &main_view, int2 main_view_extent); - template void bind_resources(draw::detail::PassBase * /*pass*/) {} + template void bind_resources(draw::detail::PassBase *pass) + { + /* Disable filter to avoid interpolation with missing background. */ + GPUSamplerState no_filter = GPUSamplerState::default_sampler(); + pass->bind_ubo(PLANAR_PROBE_BUF_SLOT, &probe_planar_buf_); + pass->bind_texture(PLANAR_PROBE_RADIANCE_TEX_SLOT, &radiance_tx_, no_filter); + pass->bind_texture(PLANAR_PROBE_DEPTH_TEX_SLOT, &depth_tx_); + } + + bool enabled() const + { + return update_probes_; + } private: PlanarProbe &find_or_insert(ObjectHandle &ob_handle); - void remove_unused_probes(); - - /** - * Create the reflection matrix that reflect along the XY plane of the given transform. - * The transform does not need to be normalized but is expected to be orthogonal. - */ - float4x4 reflection_matrix_get(const float4x4 &plane_to_world, const float4x4 &world_to_plane); - - /** - * Create the reflection clip plane equation that clips along the XY plane of the given - * transform. The `clip_offset` will push the clip plane a bit further to avoid missing pixels in - * reflections. The transform does not need to be normalized but is expected to be orthogonal. - */ - float4 reflection_clip_plane_get(const float4x4 &plane_to_world, float clip_offset); friend class Instance; + friend class HiZBuffer; friend class PlanarProbePipeline; }; diff --git a/source/blender/draw/engines/eevee_next/eevee_raytrace.cc b/source/blender/draw/engines/eevee_next/eevee_raytrace.cc index 30ac791d940..01f1867dfc1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_raytrace.cc +++ b/source/blender/draw/engines/eevee_next/eevee_raytrace.cc @@ -96,6 +96,23 @@ void RayTraceModule::sync() for (auto type : IndexRange(3)) { PassSimple &pass = PASS_VARIATION(trace_, type, _ps_); pass.init(); + if (inst_.planar_probes.enabled() && (&pass == &trace_reflect_ps_)) { + /* Inject planar tracing in the same pass as reflection tracing. */ + PassSimple::Sub &sub = pass.sub("Trace.Planar"); + sub.shader_set(inst_.shaders.static_shader_get(RAY_TRACE_PLANAR)); + sub.bind_ssbo("tiles_coord_buf", &ray_tiles_buf_); + sub.bind_image("ray_data_img", &ray_data_tx_); + sub.bind_image("ray_time_img", &ray_time_tx_); + sub.bind_image("ray_radiance_img", &ray_radiance_tx_); + sub.bind_texture("depth_tx", &depth_tx); + inst_.bind_uniform_data(&sub); + inst_.planar_probes.bind_resources(&sub); + inst_.irradiance_cache.bind_resources(&sub); + inst_.reflection_probes.bind_resources(&sub); + /* TODO(@fclem): Use another dispatch with only tiles that touches planar captures. */ + sub.dispatch(ray_dispatch_buf_); + sub.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); + } pass.shader_set(inst_.shaders.static_shader_get(SHADER_VARIATION(RAY_TRACE_SCREEN_, type))); pass.bind_ssbo("tiles_coord_buf", &ray_tiles_buf_); pass.bind_image("ray_data_img", &ray_data_tx_); diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index f9567599b70..d9f5a5c6aac 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -98,10 +98,14 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_deferred_light"; case DEFERRED_CAPTURE_EVAL: return "eevee_deferred_capture_eval"; + case DEFERRED_PLANAR_EVAL: + return "eevee_deferred_planar_eval"; case HIZ_DEBUG: return "eevee_hiz_debug"; case HIZ_UPDATE: return "eevee_hiz_update"; + case HIZ_UPDATE_LAYER: + return "eevee_hiz_update_layer"; case MOTION_BLUR_GATHER: return "eevee_motion_blur_gather"; case MOTION_BLUR_TILE_DILATE: @@ -182,6 +186,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_ray_generate_refract"; case RAY_TRACE_FALLBACK: return "eevee_ray_trace_fallback"; + case RAY_TRACE_PLANAR: + return "eevee_ray_trace_planar"; case RAY_TRACE_SCREEN_DIFFUSE: return "eevee_ray_trace_screen_diffuse"; case RAY_TRACE_SCREEN_REFLECT: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index e0169346101..08abc0de5a6 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -35,6 +35,7 @@ enum eShaderType { DEFERRED_COMBINE, DEFERRED_LIGHT, DEFERRED_CAPTURE_EVAL, + DEFERRED_PLANAR_EVAL, DEBUG_SURFELS, DEBUG_IRRADIANCE_GRID, @@ -60,6 +61,7 @@ enum eShaderType { DOF_TILES_FLATTEN, HIZ_UPDATE, + HIZ_UPDATE_LAYER, HIZ_DEBUG, LIGHT_CULLING_DEBUG, @@ -91,6 +93,7 @@ enum eShaderType { RAY_TILE_CLASSIFY, RAY_TILE_COMPACT, RAY_TRACE_FALLBACK, + RAY_TRACE_PLANAR, RAY_TRACE_SCREEN_DIFFUSE, RAY_TRACE_SCREEN_REFLECT, RAY_TRACE_SCREEN_REFRACT, 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 72ac70ea64c..68dca541f30 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -1331,6 +1331,19 @@ struct ReflectionProbeData { }; BLI_STATIC_ASSERT_ALIGN(ReflectionProbeData, 16) +struct ProbePlanarData { + /** Matrices used to render the planar capture. */ + float4x4 viewmat; + float4x4 winmat; + /** Transform world to local position with influence distance as Z scale. */ + float3x4 world_to_object_transposed; + /** World space plane normal. */ + packed_float3 normal; + /** Layer in the planar capture textures used by this probe. */ + int layer_id; +}; +BLI_STATIC_ASSERT_ALIGN(ProbePlanarData, 16) + struct ClipPlaneData { /** World space clip plane equation. Used to render planar light-probes. */ float4 plane; @@ -1454,6 +1467,7 @@ using RayTraceTileBuf = draw::StorageArrayBuffer; using SubsurfaceTileBuf = RayTraceTileBuf; using ReflectionProbeDataBuf = draw::UniformArrayBuffer; +using ProbePlanarDataBuf = draw::UniformArrayBuffer; using SamplingDataBuf = draw::StorageBuffer; using ShadowStatisticsBuf = draw::StorageBuffer; using ShadowPagesInfoDataBuf = draw::StorageBuffer; diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index 5bc5b70a0ca..2aeb2810aca 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -102,8 +102,6 @@ void ShadingView::render() update_view(); - inst_.hiz_buffer.set_dirty(); - DRW_stats_group_start(name_); DRW_view_set_active(render_view_); @@ -120,6 +118,7 @@ void ShadingView::render() GPU_framebuffer_bind(combined_fb_); GPU_framebuffer_clear_color_depth(combined_fb_, clear_color, 1.0f); + inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx); inst_.hiz_buffer.set_dirty(); inst_.pipelines.background.render(render_view_new_); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_planar_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_planar_frag.glsl new file mode 100644 index 00000000000..69a1ddf0dd2 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_planar_frag.glsl @@ -0,0 +1,48 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** + * Compute light objects lighting contribution using captured Gbuffer data. + */ + +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_FragCoord.xy); + + float depth = texelFetch(hiz_tx, texel, 0).r; + + GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel); + + vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth); + vec3 Ng = gbuf.diffuse.N; + vec3 V = cameraVec(P); + float vPz = dot(cameraForward, P) - dot(cameraForward, cameraPos); + + ClosureLightStack stack; + stack.cl[0].N = gbuf.diffuse.N; + stack.cl[0].ltc_mat = LTC_LAMBERT_MAT; + stack.cl[0].type = LIGHT_DIFFUSE; + + stack.cl[1].N = gbuf.reflection.N; + stack.cl[1].ltc_mat = LTC_GGX_MAT(dot(gbuf.reflection.N, V), gbuf.reflection.roughness); + stack.cl[1].type = LIGHT_SPECULAR; + + /* Direct light. */ + light_eval(stack, P, Ng, V, vPz, gbuf.thickness); + /* Indirect light. */ + LightProbeSample samp = lightprobe_load(P, Ng, V); + + vec3 radiance = vec3(0.0); + radiance += (stack.cl[0].light_shadowed + lightprobe_eval(samp, gbuf.diffuse, V, vec2(0.0))) * + gbuf.diffuse.color; + radiance += (stack.cl[1].light_shadowed + lightprobe_eval(samp, gbuf.reflection, V, vec2(0.0))) * + gbuf.reflection.color; + + out_radiance = vec4(radiance, 0.0); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_hiz_update_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_hiz_update_comp.glsl index 59a9ab158cd..2b153234497 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_hiz_update_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_hiz_update_comp.glsl @@ -45,7 +45,11 @@ void main() /* Copy level 0. */ ivec2 src_px = ivec2(kernel_origin + local_px) * 2; vec2 samp_co = (vec2(src_px) + 0.5) / vec2(textureSize(depth_tx, 0)); +#ifdef HIZ_LAYER + vec4 samp = textureGather(depth_layered_tx, vec3(samp_co, float(layer_id))); +#else vec4 samp = textureGather(depth_tx, samp_co); +#endif if (update_mip_0) { imageStore(out_mip_0, src_px + ivec2(0, 1), samp.xxxx); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl index a898020a8b0..b6aca4c17d4 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl @@ -44,3 +44,42 @@ ivec3 lightprobe_irradiance_grid_cell_corner(int cell_corner_id) { return (ivec3(cell_corner_id) >> ivec3(0, 1, 2)) & 1; } + +float lightprobe_planar_score(ProbePlanarData planar, vec3 P, vec3 V, vec3 L) +{ + vec3 lP = vec4(P, 1.0) * planar.world_to_object_transposed; + if (any(greaterThan(abs(lP), vec3(1.0)))) { + /* TODO: Transition in Z. Dither? */ + return 0.0; + } + /* Return how much the ray is lined up with the captured ray. */ + vec3 R = -reflect(V, planar.normal); + /* TODO: Use saturate (dependency hell). */ + return clamp(dot(L, R), 0.0, 1.0); +} + +#ifdef PLANAR_PROBES +/** + * Return the best planar probe index for a given light direction vector and postion. + */ +int lightprobe_planar_select(vec3 P, vec3 V, vec3 L) +{ + /* Initialize to the score of a camera ray. */ + /* TODO: Use saturate (dependency hell). */ + float best_score = clamp(dot(L, -V), 0.0, 1.0); + int best_index = -1; + + for (int index = 0; index < PLANAR_PROBES_MAX; index++) { + if (probe_planar_buf[index].layer_id == -1) { + /* ProbePlanarData doesn't contain any gap, exit at first item that is invalid. */ + break; + } + float score = lightprobe_planar_score(probe_planar_buf[index], P, V, L); + if (score > best_score) { + best_score = score; + best_index = index; + } + } + return best_index; +} +#endif diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl index 4417ec9a3ca..ceae8d629e0 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl @@ -22,24 +22,6 @@ #pragma BLENDER_REQUIRE(eevee_bxdf_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) -void gbuffer_load_closure_data(sampler2DArray gbuf_closure_tx, - ivec2 texel, - out ClosureDiffuse closure) -{ -} - -void gbuffer_load_closure_data(sampler2DArray gbuf_closure_tx, - ivec2 texel, - out ClosureRefraction closure) -{ -} - -void gbuffer_load_closure_data(sampler2DArray gbuf_closure_tx, - ivec2 texel, - out ClosureReflection closure) -{ -} - float bxdf_eval(ClosureDiffuse closure, vec3 L, vec3 V) { return bsdf_lambert(closure.N, L); @@ -179,7 +161,7 @@ void main() vec4 ray_radiance = imageLoad(ray_radiance_img, sample_texel); vec3 ray_direction = ray_data.xyz; - float ray_pdf_inv = ray_data.w; + float ray_pdf_inv = abs(ray_data.w); /* Skip invalid pixels. */ if (ray_pdf_inv == 0.0) { continue; 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 new file mode 100644 index 00000000000..95af9ff84ac --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_planar_comp.glsl @@ -0,0 +1,103 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** + * Use screen space tracing against depth buffer of recorded planar capture to find intersection + * with the scene and its radiance. + * This pass runs before the screen trace and evaluates valid rays for planar probes. These rays + * are then tagged to avoid re-evaluation by screen trace. + */ + +#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bxdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_ray_types_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_ray_trace_screen_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) + +void main() +{ + const uint tile_size = RAYTRACE_GROUP_SIZE; + uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]); + ivec2 texel = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size); + + vec4 ray_data = imageLoad(ray_data_img, texel); + float ray_pdf_inv = ray_data.w; + + if (ray_pdf_inv == 0.0) { + /* Invalid ray or pixels without ray. Do not trace. */ + imageStore(ray_time_img, texel, vec4(0.0)); + imageStore(ray_radiance_img, texel, vec4(0.0)); + return; + } + + ivec2 texel_fullres = texel * uniform_buf.raytrace.resolution_scale + + uniform_buf.raytrace.resolution_bias; + + float depth = texelFetch(depth_tx, texel_fullres, 0).r; + vec2 uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv; + + vec3 P = get_world_space_from_depth(uv, depth); + vec3 V = cameraVec(P); + + int planar_id = lightprobe_planar_select(P, V, ray_data.xyz); + if (planar_id == -1) { + return; + } + + ProbePlanarData planar = probe_planar_buf[planar_id]; + + /* Tag the ray data so that screen trace will not try to evaluate it and override the result. */ + imageStore(ray_data_img, texel, vec4(ray_data.xyz, -ray_data.w)); + + Ray ray; + ray.origin = P; + ray.direction = ray_data.xyz; + + vec3 radiance = vec3(0.0); + float noise_offset = sampling_rng_1D_get(SAMPLING_RAYTRACE_W); + float rand_trace = interlieved_gradient_noise(vec2(texel), 5.0, noise_offset); + + /* TODO(fclem): Take IOR into account in the roughness LOD bias. */ + /* TODO(fclem): pdf to roughness mapping is a crude approximation. Find something better. */ + float roughness = saturate(sample_pdf_uniform_hemisphere() / ray_pdf_inv); + + /* Transform the ray into planar view-space. */ + Ray ray_view; + ray_view.origin = transform_point(planar.viewmat, ray.origin); + ray_view.direction = transform_direction(planar.viewmat, ray.direction); + /* Extend the ray to cover the whole view. */ + ray_view.max_time = 1000.0; + + ScreenTraceHitData hit = raytrace_planar( + uniform_buf.raytrace, planar_depth_tx, planar, rand_trace, ray_view); + + 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. + * This is faster than loading the gbuffer again and averages between reflected and normal + * direction over many rays. */ + vec3 Ng = ray.direction; + /* Fallback to nearest light-probe. */ + LightProbeSample samp = lightprobe_load(P, Ng, V); + radiance = lightprobe_eval_direction(samp, ray.direction, safe_rcp(ray_pdf_inv)); + /* Set point really far for correct reprojection of background. */ + hit.time = 10000.0; + } + + float luma = max(1e-8, max_v3(radiance)); + radiance *= 1.0 - max(0.0, luma - uniform_buf.raytrace.brightness_clamp) / luma; + + 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 98187fdc3cd..c7dc6d9ed09 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 @@ -20,15 +20,14 @@ void main() uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]); ivec2 texel = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size); - ivec2 texel_fullres = texel * uniform_buf.raytrace.resolution_scale + - uniform_buf.raytrace.resolution_bias; - - float depth = texelFetch(depth_tx, texel_fullres, 0).r; - vec2 uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv; - vec4 ray_data = imageLoad(ray_data_img, texel); float ray_pdf_inv = ray_data.w; + if (ray_pdf_inv < 0.0) { + /* Ray destined to planar trace. */ + return; + } + if (ray_pdf_inv == 0.0) { /* Invalid ray or pixels without ray. Do not trace. */ imageStore(ray_time_img, texel, vec4(0.0)); @@ -36,6 +35,12 @@ void main() return; } + ivec2 texel_fullres = texel * uniform_buf.raytrace.resolution_scale + + uniform_buf.raytrace.resolution_bias; + + float depth = texelFetch(depth_tx, texel_fullres, 0).r; + vec2 uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv; + vec3 P = get_world_space_from_depth(uv, depth); vec3 V = cameraVec(P); Ray ray; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_lib.glsl index 5b7ce4cf9ba..44d8f4f91b2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_lib.glsl @@ -68,7 +68,7 @@ METAL_ATTR ScreenTraceHitData raytrace_screen(RayTraceData rt_data, raytrace_clip_ray_to_near_plane(ray); } - /* NOTE: The 2.0 factor here is because we are applying it in. */ + /* NOTE: The 2.0 factor here is because we are applying it in NDC space. */ ScreenSpaceRay ssray = raytrace_screenspace_ray_create( ray, 2.0 * rt_data.full_resolution_inv, rt_data.thickness); @@ -141,9 +141,9 @@ METAL_ATTR ScreenTraceHitData raytrace_screen(RayTraceData rt_data, ScreenTraceHitData result; result.valid = hit; - /* Convert to world space ray time. */ result.ss_hit_P = ssray.origin.xyz + ssray.direction.xyz * time; result.v_hit_P = project_point(drw_view.wininv, result.ss_hit_P * 2.0 - 1.0); + /* Convert to world space ray time. */ result.time = length(result.v_hit_P - ray.origin) / length(ray.direction); #ifdef METAL_AMD_RAYTRACE_WORKAROUND @@ -156,3 +156,65 @@ METAL_ATTR ScreenTraceHitData raytrace_screen(RayTraceData rt_data, } #undef METAL_ATTR + +#ifdef PLANAR_PROBES + +ScreenTraceHitData raytrace_planar(RayTraceData rt_data, + depth2DArray planar_depth_tx, + ProbePlanarData planar, + float stride_rand, + Ray ray) +{ + /* Clip to near plane for perspective view where there is a singularity at the camera origin. */ + if (ProjectionMatrix[3][3] == 0.0) { + raytrace_clip_ray_to_near_plane(ray); + } + + vec2 inv_texture_size = 1.0 / textureSize(planar_depth_tx, 0).xy; + /* NOTE: The 2.0 factor here is because we are applying it in NDC space. */ + /* TODO(@fclem): This uses the main view's projection matrix, not the planar's one. + * This works fine for reflection, but this prevent the use of any other projection capture. */ + ScreenSpaceRay ssray = raytrace_screenspace_ray_create(ray, 2.0 * inv_texture_size); + + float prev_delta = 0.0, prev_time = 0.0; + float depth_sample = texture(planar_depth_tx, vec3(ssray.origin.xy, planar.layer_id)).r; + float delta = depth_sample - ssray.origin.z; + + float t = 0.0, time = 0.0; + bool hit = false; + const int max_steps = 32; + for (int iter = 1; !hit && (time < ssray.max_time) && (iter < max_steps); iter++) { + float stride = 1.0 + float(iter) * rt_data.quality; + + prev_time = time; + prev_delta = delta; + + time = min(t + stride * stride_rand, ssray.max_time); + t += stride; + + vec4 ss_ray = ssray.origin + ssray.direction * time; + + depth_sample = texture(planar_depth_tx, vec3(ss_ray.xy, planar.layer_id)).r; + + delta = depth_sample - ss_ray.z; + /* Check if the ray is below the surface. */ + hit = (delta < 0.0); + } + /* Reject hit if background. */ + hit = hit && (depth_sample != 1.0); + /* Refine hit using intersection between the sampled height-field and the ray. + * This simplifies nicely to this single line. */ + time = mix(prev_time, time, saturate(prev_delta / (prev_delta - delta))); + + ScreenTraceHitData result; + result.valid = hit; + result.ss_hit_P = ssray.origin.xyz + ssray.direction.xyz * time; + /* TODO(@fclem): This uses the main view's projection matrix, not the planar's one. + * This works fine for reflection, but this prevent the use of any other projection capture. */ + result.v_hit_P = project_point(drw_view.wininv, result.ss_hit_P * 2.0 - 1.0); + /* Convert to world space ray time. */ + result.time = length(result.v_hit_P - ray.origin) / length(ray.direction); + return result; +} + +#endif \ No newline at end of file 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 3bf838902cb..7a079d9bb4b 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 @@ -76,5 +76,26 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_capture_eval) .fragment_source("eevee_deferred_capture_frag.glsl") .do_static_compilation(true); +GPU_SHADER_CREATE_INFO(eevee_deferred_planar_eval) + /* Early fragment test is needed to avoid processing fragments without correct GBuffer data. */ + .early_fragment_test(true) + /* Inputs. */ + .fragment_out(0, Type::VEC4, "out_radiance") + .define("REFLECTION_PROBE") + .define("SSS_TRANSMITTANCE") + .define("LIGHT_CLOSURE_EVAL_COUNT", "2") + .additional_info("eevee_shared", + "eevee_gbuffer_data", + "eevee_utility_texture", + "eevee_sampling_data", + "eevee_light_data", + "eevee_lightprobe_data", + "eevee_shadow_data", + "eevee_hiz_data", + "draw_view", + "draw_fullscreen") + .fragment_source("eevee_deferred_planar_frag.glsl") + .do_static_compilation(true); + #undef image_out #undef image_in diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh index c668dee2040..ffe0aa3d715 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh @@ -24,6 +24,13 @@ GPU_SHADER_CREATE_INFO(eevee_hiz_update) .push_constant(Type::BOOL, "update_mip_0") .compute_source("eevee_hiz_update_comp.glsl"); +GPU_SHADER_CREATE_INFO(eevee_hiz_update_layer) + .do_static_compilation(true) + .define("HIZ_LAYER") + .sampler(1, ImageType::DEPTH_2D_ARRAY, "depth_layered_tx") + .push_constant(Type::INT, "layer_id") + .additional_info("eevee_hiz_update"); + GPU_SHADER_CREATE_INFO(eevee_hiz_debug) .do_static_compilation(true) .fragment_out(0, Type::VEC4, "out_debug_color_add", DualBlend::SRC_0) 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 fc1e38ca414..c7581ec56d9 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 @@ -209,4 +209,10 @@ GPU_SHADER_CREATE_INFO(eevee_volume_probe_data) GPU_SHADER_CREATE_INFO(eevee_lightprobe_data) .additional_info("eevee_reflection_probe_data", "eevee_volume_probe_data"); +GPU_SHADER_CREATE_INFO(eevee_lightprobe_planar_data) + .define("REFLECTION_PROBE") + .uniform_buf(PLANAR_PROBE_BUF_SLOT, "ProbePlanarData", "probe_planar_buf[PLANAR_PROBES_MAX]") + .sampler(PLANAR_PROBE_RADIANCE_TEX_SLOT, ImageType::FLOAT_2D_ARRAY, "planar_radiance_tx") + .sampler(PLANAR_PROBE_DEPTH_TEX_SLOT, ImageType::DEPTH_2D_ARRAY, "planar_depth_tx"); + /** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_tracing_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_tracing_info.hh index d4b49aaee0d..652735e2218 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_tracing_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_tracing_info.hh @@ -73,6 +73,23 @@ GPU_SHADER_CREATE_INFO(eevee_ray_trace_fallback) .storage_buf(5, Qualifier::READ, "uint", "tiles_coord_buf[]") .compute_source("eevee_ray_trace_fallback_comp.glsl"); +GPU_SHADER_CREATE_INFO(eevee_ray_trace_planar) + .do_static_compilation(true) + .local_group_size(RAYTRACE_GROUP_SIZE, RAYTRACE_GROUP_SIZE) + .define("PLANAR_PROBES") + .additional_info("eevee_shared", + "eevee_global_ubo", + "eevee_sampling_data", + "draw_view", + "eevee_lightprobe_data", + "eevee_lightprobe_planar_data") + .image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "ray_data_img") + .image(1, RAYTRACE_RAYTIME_FORMAT, Qualifier::WRITE, ImageType::FLOAT_2D, "ray_time_img") + .image(2, RAYTRACE_RADIANCE_FORMAT, Qualifier::WRITE, ImageType::FLOAT_2D, "ray_radiance_img") + .sampler(2, ImageType::DEPTH_2D, "depth_tx") + .storage_buf(5, Qualifier::READ, "uint", "tiles_coord_buf[]") + .compute_source("eevee_ray_trace_planar_comp.glsl"); + GPU_SHADER_CREATE_INFO(eevee_ray_trace_screen) .local_group_size(RAYTRACE_GROUP_SIZE, RAYTRACE_GROUP_SIZE) .additional_info("eevee_shared", diff --git a/source/blender/draw/intern/draw_view.cc b/source/blender/draw/intern/draw_view.cc index ad8bdeefbf6..1ade1063f5d 100644 --- a/source/blender/draw/intern/draw_view.cc +++ b/source/blender/draw/intern/draw_view.cc @@ -84,6 +84,10 @@ void View::frustum_boundbox_calc(int view_id) for (float4 &corner : corners) { mul_m4_v3(data_[view_id].viewinv.ptr(), corner); corner.w = 1.0; + /* Special case for planar reflection. */ + if (is_inverted_) { + corner.z = -corner.z; + } } } @@ -101,6 +105,11 @@ void View::frustum_culling_planes_calc(int view_id) /* Normalize. */ for (float4 &plane : culling_[view_id].frustum_planes.planes) { plane.w /= normalize_v3(plane); + + /* Special case for planar reflection. */ + if (is_inverted_) { + plane.z = -plane.z; + } } } diff --git a/source/blender/draw/intern/draw_view.hh b/source/blender/draw/intern/draw_view.hh index a7f4def4092..7e7223e2f15 100644 --- a/source/blender/draw/intern/draw_view.hh +++ b/source/blender/draw/intern/draw_view.hh @@ -120,6 +120,12 @@ class View { return -(data_[view_id].winmat[3][2] + 1.0f) / data_[view_id].winmat[2][2]; } + const float3 &location(int view_id = 0) const + { + BLI_assert(view_id < view_len_); + return data_[view_id].viewinv.location(); + } + const float4x4 &viewmat(int view_id = 0) const { BLI_assert(view_id < view_len_);