From aedd5f283725e38a5212cddcef3ab818b359b8be Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 28 Mar 2024 12:18:37 +0100 Subject: [PATCH] EEVEE-Next: Lookdev Background Blur This PR implements the background blurring for studiolight/lookdev HDRIs. The visual appearance matches EEVEE-Classic closely. **Technical details** - LOD0 is skipped as the regular background color can be used. The regular background color is blended towards LOD1. - Volume probe is mixed in to remove baked in artifacts in the higher LODs. Pull Request: https://projects.blender.org/blender/blender/pulls/119872 --- scripts/startup/bl_ui/space_view3d.py | 6 ++---- .../engines/eevee_next/eevee_lightprobe.hh | 1 + .../draw/engines/eevee_next/eevee_lookdev.hh | 5 +++++ .../draw/engines/eevee_next/eevee_pipeline.cc | 15 +++++++++++++- .../draw/engines/eevee_next/eevee_pipeline.hh | 2 +- .../draw/engines/eevee_next/eevee_world.cc | 3 ++- .../eevee_next/shaders/eevee_surf_lib.glsl | 3 +-- .../shaders/eevee_surf_world_frag.glsl | 20 +++++++++++++++++++ .../shaders/infos/eevee_material_info.hh | 5 +++++ 9 files changed, 51 insertions(+), 9 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 3d2b34a05ce..88dbb228156 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -6573,8 +6573,7 @@ class VIEW3D_PT_shading_lighting(Panel): col.prop(shading, "studiolight_intensity") col.prop(shading, "studiolight_background_alpha") - if engine != 'BLENDER_EEVEE_NEXT': - col.prop(shading, "studiolight_background_blur") + col.prop(shading, "studiolight_background_blur") col = split.column() # to align properly with above elif shading.type == 'RENDERED': @@ -6599,8 +6598,7 @@ class VIEW3D_PT_shading_lighting(Panel): col.prop(shading, "studiolight_intensity") col.prop(shading, "studiolight_background_alpha") engine = context.scene.render.engine - if engine != 'BLENDER_EEVEE_NEXT': - col.prop(shading, "studiolight_background_blur") + col.prop(shading, "studiolight_background_blur") col = split.column() # to align properly with above diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh index 3c1095e3db8..23ae871e2cd 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh @@ -202,6 +202,7 @@ class LightProbeModule { friend class VolumeProbeModule; friend class PlanarProbeModule; friend class SphereProbeModule; + friend class BackgroundPipeline; private: Instance &inst_; diff --git a/source/blender/draw/engines/eevee_next/eevee_lookdev.hh b/source/blender/draw/engines/eevee_next/eevee_lookdev.hh index 337e714f3b4..90a60b566ff 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lookdev.hh +++ b/source/blender/draw/engines/eevee_next/eevee_lookdev.hh @@ -72,6 +72,11 @@ class LookdevWorld { { return parameters_.background_opacity; } + + float background_blur_get() + { + return parameters_.blur; + } }; /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 95c66a4eb21..6a19fbe0f31 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -24,7 +24,9 @@ namespace blender::eevee { * Used to draw background. * \{ */ -void BackgroundPipeline::sync(GPUMaterial *gpumat, const float background_opacity) +void BackgroundPipeline::sync(GPUMaterial *gpumat, + const float background_opacity, + const float background_blur) { Manager &manager = *inst_.manager; RenderBuffers &rbufs = inst_.render_buffers; @@ -33,6 +35,9 @@ void BackgroundPipeline::sync(GPUMaterial *gpumat, const float background_opacit world_ps_.state_set(DRW_STATE_WRITE_COLOR); world_ps_.material_set(manager, gpumat); world_ps_.push_constant("world_opacity_fade", background_opacity); + world_ps_.push_constant("world_background_blur", square_f(background_blur)); + SphereProbeData &world_data = *static_cast(&inst_.light_probes.world_sphere_); + world_ps_.push_constant("world_coord_packed", reinterpret_cast(&world_data.atlas_coord)); world_ps_.bind_texture("utility_tx", inst_.pipelines.utility_tx); /* RenderPasses & AOVs. Cleared by background (even if bad practice). */ world_ps_.bind_image("rp_color_img", &rbufs.rp_color_tx); @@ -41,6 +46,9 @@ void BackgroundPipeline::sync(GPUMaterial *gpumat, const float background_opacit /* Required by validation layers. */ world_ps_.bind_resources(inst_.cryptomatte); world_ps_.bind_resources(inst_.uniform_data); + world_ps_.bind_resources(inst_.sampling); + world_ps_.bind_resources(inst_.sphere_probes); + world_ps_.bind_resources(inst_.volume_probes); world_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); /* To allow opaque pass rendering over it. */ world_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); @@ -74,6 +82,8 @@ void WorldPipeline::sync(GPUMaterial *gpumat) Manager &manager = *inst_.manager; pass.material_set(manager, gpumat); pass.push_constant("world_opacity_fade", 1.0f); + pass.push_constant("world_background_blur", 0.0f); + pass.push_constant("world_coord_packed", int4(0.0f)); pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); pass.bind_image("rp_normal_img", dummy_renderpass_tx_); pass.bind_image("rp_light_img", dummy_renderpass_tx_); @@ -89,6 +99,9 @@ void WorldPipeline::sync(GPUMaterial *gpumat) /* Required by validation layers. */ pass.bind_resources(inst_.cryptomatte); pass.bind_resources(inst_.uniform_data); + pass.bind_resources(inst_.sampling); + pass.bind_resources(inst_.sphere_probes); + pass.bind_resources(inst_.volume_probes); pass.draw_procedural(GPU_PRIM_TRIS, 1, 3); } diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index 279d7891f97..9293c349654 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -40,7 +40,7 @@ class BackgroundPipeline { public: BackgroundPipeline(Instance &inst) : inst_(inst){}; - void sync(GPUMaterial *gpumat, float background_opacity); + void sync(GPUMaterial *gpumat, float background_opacity, float background_blur); void render(View &view); }; diff --git a/source/blender/draw/engines/eevee_next/eevee_world.cc b/source/blender/draw/engines/eevee_next/eevee_world.cc index a263795c2b8..f4cbac9aaa1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.cc +++ b/source/blender/draw/engines/eevee_next/eevee_world.cc @@ -133,8 +133,9 @@ void World::sync() float opacity = inst_.use_studio_light() ? lookdev_world_.background_opacity_get() : inst_.film.background_opacity_get(); + float background_blur = inst_.use_studio_light() ? lookdev_world_.background_blur_get() : 0.0; - inst_.pipelines.background.sync(gpumat, opacity); + inst_.pipelines.background.sync(gpumat, opacity, background_blur); inst_.pipelines.world.sync(gpumat); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl index b5a71fd8ae2..16c68c949e4 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl @@ -63,8 +63,7 @@ void init_globals_curves() /* Random cosine normal distribution on the hair surface. */ float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).x; # ifdef EEVEE_SAMPLING_DATA - /* Needs to check for SAMPLING_DATA, - * otherwise Surfel and World (?!?!) shader validation fails. */ + /* Needs to check for SAMPLING_DATA, otherwise surfel shader validation fails. */ noise = fract(noise + sampling_rng_1D_get(SAMPLING_CURVES_U)); # endif cos_theta = noise * 2.0 - 1.0; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl index f63504fde6b..97182633d3e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl @@ -13,6 +13,9 @@ #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_volume_eval_lib.glsl) vec4 closure_to_rgba(Closure cl) { @@ -38,6 +41,23 @@ void main() out_background.rgb = colorspace_safe_color(g_emission) * (1.0 - g_holdout); out_background.a = saturate(average(g_transmittance)) * g_holdout; + if (g_data.ray_type == RAY_TYPE_CAMERA && world_background_blur != 0.0 && + world_opacity_fade != 0.0) + { + float base_lod = sphere_probe_roughness_to_lod(world_background_blur); + float lod = max(1.0, base_lod); + float mix_factor = min(1.0, base_lod); + SphereProbeUvArea world_atlas_coord = reinterpret_as_atlas_coord(world_coord_packed); + vec4 probe_color = reflection_probes_sample(-g_data.N, lod, world_atlas_coord); + out_background.rgb = mix(out_background.rgb, probe_color.rgb, mix_factor); + + SphericalHarmonicL1 volume_irradiance = lightprobe_irradiance_sample( + g_data.P, vec3(0.0), g_data.Ng); + vec3 radiance_sh = spherical_harmonics_evaluate_lambert(-g_data.N, volume_irradiance); + float radiance_mix_factor = sphere_probe_roughness_to_mix_fac(world_background_blur); + out_background.rgb = mix(out_background.rgb, radiance_sh, radiance_mix_factor); + } + /* World opacity. */ out_background = mix(vec4(0.0, 0.0, 0.0, 1.0), out_background, world_opacity_fade); diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index 00eb23e19f4..9a00c7d1d73 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -218,9 +218,14 @@ GPU_SHADER_CREATE_INFO(eevee_surf_depth) GPU_SHADER_CREATE_INFO(eevee_surf_world) .push_constant(Type::FLOAT, "world_opacity_fade") + .push_constant(Type::FLOAT, "world_background_blur") + .push_constant(Type::IVEC4, "world_coord_packed") .fragment_out(0, Type::VEC4, "out_background") .fragment_source("eevee_surf_world_frag.glsl") .additional_info("eevee_global_ubo", + "eevee_reflection_probe_data", + "eevee_volume_probe_data", + "eevee_sampling_data", /* Optionally added depending on the material. */ // "eevee_render_pass_out", // "eevee_cryptomatte_out",