diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index c1fb3c56154..8b22bd171b5 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -536,6 +536,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl + engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl engines/eevee_next/shaders/eevee_reflection_probe_update_irradiance_comp.glsl engines/eevee_next/shaders/eevee_renderpass_lib.glsl engines/eevee_next/shaders/eevee_sampling_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 0c21d1369ec..04d6179b3fc 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -32,6 +32,7 @@ /* Reflection Probes. */ #define REFLECTION_PROBES_MAX 256 #define REFLECTION_PROBE_GROUP_SIZE 16 +#define REFLECTION_PROBE_SELECT_GROUP_SIZE 64 /* Number of additional pixels on the border of an octahedral map to reserve for fixing seams. * Border size requires depends on the max number of mipmap levels. */ #define REFLECTION_PROBE_MIPMAP_LEVELS 5 diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 44aab2ad758..5754545c012 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -283,6 +283,8 @@ void ForwardPipeline::sync() inst_.shadows.bind_resources(&opaque_ps_); inst_.sampling.bind_resources(&opaque_ps_); inst_.hiz_buffer.bind_resources(&opaque_ps_); + inst_.irradiance_cache.bind_resources(&opaque_ps_); + inst_.reflection_probes.bind_resources(&opaque_ps_); } opaque_single_sided_ps_ = &opaque_ps_.sub("SingleSided"); diff --git a/source/blender/draw/engines/eevee_next/eevee_raytrace.cc b/source/blender/draw/engines/eevee_next/eevee_raytrace.cc index da96ed84779..30ac791d940 100644 --- a/source/blender/draw/engines/eevee_next/eevee_raytrace.cc +++ b/source/blender/draw/engines/eevee_next/eevee_raytrace.cc @@ -106,6 +106,7 @@ void RayTraceModule::sync() inst_.bind_uniform_data(&pass); inst_.hiz_buffer.bind_resources(&pass); inst_.sampling.bind_resources(&pass); + inst_.irradiance_cache.bind_resources(&pass); inst_.reflection_probes.bind_resources(&pass); pass.dispatch(ray_dispatch_buf_); pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); @@ -120,6 +121,7 @@ void RayTraceModule::sync() pass.bind_image("ray_radiance_img", &ray_radiance_tx_); pass.bind_texture("depth_tx", &depth_tx); inst_.bind_uniform_data(&pass); + inst_.irradiance_cache.bind_resources(&pass); inst_.reflection_probes.bind_resources(&pass); pass.dispatch(ray_dispatch_buf_); pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); 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 3496c31592b..009f35d3f42 100644 --- a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc @@ -71,6 +71,7 @@ void ReflectionProbeModule::init() pass.dispatch(int2(1, 1)); } } + void ReflectionProbeModule::begin_sync() { for (ReflectionProbe &reflection_probe : probes_.values()) { @@ -84,6 +85,17 @@ void ReflectionProbeModule::begin_sync() update_probes_this_sample_ = true; instance_.sampling.reset(); } + + { + PassSimple &pass = select_ps_; + pass.init(); + pass.shader_set(instance_.shaders.static_shader_get(REFLECTION_PROBE_SELECT)); + pass.push_constant("reflection_probe_count", &reflection_probe_count_); + pass.bind_ssbo("reflection_probe_buf", &data_buf_); + instance_.irradiance_cache.bind_resources(&pass); + pass.dispatch(&dispatch_probe_select_); + pass.barrier(GPU_BARRIER_UNIFORM); + } } int ReflectionProbeModule::needed_layers_get() const @@ -615,4 +627,21 @@ void ReflectionProbeModule::update_probes_texture_mipmaps() GPU_texture_update_mipmap_chain(probes_tx_); } +void ReflectionProbeModule::set_view(View & /*view*/) +{ + reflection_probe_count_ = 0; + /* TODO(@fclem): Refactor to have a better way than this to get the correct count. + * Eventually we should have the unclamped number of reflection probe. */ + for (int i = 0; i < REFLECTION_PROBES_MAX; i++) { + if (data_buf_[i].layer == -1) { + break; + } + reflection_probe_count_++; + } + + dispatch_probe_select_ = int3( + divide_ceil_u(reflection_probe_count_, REFLECTION_PROBE_SELECT_GROUP_SIZE), 1, 1); + instance_.manager->submit(select_ps_); +} + } // namespace blender::eevee 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 8dac69c5569..a993a254835 100644 --- a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh @@ -84,8 +84,10 @@ class ReflectionProbeModule { PassSimple remap_ps_ = {"Probe.CubemapToOctahedral"}; PassSimple update_irradiance_ps_ = {"Probe.UpdateIrradiance"}; + PassSimple select_ps_ = {"Probe.Select"}; int3 dispatch_probe_pack_ = int3(0); + int3 dispatch_probe_select_ = int3(0); /** * Texture containing a cube-map where the probe should be rendering to. @@ -95,6 +97,8 @@ class ReflectionProbeModule { Texture cubemap_tx_ = {"Probe.Cubemap"}; /** Index of the probe being updated. */ int reflection_probe_index_ = 0; + /** Number of the probe to process in the select phase. */ + int reflection_probe_count_ = 0; bool update_probes_next_sample_ = false; bool update_probes_this_sample_ = false; @@ -119,6 +123,8 @@ class ReflectionProbeModule { void do_world_update_set(bool value); void do_world_update_irradiance_set(bool value); + void set_view(View &view); + void debug_print() const; private: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 069f0be65cb..af48ed0b183 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -204,6 +204,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_reflection_probe_remap"; case REFLECTION_PROBE_UPDATE_IRRADIANCE: return "eevee_reflection_probe_update_irradiance"; + case REFLECTION_PROBE_SELECT: + return "eevee_reflection_probe_select"; case SHADOW_CLIPMAP_CLEAR: return "eevee_shadow_clipmap_clear"; case SHADOW_DEBUG: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index e909b65a517..e0169346101 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -97,6 +97,7 @@ enum eShaderType { REFLECTION_PROBE_REMAP, REFLECTION_PROBE_UPDATE_IRRADIANCE, + REFLECTION_PROBE_SELECT, SHADOW_CLIPMAP_CLEAR, SHADOW_DEBUG, 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 2666533af2f..31574881dba 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -1278,6 +1278,12 @@ static inline float3 burley_eval(float3 d, float r) /** \name Reflection Probes * \{ */ +struct ReflectionProbeLowFreqLight { + packed_float3 direction; + float ambient; +}; +BLI_STATIC_ASSERT_ALIGN(ReflectionProbeLowFreqLight, 16) + /** Mapping data to locate a reflection probe in texture. */ struct ReflectionProbeData { /** @@ -1308,6 +1314,12 @@ struct ReflectionProbeData { * LOD factor for mipmap selection. */ float lod_factor; + + /** + * Irradiance at the probe location encoded as spherical harmonics. + * Only contain the average luminance. Used for cubemap normalization. + */ + ReflectionProbeLowFreqLight low_freq_light; }; BLI_STATIC_ASSERT_ALIGN(ReflectionProbeData, 16) diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index c236a80770e..7402a72aeda 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -124,6 +124,7 @@ void ShadingView::render() /* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */ inst_.lights.set_view(render_view_new_, extent_); + inst_.reflection_probes.set_view(render_view_new_); inst_.volume.draw_prepass(render_view_new_); diff --git a/source/blender/draw/engines/eevee_next/eevee_volume.cc b/source/blender/draw/engines/eevee_next/eevee_volume.cc index f0bc38ba7ab..1f85b462931 100644 --- a/source/blender/draw/engines/eevee_next/eevee_volume.cc +++ b/source/blender/draw/engines/eevee_next/eevee_volume.cc @@ -261,6 +261,7 @@ void VolumeModule::end_sync() scatter_ps_.shader_set(inst_.shaders.static_shader_get( data_.use_lights ? VOLUME_SCATTER_WITH_LIGHTS : VOLUME_SCATTER)); inst_.lights.bind_resources(&scatter_ps_); + inst_.reflection_probes.bind_resources(&scatter_ps_); inst_.irradiance_cache.bind_resources(&scatter_ps_); inst_.shadows.bind_resources(&scatter_ps_); inst_.sampling.bind_resources(&scatter_ps_); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl index 8be9bb9786f..97feda3fa3c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl @@ -12,6 +12,7 @@ #pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl) #pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl) #pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_reflection_probe_eval_lib.glsl) /** * Return the brick coordinate inside the grid. @@ -105,6 +106,21 @@ vec3 lightprobe_irradiance_grid_bias_sample_coord(IrradianceGridData grid_data, return 0.5 + cell_start + trilinear_coord; } +SphericalHarmonicL1 lightprobe_irradiance_sample_atlas(sampler3D atlas_tx, vec3 atlas_coord) +{ + vec4 texture_coord = vec4(atlas_coord, float(IRRADIANCE_GRID_BRICK_SIZE)) / + vec3(textureSize(atlas_tx, 0)).xyzz; + SphericalHarmonicL1 sh; + sh.L0.M0 = textureLod(atlas_tx, texture_coord.xyz, 0.0); + texture_coord.z += texture_coord.w; + sh.L1.Mn1 = textureLod(atlas_tx, texture_coord.xyz, 0.0); + texture_coord.z += texture_coord.w; + sh.L1.M0 = textureLod(atlas_tx, texture_coord.xyz, 0.0); + texture_coord.z += texture_coord.w; + sh.L1.Mp1 = textureLod(atlas_tx, texture_coord.xyz, 0.0); + return sh; +} + SphericalHarmonicL1 lightprobe_irradiance_sample( sampler3D atlas_tx, vec3 P, vec3 V, vec3 Ng, const bool do_bias) { @@ -154,27 +170,24 @@ SphericalHarmonicL1 lightprobe_irradiance_sample( vec3 atlas_coord = vec3(vec2(brick.atlas_coord), 0.0) + brick_lP; - vec4 texture_coord = vec4(atlas_coord, float(IRRADIANCE_GRID_BRICK_SIZE)) / - vec3(textureSize(atlas_tx, 0)).xyzz; - SphericalHarmonicL1 sh; - sh.L0.M0 = textureLod(atlas_tx, texture_coord.xyz, 0.0); - texture_coord.z += texture_coord.w; - sh.L1.Mn1 = textureLod(atlas_tx, texture_coord.xyz, 0.0); - texture_coord.z += texture_coord.w; - sh.L1.M0 = textureLod(atlas_tx, texture_coord.xyz, 0.0); - texture_coord.z += texture_coord.w; - sh.L1.Mp1 = textureLod(atlas_tx, texture_coord.xyz, 0.0); - return sh; + return lightprobe_irradiance_sample_atlas(atlas_tx, atlas_coord); +} + +SphericalHarmonicL1 lightprobe_irradiance_world() +{ + return lightprobe_irradiance_sample_atlas(irradiance_atlas_tx, vec3(0.0)); } -/** - * Shorter version without bias. - */ SphericalHarmonicL1 lightprobe_irradiance_sample(vec3 P) { return lightprobe_irradiance_sample(irradiance_atlas_tx, P, vec3(0), vec3(0), false); } +SphericalHarmonicL1 lightprobe_irradiance_sample(vec3 P, vec3 V, vec3 Ng) +{ + return lightprobe_irradiance_sample(irradiance_atlas_tx, P, V, Ng, true); +} + void lightprobe_eval(ClosureDiffuse diffuse, ClosureReflection reflection, vec3 P, @@ -185,8 +198,98 @@ void lightprobe_eval(ClosureDiffuse diffuse, { /* NOTE: Use the diffuse normal for biasing the probe sampling location since it is smoother than * geometric normal. Could also try to use `interp.N`. */ - SphericalHarmonicL1 irradiance = lightprobe_irradiance_sample( - irradiance_atlas_tx, P, V, diffuse.N, true); + SphericalHarmonicL1 irradiance = lightprobe_irradiance_sample(P, V, diffuse.N); out_diffuse += spherical_harmonics_evaluate_lambert(diffuse.N, irradiance); } + +#ifdef REFLECTION_PROBE + +struct LightProbeSample { + SphericalHarmonicL1 volume_irradiance; + int spherical_id; +}; + +/** + * Return cached lightprobe data at P. + * Ng and V are use for biases. + */ +LightProbeSample lightprobe_load(vec3 P, vec3 Ng, vec3 V) +{ + LightProbeSample result; + result.volume_irradiance = lightprobe_irradiance_sample(P, V, Ng); + result.spherical_id = reflection_probes_find_closest(P); + return result; +} + +/** + * Return spherical sample normalized by irradianced at sample position. + * This avoid most of light leaking and reduce the need for many local probes. + */ +vec3 lightprobe_spherical_sample_normalized(int probe_index, + vec3 L, + float lod, + SphericalHarmonicL1 P_sh) +{ + ReflectionProbeData probe = reflection_probe_buf[probe_index]; + ReflectionProbeLowFreqLight shading_sh = reflection_probes_extract_low_freq(P_sh); + vec3 normalization_factor = reflection_probes_normalization_eval( + L, shading_sh, probe.low_freq_light); + return normalization_factor * reflection_probes_sample(L, lod, probe).rgb; +} + +float pdf_to_lod(float pdf) +{ + return 1.0; /* TODO */ +} + +vec3 lightprobe_eval_direction(LightProbeSample samp, vec3 L, float pdf) +{ + vec3 radiance_sh = lightprobe_spherical_sample_normalized( + samp.spherical_id, L, pdf_to_lod(pdf), samp.volume_irradiance); + + return radiance_sh; +} + +float lightprobe_roughness_to_cube_sh_mix_fac(float roughness) +{ + /* Temporary. Do something better. */ + return square(saturate(roughness * 4.0 - 2.0)); +} + +float lightprobe_roughness_to_lod(float roughness) +{ + /* Temporary. Do something better. */ + return sqrt(roughness) * 11.0; +} + +vec3 lightprobe_eval(LightProbeSample samp, ClosureDiffuse diffuse, vec3 V, vec2 noise) +{ + vec3 radiance_sh = spherical_harmonics_evaluate_lambert(diffuse.N, samp.volume_irradiance); + return radiance_sh; +} + +vec3 lightprobe_specular_dominant_dir(vec3 N, vec3 V, float roughness) +{ + vec3 R = -reflect(V, N); + float smoothness = 1.0 - roughness; + float fac = smoothness * (sqrt(smoothness) + roughness); + return normalize(mix(N, R, fac)); +} + +vec3 lightprobe_eval(LightProbeSample samp, ClosureReflection reflection, vec3 V, vec2 noise) +{ + vec3 L = lightprobe_specular_dominant_dir(reflection.N, V, reflection.roughness); + /* TODO: Right now generate a dependency hell. */ + // vec3 L = ray_generate_direction(noise, reflection, V, pdf); + + float lod = lightprobe_roughness_to_lod(reflection.roughness); + vec3 radiance_cube = lightprobe_spherical_sample_normalized( + samp.spherical_id, L, lod, samp.volume_irradiance); + + float fac = lightprobe_roughness_to_cube_sh_mix_fac(reflection.roughness); + vec3 radiance_sh = spherical_harmonics_evaluate_lambert(L, samp.volume_irradiance); + return mix(radiance_cube, radiance_sh, fac); +} + +#endif /* REFLECTION_PROBE */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_fallback_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_fallback_comp.glsl index 3c757a652c1..27a3ed17247 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_fallback_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_fallback_comp.glsl @@ -6,7 +6,7 @@ * Does not use any tracing method. Only rely on local light probes to get the incoming radiance. */ -#pragma BLENDER_REQUIRE(eevee_reflection_probe_eval_lib.glsl) +#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) @@ -41,12 +41,16 @@ void main() ray.origin = P; ray.direction = ray_data.xyz; - int closest_probe_id = reflection_probes_find_closest(P); - ReflectionProbeData probe = reflection_probe_buf[closest_probe_id]; - vec3 radiance = reflection_probes_sample(ray.direction, 0.0, probe).rgb; + vec3 V = cameraVec(P); + + /* 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; + LightProbeSample samp = lightprobe_load(P, Ng, V); + vec3 radiance = lightprobe_eval_direction(samp, ray.direction, safe_rcp(ray_pdf_inv)); /* Set point really far for correct reprojection of background. */ - /* TODO(fclem): Could use probe depth / parallax. */ - float hit_time = 10000.0; + float hit_time = 1000.0; float luma = max(1e-8, max_v3(radiance)); radiance *= 1.0 - max(0.0, luma - uniform_buf.raytrace.brightness_clamp) / luma; 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 21f952f1eab..98187fdc3cd 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 @@ -6,7 +6,7 @@ * Use screen space tracing against depth buffer to find intersection with the scene. */ -#pragma BLENDER_REQUIRE(eevee_reflection_probe_eval_lib.glsl) +#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) @@ -37,6 +37,7 @@ void main() } vec3 P = get_world_space_from_depth(uv, depth); + vec3 V = cameraVec(P); Ray ray; ray.origin = P; ray.direction = ray_data.xyz; @@ -87,10 +88,13 @@ void main() // } } 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. */ - int closest_probe_id = reflection_probes_find_closest(P); - ReflectionProbeData probe = reflection_probe_buf[closest_probe_id]; - radiance = reflection_probes_sample(ray.direction, 0.0, probe).rgb; + 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; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl index e711be6e76b..d7477a3a83e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl @@ -8,12 +8,13 @@ #pragma BLENDER_REQUIRE(eevee_bxdf_sampling_lib.glsl) #pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl) +#ifdef REFLECTION_PROBE int reflection_probes_find_closest(vec3 P) { int closest_index = 0; float closest_distance = FLT_MAX; - /* ReflectionProbeData doesn't contain any gab, exit at first item that is invalid. */ + /* ReflectionProbeData doesn't contain any gap, exit at first item that is invalid. */ for (int index = 1; reflection_probe_buf[index].layer != -1 && index < REFLECTION_PROBES_MAX; index++) { @@ -25,6 +26,7 @@ int reflection_probes_find_closest(vec3 P) } return closest_index; } +#endif /* REFLECTION_PROBE */ #ifdef EEVEE_UTILITY_TX vec4 reflection_probe_eval(ClosureReflection reflection, @@ -32,7 +34,7 @@ vec4 reflection_probe_eval(ClosureReflection reflection, vec3 V, ReflectionProbeData probe_data) { - ivec3 texture_size = textureSize(reflectionProbes, 0); + ivec3 texture_size = textureSize(reflection_probes_tx, 0); float lod_cube_max = min(log2(float(texture_size.x)) - float(probe_data.layer_subdivision) + 1.0, float(REFLECTION_PROBE_MIPMAP_LEVELS)); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl index 9044851622c..580cc794c6d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl @@ -2,16 +2,18 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#pragma BLENDER_REQUIRE(eevee_cubemap_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) #pragma BLENDER_REQUIRE(eevee_octahedron_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) +#ifdef REFLECTION_PROBE vec4 reflection_probes_sample(vec3 L, float lod, ReflectionProbeData probe_data) { vec2 octahedral_uv_packed = octahedral_uv_from_direction(L); vec2 texel_size = vec2(1.0 / float(1 << (11 - probe_data.layer_subdivision))); vec2 octahedral_uv = octahedral_uv_to_layer_texture_coords( octahedral_uv_packed, probe_data, texel_size); - return textureLod(reflectionProbes, vec3(octahedral_uv, probe_data.layer), lod); + return textureLod(reflection_probes_tx, vec3(octahedral_uv, probe_data.layer), lod); } vec3 reflection_probes_world_sample(vec3 L, float lod) @@ -19,3 +21,30 @@ vec3 reflection_probes_world_sample(vec3 L, float lod) ReflectionProbeData probe_data = reflection_probe_buf[0]; return reflection_probes_sample(L, lod, probe_data).rgb; } +#endif + +ReflectionProbeLowFreqLight reflection_probes_extract_low_freq(SphericalHarmonicL1 sh) +{ + /* To avoid color shift and negative values, we reduce saturation and directionnality. */ + ReflectionProbeLowFreqLight result; + result.ambient = sh.L0.M0.r + sh.L0.M0.g + sh.L0.M0.b; + + mat3x4 L1_per_band; + L1_per_band[0] = sh.L1.Mn1; + L1_per_band[1] = sh.L1.M0; + L1_per_band[2] = sh.L1.Mp1; + + mat4x3 L1_per_comp = transpose(L1_per_band); + result.direction = L1_per_comp[0] + L1_per_comp[1] + L1_per_comp[2]; + + return result; +} + +vec3 reflection_probes_normalization_eval(vec3 L, + ReflectionProbeLowFreqLight numerator, + ReflectionProbeLowFreqLight denominator) +{ + /* TODO(fclem): Adjusting directionnality is tricky. + * Needs to be revisited later on. For now only use the ambient term. */ + return vec3(numerator.ambient * safe_rcp(denominator.ambient)); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl new file mode 100644 index 00000000000..d017740a4ee --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl @@ -0,0 +1,28 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** + * Select spherical probes to be considered during render and copy irradiance data from the + * irradiance cache from each spherical probe location except for the world probe. + */ + +#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl) + +void main() +{ + int idx = int(gl_GlobalInvocationID.x); + if (idx >= reflection_probe_count) { + return; + } + + SphericalHarmonicL1 sh; + if (idx == 0) { + sh = lightprobe_irradiance_world(); + } + else { + sh = lightprobe_irradiance_sample(reflection_probe_buf[idx].pos.xyz); + } + + reflection_probe_buf[idx].low_freq_light = reflection_probes_extract_low_freq(sh); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl index 3022d49d6d2..682c84c91e6 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl @@ -191,6 +191,27 @@ vec4 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2) spherical_harmonics_L2_Mp2(direction) * L2.Mp2; } +vec3 spherical_harmonics_evaluate(vec3 direction, SphericalHarmonicL0 sh) +{ + vec3 radiance = spherical_harmonics_L0_evaluate(direction, sh.L0).rgb; + return max(vec3(0.0), radiance); +} + +vec3 spherical_harmonics_evaluate(vec3 direction, SphericalHarmonicL1 sh) +{ + vec3 radiance = spherical_harmonics_L0_evaluate(direction, sh.L0).rgb + + spherical_harmonics_L1_evaluate(direction, sh.L1).rgb; + return max(vec3(0.0), radiance); +} + +vec3 spherical_harmonics_evaluate(vec3 direction, SphericalHarmonicL2 sh) +{ + vec3 radiance = spherical_harmonics_L0_evaluate(direction, sh.L0).rgb + + spherical_harmonics_L1_evaluate(direction, sh.L1).rgb + + spherical_harmonics_L2_evaluate(direction, sh.L2).rgb; + return max(vec3(0.0), radiance); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl index 92ed4ec6bf9..93a8a86cc01 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl @@ -9,6 +9,7 @@ */ #pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl) #pragma BLENDER_REQUIRE(eevee_ambient_occlusion_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) @@ -26,19 +27,26 @@ vec4 closure_to_rgba(Closure cl) vec3 refraction_light = vec3(0.0); float shadow = 1.0; + vec3 V = cameraVec(g_data.P); float vP_z = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos); light_eval(g_diffuse_data, g_reflection_data, g_data.P, g_data.Ng, - cameraVec(g_data.P), + V, vP_z, 0.01 /* TODO(fclem) thickness. */, diffuse_light, reflection_light, shadow); + vec2 noise_probe = interlieved_gradient_noise(gl_FragCoord.xy, vec2(0, 1), vec2(0.0)); + LightProbeSample samp = lightprobe_load(g_data.P, g_data.Ng, V); + + diffuse_light += lightprobe_eval(samp, g_diffuse_data, V, noise_probe); + reflection_light += lightprobe_eval(samp, g_reflection_data, V, noise_probe); + vec4 out_color; out_color.rgb = g_emission; out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight * diffuse_light; @@ -76,19 +84,26 @@ void main() vec3 refraction_light = vec3(0.0); float shadow = 1.0; + vec3 V = cameraVec(g_data.P); float vP_z = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos); light_eval(g_diffuse_data, g_reflection_data, g_data.P, g_data.Ng, - cameraVec(g_data.P), + V, vP_z, thickness, diffuse_light, reflection_light, shadow); + vec2 noise_probe = interlieved_gradient_noise(gl_FragCoord.xy, vec2(0, 1), vec2(0.0)); + LightProbeSample samp = lightprobe_load(g_data.P, g_data.Ng, V); + + diffuse_light += lightprobe_eval(samp, g_diffuse_data, V, noise_probe); + reflection_light += lightprobe_eval(samp, g_reflection_data, V, noise_probe); + g_diffuse_data.color *= g_diffuse_data.weight; g_reflection_data.color *= g_reflection_data.weight; g_refraction_data.color *= g_refraction_data.weight; 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 dbbf92b7a00..2cdda2f6b3b 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 @@ -11,9 +11,9 @@ image(slot, format, Qualifier::READ, ImageType::FLOAT_2D, name, Frequency::PASS) GPU_SHADER_CREATE_INFO(eevee_gbuffer_data) - .sampler(7, ImageType::UINT_2D, "gbuf_header_tx") - .sampler(8, ImageType::FLOAT_2D_ARRAY, "gbuf_closure_tx") - .sampler(9, ImageType::FLOAT_2D_ARRAY, "gbuf_color_tx"); + .sampler(8, ImageType::UINT_2D, "gbuf_header_tx") + .sampler(9, ImageType::FLOAT_2D_ARRAY, "gbuf_closure_tx") + .sampler(10, ImageType::FLOAT_2D_ARRAY, "gbuf_color_tx"); GPU_SHADER_CREATE_INFO(eevee_deferred_light) .fragment_source("eevee_deferred_light_frag.glsl") 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 660665291cb..4fa3fd76a3d 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 @@ -195,7 +195,7 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_load) .compute_source("eevee_lightprobe_irradiance_load_comp.glsl") .do_static_compilation(true); -GPU_SHADER_CREATE_INFO(eevee_lightprobe_data) +GPU_SHADER_CREATE_INFO(eevee_volume_probe_data) .uniform_buf(IRRADIANCE_GRID_BUF_SLOT, "IrradianceGridData", "grids_infos_buf[IRRADIANCE_GRID_MAX]") @@ -204,4 +204,7 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_data) .storage_buf(IRRADIANCE_BRICK_BUF_SLOT, Qualifier::READ, "uint", "bricks_infos_buf[]") .sampler(IRRADIANCE_ATLAS_TEX_SLOT, ImageType::FLOAT_3D, "irradiance_atlas_tx"); +GPU_SHADER_CREATE_INFO(eevee_lightprobe_data) + .additional_info("eevee_reflection_probe_data", "eevee_volume_probe_data"); + /** \} */ 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 7131da02e8b..32a25307717 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 @@ -166,6 +166,7 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward) .fragment_source("eevee_surf_forward_frag.glsl") .additional_info("eevee_global_ubo", "eevee_light_data", + "eevee_lightprobe_data", "eevee_utility_texture", "eevee_sampling_data", "eevee_shadow_data", 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 098af14b905..7e118f4d281 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 @@ -10,10 +10,11 @@ * \{ */ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_data) + .define("REFLECTION_PROBE") .uniform_buf(REFLECTION_PROBE_BUF_SLOT, "ReflectionProbeData", "reflection_probe_buf[REFLECTION_PROBES_MAX]") - .sampler(REFLECTION_PROBE_TEX_SLOT, ImageType::FLOAT_2D_ARRAY, "reflectionProbes"); + .sampler(REFLECTION_PROBE_TEX_SLOT, ImageType::FLOAT_2D_ARRAY, "reflection_probes_tx"); /* Sample cubemap and remap into an octahedral texture. */ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_remap) @@ -38,4 +39,15 @@ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_update_irradiance) .compute_source("eevee_reflection_probe_update_irradiance_comp.glsl") .do_static_compilation(true); +GPU_SHADER_CREATE_INFO(eevee_reflection_probe_select) + .local_group_size(REFLECTION_PROBE_SELECT_GROUP_SIZE) + .storage_buf(0, + Qualifier::READ_WRITE, + "ReflectionProbeData", + "reflection_probe_buf[REFLECTION_PROBES_MAX]") + .push_constant(Type::INT, "reflection_probe_count") + .additional_info("eevee_shared", "eevee_volume_probe_data") + .compute_source("eevee_reflection_probe_select_comp.glsl") + .do_static_compilation(true); + /** \} */ 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 7371ec5919c..d4b49aaee0d 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 @@ -65,15 +65,12 @@ EEVEE_RAYTRACE_CLOSURE_VARIATION(eevee_ray_generate) GPU_SHADER_CREATE_INFO(eevee_ray_trace_fallback) .do_static_compilation(true) .local_group_size(RAYTRACE_GROUP_SIZE, RAYTRACE_GROUP_SIZE) - .additional_info("eevee_shared", - "eevee_global_ubo", - "draw_view", - "eevee_reflection_probe_data") + .additional_info("eevee_shared", "eevee_global_ubo", "draw_view", "eevee_lightprobe_data") .image(0, GPU_RGBA16F, Qualifier::READ, 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(1, ImageType::DEPTH_2D, "depth_tx") - .storage_buf(4, Qualifier::READ, "uint", "tiles_coord_buf[]") + .storage_buf(5, Qualifier::READ, "uint", "tiles_coord_buf[]") .compute_source("eevee_ray_trace_fallback_comp.glsl"); GPU_SHADER_CREATE_INFO(eevee_ray_trace_screen) @@ -83,13 +80,13 @@ GPU_SHADER_CREATE_INFO(eevee_ray_trace_screen) "eevee_sampling_data", "draw_view", "eevee_hiz_data", - "eevee_reflection_probe_data") + "eevee_lightprobe_data") .image(0, GPU_RGBA16F, Qualifier::READ, 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(0, ImageType::FLOAT_2D, "screen_radiance_tx") .sampler(1, ImageType::DEPTH_2D, "depth_tx") - .storage_buf(4, Qualifier::READ, "uint", "tiles_coord_buf[]") + .storage_buf(5, Qualifier::READ, "uint", "tiles_coord_buf[]") .compute_source("eevee_ray_trace_screen_comp.glsl"); EEVEE_RAYTRACE_CLOSURE_VARIATION(eevee_ray_trace_screen)