From e0b3dee35a36e658ee4115d2ec54bbc6573b302e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sat, 18 May 2024 17:21:47 +0200 Subject: [PATCH] EEVEE-Next: Jittered Soft Shadows Jittered Soft Shadows support. Improves soft shadow quality at the cost of re-rendering shadow maps every sample. Disabled by default in the viewport unless enabled in the Scene settings. | Tracing-only | Jitter-only | Jitter+Over-blur | | --- | --- | --- | | ![imagen](/attachments/e5ca6120-0666-4e86-b6e0-3d7512587b86) | ![imagen](/attachments/a72631aa-14f8-4e10-a748-848fc4bd4ab2) | ![imagen](/attachments/07c5de65-61d2-48e7-b78c-9c3cbdcaf844) | Tracing-only is the method used by default in EEVEE-Next. Jitter-only is the method used by EEVEE-Legacy Soft Shadows. Jitter+Over-blur combines both. Co-authored by Miguel Pozo @pragma37 (initial patch #119753) Pull Request: https://projects.blender.org/blender/blender/pulls/121836 --- .../startup/bl_ui/properties_data_light.py | 10 + scripts/startup/bl_ui/properties_render.py | 2 +- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_400.cc | 7 + .../draw/engines/eevee_next/eevee_light.cc | 109 ++++--- .../draw/engines/eevee_next/eevee_light.hh | 7 +- .../draw/engines/eevee_next/eevee_sampling.cc | 10 + .../engines/eevee_next/eevee_shader_shared.hh | 276 ++++++++++++------ .../draw/engines/eevee_next/eevee_shadow.cc | 159 ++++------ .../draw/engines/eevee_next/eevee_shadow.hh | 82 +----- .../eevee_light_culling_select_comp.glsl | 13 +- .../shaders/eevee_light_eval_lib.glsl | 1 - .../eevee_next/shaders/eevee_light_lib.glsl | 23 +- .../eevee_light_shadow_setup_comp.glsl | 195 ++++++++----- .../eevee_reflection_probe_sunlight_comp.glsl | 8 +- .../shaders/eevee_sampling_lib.glsl | 7 + .../shaders/eevee_shadow_debug_frag.glsl | 2 +- .../eevee_next/shaders/eevee_shadow_lib.glsl | 2 + .../shaders/eevee_shadow_tag_usage_lib.glsl | 12 +- .../eevee_next/shaders/eevee_shadow_test.glsl | 8 +- .../eevee_shadow_tilemap_finalize_comp.glsl | 2 +- .../eevee_shadow_tilemap_init_comp.glsl | 7 +- .../shaders/eevee_shadow_tilemap_lib.glsl | 4 +- .../shaders/eevee_shadow_tracing_lib.glsl | 50 ++-- .../eevee_next/shaders/eevee_volume_lib.glsl | 4 +- .../shaders/infos/eevee_light_culling_info.hh | 7 +- source/blender/draw/tests/eevee_test.cc | 6 +- source/blender/gpu/GPU_shader_shared_utils.hh | 1 + source/blender/makesdna/DNA_light_defaults.h | 1 + source/blender/makesdna/DNA_light_types.h | 3 +- source/blender/makesrna/intern/rna_light.cc | 20 ++ source/blender/makesrna/intern/rna_scene.cc | 2 +- 32 files changed, 579 insertions(+), 463 deletions(-) diff --git a/scripts/startup/bl_ui/properties_data_light.py b/scripts/startup/bl_ui/properties_data_light.py index a84991f7218..504c64d0998 100644 --- a/scripts/startup/bl_ui/properties_data_light.py +++ b/scripts/startup/bl_ui/properties_data_light.py @@ -152,6 +152,16 @@ class DATA_PT_EEVEE_light_shadow(DataButtonsPanel, Panel): layout.use_property_split = True layout.active = context.scene.eevee.use_shadows and light.use_shadow + col = layout.column(align=False, heading="Jitter") + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(light, "use_shadow_jitter", text="") + sub = sub.row(align=True) + sub.active = light.use_shadow_jitter + sub.prop(light, "shadow_jitter_overblur", text="Overblur") + + col.separator() + col = layout.column() col.prop(light, "shadow_filter_radius", text="Filter") diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index 1811f490147..0e7bc04f2ff 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -843,7 +843,7 @@ class RENDER_PT_eevee_next_sampling_viewport(RenderButtonsPanel, Panel): col = layout.column() col.prop(props, "taa_samples", text="Samples") col.prop(props, "use_taa_reprojection", text="Temporal Reprojection") - col.prop(props, "use_shadow_jittered_viewport", text="Jittered Shadows") + col.prop(props, "use_shadow_jitter_viewport", text="Jittered Shadows") # Add SSS sample count here. diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index a61ce72a9d2..273b9471e1d 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 40 +#define BLENDER_FILE_SUBVERSION 41 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 375382d1a26..5369c9729da 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -3633,6 +3633,13 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 41)) { + const Light *default_light = DNA_struct_default_get(Light); + LISTBASE_FOREACH (Light *, light, &bmain->lights) { + light->shadow_jitter_overblur = default_light->shadow_jitter_overblur; + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/draw/engines/eevee_next/eevee_light.cc b/source/blender/draw/engines/eevee_next/eevee_light.cc index 3c3739ad8e0..465491cf24e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.cc +++ b/source/blender/draw/engines/eevee_next/eevee_light.cc @@ -72,7 +72,8 @@ void Light::sync(ShadowModule &shadows, this->object_to_world = object_to_world; - shape_parameters_set(la, scale, threshold); + shape_parameters_set( + la, scale, object_to_world.z_axis(), threshold, shadows.get_data().use_jitter); const bool diffuse_visibility = (visibility_flag & OB_HIDE_DIFFUSE) == 0; const bool glossy_visibility = (visibility_flag & OB_HIDE_GLOSSY) == 0; @@ -86,20 +87,13 @@ void Light::sync(ShadowModule &shadows, this->power[LIGHT_TRANSMISSION] = la->transmission_fac * shape_power * transmission_visibility; this->power[LIGHT_VOLUME] = la->volume_fac * point_power * volume_visibility; - this->pcf_radius = la->shadow_filter_radius; - this->lod_bias = (1.0f - la->shadow_resolution_scale) * SHADOW_TILEMAP_LOD; this->lod_min = shadow_lod_min_get(la); + this->pcf_radius = la->shadow_filter_radius; + this->shadow_jitter = (la->mode & LA_SHADOW_JITTER) != 0; if (la->mode & LA_SHADOW) { shadow_ensure(shadows); - if (is_sun_light(this->type)) { - this->directional->sync(object_to_world, la->sun_angle); - } - else { - this->punctual->sync( - this->type, object_to_world, la->spotsize, this->local.influence_radius_max); - } } else { shadow_discard_safe(shadows); @@ -152,27 +146,62 @@ float Light::attenuation_radius_get(const ::Light *la, float light_threshold, fl return sqrtf(light_power / light_threshold); } -void Light::shape_parameters_set(const ::Light *la, const float3 &scale, float threshold) +void Light::shape_parameters_set(const ::Light *la, + const float3 &scale, + const float3 &z_axis, + const float threshold, + const bool use_jitter) { using namespace blender::math; + /* Compute influence radius first. Can be amended by shape later. */ + if (is_local_light(this->type)) { + const float max_power = reduce_max(float3(&la->r)) * fabsf(la->energy / 100.0f); + const float surface_max_power = max(la->diff_fac, la->spec_fac) * max_power; + const float volume_max_power = la->volume_fac * max_power; + + float influence_radius_surface = attenuation_radius_get(la, threshold, surface_max_power); + float influence_radius_volume = attenuation_radius_get(la, threshold, volume_max_power); + + this->local.influence_radius_max = max(influence_radius_surface, influence_radius_volume); + this->local.influence_radius_invsqr_surface = safe_rcp(square(influence_radius_surface)); + this->local.influence_radius_invsqr_volume = safe_rcp(square(influence_radius_volume)); + /* TODO(fclem): This is just duplicating a member for local lights. */ + this->clip_far = float_as_int(this->local.influence_radius_max); + this->clip_near = float_as_int(this->local.influence_radius_max / 4000.0f); + } + + float trace_scaling_fac = (use_jitter && (la->mode & LA_SHADOW_JITTER)) ? + la->shadow_jitter_overblur / 100.0f : + 1.0f; + if (is_sun_light(this->type)) { - this->sun.radius = tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f); + float sun_half_angle = min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f; + /* Use non-clamped radius for soft shadows. Avoid having a minimum blur. */ + this->sun.shadow_angle = sun_half_angle * trace_scaling_fac; /* Clamp to minimum value before float imprecision artifacts appear. */ - this->sun.radius = max(0.001f, this->sun.radius); + this->sun.shape_radius = clamp(tanf(sun_half_angle), 0.001f, 20.0f); + /* Stable shading direction. */ + this->sun.direction = z_axis; } else if (is_area_light(this->type)) { const bool is_irregular = ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE); this->area.size = float2(la->area_size, is_irregular ? la->area_sizey : la->area_size); /* Scale and clamp to minimum value before float imprecision artifacts appear. */ - this->area.size = this->area.size * scale.xy() / 2.0f; - /* Do not render lights that virtually have no area or clamp to minimum value before float - * imprecision artifacts appear. */ - this->area.size = (this->area.size.x * this->area.size.y < 0.00001f) ? - float2(0.0) : - max(float2(0.003f), this->area.size * scale.xy() / 2.0f); + this->area.size *= scale.xy() / 2.0f; + /* In this case, this is just the scaling factor. */ + this->local.shadow_radius = trace_scaling_fac; + /* Set to default position. */ + this->local.shadow_position = float3(0.0f); + /* Do not render lights that have no area. */ + if (this->area.size.x * this->area.size.y < 0.00001f) { + /* Forces light to be culled. */ + this->local.influence_radius_max = 0.0f; + } + /* Clamp to minimum value before float imprecision artifacts appear. */ + this->area.size = max(float2(0.003f), this->area.size); /* For volume point lighting. */ - this->local.radius_squared = square(max(0.001f, length(this->area.size) / 2.0f)); + this->local.shape_radius = max(0.001f, length(this->area.size) / 2.0f); } else if (is_point_light(this->type)) { /* Spot size & blend */ @@ -192,28 +221,14 @@ void Light::shape_parameters_set(const ::Light *la, const float3 &scale, float t this->spot.spot_bias = 1.0f; this->spot.spot_tan = 0.0f; } - - this->spot.radius = la->radius; + /* Use unclamped radius for soft shadows. Avoid having a minimum blur. */ + this->local.shadow_radius = max(0.0f, la->radius) * trace_scaling_fac; + /* Set to default position. */ + this->local.shadow_position = float3(0.0f); /* Ensure a minimum radius/energy ratio to avoid harsh cut-offs. (See 114284) */ - this->spot.radius = max(this->spot.radius, la->energy * 2e-05f); + this->local.shape_radius = max(la->radius, la->energy * 2e-05f); /* Clamp to minimum value before float imprecision artifacts appear. */ - this->spot.radius = max(0.001f, this->spot.radius); - - /* For volume point lighting. */ - this->local.radius_squared = square(this->spot.radius); - } - - if (is_local_light(this->type)) { - const float max_power = reduce_max(float3(&la->r)) * fabsf(la->energy / 100.0f); - const float surface_max_power = max(la->diff_fac, la->spec_fac) * max_power; - const float volume_max_power = la->volume_fac * max_power; - - float influence_radius_surface = attenuation_radius_get(la, threshold, surface_max_power); - float influence_radius_volume = attenuation_radius_get(la, threshold, volume_max_power); - - this->local.influence_radius_max = max(influence_radius_surface, influence_radius_volume); - this->local.influence_radius_invsqr_surface = safe_rcp(square(influence_radius_surface)); - this->local.influence_radius_invsqr_volume = safe_rcp(square(influence_radius_volume)); + this->local.shape_radius = max(0.001f, this->local.shape_radius); } } @@ -239,13 +254,13 @@ float Light::shape_radiance_get() case LIGHT_SPOT_SPHERE: case LIGHT_SPOT_DISK: { /* Sphere area. */ - float area = float(4.0f * M_PI) * this->spot.radius_squared; + float area = float(4.0f * M_PI) * square(this->local.shape_radius); /* Convert radiant flux to radiance. */ return 1.0f / (area * float(M_PI)); } case LIGHT_SUN_ORTHO: case LIGHT_SUN: { - float inv_sin_sq = 1.0f + 1.0f / square(this->sun.radius); + float inv_sin_sq = 1.0f + 1.0f / square(this->sun.shape_radius); /* Convert irradiance to radiance. */ return float(M_1_PI) * inv_sin_sq; } @@ -510,15 +525,21 @@ void LightModule::culling_pass_sync() void LightModule::update_pass_sync() { + /* TODO(fclem): This dispatch for all light before culling. This could be made better by + * only running on lights that survive culling using an indirect dispatch. */ + uint safe_lights_len = max_ii(lights_len_, 1); + uint shadow_setup_dispatch_size = divide_ceil_u(safe_lights_len, CULLING_SELECT_GROUP_SIZE); + auto &pass = update_ps_; pass.init(); pass.shader_set(inst_.shaders.static_shader_get(LIGHT_SHADOW_SETUP)); pass.bind_ssbo("light_buf", &culling_light_buf_); pass.bind_ssbo("light_cull_buf", &culling_data_buf_); pass.bind_ssbo("tilemaps_buf", &inst_.shadows.tilemap_pool.tilemaps_data); + pass.bind_ssbo("tilemaps_clip_buf", &inst_.shadows.tilemap_pool.tilemaps_clip); pass.bind_resources(inst_.uniform_data); - /* TODO(fclem): Dispatch for all light. */ - pass.dispatch(int3(1, 1, 1)); + pass.bind_resources(inst_.sampling); + pass.dispatch(int3(shadow_setup_dispatch_size, 1, 1)); pass.barrier(GPU_BARRIER_SHADER_STORAGE); } diff --git a/source/blender/draw/engines/eevee_next/eevee_light.hh b/source/blender/draw/engines/eevee_next/eevee_light.hh index 4366976c4be..ce2acd89f2b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.hh +++ b/source/blender/draw/engines/eevee_next/eevee_light.hh @@ -89,8 +89,13 @@ struct Light : public LightData, NonCopyable { private: float shadow_lod_min_get(const ::Light *la); + float shadow_shape_size_get(const ::Light *la); float attenuation_radius_get(const ::Light *la, float light_threshold, float light_power); - void shape_parameters_set(const ::Light *la, const float3 &scale, float threshold); + void shape_parameters_set(const ::Light *la, + const float3 &scale, + const float3 &z_axis, + float threshold, + bool do_jitter); float shape_radiance_get(); float point_radiance_get(); }; diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc index ed931019197..27fbe2776ac 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc @@ -166,6 +166,16 @@ void Sampling::step() data_.dimensions[SAMPLING_RAYTRACE_V] = r[1]; data_.dimensions[SAMPLING_RAYTRACE_W] = r[2]; } + { + double3 r, offset = {0, 0, 0}; + uint3 primes = {2, 3, 5}; + BLI_halton_3d(primes, offset, sample_ + 1, r); + /* WORKAROUND: We offset the distribution to make the first sample (0,0,0). */ + /* TODO de-correlate. */ + data_.dimensions[SAMPLING_SHADOW_I] = fractf(r[0] + (1.0 / 2.0)); + data_.dimensions[SAMPLING_SHADOW_J] = fractf(r[1] + (2.0 / 3.0)); + data_.dimensions[SAMPLING_SHADOW_K] = fractf(r[2] + (4.0 / 5.0)); + } { uint64_t sample_volume = sample_; if (interactive_mode()) { 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 bba374babe7..0155da1035b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -41,7 +41,21 @@ constexpr GPUSamplerState with_filter = {GPU_SAMPLER_FILTERING_LINEAR}; # define IS_CPP 1 #endif -#define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14 +/** WORKAROUND(@fclem): This is because this file is included before common_math_lib.glsl. */ +#ifndef M_PI +# define EEVEE_PI +# define M_PI 3.14159265358979323846 /* pi */ +#endif + +enum eCubeFace : uint32_t { + /* Ordering by culling order. If cone aperture is shallow, we cull the later view. */ + Z_NEG = 0u, + X_POS = 1u, + X_NEG = 2u, + Y_POS = 3u, + Y_NEG = 4u, + Z_POS = 5u, +}; /* -------------------------------------------------------------------- */ /** \name Transform @@ -69,6 +83,23 @@ struct Transform { #endif }; +static inline float4x4 transform_to_matrix(Transform t) +{ + return float4x4(float4(t.x.x, t.y.x, t.z.x, 0.0f), + float4(t.x.y, t.y.y, t.z.y, 0.0f), + float4(t.x.z, t.y.z, t.z.z, 0.0f), + float4(t.x.w, t.y.w, t.z.w, 1.0f)); +} + +static inline Transform transform_from_matrix(float4x4 m) +{ + Transform t; + t.x = float4(m[0][0], m[1][0], m[2][0], m[3][0]); + t.y = float4(m[0][1], m[1][1], m[2][1], m[3][1]); + t.z = float4(m[0][2], m[1][2], m[2][2], m[3][2]); + return t; +} + static inline float3 transform_x_axis(Transform t) { return float3(t.x.x, t.y.x, t.z.x); @@ -86,6 +117,13 @@ static inline float3 transform_location(Transform t) return float3(t.x.w, t.y.w, t.z.w); } +#if !IS_CPP +static inline bool transform_equal(Transform a, Transform b) +{ + return all(equal(a.x, b.x)) && all(equal(a.y, b.y)) && all(equal(a.z, b.z)); +} +#endif + static inline float3 transform_point(Transform t, float3 point) { return float4(point, 1.0f) * float3x4(t.x, t.y, t.z); @@ -215,7 +253,10 @@ enum eSamplingDimension : uint32_t { SAMPLING_CURVES_U = 21u, SAMPLING_VOLUME_U = 22u, SAMPLING_VOLUME_V = 23u, - SAMPLING_VOLUME_W = 24u + SAMPLING_VOLUME_W = 24u, + SAMPLING_SHADOW_I = 25u, + SAMPLING_SHADOW_J = 26u, + SAMPLING_SHADOW_K = 27u, }; /** @@ -675,12 +716,6 @@ struct ScatterRect { }; BLI_STATIC_ASSERT_ALIGN(ScatterRect, 16) -/** WORKAROUND(@fclem): This is because this file is included before common_math_lib.glsl. */ -#ifndef M_PI -# define EEVEE_PI -# define M_PI 3.14159265358979323846 /* pi */ -#endif - static inline float coc_radius_from_camera_depth(DepthOfFieldData dof, float depth) { depth = (dof.camera_type != CAMERA_ORTHO) ? 1.0f / depth : depth; @@ -725,10 +760,6 @@ static inline float circle_to_polygon_angle(float sides_count, float theta) return side * side_angle + final_local_theta; } -#ifdef EEVEE_PI -# undef M_PI -#endif - /** \} */ /* -------------------------------------------------------------------- */ @@ -833,30 +864,27 @@ static inline bool is_local_light(eLightType type) /* Using define because GLSL doesn't have inheritance, and encapsulation forces us to add some * unneeded padding. */ #define LOCAL_LIGHT_COMMON \ - /** Special radius factor for point lighting (volume). */ \ - float radius_squared; \ + /** --- Shadow Data --- */ \ + /** Shift to apply to the light origin to get the shadow projection origin. In light space. */ \ + packed_float3 shadow_position; \ + float _pad0; \ + /** Radius of the light for shadow ray casting. Simple scaling factor for rectangle lights. */ \ + float shadow_radius; \ + /** Radius of the light for shading. Bounding radius for rectangle lights. */ \ + float shape_radius; \ /** Maximum influence radius. Used for culling. Equal to clip far distance. */ \ float influence_radius_max; \ /** Influence radius (inverted and squared) adjusted for Surface / Volume power. */ \ float influence_radius_invsqr_surface; \ float influence_radius_invsqr_volume; \ - /** --- Shadow Data --- */ \ - /** Other parts of the perspective matrix. Assumes symmetric frustum. */ \ - float clip_side; \ /** Number of allocated tilemap for this local light. */ \ - int tilemaps_count; \ - float _pad7; \ - /** Shift to apply to the light origin to get the shadow projection origin. */ \ - float shadow_projection_shift; + int tilemaps_count; /* Untyped local light data. Gets reinterpreted to LightSpotData and LightAreaData. * Allow access to local light common data without casting. */ struct LightLocalData { LOCAL_LIGHT_COMMON - /** Padding reserved for when shadow_projection_shift will become a vec3. */ - float _pad0_reserved; - float _pad1_reserved; float _pad1; float _pad2; @@ -870,11 +898,7 @@ BLI_STATIC_ASSERT_ALIGN(LightLocalData, 16) struct LightSpotData { LOCAL_LIGHT_COMMON - /** Padding reserved for when shadow_projection_shift will become a vec3. */ - float _pad0_reserved; - float _pad1_reserved; - /** Sphere light radius. */ - float radius; + float _pad1; /** Scale and bias to spot equation parameter. Used for adjusting the falloff. */ float spot_mul; @@ -889,9 +913,6 @@ BLI_STATIC_ASSERT(sizeof(LightSpotData) == sizeof(LightLocalData), "Data size mu struct LightAreaData { LOCAL_LIGHT_COMMON - /** Padding reserved for when shadow_projection_shift will become a vec3. */ - float _pad0_reserved; - float _pad1_reserved; float _pad2; float _pad3; @@ -903,21 +924,21 @@ struct LightAreaData { BLI_STATIC_ASSERT(sizeof(LightAreaData) == sizeof(LightLocalData), "Data size must match") struct LightSunData { - float radius; - float _pad0; - float _pad1; - float _pad2; + /* Sun direction for shading. Use object_to_world for getting into shadow space. */ + packed_float3 direction; + /* Radius of the sun disk, one unit away from a shading point. */ + float shape_radius; - float _pad3; - float _pad4; /** --- Shadow Data --- */ /** Offset of the LOD min in LOD min tile units. Split positive and negative for bit-shift. */ int2 clipmap_base_offset_neg; - int2 clipmap_base_offset_pos; + /** Angle covered by the light shape for shadow ray casting. */ float shadow_angle; float _pad5; + float _pad3; + float _pad4; /** Offset to convert from world units to tile space of the clipmap_lod_max. */ float2 clipmap_origin; @@ -942,7 +963,11 @@ BLI_STATIC_ASSERT(sizeof(LightSunData) == sizeof(LightLocalData), "Data size mus #endif struct LightData { - /** Normalized object to world matrix. Stored transposed for compactness. */ + /** + * Normalized object to world matrix. Stored transposed for compactness. + * Used for shading and shadowing local lights, or shadowing sun lights. + * IMPORTANT: Not used for shading sun lights as this matrix is jittered. + */ Transform object_to_world; /** Power depending on shader type. Referenced by LightingType. */ @@ -965,7 +990,8 @@ struct LightData { float lod_bias; /* Shadow Map resolution maximum resolution. */ float lod_min; - float _pad1; + /* True if the light uses jittered soft shadows. */ + bool32_t shadow_jitter; float _pad2; #if USE_LIGHT_UNION @@ -1003,10 +1029,12 @@ static inline float3 light_position_get(LightData light) # define CHECK_TYPE_PAIR(a, b) # define CHECK_TYPE(a, b) # define FLOAT_AS_INT floatBitsToInt +# define INT_AS_FLOAT intBitsToFloat # define TYPECAST_NOOP #else /* C++ */ # define FLOAT_AS_INT float_as_int +# define INT_AS_FLOAT int_as_float # define TYPECAST_NOOP #endif @@ -1033,8 +1061,9 @@ static inline float3 light_position_get(LightData light) # define GARBAGE_VALUE 0.0f # endif -# define SAFE_BEGIN(data_type, check) \ - data_type data; \ +# define SAFE_BEGIN(dst_type, src_type, src_, check) \ + src_type _src = src_; \ + dst_type _dst; \ bool _validity_check = check; \ float _garbage = GARBAGE_VALUE; @@ -1042,7 +1071,11 @@ static inline float3 light_position_get(LightData light) # define SAFE_ASSIGN_LIGHT_TYPE_CHECK(_type, _value) \ (_validity_check ? (_value) : _type(_garbage)) #else -# define SAFE_BEGIN(data_type, check) data_type data; +# define SAFE_BEGIN(dst_type, src_type, src_, check) \ + UNUSED_VARS(check); \ + src_type _src = src_; \ + dst_type _dst; + # define SAFE_ASSIGN_LIGHT_TYPE_CHECK(_type, _value) _value #endif @@ -1052,21 +1085,28 @@ static inline float3 light_position_get(LightData light) # define DATA_MEMBER do_not_access_directly #endif +#define SAFE_READ_BEGIN(dst_type, light, check) \ + SAFE_BEGIN(dst_type, LightLocalData, light.DATA_MEMBER, check) +#define SAFE_READ_END() _dst + +#define SAFE_WRITE_BEGIN(src_type, src, check) SAFE_BEGIN(LightLocalData, src_type, src, check) +#define SAFE_WRITE_END(light) light.DATA_MEMBER = _dst; + #define ERROR_OFS(a, b) "Offset of " STRINGIFY(a) " mismatch offset of " STRINGIFY(b) /* This is a dangerous process, make sure to static assert every assignment. */ #define SAFE_ASSIGN(a, reinterpret_fn, in_type, b) \ - CHECK_TYPE_PAIR(data.a, reinterpret_fn(light.DATA_MEMBER.b)); \ - data.a = reinterpret_fn(SAFE_ASSIGN_LIGHT_TYPE_CHECK(in_type, light.DATA_MEMBER.b)); \ - BLI_STATIC_ASSERT(offsetof(decltype(data), a) == offsetof(LightLocalData, b), ERROR_OFS(a, b)) + CHECK_TYPE_PAIR(_src.b, in_type(_dst.a)); \ + CHECK_TYPE_PAIR(_dst.a, reinterpret_fn(_src.b)); \ + _dst.a = reinterpret_fn(SAFE_ASSIGN_LIGHT_TYPE_CHECK(in_type, _src.b)); \ + BLI_STATIC_ASSERT(offsetof(decltype(_dst), a) == offsetof(decltype(_src), b), ERROR_OFS(a, b)) #define SAFE_ASSIGN_FLOAT(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, float, b); #define SAFE_ASSIGN_FLOAT2(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, float2, b); +#define SAFE_ASSIGN_FLOAT3(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, float3, b); #define SAFE_ASSIGN_INT(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, int, b); #define SAFE_ASSIGN_FLOAT_AS_INT(a, b) SAFE_ASSIGN(a, FLOAT_AS_INT, float, b); -#define SAFE_ASSIGN_FLOAT_AS_INT2_COMBINE(a, b, c) \ - SAFE_ASSIGN_FLOAT_AS_INT(a.x, b); \ - SAFE_ASSIGN_FLOAT_AS_INT(a.y, c); +#define SAFE_ASSIGN_INT_AS_FLOAT(a, b) SAFE_ASSIGN(a, INT_AS_FLOAT, int, b); #if !USE_LIGHT_UNION || IS_CPP @@ -1076,62 +1116,98 @@ static inline float3 light_position_get(LightData light) namespace do_not_use { # endif -static inline LightSpotData light_local_data_get(LightData light) +static inline LightSpotData light_local_data_get_ex(LightData light, bool check) { - SAFE_BEGIN(LightSpotData, is_local_light(light.type)) - SAFE_ASSIGN_FLOAT(radius_squared, radius_squared) + SAFE_READ_BEGIN(LightSpotData, light, check) + SAFE_ASSIGN_FLOAT3(shadow_position, shadow_position) + SAFE_ASSIGN_FLOAT(_pad0, _pad0) + SAFE_ASSIGN_FLOAT(shadow_radius, shadow_radius) + SAFE_ASSIGN_FLOAT(shape_radius, shape_radius) SAFE_ASSIGN_FLOAT(influence_radius_max, influence_radius_max) SAFE_ASSIGN_FLOAT(influence_radius_invsqr_surface, influence_radius_invsqr_surface) SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume) - SAFE_ASSIGN_FLOAT(clip_side, clip_side) - SAFE_ASSIGN_FLOAT(shadow_projection_shift, shadow_projection_shift) SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count) - return data; -} - -static inline LightSpotData light_spot_data_get(LightData light) -{ - SAFE_BEGIN(LightSpotData, is_spot_light(light.type) || is_point_light(light.type)) - SAFE_ASSIGN_FLOAT(radius_squared, radius_squared) - SAFE_ASSIGN_FLOAT(influence_radius_max, influence_radius_max) - SAFE_ASSIGN_FLOAT(influence_radius_invsqr_surface, influence_radius_invsqr_surface) - SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume) - SAFE_ASSIGN_FLOAT(clip_side, clip_side) - SAFE_ASSIGN_FLOAT(shadow_projection_shift, shadow_projection_shift) - SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count) - SAFE_ASSIGN_FLOAT(radius, _pad1) SAFE_ASSIGN_FLOAT(spot_mul, _pad2) SAFE_ASSIGN_FLOAT2(spot_size_inv, _pad3) SAFE_ASSIGN_FLOAT(spot_tan, _pad4) SAFE_ASSIGN_FLOAT(spot_bias, _pad5) - return data; + return SAFE_READ_END(); +} + +static inline LightData light_local_data_set(LightData light, LightSpotData spot_data) +{ + SAFE_WRITE_BEGIN(LightSpotData, spot_data, is_local_light(light.type)) + SAFE_ASSIGN_FLOAT3(shadow_position, shadow_position) + SAFE_ASSIGN_FLOAT(_pad0, _pad0) + SAFE_ASSIGN_FLOAT(shadow_radius, shadow_radius) + SAFE_ASSIGN_FLOAT(shape_radius, shape_radius) + SAFE_ASSIGN_FLOAT(influence_radius_max, influence_radius_max) + SAFE_ASSIGN_FLOAT(influence_radius_invsqr_surface, influence_radius_invsqr_surface) + SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume) + SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count) + SAFE_ASSIGN_FLOAT(_pad2, spot_mul) + SAFE_ASSIGN_FLOAT2(_pad3, spot_size_inv) + SAFE_ASSIGN_FLOAT(_pad4, spot_tan) + SAFE_ASSIGN_FLOAT(_pad5, spot_bias) + SAFE_WRITE_END(light) + return light; +} + +static inline LightSpotData light_local_data_get(LightData light) +{ + return light_local_data_get_ex(light, is_local_light(light.type)); +} + +static inline LightSpotData light_spot_data_get(LightData light) +{ + return light_local_data_get_ex(light, is_spot_light(light.type) || is_point_light(light.type)); } static inline LightAreaData light_area_data_get(LightData light) { - SAFE_BEGIN(LightAreaData, is_area_light(light.type)) - SAFE_ASSIGN_FLOAT(radius_squared, radius_squared) + SAFE_READ_BEGIN(LightAreaData, light, is_area_light(light.type)) + SAFE_ASSIGN_FLOAT(shape_radius, shape_radius) SAFE_ASSIGN_FLOAT(influence_radius_max, influence_radius_max) SAFE_ASSIGN_FLOAT(influence_radius_invsqr_surface, influence_radius_invsqr_surface) SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume) - SAFE_ASSIGN_FLOAT(clip_side, clip_side) - SAFE_ASSIGN_FLOAT(shadow_projection_shift, shadow_projection_shift) + SAFE_ASSIGN_FLOAT3(shadow_position, shadow_position) + SAFE_ASSIGN_FLOAT(shadow_radius, shadow_radius) SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count) SAFE_ASSIGN_FLOAT2(size, _pad3) - return data; + return SAFE_READ_END(); } static inline LightSunData light_sun_data_get(LightData light) { - SAFE_BEGIN(LightSunData, is_sun_light(light.type)) - SAFE_ASSIGN_FLOAT(radius, radius_squared) - SAFE_ASSIGN_FLOAT_AS_INT2_COMBINE(clipmap_base_offset_neg, _pad7, shadow_projection_shift) - SAFE_ASSIGN_FLOAT_AS_INT2_COMBINE(clipmap_base_offset_pos, _pad0_reserved, _pad1_reserved) - SAFE_ASSIGN_FLOAT(shadow_angle, _pad1) + SAFE_READ_BEGIN(LightSunData, light, is_sun_light(light.type)) + SAFE_ASSIGN_FLOAT3(direction, shadow_position) + SAFE_ASSIGN_FLOAT(shape_radius, _pad0) + SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_neg.x, shadow_radius) + SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_neg.y, shape_radius) + SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_pos.x, influence_radius_max) + SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_pos.y, influence_radius_invsqr_surface) + SAFE_ASSIGN_FLOAT(shadow_angle, influence_radius_invsqr_volume) SAFE_ASSIGN_FLOAT2(clipmap_origin, _pad3) SAFE_ASSIGN_FLOAT_AS_INT(clipmap_lod_min, _pad4) SAFE_ASSIGN_FLOAT_AS_INT(clipmap_lod_max, _pad5) - return data; + return SAFE_READ_END(); +} + +static inline LightData light_sun_data_set(LightData light, LightSunData sun_data) +{ + SAFE_WRITE_BEGIN(LightSunData, sun_data, is_sun_light(light.type)) + SAFE_ASSIGN_FLOAT3(shadow_position, direction) + SAFE_ASSIGN_FLOAT(_pad0, shape_radius) + SAFE_ASSIGN_INT_AS_FLOAT(shadow_radius, clipmap_base_offset_neg.x) + SAFE_ASSIGN_INT_AS_FLOAT(shape_radius, clipmap_base_offset_neg.y) + SAFE_ASSIGN_INT_AS_FLOAT(influence_radius_max, clipmap_base_offset_pos.x) + SAFE_ASSIGN_INT_AS_FLOAT(influence_radius_invsqr_surface, clipmap_base_offset_pos.y) + SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, shadow_angle) + SAFE_ASSIGN_FLOAT2(_pad3, clipmap_origin) + SAFE_ASSIGN_INT_AS_FLOAT(_pad4, clipmap_lod_min) + SAFE_ASSIGN_INT_AS_FLOAT(_pad5, clipmap_lod_max) + SAFE_WRITE_END(light) + return light; } # if IS_CPP @@ -1159,7 +1235,7 @@ static inline LightSunData light_sun_data_get(LightData light) #undef SAFE_ASSIGN_FLOAT2 #undef SAFE_ASSIGN_INT #undef SAFE_ASSIGN_FLOAT_AS_INT -#undef SAFE_ASSIGN_FLOAT_AS_INT2_COMBINE +#undef SAFE_ASSIGN_INT_AS_FLOAT static inline int light_tilemap_max_get(LightData light) { @@ -1171,6 +1247,20 @@ static inline int light_tilemap_max_get(LightData light) return light.tilemap_index + light_local_data_get(light).tilemaps_count - 1; } +/* Return the number of tilemap needed for a local light. */ +static inline int light_local_tilemap_count(LightData light) +{ + if (is_spot_light(light.type)) { + return (light_spot_data_get(light).spot_tan > tanf(M_PI / 4.0)) ? 5 : 1; + } + else if (is_area_light(light.type)) { + return 5; + } + else { + return 6; + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1217,14 +1307,14 @@ struct ShadowTileMapData { int tiles_index; /** Index of persistent data in the persistent data buffer. */ int clip_data_index; - - float _pad0; /** Light type this tilemap is from. */ eLightType light_type; /** True if the tilemap is part of area light shadow and is one of the side projections. */ bool32_t is_area_side; - /** Distance behind the area light a shadow is shifted. */ - float area_shift; + /** Entire tilemap (all tiles) needs to be tagged as dirty. */ + bool32_t is_dirty; + + float _pad1; /** Near and far clip distances for punctual. */ float clip_near; float clip_far; @@ -1268,6 +1358,12 @@ struct ShadowTileMapClip { /** NOTE: These are positive just like camera parameters. */ int clip_near; int clip_far; + /* Transform the shadow is rendered with. Used to detect updates on GPU. */ + Transform object_to_world; + /* Integer offset of the center of the 16x16 tiles from the origin of the tile space. */ + int2 grid_offset; + int _pad0; + int _pad1; }; BLI_STATIC_ASSERT_ALIGN(ShadowTileMapClip, 16) @@ -1472,8 +1568,8 @@ struct ShadowSceneData { int step_count; /* Bounding radius for a film pixel at 1 unit from the camera. */ float film_pixel_radius; - - float _pad0; + /* Global switch for jittered shadows. */ + bool32_t use_jitter; }; BLI_STATIC_ASSERT_ALIGN(ShadowSceneData, 16) @@ -2055,6 +2151,10 @@ float4 utility_tx_sample_lut(sampler2DArray util_tx, float cos_theta, float roug #endif +#ifdef EEVEE_PI +# undef M_PI +#endif + /** \} */ #if IS_CPP diff --git a/source/blender/draw/engines/eevee_next/eevee_shadow.cc b/source/blender/draw/engines/eevee_next/eevee_shadow.cc index f1b4e11c253..1530fc70eae 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shadow.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shadow.cc @@ -68,13 +68,8 @@ void ShadowTileMap::sync_orthographic(const float4x4 &object_mat_, 1.0); } -void ShadowTileMap::sync_cubeface(eLightType light_type_, - const float4x4 &object_mat_, - float near_, - float far_, - float side_, - float shift, - eCubeFace face) +void ShadowTileMap::sync_cubeface( + eLightType light_type_, const float4x4 &object_mat_, float near_, float far_, eCubeFace face) { if (projection_type != SHADOW_PROJECTION_CUBEFACE || (cubeface != face)) { set_dirty(); @@ -85,14 +80,13 @@ void ShadowTileMap::sync_cubeface(eLightType light_type_, light_type = light_type_; is_area_side = is_area_light(light_type) && (face != eCubeFace::Z_NEG); - if ((clip_near != near_) || (clip_far != far_) || (half_size != side_)) { + if ((clip_near != near_) || (clip_far != far_)) { set_dirty(); } clip_near = near_; + half_size = near_; clip_far = far_; - area_shift = shift; - half_size = side_; center_offset = float2(0.0f); if (!equals_m4m4(object_mat.ptr(), object_mat_.ptr())) { @@ -102,8 +96,7 @@ void ShadowTileMap::sync_cubeface(eLightType light_type_, winmat = math::projection::perspective( -half_size, half_size, -half_size, half_size, clip_near, clip_far); - viewmat = float4x4(shadow_face_mat[cubeface]) * - math::from_location(float3(0.0f, 0.0f, -shift)) * math::invert(object_mat); + viewmat = float4x4(float3x3(shadow_face_mat[cubeface])) * math::invert(object_mat); /* Update corners. */ float4x4 viewinv = object_mat; @@ -222,86 +215,38 @@ void ShadowTileMapPool::end_sync(ShadowModule &module) * * \{ */ -void ShadowPunctual::sync(eLightType light_type, - const float4x4 &object_mat, - float cone_aperture, - float max_distance) +void ShadowPunctual::release_excess_tilemaps(const Light &light) { - if (is_spot_light(light_type)) { - tilemaps_needed_ = (cone_aperture > DEG2RADF(90.0f)) ? 5 : 1; - } - else if (is_area_light(light_type)) { - tilemaps_needed_ = 5; - } - else { - tilemaps_needed_ = 6; - } - - light_type_ = light_type; - /* Clamp for near/far clip distance calculation. */ - max_distance_ = max_ff(max_distance, 4e-4f); - - position_ = float3(object_mat[3]); -} - -void ShadowPunctual::release_excess_tilemaps() -{ - if (tilemaps_.size() <= tilemaps_needed_) { + int tilemaps_needed = light_local_tilemap_count(light); + if (tilemaps_.size() <= tilemaps_needed) { return; } auto span = tilemaps_.as_span(); - shadows_.tilemap_pool.release(span.drop_front(tilemaps_needed_)); - tilemaps_ = span.take_front(tilemaps_needed_); -} - -void ShadowPunctual::compute_projection_boundaries( - float max_lit_distance, float &near, float &far, float &side, float &back_shift) -{ - far = max_lit_distance; - near = side = (max_lit_distance / 4000.0f) / M_SQRT3; - back_shift = 0.0f; + shadows_.tilemap_pool.release(span.drop_front(tilemaps_needed)); + tilemaps_ = span.take_front(tilemaps_needed); } void ShadowPunctual::end_sync(Light &light) { ShadowTileMapPool &tilemap_pool = shadows_.tilemap_pool; - float side, near, far, shift; - compute_projection_boundaries(max_distance_, near, far, side, shift); - - float4x4 obmat_tmp = light.object_to_world; + float4x4 object_to_world = light.object_to_world; /* Acquire missing tile-maps. */ - while (tilemaps_.size() < tilemaps_needed_) { + int tilemaps_needed = light_local_tilemap_count(light); + while (tilemaps_.size() < tilemaps_needed) { tilemaps_.append(tilemap_pool.acquire()); } - tilemaps_[Z_NEG]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, Z_NEG); - if (tilemaps_needed_ >= 5) { - tilemaps_[X_POS]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, X_POS); - tilemaps_[X_NEG]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, X_NEG); - tilemaps_[Y_POS]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, Y_POS); - tilemaps_[Y_NEG]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, Y_NEG); - } - if (tilemaps_needed_ == 6) { - tilemaps_[Z_POS]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, Z_POS); + float near = int_as_float(light.clip_near); + float far = int_as_float(light.clip_far); + for (int i : tilemaps_.index_range()) { + eCubeFace face = eCubeFace(Z_NEG + i); + tilemaps_[face]->sync_cubeface(light.type, object_to_world, near, far, face); } + light.local.tilemaps_count = tilemaps_needed; light.tilemap_index = tilemap_pool.tilemaps_data.size(); - - light.local.tilemaps_count = tilemaps_needed_; - /* TODO(fclem): `as_uint()`. */ - union { - float f; - int32_t i; - } as_int; - as_int.f = near; - light.clip_near = as_int.i; - as_int.f = far; - light.clip_far = as_int.i; - light.local.clip_side = side; - light.local.shadow_projection_shift = shift; - for (ShadowTileMap *tilemap : tilemaps_) { /* Add shadow tile-maps grouped by lights to the GPU buffer. */ tilemap_pool.tilemaps_data.append(*tilemap); @@ -339,20 +284,21 @@ eShadowProjectionType ShadowDirectional::directional_distribution_type_get(const ************************************************************************/ void ShadowDirectional::cascade_tilemaps_distribution_near_far_points(const Camera &camera, + const Light &light, float3 &near_point, float3 &far_point) { const CameraData &cam_data = camera.data_get(); /* Ideally we should only take the intersection with the scene bounds. */ - far_point = (camera.position() - camera.forward() * cam_data.clip_far) * - float3x3(object_mat_.view<3, 3>()); - near_point = (camera.position() - camera.forward() * cam_data.clip_near) * - float3x3(object_mat_.view<3, 3>()); + far_point = transform_direction_transposed( + light.object_to_world, camera.position() - camera.forward() * cam_data.clip_far); + near_point = transform_direction_transposed( + light.object_to_world, camera.position() - camera.forward() * cam_data.clip_near); } /* \note All tile-maps are meant to have the same LOD but we still return a range starting at the * unique LOD. */ -IndexRange ShadowDirectional::cascade_level_range(const Camera &camera) +IndexRange ShadowDirectional::cascade_level_range(const Light &light, const Camera &camera) { using namespace blender::math; @@ -361,7 +307,7 @@ IndexRange ShadowDirectional::cascade_level_range(const Camera &camera) const CameraData &cam_data = camera.data_get(); float3 near_point, far_point; - cascade_tilemaps_distribution_near_far_points(camera, near_point, far_point); + cascade_tilemaps_distribution_near_far_points(camera, light, near_point, far_point); /* This gives the maximum resolution in depth we can have with a fixed set of tile-maps. Gives * the best results when view direction is orthogonal to the light direction. */ @@ -395,12 +341,15 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera { using namespace blender::math; + float4x4 object_mat = light.object_to_world; + object_mat.location() = float3(0.0f); + /* All tile-maps use the first level size. */ float half_size = ShadowDirectional::coverage_get(levels_range.first()) / 2.0f; float tile_size = ShadowDirectional::tile_size_get(levels_range.first()); float3 near_point, far_point; - cascade_tilemaps_distribution_near_far_points(camera, near_point, far_point); + cascade_tilemaps_distribution_near_far_points(camera, light, near_point, far_point); float2 local_view_direction = normalize(far_point.xy() - near_point.xy()); float2 farthest_tilemap_center = local_view_direction * half_size * (levels_range.size() - 1); @@ -426,7 +375,7 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera /* Equal spacing between cascades layers since we want uniform shadow density. */ int2 level_offset = origin_offset + shadow_cascade_grid_offset(light.sun.clipmap_base_offset_pos, i); - tilemap->sync_orthographic(object_mat_, level_offset, level, SHADOW_PROJECTION_CASCADE); + tilemap->sync_orthographic(object_mat, level_offset, level, SHADOW_PROJECTION_CASCADE); /* Add shadow tile-maps grouped by lights to the GPU buffer. */ shadows_.tilemap_pool.tilemaps_data.append(*tilemap); @@ -469,6 +418,9 @@ IndexRange ShadowDirectional::clipmap_level_range(const Camera &cam) void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera &camera) { + float4x4 object_mat = light.object_to_world; + object_mat.location() = float3(0.0f); + for (int lod : IndexRange(levels_range.size())) { ShadowTileMap *tilemap = tilemaps_[lod]; @@ -477,10 +429,10 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera * camera position. The offset is computed in smallest tile unit. */ float tile_size = ShadowDirectional::tile_size_get(level); /* Moving to light space by multiplying by the transpose (which is the inverse). */ - float2 light_space_camera_position = camera.position() * float2x3(object_mat_.view<2, 3>()); + float2 light_space_camera_position = camera.position() * float2x3(object_mat.view<2, 3>()); int2 level_offset = int2(math::round(light_space_camera_position / tile_size)); - tilemap->sync_orthographic(object_mat_, level_offset, level, SHADOW_PROJECTION_CLIPMAP); + tilemap->sync_orthographic(object_mat, level_offset, level, SHADOW_PROJECTION_CLIPMAP); /* Add shadow tile-maps grouped by lights to the GPU buffer. */ shadows_.tilemap_pool.tilemaps_data.append(*tilemap); @@ -513,7 +465,7 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera light.type = LIGHT_SUN; /* Used for selecting the clipmap level. */ - float3 location = camera.position() * float3x3(object_mat_.view<3, 3>()); + float3 location = camera.position() * float3x3(object_mat.view<3, 3>()); light.object_to_world.x.w = location.x; light.object_to_world.y.w = location.y; light.object_to_world.z.w = location.z; @@ -524,19 +476,10 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera light.sun.clipmap_lod_max = levels_range.last(); } -void ShadowDirectional::sync(const float4x4 &object_mat, float shadow_disk_angle) -{ - object_mat_ = object_mat; - /* Remove translation. */ - object_mat_.location() = float3(0.0f); - - disk_shape_angle_ = min_ff(shadow_disk_angle, DEG2RADF(179.9f)) / 2.0f; -} - -void ShadowDirectional::release_excess_tilemaps(const Camera &camera) +void ShadowDirectional::release_excess_tilemaps(const Light &light, const Camera &camera) { IndexRange levels_new = directional_distribution_type_get(camera) == SHADOW_PROJECTION_CASCADE ? - cascade_level_range(camera) : + cascade_level_range(light, camera) : clipmap_level_range(camera); if (levels_range == levels_new) { @@ -559,7 +502,7 @@ void ShadowDirectional::end_sync(Light &light, const Camera &camera) { ShadowTileMapPool &tilemap_pool = shadows_.tilemap_pool; IndexRange levels_new = directional_distribution_type_get(camera) == SHADOW_PROJECTION_CASCADE ? - cascade_level_range(camera) : + cascade_level_range(light, camera) : clipmap_level_range(camera); if (levels_range != levels_new) { @@ -584,7 +527,6 @@ void ShadowDirectional::end_sync(Light &light, const Camera &camera) light.tilemap_index = tilemap_pool.tilemaps_data.size(); light.clip_near = 0x7F7FFFFF; /* floatBitsToOrderedInt(FLT_MAX) */ light.clip_far = int(0xFF7FFFFFu ^ 0x7FFFFFFFu); /* floatBitsToOrderedInt(-FLT_MAX) */ - light.sun.shadow_angle = disk_shape_angle_; if (directional_distribution_type_get(camera) == SHADOW_PROJECTION_CASCADE) { cascade_tilemaps_distribution(light, camera); @@ -629,17 +571,22 @@ void ShadowModule::init() } ::Scene &scene = *inst_.scene; - bool enabled = (scene.eevee.flag & SCE_EEVEE_SHADOW_ENABLED) != 0; - if (assign_if_different(enabled_, enabled)) { + + bool update_lights = false; + bool enable_shadow = (scene.eevee.flag & SCE_EEVEE_SHADOW_ENABLED) != 0; + bool use_jitter = enable_shadow && + (!inst_.is_viewport() || + (!inst_.is_navigating() && !inst_.is_transforming() && !inst_.is_playback() && + (scene.eevee.flag & SCE_EEVEE_SHADOW_JITTERED_VIEWPORT))); + update_lights |= assign_if_different(enabled_, enable_shadow); + update_lights |= assign_if_different(data_.use_jitter, bool32_t(use_jitter)); + if (update_lights) { /* Force light reset. */ for (Light &light : inst_.lights.light_map_.values()) { light.initialized = false; } } - jittered_transparency_ = !inst_.is_viewport() || - scene.eevee.flag & SCE_EEVEE_SHADOW_JITTERED_VIEWPORT; - data_.ray_count = clamp_i(scene.eevee.shadow_ray_count, 1, SHADOW_MAX_RAY); data_.step_count = clamp_i(scene.eevee.shadow_step_count, 1, SHADOW_MAX_STEP); @@ -798,7 +745,7 @@ void ShadowModule::sync_object(const Object *ob, ShadowObject &shadow_ob = objects_.lookup_or_add_default(handle.object_key); shadow_ob.used = true; const bool is_initialized = shadow_ob.resource_handle.raw != 0; - const bool has_jittered_transparency = has_transparent_shadows && jittered_transparency_; + const bool has_jittered_transparency = has_transparent_shadows && data_.use_jitter; if (is_shadow_caster && (handle.recalc || !is_initialized || has_jittered_transparency)) { if (handle.recalc && is_initialized) { past_casters_updated_.append(shadow_ob.resource_handle.raw); @@ -830,10 +777,10 @@ void ShadowModule::end_sync() light.shadow_discard_safe(*this); } else if (light.directional != nullptr) { - light.directional->release_excess_tilemaps(inst_.camera); + light.directional->release_excess_tilemaps(light, inst_.camera); } else if (light.punctual != nullptr) { - light.punctual->release_excess_tilemaps(); + light.punctual->release_excess_tilemaps(light); } } diff --git a/source/blender/draw/engines/eevee_next/eevee_shadow.hh b/source/blender/draw/engines/eevee_next/eevee_shadow.hh index 61243f87678..6138cde9e96 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shadow.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shadow.hh @@ -28,24 +28,14 @@ class ShadowModule; class ShadowPipeline; struct Light; -enum eCubeFace { - /* Ordering by culling order. If cone aperture is shallow, we cull the later view. */ - Z_NEG = 0, - X_POS, - X_NEG, - Y_POS, - Y_NEG, - Z_POS, -}; - /* To be applied after view matrix. Follow same order as eCubeFace. */ -constexpr static const float shadow_face_mat[6][4][4] = { - {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}, /* Z_NEG */ - {{0, 0, -1, 0}, {-1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_POS */ - {{0, 0, 1, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_NEG */ - {{1, 0, 0, 0}, {0, 0, -1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_POS */ - {{-1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_NEG */ - {{1, 0, 0, 0}, {0, -1, 0, 0}, {0, 0, -1, 0}, {0, 0, 0, 1}}, /* Z_POS */ +constexpr static const float shadow_face_mat[6][3][3] = { + {{+1, +0, +0}, {+0, +1, +0}, {+0, +0, +1}}, /* Z_NEG */ + {{+0, +0, -1}, {-1, +0, +0}, {+0, +1, +0}}, /* X_POS */ + {{+0, +0, +1}, {+1, +0, +0}, {+0, +1, +0}}, /* X_NEG */ + {{+1, +0, +0}, {+0, +0, -1}, {+0, +1, +0}}, /* Y_POS */ + {{-1, +0, +0}, {+0, +0, +1}, {+0, +1, +0}}, /* Y_NEG */ + {{+1, +0, +0}, {+0, -1, +0}, {+0, +0, -1}}, /* Z_POS */ }; /* Converts to [-SHADOW_TILEMAP_RES / 2..SHADOW_TILEMAP_RES / 2] for XY and [0..1] for Z. */ @@ -102,24 +92,19 @@ struct ShadowTileMap : public ShadowTileMapData { int clipmap_level, eShadowProjectionType projection_type_); - void sync_cubeface(eLightType light_type_, - const float4x4 &object_mat, - float near, - float far, - float side, - float shift, - eCubeFace face); + void sync_cubeface( + eLightType light_type_, const float4x4 &object_mat, float near, float far, eCubeFace face); void debug_draw() const; void set_dirty() { - grid_shift = int2(SHADOW_TILEMAP_RES); + is_dirty = true; } void set_updated() { - grid_shift = int2(0); + is_dirty = false; } }; @@ -215,8 +200,6 @@ class ShadowModule { /* Used to call caster_update_ps_ only once per sync (Initialized on begin_sync). */ bool update_casters_ = false; - bool jittered_transparency_ = false; - /* -------------------------------------------------------------------- */ /** \name Tile-map Management * \{ */ @@ -397,14 +380,6 @@ class ShadowPunctual : public NonCopyable, NonMovable { ShadowModule &shadows_; /** Tile-map for each cube-face needed (in eCubeFace order). */ Vector tilemaps_; - /** Shape type. */ - eLightType light_type_; - /** Light position. */ - float3 position_; - /** Used to compute near and far clip distances. */ - float max_distance_; - /** Number of tile-maps needed to cover the light angular extents. */ - int tilemaps_needed_; public: ShadowPunctual(ShadowModule &module) : shadows_(module){}; @@ -416,32 +391,15 @@ class ShadowPunctual : public NonCopyable, NonMovable { shadows_.tilemap_pool.release(tilemaps_); } - /** - * Sync shadow parameters but do not allocate any shadow tile-maps. - */ - void sync(eLightType light_type, - const float4x4 &object_mat, - float cone_aperture, - float max_distance); - /** * Release the tile-maps that will not be used in the current frame. */ - void release_excess_tilemaps(); + void release_excess_tilemaps(const Light &light); /** * Allocate shadow tile-maps and setup views for rendering. */ void end_sync(Light &light); - - private: - /** - * Compute the projection matrix inputs. - * Make sure that the projection encompass all possible rays that can start in the projection - * quadrant. - */ - void compute_projection_boundaries( - float max_lit_distance, float &near, float &far, float &side, float &back_shift); }; class ShadowDirectional : public NonCopyable, NonMovable { @@ -449,12 +407,8 @@ class ShadowDirectional : public NonCopyable, NonMovable { ShadowModule &shadows_; /** Tile-map for each clip-map level. */ Vector tilemaps_; - /** Copy of object matrix. Normalized. */ - float4x4 object_mat_; /** Current range of clip-map / cascades levels covered by this shadow. */ - IndexRange levels_range; - /** Angle of the shadowed light shape. Might be scaled compared to the shading disk. */ - float disk_shape_angle_; + IndexRange levels_range = IndexRange(0); public: ShadowDirectional(ShadowModule &module) : shadows_(module){}; @@ -466,15 +420,10 @@ class ShadowDirectional : public NonCopyable, NonMovable { shadows_.tilemap_pool.release(tilemaps_); } - /** - * Sync shadow parameters but do not allocate any shadow tile-maps. - */ - void sync(const float4x4 &object_mat, float shadow_disk_angle); - /** * Release the tile-maps that will not be used in the current frame. */ - void release_excess_tilemaps(const Camera &camera); + void release_excess_tilemaps(const Light &light, const Camera &camera); /** * Allocate shadow tile-maps and setup views for rendering. @@ -497,12 +446,13 @@ class ShadowDirectional : public NonCopyable, NonMovable { private: IndexRange clipmap_level_range(const Camera &camera); - IndexRange cascade_level_range(const Camera &camera); + IndexRange cascade_level_range(const Light &light, const Camera &camera); void cascade_tilemaps_distribution(Light &light, const Camera &camera); void clipmap_tilemaps_distribution(Light &light, const Camera &camera); void cascade_tilemaps_distribution_near_far_points(const Camera &camera, + const Light &light, float3 &near_point, float3 &far_point); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl index 1ec61c9714b..34413099e86 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl @@ -26,16 +26,11 @@ void main() if (is_world_sun_light) { light.color = sunlight_buf.color; light.object_to_world = sunlight_buf.object_to_world; + + LightSunData sun_data = light_sun_data_get(light); + sun_data.direction = transform_z_axis(sunlight_buf.object_to_world); + light = light_sun_data_set(light, sun_data); /* NOTE: Use the radius from UI instead of auto sun size for now. */ - // light.power = sunlight_buf.power; -#if USE_LIGHT_UNION - // light.sun.radius = sunlight_buf.sun.radius; - // light.sun.shadow_angle = sunlight_buf.sun.shadow_angle; -#else - // light.do_not_access_directly.radius_squared = - // sunlight_buf.do_not_access_directly.radius_squared; - // light.do_not_access_directly._pad1 = sunlight_buf.do_not_access_directly._pad1; -#endif } /* NOTE: We know the index because sun lights are packed at the start of the input buffer. */ out_light_buf[light_cull_buf.local_lights_len + l_idx] = light; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl index 6012553cc0d..802291f6501 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl @@ -228,7 +228,6 @@ void light_eval_single(uint l_idx, thickness, P, Ng, - lv.L, ray_count, ray_step_count); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl index 16b6da82d84..0d6ecb08473 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl @@ -24,7 +24,7 @@ LightVector light_vector_get(LightData light, const bool is_directional, vec3 P) { LightVector lv; if (is_directional) { - lv.L = light_z_axis(light); + lv.L = light_sun_data_get(light).direction; lv.dist = 1.0; } else { @@ -63,12 +63,10 @@ LightVector light_shape_vector_get(LightData light, const bool is_directional, v return light_vector_get(light, is_directional, P); } -/* Rotate vector to light's local space. Does not translate. */ -vec3 light_world_to_local(LightData light, vec3 L) +vec3 light_world_to_local_direction(LightData light, vec3 L) { return transform_direction_transposed(light.object_to_world, L); } - vec3 light_world_to_local_point(LightData light, vec3 point) { return transform_point_inversed(light.object_to_world, point); @@ -87,7 +85,7 @@ float light_influence_attenuation(float dist, float inv_sqr_influence) float light_spot_attenuation(LightData light, vec3 L) { LightSpotData spot = light_spot_data_get(light); - vec3 lL = light_world_to_local(light, L); + vec3 lL = light_world_to_local_direction(light, L); float ellipse = inversesqrt(1.0 + length_squared(lL.xy * spot.spot_size_inv / lL.z)); float spotmask = smoothstep(0.0, 1.0, ellipse * spot.spot_mul + spot.spot_bias); return (lL.z > 0.0) ? spotmask : 0.0; @@ -109,16 +107,15 @@ float light_attenuation_common(LightData light, const bool is_directional, vec3 float light_shape_radius(LightData light) { - float radius; if (is_sun_light(light.type)) { - return light_sun_data_get(light).radius; + return light_sun_data_get(light).shape_radius; } else if (is_area_light(light.type)) { return length(light_area_data_get(light).size); } else { - return light_spot_data_get(light).radius; + return light_local_data_get(light).shape_radius; } } @@ -190,7 +187,7 @@ float light_point_light(LightData light, const bool is_directional, LightVector * http://www.cemyuksel.com/research/pointlightattenuation/ */ float d_sqr = square(lv.dist); - float r_sqr = light_local_data_get(light).radius_squared; + float r_sqr = square(light_local_data_get(light).shape_radius); /* Using reformulation that has better numerical precision. */ float power = 2.0 / (d_sqr + r_sqr + lv.dist * sqrt(d_sqr + r_sqr)); @@ -216,7 +213,7 @@ float light_sphere_disk_radius(float sphere_radius, float distance_to_sphere) float light_ltc( sampler2DArray utility_tx, LightData light, vec3 N, vec3 V, LightVector lv, vec4 ltc_mat) { - if (is_sphere_light(light.type) && lv.dist < light_spot_data_get(light).radius) { + if (is_sphere_light(light.type) && lv.dist < light_local_data_get(light).shape_radius) { /* Inside the sphere light, integrate over the hemisphere. */ return 1.0; } @@ -251,14 +248,14 @@ float light_ltc( vec2 size; if (is_sphere_light(light.type)) { /* Spherical omni or spot light. */ - size = vec2(light_sphere_disk_radius(light_spot_data_get(light).radius, lv.dist)); + size = vec2(light_sphere_disk_radius(light_local_data_get(light).shape_radius, lv.dist)); } else if (is_oriented_disk_light(light.type)) { /* View direction-aligned disk. */ - size = vec2(light_spot_data_get(light).radius); + size = vec2(light_local_data_get(light).shape_radius); } else if (is_sun_light(light.type)) { - size = vec2(light_sun_data_get(light).radius); + size = vec2(light_sun_data_get(light).shape_radius); } else { /* Area light. */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_shadow_setup_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_shadow_setup_comp.glsl index 2924a7067f5..df756f230c8 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_shadow_setup_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_shadow_setup_comp.glsl @@ -7,7 +7,9 @@ * Dispatched one thread per light. */ +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_fast_lib.glsl) int shadow_directional_coverage_get(int level) { @@ -15,25 +17,32 @@ int shadow_directional_coverage_get(int level) } void orthographic_sync(int tilemap_id, - Transform light_tx, + Transform object_to_world, int2 origin_offset, int clipmap_level, eShadowProjectionType projection_type) { - if (all(equal(tilemaps_buf[tilemap_id].grid_shift, int2(0)))) { - /* Only replace shift if it is not already dirty. */ - tilemaps_buf[tilemap_id].grid_shift = tilemaps_buf[tilemap_id].grid_offset - origin_offset; - } - tilemaps_buf[tilemap_id].grid_offset = origin_offset; + /* Do not check translation. */ + object_to_world.x.w = 0.0; + object_to_world.y.w = 0.0; + object_to_world.z.w = 0.0; - mat3x3 object_to_world_transposed = mat3x3(tilemaps_buf[tilemap_id].viewmat); - - if (!all(equal(object_to_world_transposed[0], light_tx.x.xyz)) || - !all(equal(object_to_world_transposed[1], light_tx.y.xyz)) || - !all(equal(object_to_world_transposed[2], light_tx.z.xyz))) + int clip_index = tilemaps_buf[tilemap_id].clip_data_index; + if (tilemaps_buf[tilemap_id].is_dirty || + !transform_equal(tilemaps_clip_buf[clip_index].object_to_world, object_to_world)) { + /* Set dirty as the light direction changed. */ tilemaps_buf[tilemap_id].grid_shift = int2(SHADOW_TILEMAP_RES); + tilemaps_clip_buf[clip_index].object_to_world = object_to_world; } + else { + /* Same light direction but camera might have moved. Shift tilemap grid. */ + tilemaps_buf[tilemap_id].grid_shift = origin_offset - + tilemaps_clip_buf[clip_index].grid_offset; + } + tilemaps_clip_buf[clip_index].grid_offset = origin_offset; + /* TODO(fclem): Remove this duplicate. Only needed because of the base offset packing. */ + tilemaps_buf[tilemap_id].grid_offset = origin_offset; float level_size = shadow_directional_coverage_get(clipmap_level); float half_size = level_size / 2.0; @@ -42,9 +51,9 @@ void orthographic_sync(int tilemap_id, /* object_mat is a rotation matrix. Reduce imprecision by taking the transpose which is also the * inverse in this particular case. */ - tilemaps_buf[tilemap_id].viewmat[0] = vec4(light_tx.x.xyz, 0.0); - tilemaps_buf[tilemap_id].viewmat[1] = vec4(light_tx.y.xyz, 0.0); - tilemaps_buf[tilemap_id].viewmat[2] = vec4(light_tx.z.xyz, 0.0); + tilemaps_buf[tilemap_id].viewmat[0] = vec4(object_to_world.x.xyz, 0.0); + tilemaps_buf[tilemap_id].viewmat[1] = vec4(object_to_world.y.xyz, 0.0); + tilemaps_buf[tilemap_id].viewmat[2] = vec4(object_to_world.z.xyz, 0.0); tilemaps_buf[tilemap_id].viewmat[3] = vec4(0.0, 0.0, 0.0, 1.0); tilemaps_buf[tilemap_id].projection_type = projection_type; @@ -113,21 +122,14 @@ void cascade_sync(inout LightData light) vec2 clipmap_origin = vec2(origin_offset) * tile_size; -#if USE_LIGHT_UNION + LightSunData sun_data = light_sun_data_get(light); /* Used as origin for the clipmap_base_offset trick. */ - light.sun.clipmap_origin = clipmap_origin; + sun_data.clipmap_origin = clipmap_origin; /* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */ - light.sun.clipmap_base_offset_pos = base_offset_pos; - light.sun.clipmap_base_offset_neg = ivec2(0); -#else - /* Used as origin for the clipmap_base_offset trick. */ - light.do_not_access_directly._pad3 = clipmap_origin; - /* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */ - light.do_not_access_directly._pad0_reserved = intBitsToFloat(base_offset_pos.x); - light.do_not_access_directly._pad1_reserved = intBitsToFloat(base_offset_pos.y); - light.do_not_access_directly._pad7 = intBitsToFloat(0); - light.do_not_access_directly.shadow_projection_shift = intBitsToFloat(0); -#endif + sun_data.clipmap_base_offset_pos = base_offset_pos; + sun_data.clipmap_base_offset_neg = ivec2(0); + + light = light_sun_data_set(light, sun_data); } void clipmap_sync(inout LightData light) @@ -176,40 +178,71 @@ void clipmap_sync(inout LightData light) light.object_to_world.x.w = ls_camera_position.x; light.object_to_world.y.w = ls_camera_position.y; light.object_to_world.z.w = ls_camera_position.z; -#if USE_LIGHT_UNION + + LightSunData sun_data = light_sun_data_get(light); /* Used as origin for the clipmap_base_offset trick. */ - light.sun.clipmap_origin = clipmap_origin; + sun_data.clipmap_origin = clipmap_origin; /* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */ - light.sun.clipmap_base_offset_pos = pos_offset; - light.sun.clipmap_base_offset_neg = neg_offset; -#else - /* Used as origin for the clipmap_base_offset trick. */ - light.do_not_access_directly._pad3 = clipmap_origin; - /* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */ - light.do_not_access_directly._pad0_reserved = intBitsToFloat(pos_offset.x); - light.do_not_access_directly._pad1_reserved = intBitsToFloat(pos_offset.y); - light.do_not_access_directly._pad7 = intBitsToFloat(neg_offset.x); - light.do_not_access_directly.shadow_projection_shift = intBitsToFloat(neg_offset.y); -#endif + sun_data.clipmap_base_offset_pos = pos_offset; + sun_data.clipmap_base_offset_neg = neg_offset; + + light = light_sun_data_set(light, sun_data); } -#if 0 -void cubeface_sync(int tilemap_id, vec3 jitter_offset) +void cubeface_sync(int tilemap_id, + Transform object_to_world, + eCubeFace cubeface, + vec3 jitter_offset) { - /* Update corners. */ - viewmat = shadow_face_mat[cubeface] * from_location(float3(0.0, 0.0, -shift)) * - invert(object_mat); + vec3 world_jitter_offset = transform_point(object_to_world, jitter_offset); + object_to_world.x.w = world_jitter_offset.x; + object_to_world.y.w = world_jitter_offset.y; + object_to_world.z.w = world_jitter_offset.z; + + int clip_index = tilemaps_buf[tilemap_id].clip_data_index; + if (tilemaps_buf[tilemap_id].is_dirty || + !transform_equal(tilemaps_clip_buf[clip_index].object_to_world, object_to_world)) + { + /* Set dirty as the light direction changed. */ + tilemaps_buf[tilemap_id].grid_shift = int2(SHADOW_TILEMAP_RES); + tilemaps_clip_buf[clip_index].object_to_world = object_to_world; + } + + /* Update View Matrix. */ + /* TODO(fclem): Could avoid numerical inversion since the transform is a unit matrix. */ + mat4x4 viewmat = invert(transform_to_matrix(object_to_world)); + + /* Use switch instead of inline array of float3x3. */ + switch (cubeface) { + case Z_NEG: + viewmat = mat4x4(mat3x3(+1, +0, +0, +0, +1, +0, +0, +0, +1)) * viewmat; + break; + case X_POS: + viewmat = mat4x4(mat3x3(+0, +0, -1, -1, +0, +0, +0, +1, +0)) * viewmat; + break; + case X_NEG: + viewmat = mat4x4(mat3x3(+0, +0, +1, +1, +0, +0, +0, +1, +0)) * viewmat; + break; + case Y_POS: + viewmat = mat4x4(mat3x3(+1, +0, +0, +0, +0, -1, +0, +1, +0)) * viewmat; + break; + case Y_NEG: + viewmat = mat4x4(mat3x3(-1, +0, +0, +0, +0, +1, +0, +1, +0)) * viewmat; + break; + case Z_POS: + viewmat = mat4x4(mat3x3(+1, +0, +0, +0, -1, +0, +0, +0, -1)) * viewmat; + break; + } + + mat4x4 prev_viewmat = tilemaps_buf[tilemap_id].viewmat; + + tilemaps_buf[tilemap_id].viewmat = viewmat; /* Update corners. */ - corners[0] += jitter_offset; - corners[1] += jitter_offset; - corners[2] += jitter_offset; - corners[3] += jitter_offset; - - /* Set dirty. */ - grid_shift = int2(SHADOW_TILEMAP_RES); + tilemaps_buf[tilemap_id].corners[0].xyz += jitter_offset; + tilemaps_buf[tilemap_id].corners[1].xyz += jitter_offset; + /* Other corners are deltas. They do not change after jitter. */ } -#endif void main() { @@ -226,16 +259,20 @@ void main() if (is_sun_light(light.type)) { /* Distant lights. */ + vec3 position_on_light = vec3(0.0); -#if 0 /* Jittered shadows. */ - vec3 position_on_light = random_position_on_light(light); - vec3 light_direction = normalize(position_on_light); - float3x3 object_to_world_transposed = transpose(from_up_axis(light_direction)); + if (light.shadow_jitter && uniform_buf.shadow.use_jitter) { + /* TODO(fclem): Remove atan here. We only need the cosine of the angle. */ + float shape_angle = atan_fast(light_sun_data_get(light).shape_radius); - light.object_to_world.x.xyz = object_to_world_transposed[0]; - light.object_to_world.y.xyz = object_to_world_transposed[1]; - light.object_to_world.z.xyz = object_to_world_transposed[2]; -#endif + /* Reverse to that first sample is straight up. */ + vec2 rand = 1.0 - sampling_rng_2D_get(SAMPLING_SHADOW_I); + vec3 shadow_direction = sample_uniform_cone(rand, cos(shape_angle)); + + shadow_direction = transform_direction(light.object_to_world, shadow_direction); + + light.object_to_world = transform_from_matrix(mat4x4(from_up_axis(shadow_direction))); + } if (light.type == LIGHT_SUN_ORTHO) { cascade_sync(light); @@ -244,30 +281,34 @@ void main() clipmap_sync(light); } } -#if 0 /* Jittered shadows. */ else { /* Local lights. */ -# if 0 /* Jittered shadows. */ - vec3 position_on_light = random_position_on_light(light); - light_buf[l_idx].shadow_position = position_on_light; + vec3 position_on_light = vec3(0.0); - int tilemap_count = 0; - if (is_area_light(light.type)) { - tilemap_count = 5; - } - else if (is_spot_light(light.type)) { - tilemap_count = (spot_angle > M_PI * 0.25) ? 5 : 1; - } - else { - tilemap_count = 6; + if (light.shadow_jitter && uniform_buf.shadow.use_jitter) { + vec3 rand = sampling_rng_3D_get(SAMPLING_SHADOW_I); + + if (is_area_light(light.type)) { + vec2 point_on_unit_shape = (light.type == LIGHT_RECT) ? rand.xy * 2.0 - 1.0 : + sample_disk(rand.xy); + position_on_light = vec3(point_on_unit_shape * light_area_data_get(light).size, 0.0); + } + else { + position_on_light = sample_ball(rand) * light_local_data_get(light).shape_radius; + } } + int tilemap_count = light_local_tilemap_count(light); for (int i = 0; i < tilemap_count; i++) { - cubeface_sync(light.tilemap_id + i, position_on_light); + cubeface_sync( + light.tilemap_index + i, light.object_to_world, eCubeFace(i), position_on_light); } -# endif + + LightSpotData local_data = light_local_data_get(light); + local_data.shadow_position = position_on_light; + + light = light_local_data_set(light, local_data); } -#endif light_buf[l_idx] = light; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_sunlight_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_sunlight_comp.glsl index 9d8f6da3ff2..6d8285eb17e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_sunlight_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_sunlight_comp.glsl @@ -79,12 +79,6 @@ void main() sunlight_buf.power[LIGHT_TRANSMISSION] = shape_power; sunlight_buf.power[LIGHT_VOLUME] = point_power; -#if USE_LIGHT_UNION - sunlight_buf.sun.radius = sun_radius; - sunlight_buf.sun.shadow_angle = sun_angle; -#else - sunlight_buf.do_not_access_directly.radius_squared = sun_radius; - sunlight_buf.do_not_access_directly._pad1 = sun_angle; -#endif + /* NOTE: Use the radius from UI instead of auto sun size for now. */ } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl index b0512d03d49..6ca73d15ccc 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl @@ -212,6 +212,13 @@ vec3 sample_sphere(vec2 rand) return vec3(sin_theta * sample_circle(rand.y), cos_theta); } +/* Returns a point in a ball that is "uniformly" distributed after projection along any axis. */ +vec3 sample_ball(vec3 rand) +{ + /* Completely ad-hoc, but works well in practice and is fast. */ + return sample_sphere(rand.xy) * sqrt(sqrt(rand.z)); +} + /** * Uniform hemisphere distribution. * \a rand is 2 random float in the [0..1] range. diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl index 77ecdeca6c6..9046c756408 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl @@ -77,7 +77,7 @@ vec3 debug_tile_state_color(eLightType type, ShadowSamplingTile tile) ShadowCoordinates debug_coord_get(vec3 P, LightData light) { if (is_sun_light(light.type)) { - vec3 lP = light_world_to_local(light, P); + vec3 lP = light_world_to_local_direction(light, P); return shadow_directional_coordinates(light, lP); } else { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_lib.glsl index e923c342d96..65783ef10cf 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_lib.glsl @@ -45,7 +45,9 @@ float shadow_punctual_sample_get(SHADOW_ATLAS_TYPE atlas_tx, LightData light, vec3 P) { + vec3 shadow_position = light_local_data_get(light).shadow_position; vec3 lP = transform_point_inversed(light.object_to_world, P); + lP -= shadow_position; int face_id = shadow_punctual_face_index_get(lP); lP = shadow_punctual_local_position_to_face_local(face_id, lP); ShadowCoordinates coord = shadow_punctual_coordinates(light, lP, face_id); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl index d06391793dd..d05d398a837 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl @@ -34,7 +34,7 @@ void shadow_tag_usage_tilemap_directional_at_level(uint l_idx, vec3 P, int level return; } - vec3 lP = light_world_to_local(light, P); + vec3 lP = light_world_to_local_direction(light, P); level = clamp( level, light_sun_data_get(light).clipmap_lod_min, light_sun_data_get(light).clipmap_lod_max); @@ -51,7 +51,7 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi return; } - vec3 lP = light_world_to_local(light, P); + vec3 lP = light_world_to_local_direction(light, P); /* TODO(Miguel Pozo): Implement lod_bias support. */ if (radius == 0.0) { @@ -60,8 +60,8 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi shadow_tag_usage_tile(light, coord.tilemap_tile, 0, coord.tilemap_index); } else { - vec3 start_lP = light_world_to_local(light, P - V * radius); - vec3 end_lP = light_world_to_local(light, P + V * radius); + vec3 start_lP = light_world_to_local_direction(light, P - V * radius); + vec3 end_lP = light_world_to_local_direction(light, P + V * radius); int min_level = shadow_directional_level(light, start_lP - light_position_get(light)); int max_level = shadow_directional_level(light, end_lP - light_position_get(light)); @@ -107,8 +107,8 @@ void shadow_tag_usage_tilemap_punctual(uint l_idx, vec3 P, float radius, int lod } } - /* TODO(fclem): 3D shift for jittered soft shadows. */ - lP += vec3(0.0, 0.0, -light_local_data_get(light).shadow_projection_shift); + /* Transform to shadow local space. */ + lP -= light_local_data_get(light).shadow_position; int lod = shadow_punctual_level(light, lP, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_test.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_test.glsl index 762386d8783..9f843c41a97 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_test.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_test.glsl @@ -30,10 +30,10 @@ void set_clipmap_data(inout LightData light, void set_clipmap_base_offset(inout LightData light, ivec2 clipmap_base_offset) { /* WATCH: Can get out of sync with light_sun_data_get(). */ - light.do_not_access_directly._pad7 = intBitsToFloat(0); - light.do_not_access_directly.shadow_projection_shift = intBitsToFloat(0); - light.do_not_access_directly._pad0_reserved = intBitsToFloat(clipmap_base_offset.x); - light.do_not_access_directly._pad1_reserved = intBitsToFloat(clipmap_base_offset.y); + light.do_not_access_directly.tilemaps_count = clipmap_base_offset.x; + light.do_not_access_directly.shadow_radius = intBitsToFloat(clipmap_base_offset.y); + light.do_not_access_directly.shape_radius = intBitsToFloat(0); + light.do_not_access_directly.influence_radius_max = intBitsToFloat(0); } void main() diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl index bb0b6b13ea5..c1698ae2a9b 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl @@ -140,7 +140,7 @@ void main() /* Clipping setup. */ if (tilemap_data.is_area_side) { /* Negative for tagging this case. See shadow_clip_vector_get for explanation. */ - render_view_buf[view_index].clip_distance_inv = -M_SQRT1_3 / tilemap_data.area_shift; + render_view_buf[view_index].clip_distance_inv = -1.0; } else if (is_point_light(tilemap_data.light_type)) { /* Clip as a sphere around the clip_near cube. */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl index 648c5c27156..0338be93bf3 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl @@ -68,9 +68,10 @@ void main() barrier(); ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy); - ivec2 tile_shifted = tile_co + clamp(tilemap.grid_shift, - ivec2(-SHADOW_TILEMAP_RES), - ivec2(SHADOW_TILEMAP_RES)); + ivec2 tile_shifted = tile_co + + clamp(tilemap.is_dirty ? ivec2(SHADOW_TILEMAP_RES) : tilemap.grid_shift, + ivec2(-SHADOW_TILEMAP_RES), + ivec2(SHADOW_TILEMAP_RES)); ivec2 tile_wrapped = ivec2((ivec2(SHADOW_TILEMAP_RES) + tile_shifted) % SHADOW_TILEMAP_RES); /* If this tile was shifted in and contains old information, update it. diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl index 6675a01be03..5bc4efd5b20 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl @@ -353,10 +353,8 @@ int shadow_punctual_face_index_get(vec3 lL) */ ShadowCoordinates shadow_punctual_coordinates(LightData light, vec3 lP, int face_id) { - float clip_near = intBitsToFloat(light.clip_near); - float clip_side = light_local_data_get(light).clip_side; /* UVs in [-1..+1] range. */ - vec2 tilemap_uv = (lP.xy * clip_near) / abs(lP.z * clip_side); + vec2 tilemap_uv = lP.xy / abs(lP.z); /* UVs in [0..1] range. */ tilemap_uv = saturate(tilemap_uv * 0.5 + 0.5); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl index 1520706d4ec..859760159b4 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl @@ -131,11 +131,11 @@ void shadow_map_trace_hit_check(inout ShadowMapTracingState state, ShadowTracing /* If the ray direction `L` is below the horizon defined by N (normalized) at the shading point, * push it just above the horizon so that this ray will never be below it and produce * over-shadowing (since light evaluation already clips the light shape). */ -vec3 shadow_ray_above_horizon_ensure(vec3 L, vec3 N) +vec3 shadow_ray_above_horizon_ensure(vec3 L, vec3 N, float max_clip_distance) { - float distance_to_plan = dot(L, -N); - if (distance_to_plan > 0.0) { - L += N * (0.01 + distance_to_plan); + float distance_to_plane = dot(L, -N); + if (distance_to_plane > 0.0 && distance_to_plane < 0.01 + max_clip_distance) { + L += N * (0.01 + distance_to_plane); } return L; } @@ -163,13 +163,14 @@ ShadowRayDirectional shadow_ray_generate_directional( float dist_to_near_plane = -lP.z - clip_near; /* Trace in a radius that is covered by low resolution page inflation. */ float max_tracing_distance = texel_radius * float(SHADOW_PAGE_RES << SHADOW_TILEMAP_LOD); + /* TODO(fclem): Remove atan here. We only need the cosine of the angle. */ float max_tracing_angle = atan_fast(max_tracing_distance / dist_to_near_plane); float shadow_angle = min(light_sun_data_get(light).shadow_angle, max_tracing_angle); /* Light shape is 1 unit away from the shading point. */ vec3 direction = sample_uniform_cone(sample_cylinder(random_2d), shadow_angle); - direction = shadow_ray_above_horizon_ensure(direction, lNg); + direction = shadow_ray_above_horizon_ensure(direction, lNg, max_tracing_distance); /* It only make sense to trace where there can be occluder. Clamp by distance to near plane. */ direction *= dist_to_near_plane / direction.z; @@ -237,33 +238,30 @@ ShadowRayPunctual shadow_ray_generate_punctual(LightData light, vec2 random_2d, float clip_far = intBitsToFloat(light.clip_far); float clip_near = intBitsToFloat(light.clip_near); - float clip_side = light_local_data_get(light).clip_side; + float shape_radius = light_spot_data_get(light).shadow_radius; - /* TODO(fclem): 3D shift for jittered soft shadows. */ - vec3 projection_origin = vec3(0.0, 0.0, -light_local_data_get(light).shadow_projection_shift); vec3 direction; if (is_area_light(light.type)) { random_2d *= light_area_data_get(light).size; - vec3 point_on_light_shape = vec3(random_2d, 0.0); + vec3 point_on_light_shape = vec3(random_2d * shape_radius, 0.0); direction = point_on_light_shape - lP; - direction = shadow_ray_above_horizon_ensure(direction, lNg); + direction = shadow_ray_above_horizon_ensure(direction, lNg, shape_radius); /* Clip the ray to not cross the near plane. * Scale it so that it encompass the whole cube (with a safety margin). */ float clip_distance = clip_near + 0.001; - float ray_length = max(abs(direction.x), max(abs(direction.y), abs(direction.z))); + float ray_length = reduce_max(abs(direction)); direction *= saturate((ray_length - clip_distance) / ray_length); } else { float dist; - vec3 L = normalize_and_get_length(lP, dist); + vec3 lL = normalize_and_get_length(lP, dist); /* Disk rotated towards light vector. */ vec3 right, up; - make_orthonormal_basis(L, right, up); + make_orthonormal_basis(lL, right, up); - float shape_radius = light_spot_data_get(light).radius; if (is_sphere_light(light.type)) { /* FIXME(weizhen): this is not well-defined when `dist < light.spot.radius`. */ shape_radius = light_sphere_disk_radius(shape_radius, dist); @@ -273,17 +271,19 @@ ShadowRayPunctual shadow_ray_generate_punctual(LightData light, vec2 random_2d, vec3 point_on_light_shape = right * random_2d.x + up * random_2d.y; direction = point_on_light_shape - lP; - direction = shadow_ray_above_horizon_ensure(direction, lNg); + direction = shadow_ray_above_horizon_ensure(direction, lNg, shape_radius); /* Clip the ray to not cross the light shape. */ - float clip_distance = light_spot_data_get(light).radius; + float clip_distance = clip_near + 0.001; direction *= saturate((dist - clip_distance) / dist); } + vec3 shadow_position = light_local_data_get(light).shadow_position; /* Compute the ray again. */ ShadowRayPunctual ray; - ray.origin = lP; - ray.direction = direction; + /* Transform to shadow local space. */ + ray.origin = lP - shadow_position; + ray.direction = direction + shadow_position; ray.light_tilemap_index = light.tilemap_index; ray.local_ray_up = safe_normalize(cross(cross(ray.origin, ray.direction), ray.direction)); ray.light = light; @@ -365,6 +365,7 @@ float shadow_texel_radius_at_position(LightData light, const bool is_directional } } else { + lP -= light_local_data_get(light).shadow_position; /* Simplification of `exp2(shadow_punctual_level_fractional)`. */ scale = shadow_punctual_pixel_ratio(light, lP, @@ -412,7 +413,6 @@ float shadow_eval(LightData light, float thickness, /* Only used if is_transmission is true. */ vec3 P, vec3 Ng, - vec3 L, int ray_count, int ray_step_count) { @@ -431,6 +431,12 @@ float shadow_eval(LightData light, vec2 random_pcf_2d = vec2(0.0); #endif + /* Direction towards the shadow center (punctual) or direction (direction). + * Not the same as the light vector if the shadow is jittered. */ + vec3 L = is_directional ? light_z_axis(light) : + normalize(light_position_get(light) + + light_local_data_get(light).shadow_position - P); + bool is_facing_light = (dot(Ng, L) > 0.0); /* Still bias the transmission surfaces towards the light if they are facing away. */ vec3 N_bias = (is_transmission && !is_facing_light) ? reflect(Ng, L) : Ng; @@ -451,9 +457,9 @@ float shadow_eval(LightData light, /* Add normal bias to avoid aliasing artifacts. */ P += N_bias * (texel_radius * shadow_normal_offset(Ng, L)); - vec3 lP = is_directional ? light_world_to_local(light, P) : - light_world_to_local(light, P - light_position_get(light)); - vec3 lNg = light_world_to_local(light, Ng); + vec3 lP = is_directional ? light_world_to_local_direction(light, P) : + light_world_to_local_point(light, P); + vec3 lNg = light_world_to_local_direction(light, Ng); /* Invert horizon clipping. */ lNg = (is_transmission) ? -lNg : lNg; /* Don't do a any horizon clipping in this case as the closure is lit from both sides. */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_volume_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_volume_lib.glsl index ca84d7d81cf..55daa78d14b 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_volume_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_volume_lib.glsl @@ -184,7 +184,7 @@ vec3 volume_light(LightData light, const bool is_directional, LightVector lv) { float power = 1.0; if (!is_directional) { - float volume_radius_squared = light_local_data_get(light).radius_squared; + float light_radius = light_local_data_get(light).shape_radius; /** * Using "Point Light Attenuation Without Singularity" from Cem Yuksel * http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf @@ -192,7 +192,7 @@ vec3 volume_light(LightData light, const bool is_directional, LightVector lv) */ float d = lv.dist; float d_sqr = square(d); - float r_sqr = volume_radius_squared; + float r_sqr = square(light_radius); /* Using reformulation that has better numerical precision. */ power = 2.0 / (d_sqr + r_sqr + d * sqrt(d_sqr + r_sqr)); diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh index 2e2e6b1cc97..ed3449e6512 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh @@ -64,11 +64,16 @@ GPU_SHADER_CREATE_INFO(eevee_light_culling_tile) GPU_SHADER_CREATE_INFO(eevee_light_shadow_setup) .do_static_compilation(true) - .additional_info("eevee_shared", "draw_view", "draw_view_culling", "eevee_global_ubo") + .additional_info("eevee_shared", + "draw_view", + "draw_view_culling", + "eevee_sampling_data", + "eevee_global_ubo") .local_group_size(CULLING_SELECT_GROUP_SIZE) .storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf") .storage_buf(1, Qualifier::READ_WRITE, "LightData", "light_buf[]") .storage_buf(2, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]") + .storage_buf(3, Qualifier::READ_WRITE, "ShadowTileMapClip", "tilemaps_clip_buf[]") .compute_source("eevee_light_shadow_setup_comp.glsl"); /** \} */ diff --git a/source/blender/draw/tests/eevee_test.cc b/source/blender/draw/tests/eevee_test.cc index f98c01b3956..93e588ecd5e 100644 --- a/source/blender/draw/tests/eevee_test.cc +++ b/source/blender/draw/tests/eevee_test.cc @@ -233,8 +233,7 @@ static void test_eevee_shadow_tag_update() { ShadowTileMap tilemap(0 * SHADOW_TILEDATA_PER_TILEMAP); - tilemap.sync_cubeface( - LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG); + tilemap.sync_cubeface(LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, Z_NEG); tilemaps_data.append(tilemap); } { @@ -1542,8 +1541,7 @@ static void test_eevee_shadow_page_mask_ex(int max_view_per_tilemap) { ShadowTileMap tilemap(0); - tilemap.sync_cubeface( - LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG); + tilemap.sync_cubeface(LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, Z_NEG); tilemaps_data.append(tilemap); } diff --git a/source/blender/gpu/GPU_shader_shared_utils.hh b/source/blender/gpu/GPU_shader_shared_utils.hh index fcee5e223f5..5fe801e44e1 100644 --- a/source/blender/gpu/GPU_shader_shared_utils.hh +++ b/source/blender/gpu/GPU_shader_shared_utils.hh @@ -34,6 +34,7 @@ # define BLI_STATIC_ASSERT_ALIGN(type_, align_) # define BLI_STATIC_ASSERT_SIZE(type_, size_) # define ENUM_OPERATORS(a, b) +# define UNUSED_VARS(a) /* Incompatible keywords. */ # define static # define inline diff --git a/source/blender/makesdna/DNA_light_defaults.h b/source/blender/makesdna/DNA_light_defaults.h index bbc94a7be33..f2c8899d1b8 100644 --- a/source/blender/makesdna/DNA_light_defaults.h +++ b/source/blender/makesdna/DNA_light_defaults.h @@ -45,6 +45,7 @@ .shadow_filter_radius = 1.0f, \ .shadow_resolution_scale = 1.0f, \ .shadow_maximum_resolution = 0.001f, \ + .shadow_jitter_overblur = 10.0f, \ .att_dist = 40.0f, \ .sun_angle = DEG2RADF(0.526f), \ .area_spread = DEG2RADF(180.0f), \ diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h index 0bdb438e3c5..7db467f0eb8 100644 --- a/source/blender/makesdna/DNA_light_types.h +++ b/source/blender/makesdna/DNA_light_types.h @@ -82,7 +82,7 @@ typedef struct Light { float shadow_filter_radius; float shadow_resolution_scale; float shadow_maximum_resolution; - char _pad3[4]; + float shadow_jitter_overblur; /* Preview */ struct PreviewImage *preview; @@ -147,6 +147,7 @@ enum { LA_USE_SOFT_FALLOFF = 1 << 21, /** Use absolute resolution clamping instead of relative. */ LA_SHAD_RES_ABSOLUTE = 1 << 22, + LA_SHADOW_JITTER = 1 << 23, }; /** #Light::falloff_type */ diff --git a/source/blender/makesrna/intern/rna_light.cc b/source/blender/makesrna/intern/rna_light.cc index 3ba345139f9..ef3be414d64 100644 --- a/source/blender/makesrna/intern/rna_light.cc +++ b/source/blender/makesrna/intern/rna_light.cc @@ -326,6 +326,26 @@ static void rna_def_light_shadow(StructRNA *srna, bool sun) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, 0, "rna_Light_update"); + prop = RNA_def_property(srna, "use_shadow_jitter", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "mode", LA_SHADOW_JITTER); + RNA_def_property_ui_text( + prop, + "Shadow Jitter", + "Enable jittered soft shadows to increase shadow precision (disabled in viewport unless " + "enabled in the render settings). Has a high performance impact"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, 0, "rna_Light_update"); + + prop = RNA_def_property(srna, "shadow_jitter_overblur", PROP_FLOAT, PROP_PERCENTAGE); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_ui_range(prop, 0.0f, 20.0f, 10.0f, 0); + RNA_def_property_ui_text( + prop, + "Shadow Jitter Overblur", + "Apply shadow tracing to each jittered sample to reduce under-sampling artifacts"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, 0, "rna_Light_update"); + if (sun) { prop = RNA_def_property(srna, "shadow_cascade_max_distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, nullptr, "cascade_max_dist"); diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 92ee0af9dc0..7feae92623a 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -8059,7 +8059,7 @@ static void rna_def_scene_eevee(BlenderRNA *brna) prop, "Tracing Method", "Select the tracing method used to find scene-ray intersections"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); - prop = RNA_def_property(srna, "use_shadow_jittered_viewport", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_shadow_jitter_viewport", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", SCE_EEVEE_SHADOW_JITTERED_VIEWPORT); RNA_def_property_ui_text(prop, "Jittered Shadows (Viewport)",