diff --git a/scripts/startup/bl_ui/properties_data_lightprobe.py b/scripts/startup/bl_ui/properties_data_lightprobe.py index 26c393dd021..73d75a0d37a 100644 --- a/scripts/startup/bl_ui/properties_data_lightprobe.py +++ b/scripts/startup/bl_ui/properties_data_lightprobe.py @@ -114,6 +114,9 @@ class DATA_PT_lightprobe_eevee_next(DataButtonsPanel, Panel): elif probe.type == 'CUBEMAP': col = layout.column() col.prop(probe, "resolution") + sub = layout.column(align=True) + sub.prop(probe, "clip_start", text="Clipping Start") + sub.prop(probe, "clip_end", text="End") elif probe.type == 'PLANAR': # Currently unsupported diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 44450299725..a3a64026db3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -273,6 +273,17 @@ void Instance::render_sync() // DRW_hair_update(); } +bool Instance::do_probe_sync() const +{ + if (materials.queued_shaders_count > 0) { + return false; + } + if (!reflection_probes.update_probes_this_sample_) { + return false; + } + return true; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -297,7 +308,9 @@ void Instance::render_sample() sampling.step(); - capture_view.render(); + capture_view.render_world(); + capture_view.render_probes(); + main_view.render(); motion_blur.step(); @@ -379,6 +392,12 @@ void Instance::render_read_result(RenderLayer *render_layer, const char *view_na void Instance::render_frame(RenderLayer *render_layer, const char *view_name) { + /* TODO(jbakker): should we check on the subtype as well? Now it also populates even when there + * are other light probes in the scene. */ + if (DEG_id_type_any_exists(this->depsgraph, ID_LP)) { + reflection_probes.update_probes_next_sample_ = true; + } + while (!sampling.finished()) { this->render_sample(); @@ -523,7 +542,7 @@ void Instance::light_bake_irradiance( render_sync(); manager->end_sync(); - capture_view.render(); + capture_view.render_world(); irradiance_cache.bake.surfels_create(probe); irradiance_cache.bake.surfels_lights_eval(); diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index c2b1746b3d5..d81fb325687 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -147,6 +147,11 @@ class Instance { void object_sync(Object *ob); void end_sync(); + /** + * Return true when probe pipeline is used during this sample. + */ + bool do_probe_sync() const; + /* Render. */ void render_sync(); diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index 5a9c3090652..e925208ece1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -158,7 +158,8 @@ void MaterialModule::begin_sync() MaterialPass MaterialModule::material_pass_get(Object *ob, ::Material *blender_mat, eMaterialPipeline pipeline_type, - eMaterialGeometry geometry_type) + eMaterialGeometry geometry_type, + bool probe_capture) { bNodeTree *ntree = (blender_mat->use_nodes && blender_mat->nodetree != nullptr) ? blender_mat->nodetree : @@ -205,11 +206,13 @@ MaterialPass MaterialModule::material_pass_get(Object *ob, matpass.sub_pass = nullptr; } else { - ShaderKey shader_key(matpass.gpumat, geometry_type, pipeline_type, blender_mat->blend_flag); + ShaderKey shader_key( + matpass.gpumat, geometry_type, pipeline_type, blender_mat->blend_flag, probe_capture); PassMain::Sub *shader_sub = shader_map_.lookup_or_add_cb(shader_key, [&]() { /* First time encountering this shader. Create a sub that will contain materials using it. */ - return inst_.pipelines.material_add(ob, blender_mat, matpass.gpumat, pipeline_type); + return inst_.pipelines.material_add( + ob, blender_mat, matpass.gpumat, pipeline_type, probe_capture); }); if (shader_sub != nullptr) { @@ -248,12 +251,23 @@ Material &MaterialModule::material_sync(Object *ob, * to avoid this shader compilation in another context. */ mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type); mat.capture = material_pass_get(ob, blender_mat, MAT_PIPE_CAPTURE, geometry_type); + mat.probe_prepass = MaterialPass(); + mat.probe_shading = MaterialPass(); } else { /* Order is important for transparent. */ mat.prepass = material_pass_get(ob, blender_mat, prepass_pipe, geometry_type); mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type); mat.capture = MaterialPass(); + mat.probe_prepass = MaterialPass(); + mat.probe_shading = MaterialPass(); + + if (inst_.do_probe_sync()) { + mat.probe_prepass = material_pass_get( + ob, blender_mat, MAT_PIPE_DEFERRED_PREPASS, geometry_type, true); + mat.probe_shading = material_pass_get( + ob, blender_mat, MAT_PIPE_DEFERRED, geometry_type, true); + } } if (blender_mat->blend_shadow == MA_BS_NONE) { diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh index e458376cee3..0d779072974 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.hh +++ b/source/blender/draw/engines/eevee_next/eevee_material.hh @@ -149,12 +149,14 @@ struct ShaderKey { ShaderKey(GPUMaterial *gpumat, eMaterialGeometry geometry, eMaterialPipeline pipeline, - char blend_flags) + char blend_flags, + bool probe_capture) { shader = GPU_material_get_shader(gpumat); options = blend_flags; options = (options << 6u) | shader_uuid_from_material_type(pipeline, geometry); options = (options << 16u) | shader_closure_bits_from_flag(gpumat); + options = (options << 1u) | probe_capture; } uint64_t hash() const @@ -214,7 +216,7 @@ struct MaterialPass { struct Material { bool is_alpha_blend_transparent; - MaterialPass shadow, shading, prepass, capture; + MaterialPass shadow, shading, prepass, capture, probe_prepass, probe_shading; }; struct MaterialArray { @@ -268,7 +270,8 @@ class MaterialModule { MaterialPass material_pass_get(Object *ob, ::Material *blender_mat, eMaterialPipeline pipeline_type, - eMaterialGeometry geometry_type); + eMaterialGeometry geometry_type, + bool probe_capture = false); }; /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 8e117a35c88..03724e32a40 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -63,7 +63,8 @@ void BackgroundPipeline::render(View &view) void WorldPipeline::sync(GPUMaterial *gpumat) { const int2 extent(1); - constexpr eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_WRITE; + constexpr eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_WRITE | + GPU_TEXTURE_USAGE_SHADER_READ; dummy_cryptomatte_tx_.ensure_2d(GPU_RGBA32F, extent, usage); dummy_renderpass_tx_.ensure_2d(GPU_RGBA16F, extent, usage); dummy_aov_color_tx_.ensure_2d_array(GPU_RGBA16F, extent, 1, usage); @@ -557,6 +558,201 @@ void DeferredPipeline::render(View &view, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Deferred Probe Layer + * \{ */ + +void DeferredProbeLayer::begin_sync() +{ + { + prepass_ps_.init(); + { + /* Common resources. */ + + /* Textures. */ + prepass_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + /* Uniform Buffer. */ + prepass_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get()); + + inst_.velocity.bind_resources(&prepass_ps_); + inst_.sampling.bind_resources(&prepass_ps_); + } + + DRWState state_depth_only = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + + prepass_double_sided_ps_ = &prepass_ps_.sub("DoubleSided"); + prepass_double_sided_ps_->state_set(state_depth_only); + + prepass_single_sided_ps_ = &prepass_ps_.sub("SingleSided"); + prepass_single_sided_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK); + } + { + gbuffer_ps_.init(); + gbuffer_ps_.clear_stencil(0x00u); + gbuffer_ps_.state_stencil(0xFFu, 0xFFu, 0xFFu); + + { + /* Common resources. */ + + /* G-buffer. */ + gbuffer_ps_.bind_image(GBUF_CLOSURE_SLOT, &inst_.gbuffer.closure_tx); + gbuffer_ps_.bind_image(GBUF_COLOR_SLOT, &inst_.gbuffer.color_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); + /* Cryptomatte. */ + gbuffer_ps_.bind_image(RBUFS_CRYPTOMATTE_SLOT, &inst_.render_buffers.cryptomatte_tx); + /* Storage Buffer. */ + /* Textures. */ + gbuffer_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + /* Uniform Buffer. */ + gbuffer_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get()); + gbuffer_ps_.bind_ubo(RBUFS_BUF_SLOT, &inst_.render_buffers.data); + + inst_.sampling.bind_resources(&gbuffer_ps_); + inst_.hiz_buffer.bind_resources(&gbuffer_ps_); + inst_.ambient_occlusion.bind_resources(&gbuffer_ps_); + inst_.cryptomatte.bind_resources(&gbuffer_ps_); + } + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM | DRW_STATE_DEPTH_EQUAL | + DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS; + + gbuffer_double_sided_ps_ = &gbuffer_ps_.sub("DoubleSided"); + gbuffer_double_sided_ps_->state_set(state); + + gbuffer_single_sided_ps_ = &gbuffer_ps_.sub("SingleSided"); + gbuffer_single_sided_ps_->state_set(state | DRW_STATE_CULL_BACK); + } + + /* Light eval resources.*/ + { + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE; + dummy_light_tx_.ensure_2d(GPU_RGBA16F, int2(1), usage); + } +} + +void DeferredProbeLayer::end_sync() +{ + if (closure_bits_ & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION)) { + const bool is_last_eval_pass = !(closure_bits_ & CLOSURE_SSS); + + eval_light_ps_.init(); + /* Use stencil test to reject pixel not written by this layer. */ + eval_light_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | + DRW_STATE_BLEND_CUSTOM); + eval_light_ps_.state_stencil(0x00u, 0x00u, (CLOSURE_DIFFUSE | CLOSURE_REFLECTION)); + eval_light_ps_.shader_set(inst_.shaders.static_shader_get(DEFERRED_LIGHT_DIFFUSE_ONLY)); + eval_light_ps_.bind_image("out_diffuse_light_img", dummy_light_tx_); + eval_light_ps_.bind_image("out_specular_light_img", dummy_light_tx_); + eval_light_ps_.bind_texture("gbuffer_closure_tx", &inst_.gbuffer.closure_tx); + eval_light_ps_.bind_texture("gbuffer_color_tx", &inst_.gbuffer.color_tx); + eval_light_ps_.push_constant("is_last_eval_pass", is_last_eval_pass); + eval_light_ps_.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx); + eval_light_ps_.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx); + eval_light_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + eval_light_ps_.bind_texture(SSS_TRANSMITTANCE_TEX_SLOT, + inst_.subsurface.transmittance_tx_get()); + eval_light_ps_.bind_ubo(RBUFS_BUF_SLOT, &inst_.render_buffers.data); + + inst_.lights.bind_resources(&eval_light_ps_); + inst_.shadows.bind_resources(&eval_light_ps_); + inst_.sampling.bind_resources(&eval_light_ps_); + inst_.hiz_buffer.bind_resources(&eval_light_ps_); + inst_.ambient_occlusion.bind_resources(&eval_light_ps_); + inst_.reflection_probes.bind_resources(&eval_light_ps_); + inst_.irradiance_cache.bind_resources(&eval_light_ps_); + + eval_light_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); + eval_light_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); + } +} + +PassMain::Sub *DeferredProbeLayer::prepass_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? + prepass_single_sided_ps_ : + prepass_double_sided_ps_; + + return &pass->sub(GPU_material_get_name(gpumat)); +} + +PassMain::Sub *DeferredProbeLayer::material_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + eClosureBits closure_bits = shader_closure_bits_from_flag(gpumat); + closure_bits_ |= closure_bits; + + PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? + gbuffer_single_sided_ps_ : + gbuffer_double_sided_ps_; + pass = &pass->sub(GPU_material_get_name(gpumat)); + pass->state_stencil(closure_bits, 0xFFu, 0xFFu); + return pass; +} + +void DeferredProbeLayer::render(View &view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, + int2 extent) +{ + GPU_framebuffer_bind(prepass_fb); + inst_.manager->submit(prepass_ps_, view); + + inst_.hiz_buffer.set_dirty(); + inst_.lights.set_view(view, extent); + inst_.shadows.set_view(view); + inst_.irradiance_cache.set_view(view); + + inst_.gbuffer.acquire(extent, closure_bits_); + + GPU_framebuffer_bind(combined_fb); + inst_.manager->submit(gbuffer_ps_, view); + + inst_.manager->submit(eval_light_ps_, view); + + inst_.gbuffer.release(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Deferred Probe Pipeline + * + * Closure data are written to intermediate buffer allowing screen space processing. + * \{ */ + +void DeferredProbePipeline::begin_sync() +{ + opaque_layer_.begin_sync(); +} + +void DeferredProbePipeline::end_sync() +{ + opaque_layer_.end_sync(); +} + +PassMain::Sub *DeferredProbePipeline::prepass_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + return opaque_layer_.prepass_add(blender_mat, gpumat); +} + +PassMain::Sub *DeferredProbePipeline::material_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + return opaque_layer_.material_add(blender_mat, gpumat); +} + +void DeferredProbePipeline::render(View &view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, + int2 extent) +{ + GPU_debug_group_begin("Probe.Render"); + opaque_layer_.render(view, prepass_fb, combined_fb, extent); + GPU_debug_group_end(); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Capture Pipeline * diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index 69fa4014402..467dd444d53 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -210,6 +210,58 @@ class DeferredPipeline { /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Deferred Probe Capture. + * \{ */ +class DeferredProbeLayer { + private: + Instance &inst_; + + PassMain prepass_ps_ = {"Prepass"}; + PassMain::Sub *prepass_single_sided_ps_ = nullptr; + PassMain::Sub *prepass_double_sided_ps_ = nullptr; + + PassMain gbuffer_ps_ = {"Shading"}; + PassMain::Sub *gbuffer_single_sided_ps_ = nullptr; + PassMain::Sub *gbuffer_double_sided_ps_ = nullptr; + + PassSimple eval_light_ps_ = {"EvalLights"}; + + /* Closures bits from the materials in this pass. */ + eClosureBits closure_bits_; + + Texture dummy_light_tx_ = {"dummy_light_accum_tx"}; + + public: + DeferredProbeLayer(Instance &inst) : inst_(inst){}; + + void begin_sync(); + void end_sync(); + + PassMain::Sub *prepass_add(::Material *blender_mat, GPUMaterial *gpumat); + PassMain::Sub *material_add(::Material *blender_mat, GPUMaterial *gpumat); + + void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent); +}; + +class DeferredProbePipeline { + private: + DeferredProbeLayer opaque_layer_; + + public: + DeferredProbePipeline(Instance &inst) : opaque_layer_(inst){}; + + void begin_sync(); + void end_sync(); + + PassMain::Sub *prepass_add(::Material *material, GPUMaterial *gpumat); + PassMain::Sub *material_add(::Material *material, GPUMaterial *gpumat); + + void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent); +}; + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Capture Pipeline * @@ -317,6 +369,7 @@ class PipelineModule { public: BackgroundPipeline background; WorldPipeline world; + DeferredProbePipeline probe; DeferredPipeline deferred; ForwardPipeline forward; ShadowPipeline shadow; @@ -328,6 +381,7 @@ class PipelineModule { PipelineModule(Instance &inst) : background(inst), world(inst), + probe(inst), deferred(inst), forward(inst), shadow(inst), @@ -335,6 +389,7 @@ class PipelineModule { void begin_sync() { + probe.begin_sync(); deferred.begin_sync(); forward.sync(); shadow.sync(); @@ -343,14 +398,27 @@ class PipelineModule { void end_sync() { + probe.end_sync(); deferred.end_sync(); } PassMain::Sub *material_add(Object *ob, ::Material *blender_mat, GPUMaterial *gpumat, - eMaterialPipeline pipeline_type) + eMaterialPipeline pipeline_type, + bool probe_capture) { + if (probe_capture) { + switch (pipeline_type) { + case MAT_PIPE_DEFERRED_PREPASS: + return probe.prepass_add(blender_mat, gpumat); + case MAT_PIPE_DEFERRED: + return probe.material_add(blender_mat, gpumat); + default: + break; + } + } + switch (pipeline_type) { case MAT_PIPE_DEFERRED_PREPASS: return deferred.prepass_add(blender_mat, gpumat, false); 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 d638ff5a8bf..ea3a9df451d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc @@ -5,13 +5,6 @@ #include "eevee_reflection_probes.hh" #include "eevee_instance.hh" -/* Generate dummy light probe texture. - * - * Baking of Light probes aren't implemented yet. For testing purposes this can be enabled to - * generate a dummy texture. - */ -#define GENERATE_DUMMY_LIGHT_PROBE_TEXTURE false - namespace blender::eevee { void ReflectionProbeModule::init() @@ -36,29 +29,29 @@ void ReflectionProbeModule::init() world_probe.do_update_data = true; world_probe.do_render = true; world_probe.index = 0; + world_probe.clipping_distances = float2(1.0f, 10.0f); probes_.add(world_object_key_, world_probe); probes_tx_.ensure_2d_array(GPU_RGBA16F, int2(max_resolution_), init_num_probes_, - GPU_TEXTURE_USAGE_SHADER_WRITE, + GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_SHADER_READ, nullptr, - REFLECTION_PROBE_MIPMAP_LEVELS); + 9999); GPU_texture_mipmap_mode(probes_tx_, true, true); - /* Cube-map is half of the resolution of the octahedral map. */ - cubemap_tx_.ensure_cube( - GPU_RGBA16F, max_resolution_ / 2, GPU_TEXTURE_USAGE_ATTACHMENT, nullptr, 9999); - GPU_texture_mipmap_mode(cubemap_tx_, true, true); + recalc_lod_factors(); + data_buf_.push_update(); } { PassSimple &pass = remap_ps_; pass.init(); pass.shader_set(instance_.shaders.static_shader_get(REFLECTION_PROBE_REMAP)); - pass.bind_texture("cubemap_tx", cubemap_tx_); - pass.bind_image("octahedral_img", probes_tx_); + pass.bind_texture("cubemap_tx", &cubemap_tx_); + pass.bind_image("octahedral_img", &probes_tx_); pass.bind_ssbo(REFLECTION_PROBE_BUF_SLOT, data_buf_); + pass.push_constant("reflection_probe_index", &reflection_probe_index_); pass.dispatch(&dispatch_probe_pack_); } } @@ -69,39 +62,24 @@ void ReflectionProbeModule::begin_sync() reflection_probe.is_probe_used = false; } } + + update_probes_this_sample_ = false; + if (update_probes_next_sample_) { + update_probes_this_sample_ = true; + instance_.sampling.reset(); + } } int ReflectionProbeModule::needed_layers_get() const { - const int max_probe_data_index = reflection_probe_data_index_max(); int max_layer = 0; - for (const ReflectionProbeData &data : - Span(data_buf_.data(), max_probe_data_index + 1)) - { - max_layer = max_ii(max_layer, data.layer); + for (const ReflectionProbe &probe : probes_.values()) { + const ReflectionProbeData &probe_data = data_buf_[probe.index]; + max_layer = max_ii(max_layer, probe_data.layer); } return max_layer + 1; } -void ReflectionProbeModule::sync(ReflectionProbe &probe) -{ - switch (probe.type) { - case ReflectionProbe::Type::World: { - break; - } - case ReflectionProbe::Type::Probe: { - if (probe.do_render) { - upload_dummy_texture(probe); - probe.do_render = false; - } - break; - } - case ReflectionProbe::Type::Unused: { - break; - } - } -} - static int layer_subdivision_for(const int max_resolution, const eLightProbeResolution probe_resolution) { @@ -119,7 +97,6 @@ void ReflectionProbeModule::sync_world(::World *world, WorldHandle & /*ob_handle void ReflectionProbeModule::sync_object(Object *ob, ObjectHandle &ob_handle) { -#if GENERATE_DUMMY_LIGHT_PROBE_TEXTURE const ::LightProbe *light_probe = (::LightProbe *)ob->data; if (light_probe->type != LIGHTPROBE_TYPE_CUBE) { return; @@ -128,15 +105,27 @@ void ReflectionProbeModule::sync_object(Object *ob, ObjectHandle &ob_handle) int subdivision = layer_subdivision_for( max_resolution_, static_cast(light_probe->resolution)); ReflectionProbe &probe = find_or_insert(ob_handle, subdivision); - probe.do_update_data |= is_dirty; + probe.do_render |= is_dirty; probe.is_probe_used = true; + /* Only update data when rerendering the probes to reduce flickering. */ + if (!instance_.do_probe_sync()) { + update_probes_next_sample_ = true; + return; + } + + probe.do_update_data |= is_dirty; + probe.clipping_distances = float2(light_probe->clipsta, light_probe->clipend); + ReflectionProbeData &probe_data = data_buf_[probe.index]; + if (probe_data.layer_subdivision != subdivision) { + ReflectionProbeData new_probe_data = find_empty_reflection_probe_data(subdivision); + probe_data.layer = new_probe_data.layer; + probe_data.layer_subdivision = new_probe_data.layer_subdivision; + probe_data.area_index = new_probe_data.area_index; + } + probe_data.pos = float3(float4x4(ob->object_to_world) * float4(0.0, 0.0, 0.0, 1.0)); - probe_data.layer_subdivision = subdivision; -#else - UNUSED_VARS(ob, ob_handle); -#endif } ReflectionProbe &ReflectionProbeModule::find_or_insert(ObjectHandle &ob_handle, @@ -290,62 +279,32 @@ void ReflectionProbeModule::end_sync() { remove_unused_probes(); + const bool probe_sync_done = instance_.do_probe_sync(); + if (!probe_sync_done) { + return; + } + int number_layers_needed = needed_layers_get(); int current_layers = probes_tx_.depth(); bool resize_layers = current_layers < number_layers_needed; + if (resize_layers) { - /* TODO: Create new texture and copy previous texture so we don't need to rerender all the - * probes.*/ probes_tx_.ensure_2d_array(GPU_RGBA16F, int2(max_resolution_), number_layers_needed, - GPU_TEXTURE_USAGE_SHADER_WRITE, + GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_SHADER_READ, nullptr, - REFLECTION_PROBE_MIPMAP_LEVELS); + 9999); GPU_texture_mipmap_mode(probes_tx_, true, true); + + for (ReflectionProbe &probe : probes_.values()) { + probe.do_update_data = true; + probe.do_render = true; + } } recalc_lod_factors(); data_buf_.push_update(); - - /* Regenerate mipmaps when a probe texture is updated. It can be postponed when the world probe - * is also updated. In this case it would happen as part of the WorldProbePipeline. */ - bool regenerate_mipmaps = false; - bool regenerate_mipmaps_postponed = false; - - for (ReflectionProbe &probe : probes_.values()) { - if (resize_layers) { - probe.do_update_data = true; - probe.do_render = true; - } - - if (!probe.needs_update()) { - continue; - } - sync(probe); - - switch (probe.type) { - case ReflectionProbe::Type::World: - regenerate_mipmaps_postponed = true; - break; - - case ReflectionProbe::Type::Probe: - regenerate_mipmaps = probe.do_render; - break; - - case ReflectionProbe::Type::Unused: - BLI_assert_unreachable(); - break; - } - probe.do_update_data = false; - } - - if (regenerate_mipmaps) { - GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); - if (!regenerate_mipmaps_postponed) { - GPU_texture_update_mipmap_chain(probes_tx_); - } - } } void ReflectionProbeModule::remove_unused_probes() @@ -470,57 +429,73 @@ std::ostream &operator<<(std::ostream &os, const ReflectionProbe &probe) return os; } -void ReflectionProbeModule::upload_dummy_texture(const ReflectionProbe &probe) -{ - const ReflectionProbeData &probe_data = data_buf_[probe.index]; - const int resolution = max_resolution_ >> probe_data.layer_subdivision; - float4 *data = static_cast( - MEM_mallocN(sizeof(float4) * resolution * resolution, __func__)); - - /* Generate dummy checker pattern. */ - int index = 0; - const int BLOCK_SIZE = max_ii(1024 >> probe_data.layer_subdivision, 1); - for (int y : IndexRange(resolution)) { - for (int x : IndexRange(resolution)) { - int tx = (x / BLOCK_SIZE) & 1; - int ty = (y / BLOCK_SIZE) & 1; - bool solid = (tx + ty) & 1; - if (solid) { - data[index] = float4((probe.index & 1) == 0 ? 0.0f : 1.0f, - (probe.index & 2) == 0 ? 0.0f : 1.0f, - (probe.index & 4) == 0 ? 0.0f : 1.0f, - 1.0f); - } - else { - data[index] = float4(0.0f); - } - - index++; - } - } - - /* Upload the checker pattern. */ - int probes_per_dimension = 1 << probe_data.layer_subdivision; - int2 probe_area_pos(probe_data.area_index % probes_per_dimension, - probe_data.area_index / probes_per_dimension); - int2 pos = probe_area_pos * int2(max_resolution_ / probes_per_dimension); - GPU_texture_update_sub( - probes_tx_, GPU_DATA_FLOAT, data, UNPACK2(pos), probe_data.layer, resolution, resolution, 1); - - MEM_freeN(data); -} - /** \} */ -void ReflectionProbeModule::remap_to_octahedral_projection() +std::optional ReflectionProbeModule::update_info_pop( + const ReflectionProbe::Type probe_type) { - const ReflectionProbe &world_probe = probes_.lookup(world_object_key_); - const ReflectionProbeData &probe_data = data_buf_[world_probe.index]; + const bool do_probe_sync = instance_.do_probe_sync(); + const int max_shift = int(log2(max_resolution_)); + for (const Map::Item &item : probes_.items()) { + if (!item.value.do_render) { + continue; + } + if (probe_type == ReflectionProbe::Type::World && item.value.type != probe_type) { + return std::nullopt; + } + if (probe_type == ReflectionProbe::Type::Probe && item.value.type != probe_type) { + continue; + } + /* Do not update this probe during this sample. */ + if (item.value.type == ReflectionProbe::Type::Probe && !do_probe_sync) { + continue; + } + probes_.lookup(item.key).do_render = false; + + ReflectionProbeData &probe_data = data_buf_[item.value.index]; + ReflectionProbeUpdateInfo info = {}; + info.probe_type = item.value.type; + info.object_key = item.key; + info.resolution = 1 << (max_shift - probe_data.layer_subdivision - 1); + info.clipping_distances = item.value.clipping_distances; + info.probe_pos = probe_data.pos; + + if (cubemap_tx_.ensure_cube(GPU_RGBA16F, + info.resolution, + GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ)) + { + GPU_texture_mipmap_mode(cubemap_tx_, false, true); + } + + return info; + } + + /* Check reset probe updating as we completed rendering all Probes. */ + if (probe_type == ReflectionProbe::Type::Probe && update_probes_this_sample_ && + update_probes_next_sample_) + { + update_probes_next_sample_ = false; + } + + return std::nullopt; +} + +void ReflectionProbeModule::remap_to_octahedral_projection(uint64_t object_key) +{ + const ReflectionProbe &probe = probes_.lookup(object_key); + const ReflectionProbeData &probe_data = data_buf_[probe.index]; + + /* Update shader parameters that change per dispatch. */ + reflection_probe_index_ = probe.index; dispatch_probe_pack_ = int3(int2(ceil_division(max_resolution_ >> probe_data.layer_subdivision, REFLECTION_PROBE_GROUP_SIZE)), 1); + instance_.manager->submit(remap_ps_); - /* TODO: Performance - Should only update the area that has changed. */ +} + +void ReflectionProbeModule::update_probes_texture_mipmaps() +{ GPU_texture_update_mipmap_chain(probes_tx_); } diff --git a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh index 74790c10bcb..66624d99e43 100644 --- a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh @@ -22,9 +22,10 @@ class Instance; struct ObjectHandle; struct WorldHandle; class CaptureView; +struct ReflectionProbeUpdateInfo; /* -------------------------------------------------------------------- */ -/** \name Reflection Probes +/** \name Reflection Probe * \{ */ struct ReflectionProbe { @@ -32,7 +33,8 @@ struct ReflectionProbe { Type type = Type::Unused; - /* Probe data needs to be updated. */ + /* Probe data needs to be updated. + * TODO: Remove this flag? */ bool do_update_data = false; /* Should the area in the probes_tx_ be updated? */ bool do_render = false; @@ -50,6 +52,11 @@ struct ReflectionProbe { */ int index = -1; + /** + * Far and near clipping distances for rendering + */ + float2 clipping_distances; + /** * Check if the probe needs to be updated during this sample. */ @@ -67,6 +74,12 @@ struct ReflectionProbe { } }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Reflection Probe Module + * \{ */ + class ReflectionProbeModule { private: /** The max number of probes to initially allocate. */ @@ -85,8 +98,6 @@ class ReflectionProbeModule { ReflectionProbeDataBuf data_buf_; Map probes_; - /** Texture containing a cubemap used as input for updating #probes_tx_. */ - Texture cubemap_tx_ = {"Probe.Cubemap"}; /** Probes texture stored in octahedral mapping. */ Texture probes_tx_ = {"Probes"}; @@ -94,6 +105,17 @@ class ReflectionProbeModule { int3 dispatch_probe_pack_ = int3(0); + /** + * Texture containing a cubemap where the probe should be rendering to. + * + * NOTE: TextureFromPool doesn't support cubemaps. + */ + Texture cubemap_tx_ = {"Probe.Cubemap"}; + int reflection_probe_index_ = 0; + + bool update_probes_next_sample_ = false; + bool update_probes_this_sample_ = false; + public: ReflectionProbeModule(Instance &instance) : instance_(instance) {} @@ -115,7 +137,6 @@ class ReflectionProbeModule { void debug_print() const; private: - void sync(ReflectionProbe &cubemap); ReflectionProbe &find_or_insert(ObjectHandle &ob_handle, int subdivision_level); /** Get the number of layers that is needed to store probes. */ @@ -140,16 +161,41 @@ class ReflectionProbeModule { */ ReflectionProbeData find_empty_reflection_probe_data(int subdivision_level) const; - void upload_dummy_texture(const ReflectionProbe &probe); - - void remap_to_octahedral_projection(); + /** + * Pop the next reflection probe that requires to be updated. + */ + std::optional update_info_pop(ReflectionProbe::Type probe_type); + void remap_to_octahedral_projection(uint64_t object_key); + void update_probes_texture_mipmaps(); /* Capture View requires access to the cube-maps texture for frame-buffer configuration. */ friend class CaptureView; + /* Instance requires access to #update_probes_this_sample_ */ + friend class Instance; }; std::ostream &operator<<(std::ostream &os, const ReflectionProbeModule &module); std::ostream &operator<<(std::ostream &os, const ReflectionProbeData &probe_data); std::ostream &operator<<(std::ostream &os, const ReflectionProbe &probe); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Reflection Probe Update Info + * \{ */ + +struct ReflectionProbeUpdateInfo { + float3 probe_pos; + ReflectionProbe::Type probe_type; + /** + * Resolution of the cubemap to be rendered. + */ + int resolution; + + float2 clipping_distances; + uint64_t object_key; +}; + +/** \} */ + } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 997e4ed9aae..cd0160de836 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -90,6 +90,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_film_cryptomatte_post"; case DEFERRED_LIGHT: return "eevee_deferred_light"; + case DEFERRED_LIGHT_DIFFUSE_ONLY: + return "eevee_deferred_light_diffuse"; case HIZ_DEBUG: return "eevee_hiz_debug"; case HIZ_UPDATE: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 820904f5b4b..840192ba07f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -33,6 +33,7 @@ enum eShaderType { FILM_CRYPTOMATTE_POST, DEFERRED_LIGHT, + DEFERRED_LIGHT_DIFFUSE_ONLY, DEBUG_SURFELS, diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc index 24a96b5767f..c510891d5ba 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc @@ -122,6 +122,7 @@ void SyncModule::sync_mesh(Object *ob, bool is_shadow_caster = false; bool is_alpha_blend = false; + bool do_probe_sync = inst_.do_probe_sync(); for (auto i : material_array.gpu_materials.index_range()) { GPUBatch *geom = mat_geom[i]; if (geom == nullptr) { @@ -132,6 +133,10 @@ void SyncModule::sync_mesh(Object *ob, geometry_call(material.prepass.sub_pass, geom, res_handle); geometry_call(material.shadow.sub_pass, geom, res_handle); geometry_call(material.capture.sub_pass, geom, res_handle); + if (do_probe_sync) { + geometry_call(material.probe_prepass.sub_pass, geom, res_handle); + geometry_call(material.probe_shading.sub_pass, geom, res_handle); + } is_shadow_caster = is_shadow_caster || material.shadow.sub_pass != nullptr; is_alpha_blend = is_alpha_blend || material.is_alpha_blend_transparent; diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index b4ac0646caf..25a265ff43a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -195,31 +195,89 @@ void ShadingView::update_view() /** \name Capture View * \{ */ -void CaptureView::render() +void CaptureView::render_world() { - if (!inst_.reflection_probes.do_world_update_get()) { + const std::optional update_info = + inst_.reflection_probes.update_info_pop(ReflectionProbe::Type::World); + if (!update_info.has_value()) { return; } - inst_.reflection_probes.do_world_update_set(false); + View view = {"Capture.View"}; GPU_debug_group_begin("World.Capture"); - View view = {"World.Capture.View"}; for (int face : IndexRange(6)) { + float4x4 view_m4 = cubeface_mat(face); + float4x4 win_m4 = math::projection::perspective(-update_info->clipping_distances.x, + update_info->clipping_distances.x, + -update_info->clipping_distances.x, + update_info->clipping_distances.x, + update_info->clipping_distances.x, + update_info->clipping_distances.y); + view.sync(view_m4, win_m4); + capture_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_CUBEFACE(inst_.reflection_probes.cubemap_tx_, face)); GPU_framebuffer_bind(capture_fb_); - - float4x4 view_m4 = cubeface_mat(face); - float4x4 win_m4 = math::projection::perspective(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 10.0f); - view.sync(view_m4, win_m4); inst_.pipelines.world.render(view); } - GPU_texture_update_mipmap_chain(inst_.reflection_probes.cubemap_tx_); - inst_.reflection_probes.remap_to_octahedral_projection(); + + inst_.reflection_probes.remap_to_octahedral_projection(update_info->object_key); + inst_.reflection_probes.update_probes_texture_mipmaps(); + GPU_debug_group_end(); } +void CaptureView::render_probes() +{ + Framebuffer prepass_fb; + View view = {"Capture.View"}; + bool do_update_mipmap_chain = false; + while (const std::optional update_info = + inst_.reflection_probes.update_info_pop(ReflectionProbe::Type::Probe)) + { + GPU_debug_group_begin("Probe.Capture"); + do_update_mipmap_chain = true; + + int2 extent = int2(update_info->resolution); + inst_.render_buffers.acquire(extent); + + inst_.render_buffers.vector_tx.clear(float4(0.0f)); + prepass_fb.ensure(GPU_ATTACHMENT_TEXTURE(inst_.render_buffers.depth_tx), + GPU_ATTACHMENT_TEXTURE(inst_.render_buffers.vector_tx)); + + for (int face : IndexRange(6)) { + float4x4 view_m4 = cubeface_mat(face); + view_m4 = math::translate(view_m4, -update_info->probe_pos); + float4x4 win_m4 = math::projection::perspective(-update_info->clipping_distances.x, + update_info->clipping_distances.x, + -update_info->clipping_distances.x, + update_info->clipping_distances.x, + update_info->clipping_distances.x, + update_info->clipping_distances.y); + view.sync(view_m4, win_m4); + + capture_fb_.ensure( + GPU_ATTACHMENT_TEXTURE(inst_.render_buffers.depth_tx), + GPU_ATTACHMENT_TEXTURE_CUBEFACE(inst_.reflection_probes.cubemap_tx_, face)); + + GPU_framebuffer_bind(capture_fb_); + GPU_framebuffer_clear_color_depth(capture_fb_, float4(0.0f, 0.0f, 0.0f, 1.0f), 1.0f); + inst_.pipelines.probe.render(view, prepass_fb, capture_fb_, extent); + } + + inst_.render_buffers.release(); + GPU_debug_group_end(); + inst_.reflection_probes.remap_to_octahedral_projection(update_info->object_key); + } + + if (do_update_mipmap_chain) { + /* TODO: only update the regions that have been updated. */ + /* TODO: Composite world into the probes. */ + inst_.reflection_probes.update_probes_texture_mipmaps(); + } +} + /** \} */ } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh index 52281e91e27..e58aa938beb 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.hh +++ b/source/blender/draw/engines/eevee_next/eevee_view.hh @@ -155,7 +155,8 @@ class CaptureView { public: CaptureView(Instance &inst) : inst_(inst) {} - void render(); + void render_world(); + void render_probes(); }; /** \} */ 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 81fd2c73efb..67702104b8c 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 @@ -55,7 +55,10 @@ void main() vec3 reflection_light = vec3(0.0); float shadow = 1.0; +#ifdef DO_REFLECTION_PROBES reflection_probes_eval(reflection_data, P, V, reflection_light); +#endif + lightprobe_eval(diffuse_data, reflection_data, P, Ng, V, diffuse_light, reflection_light); light_eval(diffuse_data, 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 ade2d3326e2..2a18691a6b5 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 @@ -5,7 +5,7 @@ void main() { - ReflectionProbeData probe_data = reflection_probe_buf[0]; + ReflectionProbeData probe_data = reflection_probe_buf[reflection_probe_index]; ivec3 texture_coord = ivec3(gl_GlobalInvocationID.xyz); ivec3 texture_size = imageSize(octahedral_img); @@ -25,12 +25,14 @@ void main() vec3 R = octahedral_uv_to_direction(octahedral_uv); vec4 col = textureLod(cubemap_tx, R, float(probe_data.layer_subdivision)); - // col.xy = octahedral_uv; int probes_per_dimension = 1 << probe_data.layer_subdivision; ivec2 area_coord = ivec2(probe_data.area_index % probes_per_dimension, probe_data.area_index / probes_per_dimension); ivec2 area_offset = area_coord * octahedral_size; - imageStore(octahedral_img, octahedral_coord + ivec3(area_offset, 0), col); + /* Convert transmittance to transparency. */ + col.a = 1.0 - col.a; + + imageStore(octahedral_img, octahedral_coord + ivec3(area_offset, probe_data.layer), col); } 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 660a5970f46..02500247454 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 @@ -30,7 +30,7 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_base) .image_out(2, Qualifier::WRITE, GPU_RGBA16F, "out_diffuse_light_img") .image_out(3, Qualifier::WRITE, GPU_RGBA16F, "out_specular_light_img"); -GPU_SHADER_CREATE_INFO(eevee_deferred_light) +GPU_SHADER_CREATE_INFO(eevee_deferred_light_base) .fragment_source("eevee_deferred_light_frag.glsl") .sampler(0, ImageType::FLOAT_2D_ARRAY, "gbuffer_closure_tx") .sampler(1, ImageType::FLOAT_2D_ARRAY, "gbuffer_color_tx") @@ -46,7 +46,15 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_light) "eevee_hiz_data", "eevee_render_pass_out", "draw_view", - "draw_fullscreen") + "draw_fullscreen"); + +GPU_SHADER_CREATE_INFO(eevee_deferred_light) + .additional_info("eevee_deferred_light_base") + .define("DO_REFLECTION_PROBES") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(eevee_deferred_light_diffuse) + .additional_info("eevee_deferred_light_base") .do_static_compilation(true); #undef image_array_out 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 1980b114053..a6699a2931a 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 @@ -19,6 +19,7 @@ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_data) /* Sample cubemap and remap into an octahedral texture. */ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_remap) .local_group_size(REFLECTION_PROBE_GROUP_SIZE, REFLECTION_PROBE_GROUP_SIZE) + .push_constant(Type::INT, "reflection_probe_index") .storage_buf(REFLECTION_PROBE_BUF_SLOT, Qualifier::READ, "ReflectionProbeData",