From b4316445a8cbdbd59148a7a28af57dc9566e7f24 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Tue, 7 Nov 2023 15:28:07 +0100 Subject: [PATCH] EEVEE-Next: Add Max Displacement option Add a Max Displacement option to Material settings, so frustum culling can work correctly with vertex displacement. Pull Request: https://projects.blender.org/blender/blender/pulls/114200 --- scripts/startup/bl_ui/properties_material.py | 3 +- .../draw/engines/eevee_next/eevee_instance.cc | 11 +++-- .../draw/engines/eevee_next/eevee_sync.cc | 44 +++++++++++++++---- .../draw/engines/eevee_next/eevee_sync.hh | 1 + source/blender/draw/intern/draw_manager.hh | 29 ++++++++++-- source/blender/draw/intern/draw_resource.hh | 12 ++++- .../blender/draw/intern/draw_shader_shared.h | 2 +- source/blender/gpu/GPU_material.h | 1 + source/blender/gpu/intern/gpu_material.cc | 7 +++ source/blender/makesdna/DNA_material_types.h | 5 +++ .../blender/makesrna/intern/rna_material.cc | 9 ++++ 11 files changed, 105 insertions(+), 19 deletions(-) diff --git a/scripts/startup/bl_ui/properties_material.py b/scripts/startup/bl_ui/properties_material.py index 74f7b3d0407..c48a2b9fde6 100644 --- a/scripts/startup/bl_ui/properties_material.py +++ b/scripts/startup/bl_ui/properties_material.py @@ -299,7 +299,8 @@ class EEVEE_NEXT_MATERIAL_PT_settings_surface(MaterialButtonsPanel, Panel): col.prop(mat, "use_backface_culling", text="Camera") col.prop(mat, "use_backface_culling_shadow", text="Shadow") - # TODO(fclem): Displacement option + layout.prop(mat, "max_vertex_displacement", text="Max Displacement") + layout.prop(mat, "use_transparent_shadow") col = layout.column() diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index df7a057be55..8dae3eb3ac5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -196,6 +196,7 @@ void Instance::object_sync(Object *ob) OB_VOLUME, OB_LAMP, OB_LIGHTPROBE); + const bool is_drawable_type = is_renderable_type && !ELEM(ob->type, OB_LAMP, OB_LIGHTPROBE); const int ob_visibility = DRW_object_visibility_in_active_context(ob); const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 && (ob->type == OB_MESH); @@ -208,15 +209,17 @@ void Instance::object_sync(Object *ob) /* TODO cleanup. */ ObjectRef ob_ref = DRW_object_ref_get(ob); - ResourceHandle res_handle = manager->resource_handle(ob_ref); - ObjectHandle &ob_handle = sync.sync_object(ob); + ResourceHandle res_handle = {0}; + if (is_drawable_type) { + res_handle = manager->resource_handle(ob_ref); + } if (partsys_is_visible && ob != DRW_context_state_get()->object_edit) { auto sync_hair = [&](ObjectHandle hair_handle, ModifierData &md, ParticleSystem &particle_sys) { ResourceHandle _res_handle = manager->resource_handle(float4x4(ob->object_to_world)); - sync.sync_curves(ob, hair_handle, _res_handle, &md, &particle_sys); + sync.sync_curves(ob, hair_handle, _res_handle, ob_ref, &md, &particle_sys); }; foreach_hair_particle_handle(ob, ob_handle, sync_hair); } @@ -238,7 +241,7 @@ void Instance::object_sync(Object *ob) sync.sync_volume(ob, ob_handle, res_handle); break; case OB_CURVES: - sync.sync_curves(ob, ob_handle, res_handle); + sync.sync_curves(ob, ob_handle, res_handle, ob_ref); break; case OB_GPENCIL_LEGACY: sync.sync_gpencil(ob, ob_handle, res_handle); diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc index 4184c99b4bf..a633a729b04 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc @@ -141,6 +141,7 @@ void SyncModule::sync_mesh(Object *ob, } bool is_alpha_blend = false; + float inflate_bounds = 0.0f; for (auto i : material_array.gpu_materials.index_range()) { GPUBatch *geom = mat_geom[i]; if (geom == nullptr) { @@ -176,6 +177,14 @@ void SyncModule::sync_mesh(Object *ob, ::Material *mat = GPU_material_get_material(gpu_material); inst_.cryptomatte.sync_material(mat); + + if (GPU_material_has_displacement_output(gpu_material)) { + inflate_bounds = math::max(inflate_bounds, mat->inflate_bounds); + } + } + + if (inflate_bounds != 0.0f) { + inst_.manager->update_handle_bounds(res_handle, ob_ref, inflate_bounds); } inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials); @@ -202,18 +211,11 @@ bool SyncModule::sync_sculpt(Object *ob, return false; } - /* Use a valid bounding box. The PBVH module already does its own culling, but a valid */ - /* bounding box is still needed for directional shadow tile-map bounds computation. */ - float3 min, max; - BKE_pbvh_bounding_box(ob_ref.object->sculpt->pbvh, min, max); - float3 center = (min + max) * 0.5; - float3 half_extent = max - center; - res_handle = inst_.manager->resource_handle(ob_ref, nullptr, ¢er, &half_extent); - bool has_motion = false; MaterialArray &material_array = inst_.materials.material_array_get(ob, has_motion); bool is_alpha_blend = false; + float inflate_bounds = 0.0f; for (SculptBatch &batch : sculpt_batches_per_material_get(ob_ref.object, material_array.gpu_materials)) { @@ -251,8 +253,21 @@ bool SyncModule::sync_sculpt(Object *ob, GPUMaterial *gpu_material = material_array.gpu_materials[batch.material_slot]; ::Material *mat = GPU_material_get_material(gpu_material); inst_.cryptomatte.sync_material(mat); + + if (GPU_material_has_displacement_output(gpu_material)) { + inflate_bounds = math::max(inflate_bounds, mat->inflate_bounds); + } } + /* Use a valid bounding box. The PBVH module already does its own culling, but a valid */ + /* bounding box is still needed for directional shadow tile-map bounds computation. */ + float3 min, max; + BKE_pbvh_bounding_box(ob_ref.object->sculpt->pbvh, min, max); + float3 center = (min + max) * 0.5; + float3 half_extent = max - center; + half_extent += inflate_bounds; + inst_.manager->update_handle_bounds(res_handle, center, half_extent); + 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); @@ -270,7 +285,7 @@ bool SyncModule::sync_sculpt(Object *ob, void SyncModule::sync_point_cloud(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle, - const ObjectRef & /*ob_ref*/) + const ObjectRef &ob_ref) { const int material_slot = POINTCLOUD_MATERIAL_NR; @@ -319,6 +334,11 @@ void SyncModule::sync_point_cloud(Object *ob, 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); } @@ -494,6 +514,7 @@ void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandl void SyncModule::sync_curves(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle, + const ObjectRef &ob_ref, ModifierData *modifier_data, ParticleSystem *particle_sys) { @@ -552,6 +573,11 @@ void SyncModule::sync_curves(Object *ob, 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); } diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.hh b/source/blender/draw/engines/eevee_next/eevee_sync.hh index e455b6b50fc..cdaf46777ab 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sync.hh @@ -161,6 +161,7 @@ class SyncModule { void sync_curves(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle, + const ObjectRef &ob_ref, ModifierData *modifier_data = nullptr, ParticleSystem *particle_sys = nullptr); void sync_light_probe(Object *ob, ObjectHandle &ob_handle); diff --git a/source/blender/draw/intern/draw_manager.hh b/source/blender/draw/intern/draw_manager.hh index efb8732023e..30e871554b8 100644 --- a/source/blender/draw/intern/draw_manager.hh +++ b/source/blender/draw/intern/draw_manager.hh @@ -119,7 +119,7 @@ class Manager { /** * Create a new resource handle for the given object. */ - ResourceHandle resource_handle(const ObjectRef ref); + ResourceHandle resource_handle(const ObjectRef ref, float inflate_bounds = 0.0f); /** * Create a new resource handle for the given object, but optionally override model matrix and * bounds. @@ -142,6 +142,15 @@ class Manager { const float3 &bounds_center, const float3 &bounds_half_extent); + /** Update the bounds of an already created handle. */ + void update_handle_bounds(ResourceHandle handle, + const ObjectRef ref, + float inflate_bounds = 0.0f); + /** Update the bounds of an already created handle. */ + void update_handle_bounds(ResourceHandle handle, + const float3 &bounds_center, + const float3 &bounds_half_extent); + /** * Populate additional per resource data on demand. */ @@ -206,11 +215,11 @@ class Manager { void sync_layer_attributes(); }; -inline ResourceHandle Manager::resource_handle(const ObjectRef ref) +inline ResourceHandle Manager::resource_handle(const ObjectRef ref, float inflate_bounds) { bool is_active_object = (ref.dupli_object ? ref.dupli_parent : ref.object) == object_active; matrix_buf.current().get_or_resize(resource_len_).sync(*ref.object); - bounds_buf.current().get_or_resize(resource_len_).sync(*ref.object); + bounds_buf.current().get_or_resize(resource_len_).sync(*ref.object, inflate_bounds); infos_buf.current().get_or_resize(resource_len_).sync(ref, is_active_object); return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0); } @@ -255,6 +264,20 @@ inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix, return ResourceHandle(resource_len_++, false); } +inline void Manager::update_handle_bounds(ResourceHandle handle, + const ObjectRef ref, + float inflate_bounds) +{ + bounds_buf.current()[handle.resource_index()].sync(*ref.object, inflate_bounds); +} + +inline void Manager::update_handle_bounds(ResourceHandle handle, + const float3 &bounds_center, + const float3 &bounds_half_extent) +{ + bounds_buf.current()[handle.resource_index()].sync(bounds_center, bounds_half_extent); +} + inline void Manager::extract_object_attributes(ResourceHandle handle, const ObjectRef &ref, Span materials) diff --git a/source/blender/draw/intern/draw_resource.hh b/source/blender/draw/intern/draw_resource.hh index b8c5471d92c..5821ef65d6b 100644 --- a/source/blender/draw/intern/draw_resource.hh +++ b/source/blender/draw/intern/draw_resource.hh @@ -159,7 +159,7 @@ inline void ObjectBounds::sync() bounding_sphere.w = -1.0f; /* Disable test. */ } -inline void ObjectBounds::sync(Object &ob) +inline void ObjectBounds::sync(Object &ob, float inflate_bounds) { const std::optional bbox = BKE_object_boundbox_get(&ob); if (!bbox) { @@ -171,6 +171,16 @@ inline void ObjectBounds::sync(Object &ob) *reinterpret_cast(&bounding_corners[2]) = bbox->vec[3]; *reinterpret_cast(&bounding_corners[3]) = bbox->vec[1]; bounding_sphere.w = 0.0f; /* Enable test. */ + + if (inflate_bounds != 0.0f) { + BLI_assert(inflate_bounds >= 0.0f); + float p = inflate_bounds; + float n = -inflate_bounds; + bounding_corners[0] += float4(n, n, n, 0.0f); + bounding_corners[1] += float4(p, n, n, 0.0f); + bounding_corners[2] += float4(n, p, n, 0.0f); + bounding_corners[3] += float4(n, n, p, 0.0f); + } } inline void ObjectBounds::sync(const float3 ¢er, const float3 &size) diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index a4ff34091fc..887d4727212 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -204,7 +204,7 @@ struct ObjectBounds { #if !defined(GPU_SHADER) && defined(__cplusplus) void sync(); - void sync(Object &ob); + void sync(Object &ob, float inflate_bounds = 0.0f); void sync(const float3 ¢er, const float3 &size); #endif }; diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 403eb048fcc..e96903a576a 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -320,6 +320,7 @@ struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void); bool GPU_material_has_surface_output(GPUMaterial *mat); bool GPU_material_has_volume_output(GPUMaterial *mat); +bool GPU_material_has_displacement_output(GPUMaterial *mat); void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag); bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag); diff --git a/source/blender/gpu/intern/gpu_material.cc b/source/blender/gpu/intern/gpu_material.cc index c75169e1db5..967ed91acc7 100644 --- a/source/blender/gpu/intern/gpu_material.cc +++ b/source/blender/gpu/intern/gpu_material.cc @@ -130,6 +130,7 @@ struct GPUMaterial { /** DEPRECATED: To remove. */ bool has_surface_output; bool has_volume_output; + bool has_displacement_output; /** DEPRECATED: To remove. */ GPUUniformBuf *sss_profile; /* UBO containing SSS profile. */ GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */ @@ -662,6 +663,7 @@ void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link) { if (!material->graph.outlink_displacement) { material->graph.outlink_displacement = link; + material->has_displacement_output = true; } } @@ -779,6 +781,11 @@ bool GPU_material_has_volume_output(GPUMaterial *mat) return mat->has_volume_output; } +bool GPU_material_has_displacement_output(GPUMaterial *mat) +{ + return mat->has_displacement_output; +} + void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag) { mat->flag |= flag; diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index 159c34335ea..a1fa9a35ae1 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -221,6 +221,11 @@ typedef struct Material { /* Volume. */ char volume_intersection_method; + /* Displacement*/ + float inflate_bounds; + + char _pad3[4]; + /** * Cached slots for texture painting, must be refreshed in * refresh_texpaint_image_cache before using. diff --git a/source/blender/makesrna/intern/rna_material.cc b/source/blender/makesrna/intern/rna_material.cc index c11d0ea30d8..2d9f6650ffa 100644 --- a/source/blender/makesrna/intern/rna_material.cc +++ b/source/blender/makesrna/intern/rna_material.cc @@ -964,6 +964,15 @@ void RNA_def_material(BlenderRNA *brna) "Determines which inner part of the mesh will produce volumetric effect"); RNA_def_property_update(prop, 0, "rna_Material_draw_update"); + prop = RNA_def_property(srna, "max_vertex_displacement", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, nullptr, "inflate_bounds"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_text(prop, + "Max Vertex Displacement", + "The max distance a vertex can be displaced." + "Displacements over this threshold may cause visibility issues"); + RNA_def_property_update(prop, 0, "rna_Material_draw_update"); + /* For Preview Render */ prop = RNA_def_property(srna, "preview_render_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, nullptr, "pr_type");