From 1a24b5f81f891b4d7858b91c8fbf556ff3fcd2ad Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Fri, 14 Jul 2023 18:23:26 +0200 Subject: [PATCH] EEVEE Next: Hair & Curves Finalize the hair & curves implementation for EEVEE Next. - Ensure Hair particles have their own `ResourceHandle` and `ObjectKey`, so Motion Blur works correctly. (Note that the `ObjectHandle` and the `ObjectKey` are always created "on the fly" instead of being stored as `DrawData`, since it's not supported for particle system `ID`s). - The (unused) `ObjectKey::use_particle_hair` has been replaced by an integer `sub_key`, so multiple particle systems per object can be supported. - `VelocityModule::step_object_sync` now has 2 extra optional parameters for syncing Hair particle systems. - Update `DRW_curves_update` so it's safe to call it from "Next" engines. - Disable the `sampling.reset()` call from `step_object_sync`, since `is_deform` is always true for objects with particle modifiers, and this causes the renderer to get stuck at sample 1. Pull Request: https://projects.blender.org/blender/blender/pulls/109833 --- .../draw/engines/eevee_next/eevee_instance.cc | 23 +++++-- .../draw/engines/eevee_next/eevee_sampling.cc | 2 + .../engines/eevee_next/eevee_shader_shared.hh | 1 + .../draw/engines/eevee_next/eevee_sync.cc | 66 ++++++++----------- .../draw/engines/eevee_next/eevee_sync.hh | 28 ++++---- .../draw/engines/eevee_next/eevee_velocity.cc | 15 ++++- .../draw/engines/eevee_next/eevee_velocity.hh | 4 +- .../shaders/eevee_geom_curves_vert.glsl | 7 +- .../eevee_next/shaders/eevee_surf_lib.glsl | 13 ++++ .../shaders/infos/eevee_material_info.hh | 7 +- source/blender/draw/intern/draw_curves.cc | 12 ++++ 11 files changed, 111 insertions(+), 67 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 530cf1daa85..6a48ddcbaab 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -23,6 +23,8 @@ #include "eevee_engine.h" #include "eevee_instance.hh" +#include "DNA_particle_types.h" + namespace blender::eevee { /* -------------------------------------------------------------------- */ @@ -193,9 +195,24 @@ void Instance::object_sync(Object *ob) ObjectHandle &ob_handle = sync.sync_object(ob); if (partsys_is_visible && ob != DRW_context_state_get()->object_edit) { + int sub_key = 1; LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { if (md->type == eModifierType_ParticleSystem) { - sync.sync_curves(ob, ob_handle, res_handle, md); + ParticleSystem *particle_sys = reinterpret_cast(md)->psys; + ParticleSettings *part_settings = particle_sys->part; + const int draw_as = (part_settings->draw_as == PART_DRAW_REND) ? part_settings->ren_as : + part_settings->draw_as; + if (draw_as != PART_DRAW_PATH || + !DRW_object_is_visible_psys_in_active_context(ob, particle_sys)) { + continue; + } + + ObjectHandle _ob_handle = {0}; + _ob_handle.object_key = ObjectKey(ob_handle.object_key.ob, sub_key++); + _ob_handle.recalc = particle_sys->recalc; + ResourceHandle _res_handle = manager->resource_handle(float4x4(ob->object_to_world)); + + sync.sync_curves(ob, _ob_handle, _res_handle, md, particle_sys); } } } @@ -270,9 +287,7 @@ void Instance::render_sync() /* TODO: Remove old draw manager calls. */ DRW_render_instance_buffer_finish(); - /* Also we weed to have a correct FBO bound for #DRW_hair_update */ - // GPU_framebuffer_bind(); - // DRW_hair_update(); + DRW_curves_update(); } bool Instance::do_probe_sync() const diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc index 0006b7fb8f3..d08f6a9631f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc @@ -127,6 +127,8 @@ void Sampling::step() /* TODO de-correlate. */ data_.dimensions[SAMPLING_AO_U] = r[0]; data_.dimensions[SAMPLING_AO_V] = r[1]; + /* TODO de-correlate. */ + data_.dimensions[SAMPLING_CURVES_U] = r[0]; } { /* Using leaped Halton sequence so we can reused the same primes as lens. */ 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 8f36487fc1e..d60cf912931 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -101,6 +101,7 @@ enum eSamplingDimension : uint32_t { SAMPLING_RAYTRACE_X = 18u, SAMPLING_AO_U = 19u, SAMPLING_AO_V = 20u, + SAMPLING_CURVES_U = 21u, }; /** diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc index 04f3a06a8f3..153bbf03649 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc @@ -343,52 +343,41 @@ void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandl /** \name Hair * \{ */ -static void shgroup_curves_call(MaterialPass &matpass, - Object *ob, - ParticleSystem *part_sys = nullptr, - ModifierData *modifier_data = nullptr) -{ - UNUSED_VARS(ob, modifier_data); - if (matpass.sub_pass == nullptr) { - return; - } - if (part_sys != nullptr) { - // DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.sub_pass, matpass.gpumat); - } - else { - // DRW_shgroup_curves_create_sub(ob, matpass.sub_pass, matpass.gpumat); - } -} - void SyncModule::sync_curves(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle, - ModifierData *modifier_data) + ModifierData *modifier_data, + ParticleSystem *particle_sys) { - UNUSED_VARS(res_handle); int mat_nr = CURVES_MATERIAL_NR; - - ParticleSystem *part_sys = nullptr; - if (modifier_data != nullptr) { - part_sys = reinterpret_cast(modifier_data)->psys; - if (!DRW_object_is_visible_psys_in_active_context(ob, part_sys)) { - return; - } - ParticleSettings *part_settings = part_sys->part; - const int draw_as = (part_settings->draw_as == PART_DRAW_REND) ? part_settings->ren_as : - part_settings->draw_as; - if (draw_as != PART_DRAW_PATH) { - return; - } - mat_nr = part_settings->omat; + if (particle_sys != nullptr) { + mat_nr = particle_sys->part->omat; } - bool has_motion = inst_.velocity.step_object_sync(ob, ob_handle.object_key, ob_handle.recalc); + bool has_motion = inst_.velocity.step_object_sync( + ob, ob_handle.object_key, res_handle, ob_handle.recalc, modifier_data, particle_sys); Material &material = inst_.materials.material_get(ob, has_motion, mat_nr - 1, MAT_GEOM_CURVES); - shgroup_curves_call(material.shading, ob, part_sys, modifier_data); - shgroup_curves_call(material.prepass, ob, part_sys, modifier_data); - shgroup_curves_call(material.shadow, ob, part_sys, modifier_data); + auto drawcall_add = [&](MaterialPass &matpass) { + if (matpass.sub_pass == nullptr) { + return; + } + if (particle_sys != nullptr) { + PassMain::Sub &sub_pass = matpass.sub_pass->sub("Hair SubPass"); + GPUBatch *geometry = hair_sub_pass_setup( + sub_pass, inst_.scene, ob, particle_sys, modifier_data, matpass.gpumat); + sub_pass.draw(geometry, res_handle); + } + else { + PassMain::Sub &sub_pass = matpass.sub_pass->sub("Curves SubPass"); + GPUBatch *geometry = curves_sub_pass_setup(sub_pass, inst_.scene, ob, matpass.gpumat); + sub_pass.draw(geometry, res_handle); + } + }; + + drawcall_add(material.shading); + drawcall_add(material.prepass); + drawcall_add(material.shadow); inst_.cryptomatte.sync_object(ob, res_handle); GPUMaterial *gpu_material = @@ -396,9 +385,6 @@ void SyncModule::sync_curves(Object *ob, ::Material *mat = GPU_material_get_material(gpu_material); inst_.cryptomatte.sync_material(mat); - /* TODO(fclem) Hair velocity. */ - // shading_passes.velocity.gpencil_add(ob, ob_handle); - bool is_caster = material.shadow.sub_pass != nullptr; bool is_alpha_blend = material.is_alpha_blend_transparent; inst_.shadows.sync_object(ob_handle, res_handle, is_caster, 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 8d29a574a6c..c8185d10092 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sync.hh @@ -40,15 +40,15 @@ struct ObjectKey { Object *parent; /** Dupli objects recursive unique identifier */ int id[MAX_DUPLI_RECUR]; - /** If object uses particle system hair. */ - bool use_particle_hair; + /** Used for particle system hair. */ + int sub_key_; #ifdef DEBUG char name[64]; #endif ObjectKey() : ob(nullptr), parent(nullptr){}; - ObjectKey(Object *ob_, Object *parent_, int id_[MAX_DUPLI_RECUR], bool use_particle_hair_) - : ob(ob_), parent(parent_), use_particle_hair(use_particle_hair_) + ObjectKey(Object *ob_, Object *parent_, int id_[MAX_DUPLI_RECUR], int sub_key_ = 0) + : ob(ob_), parent(parent_), sub_key_(sub_key_) { if (id_) { memcpy(id, id_, sizeof(id)); @@ -67,16 +67,19 @@ struct ObjectKey { break; } } + if (sub_key_ != 0) { + hash_value = BLI_ghashutil_combine_hash(hash_value, sub_key_); + } #ifdef DEBUG STRNCPY(name, ob->id.name); #endif } - ObjectKey(Object *ob, DupliObject *dupli, Object *parent) - : ObjectKey(ob, parent, dupli ? dupli->persistent_id : nullptr, false){}; + ObjectKey(Object *ob, DupliObject *dupli, Object *parent, int sub_key_ = 0) + : ObjectKey(ob, parent, dupli ? dupli->persistent_id : nullptr, sub_key_){}; - ObjectKey(Object *ob) - : ObjectKey(ob, DRW_object_get_dupli(ob), DRW_object_get_dupli_parent(ob)){}; + ObjectKey(Object *ob, int sub_key_ = 0) + : ObjectKey(ob, DRW_object_get_dupli(ob), DRW_object_get_dupli_parent(ob), sub_key_){}; uint64_t hash() const { @@ -91,8 +94,8 @@ struct ObjectKey { if (parent != k.parent) { return (parent < k.parent); } - if (use_particle_hair != k.use_particle_hair) { - return (use_particle_hair < k.use_particle_hair); + if (sub_key_ != k.sub_key_) { + return (sub_key_ < k.sub_key_); } return memcmp(id, k.id, sizeof(id)) < 0; } @@ -105,7 +108,7 @@ struct ObjectKey { if (parent != k.parent) { return false; } - if (use_particle_hair != k.use_particle_hair) { + if (sub_key_ != k.sub_key_) { return false; } return memcmp(id, k.id, sizeof(id)) == 0; @@ -172,7 +175,8 @@ class SyncModule { void sync_curves(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle, - ModifierData *modifier_data = nullptr); + ModifierData *modifier_data = nullptr, + ParticleSystem *particle_sys = nullptr); void sync_light_probe(Object *ob, ObjectHandle &ob_handle); }; diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.cc b/source/blender/draw/engines/eevee_next/eevee_velocity.cc index 503fd7daef8..2bb208d555b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_velocity.cc +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.cc @@ -15,6 +15,7 @@ #include "BKE_object.h" #include "BLI_map.hh" #include "DEG_depsgraph_query.h" +#include "DNA_particle_types.h" #include "DNA_rigidbody_types.h" #include "draw_cache_impl.h" @@ -87,7 +88,9 @@ void VelocityModule::step_camera_sync() bool VelocityModule::step_object_sync(Object *ob, ObjectKey &object_key, ResourceHandle resource_handle, - int /*IDRecalcFlag*/ recalc) + int /*IDRecalcFlag*/ recalc, + ModifierData *modifier_data /*= nullptr*/, + ParticleSystem *particle_sys /*= nullptr*/) { bool has_motion = object_has_velocity(ob) || (recalc & ID_RECALC_TRANSFORM); /* NOTE: Fragile. This will only work with 1 frame of lag since we can't record every geometry @@ -107,7 +110,7 @@ bool VelocityModule::step_object_sync(Object *ob, VelocityObjectData &vel = velocity_map.lookup_or_add_default(object_key); vel.obj.ofs[step_] = object_steps_usage[step_]++; vel.obj.resource_id = resource_handle.resource_index(); - vel.id = (ID *)ob->data; + vel.id = particle_sys ? &particle_sys->part->id : &ob->id; object_steps[step_]->get_or_resize(vel.obj.ofs[step_]) = float4x4_view(ob->object_to_world); if (step_ == STEP_CURRENT) { /* Replace invalid steps. Can happen if object was hidden in one of those steps. */ @@ -127,6 +130,10 @@ bool VelocityModule::step_object_sync(Object *ob, if (has_deform) { auto add_cb = [&]() { VelocityGeometryData data; + if (particle_sys) { + data.pos_buf = DRW_hair_pos_buffer_get(ob, particle_sys, modifier_data); + return data; + } switch (ob->type) { case OB_CURVES: data.pos_buf = DRW_curves_pos_buffer_get(ob); @@ -178,7 +185,9 @@ bool VelocityModule::step_object_sync(Object *ob, } /* TODO(@fclem): Reset sampling here? Should ultimately be covered by depsgraph update tags. */ - inst_.sampling.reset(); + /* NOTE(Miguel Pozo): Disable, since is_deform is always true for objects with particle + * modifiers, and this causes the renderer to get stuck at sample 1. */ + // inst_.sampling.reset(); return true; } diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.hh b/source/blender/draw/engines/eevee_next/eevee_velocity.hh index 2daf3f92bba..f410ce36411 100644 --- a/source/blender/draw/engines/eevee_next/eevee_velocity.hh +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.hh @@ -108,7 +108,9 @@ class VelocityModule { bool step_object_sync(Object *ob, ObjectKey &object_key, ResourceHandle resource_handle, - int recalc = 0); + int recalc = 0, + ModifierData *modifier_data = nullptr, + ParticleSystem *particle_sys = nullptr); /* Moves next frame data to previous frame data. Nullify next frame data. */ void step_swap(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl index fb5d2894bcf..b304e5ad920 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl @@ -16,8 +16,6 @@ void main() init_interface(); - vec3 T; - bool is_persp = (ProjectionMatrix[3][3] == 0.0); hair_get_pos_tan_binor_time(is_persp, ModelMatrixInverse, @@ -30,14 +28,15 @@ void main() interp.curves_thickness, interp.curves_time_width); - interp.N = cross(T, interp.curves_binormal); + interp.N = cross(interp.curves_tangent, interp.curves_binormal); interp.curves_strand_id = hair_get_strand_id(); interp.barycentric_coords = hair_get_barycentric(); #ifdef MAT_VELOCITY /* Due to the screen space nature of the vertex positioning, we compute only the motion of curve * strand, not its cylinder. Otherwise we would add the rotation velocity. */ int vert_idx = hair_get_base_id(); - vec3 prv, nxt, pos = texelFetch(hairPointBuffer, vert_idx).point_position; + vec3 prv, nxt; + vec3 pos = texelFetch(hairPointBuffer, vert_idx).point_position; velocity_local_pos_get(pos, vert_idx, prv, nxt); /* FIXME(fclem): Evaluating before displacement avoid displacement being treated as motion but * ignores motion from animated displacement. Supporting animated displacement motion vectors diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl index 6c1fc818f41..a4bddd859dd 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl @@ -2,6 +2,7 @@ #pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) #if defined(USE_BARYCENTRICS) && defined(GPU_FRAGMENT_SHADER) && defined(MAT_GEOM_MESH) vec3 barycentric_distances_get() @@ -39,6 +40,18 @@ void init_globals_curves() { /* Shade as a cylinder. */ float cos_theta = interp.curves_time_width / interp.curves_thickness; +#if defined(GPU_FRAGMENT_SHADER) && defined(MAT_GEOM_CURVES) + if (hairThicknessRes == 1) { + /* Random cosine normal distribution on the hair surface. */ + float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).x; +# ifdef EEVEE_SAMPLING_DATA + /* Needs to check for SAMPLING_DATA, + * otherwise Surfel and World (?!?!) shader validation fails. */ + noise = fract(noise + sampling_rng_1D_get(SAMPLING_CURVES_U)); +# endif + cos_theta = noise * 2.0 - 1.0; + } +#endif float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); g_data.N = g_data.Ni = normalize(interp.N * sin_theta + interp.curves_binormal * cos_theta); diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index 17ddbda68d1..a71706c6ff0 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -67,10 +67,11 @@ GPU_SHADER_CREATE_INFO(eevee_geom_curves) .additional_info("eevee_shared") .define("MAT_GEOM_CURVES") .vertex_source("eevee_geom_curves_vert.glsl") - .additional_info("draw_hair", - "draw_curves_infos", + .additional_info("draw_modelmat_new", "draw_resource_id_varying", - "draw_resource_id_new"); + "draw_view", + "draw_hair_new", + "draw_curves_infos"); GPU_SHADER_CREATE_INFO(eevee_geom_world) .additional_info("eevee_shared") diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc index d51a02ad886..e83913add6e 100644 --- a/source/blender/draw/intern/draw_curves.cc +++ b/source/blender/draw/intern/draw_curves.cc @@ -410,6 +410,18 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, void DRW_curves_update() { + + /* Ensure there's a valid active view. + * "Next" engines use this function, but this still uses the old Draw Manager. */ + if (DRW_view_default_get() == nullptr) { + /* Create a dummy default view, it's not really used. */ + DRW_view_default_set(DRW_view_create( + float4x4::identity().ptr(), float4x4::identity().ptr(), nullptr, nullptr, nullptr)); + } + if (DRW_view_get_active() == nullptr) { + DRW_view_set_active(DRW_view_default_get()); + } + /* Update legacy hair too, to avoid verbosity in callers. */ DRW_hair_update();