diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index 877d54bee7d..af38e5323bd 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -727,6 +727,9 @@ class RENDER_PT_eevee_next_shadows(RenderButtonsPanel, Panel): col = layout.column() col.prop(props, "shadow_normal_bias", text="Normal Bias") + col = layout.column() + col.prop(props, "use_shadow_jittered_viewport", text="Jittered Transparency (Viewport)") + class RENDER_PT_eevee_sampling(RenderButtonsPanel, Panel): bl_label = "Sampling" diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index 29e64f9763a..70f4e62dce6 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -356,6 +356,10 @@ Material &MaterialModule::material_sync(Object *ob, mat.is_alpha_blend_transparent = use_forward_pipeline && GPU_material_flag_get(mat.shading.gpumat, GPU_MATFLAG_TRANSPARENT); + mat.has_transparent_shadows = blender_mat->blend_flag & MA_BL_TRANSPARENT_SHADOW && + GPU_material_flag_get(mat.shading.gpumat, + GPU_MATFLAG_TRANSPARENT); + return mat; }); diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh index ed4ded64cf9..456cd1cff70 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.hh +++ b/source/blender/draw/engines/eevee_next/eevee_material.hh @@ -291,6 +291,7 @@ struct MaterialPass { struct Material { bool is_alpha_blend_transparent; + bool has_transparent_shadows; bool has_surface; bool has_volume; MaterialPass shadow; diff --git a/source/blender/draw/engines/eevee_next/eevee_shadow.cc b/source/blender/draw/engines/eevee_next/eevee_shadow.cc index 1ca795b803e..ac5053c4f13 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shadow.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shadow.cc @@ -742,6 +742,9 @@ void ShadowModule::init() } } + jittered_transparency_ = !inst_.is_viewport() || + scene.eevee.flag & SCE_EEVEE_SHADOW_JITTERED_VIEWPORT; + data_.ray_count = clamp_i(inst_.scene->eevee.shadow_ray_count, 1, SHADOW_MAX_RAY); data_.step_count = clamp_i(inst_.scene->eevee.shadow_step_count, 1, SHADOW_MAX_STEP); data_.normal_bias = max_ff(inst_.scene->eevee.shadow_normal_bias, 0.0f); @@ -824,6 +827,8 @@ void ShadowModule::begin_sync() past_casters_updated_.clear(); curr_casters_updated_.clear(); curr_casters_.clear(); + jittered_transparent_casters_.clear(); + update_casters_ = true; { Manager &manager = *inst_.manager; @@ -897,7 +902,8 @@ void ShadowModule::begin_sync() void ShadowModule::sync_object(const Object *ob, const ObjectHandle &handle, const ResourceHandle &resource_handle, - bool is_alpha_blend) + bool is_alpha_blend, + bool has_transparent_shadows) { bool is_shadow_caster = !(ob->visibility_flag & OB_HIDE_SHADOW); if (!is_shadow_caster && !is_alpha_blend) { @@ -907,11 +913,18 @@ 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; - if ((handle.recalc != 0 || !is_initialized) && is_shadow_caster) { - if (shadow_ob.resource_handle.raw != 0) { + const bool has_jittered_transparency = has_transparent_shadows && jittered_transparency_; + 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); } - curr_casters_updated_.append(resource_handle.raw); + + if (has_jittered_transparency) { + jittered_transparent_casters_.append(resource_handle.raw); + } + else { + curr_casters_updated_.append(resource_handle.raw); + } } shadow_ob.resource_handle = resource_handle; @@ -973,6 +986,7 @@ void ShadowModule::end_sync() } past_casters_updated_.push_update(); curr_casters_updated_.push_update(); + jittered_transparent_casters_.push_update(); curr_casters_.push_update(); @@ -1080,6 +1094,22 @@ void ShadowModule::end_sync() pass.barrier(GPU_BARRIER_SHADER_STORAGE); } + { + /* Mark for update all shadow pages touching a jittered transparency shadow caster. */ + PassSimple &pass = jittered_transparent_caster_update_ps_; + pass.init(); + if (jittered_transparent_casters_.size() > 0) { + pass.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_UPDATE)); + pass.bind_ssbo("tilemaps_buf", tilemap_pool.tilemaps_data); + pass.bind_ssbo("tiles_buf", tilemap_pool.tiles_data); + pass.bind_ssbo("bounds_buf", &manager.bounds_buf.current()); + pass.bind_ssbo("resource_ids_buf", jittered_transparent_casters_); + pass.dispatch( + int3(jittered_transparent_casters_.size(), 1, tilemap_pool.tilemaps_data.size())); + pass.barrier(GPU_BARRIER_SHADER_STORAGE); + } + } + /* Non volume usage tagging happens between these two steps. * (Setup at begin_sync) */ @@ -1339,7 +1369,7 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx) } inst_.hiz_buffer.update(); - bool update_casters = true; + bool first_loop = true; do { DRW_stats_group_start("Shadow"); @@ -1347,12 +1377,15 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx) GPU_uniformbuf_clear_to_zero(shadow_multi_view_.matrices_ubo_get()); inst_.manager->submit(tilemap_setup_ps_, view); - if (assign_if_different(update_casters, false)) { + if (assign_if_different(update_casters_, false)) { /* Run caster update only once. */ /* TODO(fclem): There is an optimization opportunity here where we can * test casters only against the static tilemaps instead of all of them. */ inst_.manager->submit(caster_update_ps_, view); } + if (assign_if_different(first_loop, false)) { + inst_.manager->submit(jittered_transparent_caster_update_ps_, view); + } inst_.manager->submit(tilemap_usage_ps_, view); inst_.manager->submit(tilemap_update_ps_, view); diff --git a/source/blender/draw/engines/eevee_next/eevee_shadow.hh b/source/blender/draw/engines/eevee_next/eevee_shadow.hh index 9c879cddfe5..335369c4ee3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shadow.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shadow.hh @@ -213,6 +213,11 @@ class ShadowModule { /** Map of shadow casters to track deletion & update of intersected shadows. */ Map objects_; + /* 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 * \{ */ @@ -229,9 +234,11 @@ class ShadowModule { Framebuffer usage_tag_fb; PassSimple caster_update_ps_ = {"CasterUpdate"}; + PassSimple jittered_transparent_caster_update_ps_ = {"TransparentCasterUpdate"}; /** List of Resource IDs (to get bounds) for tagging passes. */ StorageVectorBuffer past_casters_updated_ = {"PastCastersUpdated"}; StorageVectorBuffer curr_casters_updated_ = {"CurrCastersUpdated"}; + StorageVectorBuffer jittered_transparent_casters_ = {"JitteredTransparentCasters"}; /** List of Resource IDs (to get bounds) for getting minimum clip-maps bounds. */ StorageVectorBuffer curr_casters_ = {"CurrCasters"}; @@ -332,7 +339,8 @@ class ShadowModule { void sync_object(const Object *ob, const ObjectHandle &handle, const ResourceHandle &resource_handle, - bool is_alpha_blend); + bool is_alpha_blend, + bool has_transparent_shadows); void end_sync(); void set_lights_data(); diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc index 2a9a83f3e5c..81fb0755328 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc @@ -114,6 +114,7 @@ void SyncModule::sync_mesh(Object *ob, } bool is_alpha_blend = false; + bool has_transparent_shadows = false; float inflate_bounds = 0.0f; for (auto i : material_array.gpu_materials.index_range()) { GPUBatch *geom = mat_geom[i]; @@ -147,6 +148,7 @@ void SyncModule::sync_mesh(Object *ob, geometry_call(material.reflection_probe_shading.sub_pass, geom, res_handle); is_alpha_blend = is_alpha_blend || material.is_alpha_blend_transparent; + has_transparent_shadows = has_transparent_shadows || material.has_transparent_shadows; ::Material *mat = GPU_material_get_material(gpu_material); inst_.cryptomatte.sync_material(mat); @@ -162,7 +164,7 @@ void SyncModule::sync_mesh(Object *ob, inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials); - inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend); + inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend, has_transparent_shadows); inst_.cryptomatte.sync_object(ob, res_handle); } @@ -192,6 +194,7 @@ bool SyncModule::sync_sculpt(Object *ob, MaterialArray &material_array = inst_.materials.material_array_get(ob, has_motion); bool is_alpha_blend = false; + bool has_transparent_shadows = false; float inflate_bounds = 0.0f; for (SculptBatch &batch : sculpt_batches_per_material_get(ob_ref.object, material_array.gpu_materials)) @@ -226,6 +229,7 @@ bool SyncModule::sync_sculpt(Object *ob, geometry_call(material.reflection_probe_shading.sub_pass, geom, res_handle); is_alpha_blend = is_alpha_blend || material.is_alpha_blend_transparent; + has_transparent_shadows = has_transparent_shadows || material.has_transparent_shadows; GPUMaterial *gpu_material = material_array.gpu_materials[batch.material_slot]; ::Material *mat = GPU_material_get_material(gpu_material); @@ -245,7 +249,7 @@ bool SyncModule::sync_sculpt(Object *ob, inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials); - inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend); + inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend, has_transparent_shadows); inst_.cryptomatte.sync_object(ob, res_handle); return true; @@ -308,13 +312,15 @@ void SyncModule::sync_point_cloud(Object *ob, ::Material *mat = GPU_material_get_material(gpu_material); inst_.cryptomatte.sync_material(mat); - bool is_alpha_blend = material.is_alpha_blend_transparent; - if (GPU_material_has_displacement_output(gpu_material) && mat->inflate_bounds != 0.0f) { inst_.manager->update_handle_bounds(res_handle, ob_ref, mat->inflate_bounds); } - inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend); + inst_.shadows.sync_object(ob, + ob_handle, + res_handle, + material.is_alpha_blend_transparent, + material.has_transparent_shadows); } /** \} */ @@ -485,8 +491,9 @@ void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandl gpencil_drawcall_flush(iter); - bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */ - inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend); + bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */ + bool has_transparent_shadows = true; /* TODO material.has_transparent_shadows. */ + inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend, has_transparent_shadows); } /** \} */ @@ -560,13 +567,15 @@ void SyncModule::sync_curves(Object *ob, ::Material *mat = GPU_material_get_material(gpu_material); inst_.cryptomatte.sync_material(mat); - bool is_alpha_blend = material.is_alpha_blend_transparent; - if (GPU_material_has_displacement_output(gpu_material) && mat->inflate_bounds != 0.0f) { inst_.manager->update_handle_bounds(res_handle, ob_ref, mat->inflate_bounds); } - inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend); + inst_.shadows.sync_object(ob, + ob_handle, + res_handle, + material.is_alpha_blend_transparent, + material.has_transparent_shadows); } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl index 99b391f5174..46a44988966 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl @@ -31,7 +31,8 @@ void main() nodetree_surface(0.0); float noise_offset = sampling_rng_1D_get(SAMPLING_TRANSPARENCY); - float random_threshold = transparency_hashed_alpha_threshold(1.0, noise_offset, g_data.P); + float random_threshold = transparency_hashed_alpha_threshold( + uniform_buf.pipeline.alpha_hash_scale, noise_offset, g_data.P); float transparency = average(g_transmittance); if (transparency > random_threshold) { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index f87876706ea..435279b9aa6 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -2887,6 +2887,7 @@ enum { SCE_EEVEE_DOF_JITTER = (1 << 23), SCE_EEVEE_SHADOW_ENABLED = (1 << 24), SCE_EEVEE_RAYTRACE_OPTIONS_SPLIT = (1 << 25), + SCE_EEVEE_SHADOW_JITTERED_VIEWPORT = (1 << 26), }; typedef enum RaytraceEEVEE_Flag { diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index b8cc87bad26..186a285d0d7 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -8038,6 +8038,14 @@ 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); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", SCE_EEVEE_SHADOW_JITTERED_VIEWPORT); + RNA_def_property_ui_text(prop, + "Jittered Shadows (Viewport)", + "Enable jittered shadows on the viewport. (Jittered shadows are always " + "enabled for final renders)"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + /* Volumetrics */ prop = RNA_def_property(srna, "volumetric_start", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_ui_text(prop, "Start", "Start distance of the volumetric effect");