diff --git a/scripts/startup/bl_ui/properties_data_light.py b/scripts/startup/bl_ui/properties_data_light.py index 7ae6221f0d0..f40a099de99 100644 --- a/scripts/startup/bl_ui/properties_data_light.py +++ b/scripts/startup/bl_ui/properties_data_light.py @@ -200,9 +200,6 @@ def draw(self, context): row.prop(light, "shadow_maximum_resolution", text="Resolution Limit") if light.type != 'SUN': row.prop(light, "use_absolute_resolution", text="", icon='DRIVER_DISTANCE') - if light.type == 'SUN': - sub = sub.column(align=True) - sub.prop(light, "shadow_map_scale", text="Shadow Map Scale") class DATA_PT_EEVEE_light_influence(DataButtonsPanel, Panel): diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index d1daf388bd2..9cc3abbe60b 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -690,6 +690,7 @@ def draw(self, context): col = layout.column() col.prop(props, "shadow_resolution_scale", text="Resolution") + col.prop(props, "shadow_page_resolution", text="Page Resolution") class RENDER_PT_eevee_sampling(RenderButtonsPanel, Panel): diff --git a/source/blender/blenloader/intern/versioning_510.cc b/source/blender/blenloader/intern/versioning_510.cc index 31ae6fba4d9..6c68775e12a 100644 --- a/source/blender/blenloader/intern/versioning_510.cc +++ b/source/blender/blenloader/intern/versioning_510.cc @@ -1306,14 +1306,6 @@ void blo_do_versions_510(FileData *fd, Library * /*lib*/, Main *bmain) version_replace_outline_control_width_variation(bmain); } - if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 47)) { - for (Light &light : bmain->lights) { - if (light.shadow_map_scale <= 0.0f) { - light.shadow_map_scale = 1.0f; - } - } - } - if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 48) || !DNA_struct_member_exists(fd->filesdna, "Material", "char", "stencil_enabled")) { diff --git a/source/blender/blenloader/intern/versioning_520.cc b/source/blender/blenloader/intern/versioning_520.cc index e876c162203..ac565b148b1 100644 --- a/source/blender/blenloader/intern/versioning_520.cc +++ b/source/blender/blenloader/intern/versioning_520.cc @@ -1041,6 +1041,12 @@ void blo_do_versions_520(FileData * /*fd*/, Library * /*lib*/, Main *bmain) version_npr_image_sample_offset_socket_identifier(bmain); } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 47)) { + for (Scene &scene : bmain->scenes) { + scene.eevee.shadow_page_resolution = 256; + } + } + /** * 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/eevee_defines.hh b/source/blender/draw/engines/eevee/eevee_defines.hh index 5a6a0db6d22..5202190447c 100644 --- a/source/blender/draw/engines/eevee/eevee_defines.hh +++ b/source/blender/draw/engines/eevee/eevee_defines.hh @@ -102,8 +102,8 @@ # define SHADOW_PAGE_LOD 3 /* LOG2(SHADOW_PAGE_RES) */ #else # define SHADOW_PAGE_CLEAR_GROUP_SIZE 32 -# define SHADOW_PAGE_RES 256 # define SHADOW_PAGE_LOD 8 /* LOG2(SHADOW_PAGE_RES) */ +# define SHADOW_PAGE_RES (1 << SHADOW_PAGE_LOD) #endif /* For testing only. */ // #define SHADOW_FORCE_LOD0 diff --git a/source/blender/draw/engines/eevee/eevee_light.cc b/source/blender/draw/engines/eevee/eevee_light.cc index e24442f5d76..8ba81267061 100644 --- a/source/blender/draw/engines/eevee/eevee_light.cc +++ b/source/blender/draw/engines/eevee/eevee_light.cc @@ -499,11 +499,12 @@ void Light::sync(ShadowModule &shadows, this->power[LIGHT_VOLUME] = la->volume_fac * point_power * volume_visibility; this->lod_bias = shadows.global_lod_bias(); + this->shadow_page_lod = float(shadows.shadow_page_lod()); + this->shadow_page_res = float(1 << shadows.shadow_page_lod()); this->lod_min = shadow_lod_min_get(la); this->filter_radius = la->shadow_filter_radius; this->shadow_jitter = (la->mode & LA_SHADOW_JITTER) != 0; this->lightgroup_id = max_ii(lightgroup_id, 0); - this->shadow_map_scale = max_ff(la->shadow_map_scale, 0.0001f); this->visible_camera = (visibility_flag & OB_HIDE_CAMERA) == 0; if (la->mode & LA_SHADOW) { @@ -531,7 +532,8 @@ float Light::shadow_lod_min_get(const blender::Light *la) /* Property is in mm. Convert to unit. */ float max_res_unit = la->shadow_maximum_resolution; if (is_sun_light(this->type)) { - return log2f(max_res_unit * SHADOW_MAP_MAX_RES) - 1.0f; + float map_max_res = float((1 << int(this->shadow_page_lod)) * SHADOW_TILEMAP_RES); + return log2f(max_res_unit * map_max_res) - 1.0f; } /* Store absolute mode as negative. */ return (la->mode & LA_SHAD_RES_ABSOLUTE) ? -max_res_unit : max_res_unit; diff --git a/source/blender/draw/engines/eevee/eevee_light_shared.hh b/source/blender/draw/engines/eevee/eevee_light_shared.hh index 5c7771ec43d..a79f31fb237 100644 --- a/source/blender/draw/engines/eevee/eevee_light_shared.hh +++ b/source/blender/draw/engines/eevee/eevee_light_shared.hh @@ -213,12 +213,12 @@ struct [[host_shared]] LightData { * to zero after each sync cycle. */ uint2 shadow_set_membership; - float shadow_map_scale; - float _pad7; - float _pad8; + float shadow_page_lod; + float shadow_page_res; float _pad9; float _pad10; float _pad11; + float _pad13; union { union_t local; diff --git a/source/blender/draw/engines/eevee/eevee_pipeline.cc b/source/blender/draw/engines/eevee/eevee_pipeline.cc index d2e0ed463ad..e8abe5c0cb3 100644 --- a/source/blender/draw/engines/eevee/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee/eevee_pipeline.cc @@ -583,6 +583,8 @@ PassMain::Sub *ShadowPipeline::surface_material_add(blender::Material *material, material_pass->shader_set(GPU_pass_shader_get(gpupass)); material_pass->push_constant("use_shadow_caster_atlas", inst_.shadows.use_caster_atlas_push_ref()); + material_pass->push_constant("shadow_page_lod", + inst_.shadows.shadow_page_lod_push_ref()); return material_pass; } diff --git a/source/blender/draw/engines/eevee/eevee_shadow.cc b/source/blender/draw/engines/eevee/eevee_shadow.cc index 5054eb59bd4..96337ff37c2 100644 --- a/source/blender/draw/engines/eevee/eevee_shadow.cc +++ b/source/blender/draw/engines/eevee/eevee_shadow.cc @@ -63,7 +63,6 @@ ShadowTechnique ShadowModule::shadow_technique = ShadowTechnique::ATOMIC_RASTER; void ShadowTileMap::sync_orthographic(const float4x4 &object_mat_, int2 origin_offset, int clipmap_level, - float shadow_map_scale_, eShadowProjectionType projection_type_, uint2 shadow_set_membership_) { @@ -77,13 +76,6 @@ void ShadowTileMap::sync_orthographic(const float4x4 &object_mat_, light_type = eLightType::LIGHT_SUN; shadow_set_membership = shadow_set_membership_; - /* If the shadow map scale changed, mark the tilemap dirty so it gets re-generated. - * This mirrors the behaviour when the light direction / object matrix changes. */ - if (shadow_map_scale != shadow_map_scale_) { - set_dirty(); - } - shadow_map_scale = shadow_map_scale_; - grid_shift = origin_offset - grid_offset; grid_offset = origin_offset; @@ -328,8 +320,11 @@ void ShadowPunctual::end_sync(Light &light) eShadowProjectionType ShadowDirectional::directional_distribution_type_get(const Camera &camera) { - /* TODO(fclem): Enable the cascade projection if the FOV is tiny in perspective mode. */ - return camera.is_perspective() ? SHADOW_PROJECTION_CLIPMAP : SHADOW_PROJECTION_CASCADE; + if (camera.is_perspective()) { + /* TODO: Make telephoto auto-switch a user-configurable option. */ + return SHADOW_PROJECTION_CLIPMAP; + } + return SHADOW_PROJECTION_CASCADE; } static int clipmap_level_perspective_bias(const Camera &camera) @@ -521,7 +516,6 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera tilemap->sync_orthographic(object_mat, level_offset, level, - light.shadow_map_scale, SHADOW_PROJECTION_CASCADE, light.shadow_set_membership); @@ -550,7 +544,7 @@ ShadowDirectional::LevelSpan ShadowDirectional::clipmap_level_range(const Camera const CameraData &cam_data = cam.data_get(); /* Covers the closest points of the view. */ - int min_level = max_ii(0, floor(log2(max_ff(cam_data.clip_near, 1e-8f)))); + int min_level = floor(log2(max_ff(cam_data.clip_near, 1e-8f))); /* Covers the farthest points of the view. */ int max_level = ceil(log2(cam.bound_radius() + distance(cam.bound_center(), cam.position()))); @@ -594,7 +588,6 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera tilemap->sync_orthographic(object_mat, level_offset, level, - light.shadow_map_scale, SHADOW_PROJECTION_CLIPMAP, light.shadow_set_membership); @@ -813,7 +806,18 @@ void ShadowModule::init() data_.step_count = 1; } - const int2 atlas_extent = shadow_page_size_ * int2(SHADOW_PAGE_PER_ROW, SHADOW_PAGE_PER_COL); + /* Read shadow page resolution from user setting. */ + const int new_lod = log2_ceil_u(scene.eevee.shadow_page_resolution); + if (shadow_page_lod_ != new_lod) { + shadow_page_lod_ = new_lod; + /* Force full rebuild on resolution change. */ + do_full_update_ = true; + for (Light &light : inst_.lights.light_map_.values()) { + light.initialized = false; + } + } + + const int2 atlas_extent = (1 << shadow_page_lod_) * int2(SHADOW_PAGE_PER_ROW, SHADOW_PAGE_PER_COL); eGPUTextureUsage tex_usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE; if (ShadowModule::shadow_technique == ShadowTechnique::ATOMIC_RASTER) { @@ -834,7 +838,7 @@ void ShadowModule::init() shadow_pool_retry_countdown_ = 0; } - const size_t page_byte_size = square_i(shadow_page_size_) * sizeof(int); + const size_t page_byte_size = square_i(1 << shadow_page_lod_) * sizeof(int); const uint64_t requested_pool_byte_size = uint64_t(shadow_pool_size_requested_) * square_i(1024); const int requested_page_len = enabled_ ? max_ii(1, @@ -964,8 +968,8 @@ void ShadowModule::init() int size_in_tile = min_ii(1 << i, SHADOW_TILEMAP_RES); multi_viewports_[i][0] = 0; multi_viewports_[i][1] = 0; - multi_viewports_[i][2] = size_in_tile * shadow_page_size_; - multi_viewports_[i][3] = size_in_tile * shadow_page_size_; + multi_viewports_[i][2] = size_in_tile * (1 << shadow_page_lod_); + multi_viewports_[i][3] = size_in_tile * (1 << shadow_page_lod_); } } @@ -1396,6 +1400,8 @@ void ShadowModule::end_sync() /* De-fragment the free page heap after cache reuse phase which can leave hole. */ PassSimple::Sub &sub = pass.sub("Defrag"); sub.shader_set(inst_.shaders.static_shader_get(SHADOW_PAGE_DEFRAG)); + sub.push_constant("shadow_page_lod", shadow_page_lod_); + sub.push_constant("shadow_page_res", 1 << shadow_page_lod_); sub.bind_ssbo("pages_infos_buf", pages_infos_data_); sub.bind_ssbo("pages_free_buf", pages_free_data_); sub.bind_ssbo("pages_cached_buf", pages_cached_data_); @@ -1469,6 +1475,8 @@ void ShadowModule::end_sync() sub.framebuffer_set(&render_fb_); sub.state_set(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS); sub.shader_set(inst_.shaders.static_shader_get(SHADOW_PAGE_CLEAR)); + sub.push_constant("shadow_page_lod", shadow_page_lod_); + sub.push_constant("shadow_page_res", 1 << shadow_page_lod_); sub.bind_ssbo("pages_infos_buf", pages_infos_data_); sub.bind_ssbo("dst_coord_buf", dst_coord_buf_); sub.push_constant("use_shadow_caster_atlas", use_caster_atlas_push_); @@ -1680,7 +1688,7 @@ void ShadowModule::render(View &view, int2 extent) usage_tag_fb.ensure(usage_tag_fb_resolution_); eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_MEMORYLESS; - int2 fb_size = int2(SHADOW_TILEMAP_RES * shadow_page_size_); + int2 fb_size = int2(SHADOW_TILEMAP_RES * (1 << shadow_page_lod_)); int fb_layers = SHADOW_VIEW_MAX; if (shadow_technique == ShadowTechnique::ATOMIC_RASTER) { diff --git a/source/blender/draw/engines/eevee/eevee_shadow.hh b/source/blender/draw/engines/eevee/eevee_shadow.hh index 3fbd1c810e8..0f3f80381d4 100644 --- a/source/blender/draw/engines/eevee/eevee_shadow.hh +++ b/source/blender/draw/engines/eevee/eevee_shadow.hh @@ -79,7 +79,6 @@ struct ShadowTileMap : public ShadowTileMapData { eCubeFace cubeface = Z_NEG; /** Cached, used for detecting updates. */ float4x4 object_mat = float4x4::identity(); - float shadow_map_scale = 1.0f; public: ShadowTileMap(int tiles_index_) : ShadowTileMapData{} @@ -96,7 +95,6 @@ struct ShadowTileMap : public ShadowTileMapData { void sync_orthographic(const float4x4 &object_mat_, int2 origin_offset, int clipmap_level, - float shadow_map_scale, eShadowProjectionType projection_type_, uint2 shadow_set_membership_ = ~uint2(0)); @@ -350,10 +348,8 @@ class ShadowModule { /* Render setting that reduces the LOD for every light. */ float global_lod_bias_ = 0.0f; - /* Scale of sunlight shadow map size, also length of LOD0. */ - float shadow_map_scale = 1.0f; - /** For now, needs to be hardcoded. */ - int shadow_page_size_ = SHADOW_PAGE_RES; + /** Page pixel resolution LOD. Page resolution = 1 << shadow_page_lod_. */ + int shadow_page_lod_ = SHADOW_PAGE_LOD; /** Maximum number of allocated pages. Maximum value is SHADOW_MAX_PAGE. */ int shadow_page_len_ = SHADOW_MAX_PAGE; /** Requested and effectively allocated pool size in megabytes. */ @@ -446,6 +442,16 @@ class ShadowModule { return global_lod_bias_; } + int shadow_page_lod() const + { + return shadow_page_lod_; + } + + const int *shadow_page_lod_push_ref() const + { + return &shadow_page_lod_; + } + bool viewport_history_invalidated() const { return viewport_history_invalidated_; diff --git a/source/blender/draw/engines/eevee/shaders/eevee_debug.bsl.hh b/source/blender/draw/engines/eevee/shaders/eevee_debug.bsl.hh index 743ee080d1d..d261249e915 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_debug.bsl.hh +++ b/source/blender/draw/engines/eevee/shaders/eevee_debug.bsl.hh @@ -245,7 +245,7 @@ ShadowDebugOutput debug_atlas_values([[resource_table]] const ShadowDebug &srt, [[resource_table]] const ShadowRenderData &srd = srt.shadow_data; ShadowCoordinates coord = srt.debug_coord_get(P, light); - float depth = srd.read_depth(coord); + float depth = srd.read_depth(coord, int(light.shadow_page_lod)); return { .valid = true, .color_add = float4((depth == -1) ? float3(1.0f, 0.0f, 0.0f) : float3(1.0f / depth), 0.0f) * @@ -263,7 +263,7 @@ struct AtomicCostCtx { [[resource_table]] const ShadowRenderData &srd = srt.shadow_data; ShadowCoordinates coord = srt.debug_coord_get(P, light); - uint u_cost = floatBitsToUint(srd.read_depth(coord)); + uint u_cost = floatBitsToUint(srd.read_depth(coord, int(light.shadow_page_lod))); return float(u_cost - floatBitsToUint(FLT_MAX)); } diff --git a/source/blender/draw/engines/eevee/shaders/eevee_light_shadow_setup.bsl.hh b/source/blender/draw/engines/eevee/shaders/eevee_light_shadow_setup.bsl.hh index 868b1a25483..8fe050f7c23 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_light_shadow_setup.bsl.hh +++ b/source/blender/draw/engines/eevee/shaders/eevee_light_shadow_setup.bsl.hh @@ -15,9 +15,9 @@ namespace eevee::light { -int shadow_directional_coverage_get(int level) +float shadow_directional_coverage_get(int level) { - return 1 << level; + return exp2(float(level)); } struct Resources { @@ -166,7 +166,7 @@ struct Resources { int level = level_min + lod; /* Compute full offset from world origin to the smallest clipmap tile centered around the * camera position. The offset is computed in smallest tile unit. */ - float tile_size = float(1 << level) / float(SHADOW_TILEMAP_RES); + float tile_size = exp2(float(level)) / float(SHADOW_TILEMAP_RES); int2 level_offset = int2(round(ls_camera_position.xy / tile_size)); orthographic_sync(light.tilemap_index + lod, diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow.bsl.hh b/source/blender/draw/engines/eevee/shaders/eevee_shadow.bsl.hh index c5530aa6215..b08d3e71828 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_shadow.bsl.hh +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow.bsl.hh @@ -26,7 +26,7 @@ struct ShadowRenderData { [[resource_table, condition(shadow_random)]] srt_t sampling; [[resource_table, condition(shadow_random)]] srt_t util_tx; - float read_depth(ShadowCoordinates coord) const + float read_depth(ShadowCoordinates coord, int shadow_page_lod) const { ShadowSamplingTile tile = shadow_tile_load( shadow_tilemaps_tx, coord.tilemap_tile, coord.tilemap_index); @@ -34,12 +34,11 @@ struct ShadowRenderData { return -1.0f; } /* Using bitwise ops is way faster than integer ops. */ - constexpr uint page_shift = uint(SHADOW_PAGE_LOD); - constexpr uint page_mask = ~(0xFFFFFFFFu << uint(SHADOW_PAGE_LOD)); + uint page_shift = uint(shadow_page_lod); + uint page_mask = ~(0xFFFFFFFFu << page_shift); uint2 texel = coord.tilemap_texel; - /* Shift LOD0 pixels so that they get wrapped at the right position for the given LOD. */ - texel += uint2(tile.lod_offset << SHADOW_PAGE_LOD); + texel += uint2(tile.lod_offset << page_shift); /* Scale to LOD pixels (merge LOD0 pixels together) then mask to get pixel in page. */ uint2 texel_page = (texel >> tile.lod) & page_mask; texel = (uint2(tile.page.xy) << page_shift) | texel_page; @@ -56,7 +55,7 @@ struct ShadowRenderData { lP = shadow_punctual_local_position_to_face_local(face_id, lP); ShadowCoordinates coord = shadow_punctual_coordinates(light, lP, face_id); - float radial_dist = read_depth(coord); + float radial_dist = read_depth(coord, int(light.shadow_page_lod)); if (radial_dist == -1.0f) { return 1e10f; } @@ -70,7 +69,7 @@ struct ShadowRenderData { float3 lP = transform_direction_transposed(light.object_to_world, P); ShadowCoordinates coord = shadow_directional_coordinates(light, lP); - float depth = read_depth(coord); + float depth = read_depth(coord, int(light.shadow_page_lod)); if (depth == -1.0f) { return 1e10f; } diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_clear.bsl.hh b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_clear.bsl.hh index d83d498232c..4b2b6722102 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_clear.bsl.hh +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_clear.bsl.hh @@ -18,6 +18,8 @@ struct PageClear { shadow_caster_atlas_img; [[push_constant]] const int use_shadow_caster_atlas; + [[push_constant]] const int shadow_page_lod; + [[push_constant]] const int shadow_page_res; }; /** @@ -32,7 +34,7 @@ void page_clear_comp([[resource_table]] PageClear &srt, /* We clear the destination pixels directly for the atomicMin technique. */ uint page_packed = srt.dst_coord_buf[global_id.z]; uint3 page_co = shadow_page_unpack(page_packed); - page_co.xy = page_co.xy * SHADOW_PAGE_RES + global_id.xy; + page_co.xy = page_co.xy * srt.shadow_page_res + global_id.xy; imageStoreFast(srt.shadow_atlas_img, int3(page_co), uint4(floatBitsToUint(FLT_MAX))); if (srt.use_shadow_caster_atlas != 0) { diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_defrag.bsl.hh b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_defrag.bsl.hh index b053f06038b..9c0ae8afc7b 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_defrag.bsl.hh +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_defrag.bsl.hh @@ -32,6 +32,8 @@ using TileMaps = eevee::shadow::TileMaps; struct Commands { [[storage(5, write)]] DispatchCommand &clear_dispatch_buf; [[storage(6, write)]] DrawCommandArray &tile_draw_buf; + [[push_constant]] const int shadow_page_lod; + [[push_constant]] const int shadow_page_res; }; [[compute, local_size(1)]] @@ -49,8 +51,8 @@ void defrag([[resource_table]] PageAllocator &allocator, stats.statistics_buf.view_needed_count = 0; /* Reset clear command indirect buffer. */ - cmds.clear_dispatch_buf.num_groups_x = SHADOW_PAGE_RES / SHADOW_PAGE_CLEAR_GROUP_SIZE; - cmds.clear_dispatch_buf.num_groups_y = SHADOW_PAGE_RES / SHADOW_PAGE_CLEAR_GROUP_SIZE; + cmds.clear_dispatch_buf.num_groups_x = cmds.shadow_page_res / SHADOW_PAGE_CLEAR_GROUP_SIZE; + cmds.clear_dispatch_buf.num_groups_y = cmds.shadow_page_res / SHADOW_PAGE_CLEAR_GROUP_SIZE; cmds.clear_dispatch_buf.num_groups_z = 0; /* Reset TBDR command indirect buffer. */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.bsl.hh b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.bsl.hh index 093320932e9..a6ec1dacea8 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.bsl.hh +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.bsl.hh @@ -171,9 +171,8 @@ float shadow_directional_level_fractional(LightData light, float3 lP) /* Since the distance is centered around the camera (and thus by extension the tile-map), * we need to multiply by 2 to get the lod level which covers the following range: * [-coverage_get(lod)/2..coverage_get(lod)/2] */ - float scale = light.shadow_map_scale; - lod = log2(shadow_directional_clipmap_scale(light, lP) * narrowing / scale); + lod = log2(shadow_directional_clipmap_scale(light, lP) * narrowing); lod = max(lod, light.lod_min); } @@ -223,7 +222,8 @@ float shadow_punctual_pixel_ratio(LightData light, /* Clamp in shadow space. */ film_pixel_footprint = max(film_pixel_footprint, -light.lod_min); /* Cube-face diagonal divided by LOD0 resolution. */ - constexpr float shadow_pixel_radius = (2.0f * M_SQRT2) / SHADOW_MAP_MAX_RES; + float shadow_map_max_res = float(int(light.shadow_page_res) * SHADOW_TILEMAP_RES); + float shadow_pixel_radius = (2.0f * M_SQRT2) / shadow_map_max_res; return saturate(shadow_pixel_radius / film_pixel_footprint); } @@ -263,12 +263,15 @@ struct ShadowCoordinates { }; /* Assumes tilemap_uv is already saturated. */ -ShadowCoordinates shadow_coordinate_from_uvs(int tilemap_index, float2 tilemap_uv) +ShadowCoordinates shadow_coordinate_from_uvs(int tilemap_index, + float2 tilemap_uv, + int shadow_page_lod) { ShadowCoordinates ret; ret.tilemap_index = tilemap_index; - ret.tilemap_texel = uint2(tilemap_uv * (float(SHADOW_MAP_MAX_RES) - 1e-2f)); - ret.tilemap_tile = ret.tilemap_texel >> uint(SHADOW_PAGE_LOD); + ret.tilemap_texel = uint2(tilemap_uv * + (float((1 << shadow_page_lod) * SHADOW_TILEMAP_RES) - 1e-2f)); + ret.tilemap_tile = ret.tilemap_texel >> uint(shadow_page_lod); return ret; } @@ -300,11 +303,13 @@ ShadowCoordinates shadow_directional_coordinates_at_level(LightData light, float level_relative); /* UV in [0..1] range over the tilemap. */ float2 tilemap_uv = lP.xy - light.sun().clipmap_origin; - tilemap_uv *= exp2(float(-lod_relative)) / light.shadow_map_scale; + tilemap_uv *= exp2(float(-lod_relative)); tilemap_uv -= float2(clipmap_offset) * (1.0f / float(SHADOW_TILEMAP_RES)); tilemap_uv = saturate(tilemap_uv + 0.5f); - return shadow_coordinate_from_uvs(light.tilemap_index + level_relative, tilemap_uv); + return shadow_coordinate_from_uvs(light.tilemap_index + level_relative, + tilemap_uv, + int(light.shadow_page_lod)); } /** @@ -378,7 +383,9 @@ ShadowCoordinates shadow_punctual_coordinates(LightData light, float3 lP, int fa /* UVs in [0..1] range. */ tilemap_uv = saturate(tilemap_uv * 0.5f + 0.5f); - return shadow_coordinate_from_uvs(light.tilemap_index + face_id, tilemap_uv); + return shadow_coordinate_from_uvs(light.tilemap_index + face_id, + tilemap_uv, + int(light.shadow_page_lod)); } /** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tracing.bsl.hh b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tracing.bsl.hh index 2fbd9ca20e1..168995eaba9 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tracing.bsl.hh +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tracing.bsl.hh @@ -158,7 +158,8 @@ ShadowRayDirectional shadow_ray_generate_directional(LightData light, /* Assumed to be non-null. */ 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); + float max_tracing_distance = texel_radius * + float(int(light.shadow_page_res) << SHADOW_TILEMAP_LOD); float max_tracing_angle_cos = cos_from_tan(max_tracing_distance / dist_to_near_plane); /* Taking max of cosines to get the minimum of the angles. */ float shadow_angle_cos = max(light.sun().shadow_angle_cos, max_tracing_angle_cos); @@ -188,7 +189,7 @@ ShadowTracingSample shadow_map_trace_sample([[resource_table]] ShadowRenderData ShadowCoordinates coord = shadow_directional_coordinates(ray.light, ray_pos); - float depth = srd.read_depth(coord); + float depth = srd.read_depth(coord, int(ray.light.shadow_page_lod)); /* Distance from near plane. */ float clip_near = orderedIntBitsToFloat(ray.light.clip_near); float3 occluder_pos = float3(ray_pos.xy, -depth - clip_near); @@ -295,7 +296,7 @@ ShadowTracingSample shadow_map_trace_sample([[resource_table]] ShadowRenderData float3 face_pos = shadow_punctual_local_position_to_face_local(face_id, receiver_pos); ShadowCoordinates coord = shadow_punctual_coordinates(ray.light, face_pos, face_id); - float radial_occluder_depth = srd.read_depth(coord); + float radial_occluder_depth = srd.read_depth(coord, int(ray.light.shadow_page_lod)); float3 occluder_pos = receiver_pos * (radial_occluder_depth / length(receiver_pos)); /* Transform to ray local space. */ @@ -391,7 +392,8 @@ float shadow_texel_radius_at_position([[resource_table]] const Uniform &uni, } /* Pixel bounding radius inside a tilemap of unit scale. * Take only half of it because we want the radius and not the diameter. */ - constexpr float texel_radius = M_SQRT2 / SHADOW_MAP_MAX_RES; + float map_max_res = float(int(light.shadow_page_res) * SHADOW_TILEMAP_RES); + float texel_radius = M_SQRT2 / map_max_res; return texel_radius * scale; } @@ -615,7 +617,9 @@ struct ShadowCasterTraceResult { uint caster_id; }; -uint shadow_caster_id_read([[resource_table]] ShadowRenderData &srd, ShadowCoordinates coord) +uint shadow_caster_id_read([[resource_table]] ShadowRenderData &srd, + ShadowCoordinates coord, + int shadow_page_lod) { ShadowSamplingTile tile = shadow_tile_load( srd.shadow_tilemaps_tx, coord.tilemap_tile, coord.tilemap_index); @@ -623,11 +627,11 @@ uint shadow_caster_id_read([[resource_table]] ShadowRenderData &srd, ShadowCoord return SHADOW_CASTER_UNKNOWN_ID; } - constexpr uint page_shift = uint(SHADOW_PAGE_LOD); - constexpr uint page_mask = ~(0xFFFFFFFFu << uint(SHADOW_PAGE_LOD)); + uint page_shift = uint(shadow_page_lod); + uint page_mask = ~(0xFFFFFFFFu << page_shift); uint2 texel = coord.tilemap_texel; - texel += uint2(tile.lod_offset << SHADOW_PAGE_LOD); + texel += uint2(tile.lod_offset << page_shift); uint2 texel_page = (texel >> tile.lod) & page_mask; texel = (uint2(tile.page.xy) << page_shift) | texel_page; @@ -668,7 +672,8 @@ ShadowCasterTraceResult shadow_trace_caster_id([[resource_table]] ShadowRenderDa ShadowTracingSample samp = shadow_map_trace_sample(srd, state, ray); if (!samp.skip_sample) { - caster_id = shadow_caster_id_read(srd, shadow_caster_sample_coord(ray, state.ray_time)); + caster_id = shadow_caster_id_read( + srd, shadow_caster_sample_coord(ray, state.ray_time), int(ray.light.shadow_page_lod)); } shadow_map_trace_hit_check(state, samp, i == sample_count); } diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surf_shadow.bsl.hh b/source/blender/draw/engines/eevee/shaders/eevee_surf_shadow.bsl.hh index e548886c172..c9c1ec83e92 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_surf_shadow.bsl.hh +++ b/source/blender/draw/engines/eevee/shaders/eevee_surf_shadow.bsl.hh @@ -45,6 +45,7 @@ struct SurfShadow { shadow_caster_atlas_img; [[push_constant]] const int use_shadow_caster_atlas; + [[push_constant]] const int shadow_page_lod; }; [[fragment]] [[texture_atomic]] @@ -87,8 +88,8 @@ void surf_shadow([[resource_table]] PipelineConstants &pipe, int2 texel_co = int2(frag_co.xy); /* Using bitwise ops is way faster than integer ops. */ - constexpr int page_shift = SHADOW_PAGE_LOD; - constexpr int page_mask = ~(0xFFFFFFFF << SHADOW_PAGE_LOD); + int page_shift = srt.shadow_page_lod; + int page_mask = ~(0xFFFFFFFF << srt.shadow_page_lod); int2 tile_co = texel_co >> page_shift; int2 texel_page = texel_co & page_mask; diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h index 83512767f05..90eb8b3d68b 100644 --- a/source/blender/makesdna/DNA_light_types.h +++ b/source/blender/makesdna/DNA_light_types.h @@ -173,7 +173,7 @@ struct Light { /* Deprecated. */ DNA_DEPRECATED float energy_deprecated = 10.0f; - float shadow_map_scale = 1.0f; + char _pad_scale[4] = {}; int lightgroup_id = 0; int _pad3 = 0; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 7041db019a1..66d359f8fbe 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -2802,6 +2802,8 @@ struct SceneEEVEE { int stage_output_view = SCE_EEVEE_STAGE_VIEW_OFF; char use_outline = true; char _pad2[3] = {}; + int shadow_page_resolution = 256; + char _pad5[4] = {}; }; struct SceneGpencil { diff --git a/source/blender/makesrna/intern/rna_light.cc b/source/blender/makesrna/intern/rna_light.cc index fba7db5a97a..afd15726725 100644 --- a/source/blender/makesrna/intern/rna_light.cc +++ b/source/blender/makesrna/intern/rna_light.cc @@ -393,14 +393,6 @@ static void rna_def_light_shadow(StructRNA *srna, bool sun) "the cost of shadow quality."); RNA_def_property_update(prop, 0, "rna_Light_update"); - prop = RNA_def_property(srna, "shadow_map_scale", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "shadow_map_scale"); - RNA_def_property_float_default(prop, 1.0f); - RNA_def_property_range(prop, 0.0001f, FLT_MAX); - RNA_def_property_ui_range(prop, 0.0001f, FLT_MAX, 0.05f, 2); - RNA_def_property_ui_text(prop, "Shadows Map Scale", " "); - 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( diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 5443e240614..a4c061fb693 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -9244,6 +9244,14 @@ static void rna_def_scene_eevee(BlenderRNA *brna) {0, nullptr, 0, nullptr, nullptr}, }; + static const EnumPropertyItem eevee_shadow_page_resolution_items[] = { + {256, "256", 0, "256 px", ""}, + {512, "512", 0, "512 px", ""}, + {1024, "1024", 0, "1024 px", ""}, + {2048, "2048", 0, "2048 px", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + static const EnumPropertyItem eevee_gi_visibility_size_items[] = { {8, "8", 0, "8 px", ""}, {16, "16", 0, "16 px", ""}, @@ -9796,6 +9804,15 @@ static void rna_def_scene_eevee(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + prop = RNA_def_property(srna, "shadow_page_resolution", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, eevee_shadow_page_resolution_items); + RNA_def_property_ui_text(prop, + "Shadow Page Resolution", + "Pixel resolution of each shadow page. " + "Higher values improve shadow quality but use more memory per page"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + prop = RNA_def_property(srna, "shadow_ray_count", PROP_INT, PROP_UNSIGNED); RNA_def_property_range(prop, 1, 4); RNA_def_property_ui_text(