/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2016 Blender Foundation. */ /** \file * \ingroup draw_engine * * Gather all screen space effects technique such as Bloom, Motion Blur, DoF, SSAO, SSR, ... */ #include "DRW_render.h" #include "BLI_rand.h" #include "BLI_string_utils.h" #include "BKE_animsys.h" #include "BKE_camera.h" #include "BKE_duplilist.h" #include "BKE_object.h" #include "BKE_screen.h" #include "DNA_anim_types.h" #include "DNA_camera_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" #include "DNA_particle_types.h" #include "DNA_rigidbody_types.h" #include "DNA_screen_types.h" #include "ED_screen.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" #include "GPU_batch.h" #include "GPU_texture.h" #include "eevee_private.h" int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) { EEVEE_StorageList *stl = vedata->stl; EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_EffectsInfo *effects = stl->effects; const DRWContextState *draw_ctx = DRW_context_state_get(); Scene *scene = draw_ctx->scene; /* Viewport not supported for now. */ if (!DRW_state_is_scene_render()) { return 0; } effects->motion_blur_max = max_ii(0, scene->eevee.motion_blur_max); if ((effects->motion_blur_max > 0) && (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED)) { if (DRW_state_is_scene_render()) { int mb_step = effects->motion_blur_step; DRW_view_viewmat_get(NULL, effects->motion_blur.camera[mb_step].viewmat, false); DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persmat, false); DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persinv, true); } const float *fs_size = DRW_viewport_size_get(); const int tx_size[2] = { 1 + ((int)fs_size[0] / EEVEE_VELOCITY_TILE_SIZE), 1 + ((int)fs_size[1] / EEVEE_VELOCITY_TILE_SIZE), }; effects->velocity_tiles_x_tx = DRW_texture_pool_query_2d( tx_size[0], fs_size[1], GPU_RGBA16, &draw_engine_eevee_type); GPU_framebuffer_ensure_config(&fbl->velocity_tiles_fb[0], { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->velocity_tiles_x_tx), }); effects->velocity_tiles_tx = DRW_texture_pool_query_2d( tx_size[0], tx_size[1], GPU_RGBA16, &draw_engine_eevee_type); GPU_framebuffer_ensure_config(&fbl->velocity_tiles_fb[1], { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->velocity_tiles_tx), }); return EFFECT_MOTION_BLUR | EFFECT_POST_BUFFER | EFFECT_VELOCITY_BUFFER; } return 0; } void EEVEE_motion_blur_step_set(EEVEE_Data *vedata, int step) { BLI_assert(step < 3); vedata->stl->effects->motion_blur_step = step; } static void eevee_motion_blur_sync_camera(EEVEE_Data *vedata) { EEVEE_EffectsInfo *effects = vedata->stl->effects; if (DRW_state_is_scene_render()) { int mb_step = effects->motion_blur_step; DRW_view_viewmat_get(NULL, effects->motion_blur.camera[mb_step].viewmat, false); DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persmat, false); DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persinv, true); } effects->motion_blur_near_far[0] = fabsf(DRW_view_near_distance_get(NULL)); effects->motion_blur_near_far[1] = fabsf(DRW_view_far_distance_get(NULL)); } void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) { EEVEE_PassList *psl = vedata->psl; EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; EEVEE_MotionBlurData *mb_data = &effects->motion_blur; DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); const DRWContextState *draw_ctx = DRW_context_state_get(); Scene *scene = draw_ctx->scene; if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) { const float *fs_size = DRW_viewport_size_get(); const int tx_size[2] = { GPU_texture_width(effects->velocity_tiles_tx), GPU_texture_height(effects->velocity_tiles_tx), }; eevee_motion_blur_sync_camera(vedata); DRWShadingGroup *grp; { DRW_PASS_CREATE(psl->velocity_tiles_x, DRW_STATE_WRITE_COLOR); DRW_PASS_CREATE(psl->velocity_tiles, DRW_STATE_WRITE_COLOR); /* Create max velocity tiles in 2 passes. One for X and one for Y */ GPUShader *sh = EEVEE_shaders_effect_motion_blur_velocity_tiles_sh_get(); grp = DRW_shgroup_create(sh, psl->velocity_tiles_x); DRW_shgroup_uniform_texture(grp, "velocityBuffer", effects->velocity_tx); DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", (int[2]){fs_size[0], fs_size[1]}); DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1); DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1); DRW_shgroup_uniform_ivec2_copy(grp, "gatherStep", (int[2]){1, 0}); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); grp = DRW_shgroup_create(sh, psl->velocity_tiles); DRW_shgroup_uniform_texture(grp, "velocityBuffer", effects->velocity_tiles_x_tx); DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", (int[2]){tx_size[0], fs_size[1]}); DRW_shgroup_uniform_ivec2_copy(grp, "gatherStep", (int[2]){0, 1}); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); /* Expand max tiles by keeping the max tile in each tile neighborhood. */ DRW_PASS_CREATE(psl->velocity_tiles_expand[0], DRW_STATE_WRITE_COLOR); DRW_PASS_CREATE(psl->velocity_tiles_expand[1], DRW_STATE_WRITE_COLOR); for (int i = 0; i < 2; i++) { GPUTexture *tile_tx = (i == 0) ? effects->velocity_tiles_tx : effects->velocity_tiles_x_tx; GPUShader *sh_expand = EEVEE_shaders_effect_motion_blur_velocity_tiles_expand_sh_get(); grp = DRW_shgroup_create(sh_expand, psl->velocity_tiles_expand[i]); DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", tx_size); DRW_shgroup_uniform_texture(grp, "velocityBuffer", tile_tx); DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1); DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } } { DRW_PASS_CREATE(psl->motion_blur, DRW_STATE_WRITE_COLOR); eGPUSamplerState state = 0; int expand_steps = 1 + (max_ii(0, effects->motion_blur_max - 1) / EEVEE_VELOCITY_TILE_SIZE); GPUTexture *tile_tx = (expand_steps & 1) ? effects->velocity_tiles_x_tx : effects->velocity_tiles_tx; grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_sh_get(), psl->motion_blur); DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &effects->source_buffer, state); DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &dtxl->depth, state); DRW_shgroup_uniform_texture_ref_ex(grp, "velocityBuffer", &effects->velocity_tx, state); DRW_shgroup_uniform_texture(grp, "tileMaxBuffer", tile_tx); DRW_shgroup_uniform_float_copy(grp, "depthScale", scene->eevee.motion_blur_depth_scale); DRW_shgroup_uniform_vec2(grp, "nearFar", effects->motion_blur_near_far, 1); DRW_shgroup_uniform_bool_copy(grp, "isPerspective", DRW_view_is_persp_get(NULL)); DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1); DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1); DRW_shgroup_uniform_ivec2_copy(grp, "tileBufferSize", tx_size); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } { DRW_PASS_CREATE(psl->velocity_object, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_object_sh_get(), psl->velocity_object); DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat); DRW_shgroup_uniform_mat4(grp, "currViewProjMatrix", mb_data->camera[MB_CURR].persmat); DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat); DRW_PASS_CREATE(psl->velocity_hair, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); mb_data->hair_grp = grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_hair_sh_get(), psl->velocity_hair); DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat); DRW_shgroup_uniform_mat4(grp, "currViewProjMatrix", mb_data->camera[MB_CURR].persmat); DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat); DRW_pass_link(psl->velocity_object, psl->velocity_hair); } EEVEE_motion_blur_data_init(mb_data); } else { psl->motion_blur = NULL; psl->velocity_object = NULL; psl->velocity_hair = NULL; } } void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, Object *ob, ParticleSystem *psys, ModifierData *md) { EEVEE_PassList *psl = vedata->psl; EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; DRWShadingGroup *grp = NULL; if (!DRW_state_is_scene_render() || psl->velocity_hair == NULL) { return; } /* For now we assume hair objects are always moving. */ EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( &effects->motion_blur, ob, true); if (mb_data) { int mb_step = effects->motion_blur_step; /* Store transform. */ DRW_hair_duplimat_get(ob, psys, md, mb_data->obmat[mb_step]); EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(mb_data, ob); int psys_id = (md != NULL) ? BLI_findindex(&ob->modifiers, md) : 0; if (psys_id >= mb_hair->psys_len) { /* This should never happen. It means the modifier list was changed by frame evaluation. */ BLI_assert(0); return; } if (mb_step == MB_CURR) { /* Fill missing matrices if the object was hidden in previous or next frame. */ if (is_zero_m4(mb_data->obmat[MB_PREV])) { copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]); } if (is_zero_m4(mb_data->obmat[MB_NEXT])) { copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]); } GPUTexture *tex_prev = mb_hair->psys[psys_id].step_data[MB_PREV].hair_pos_tx; GPUTexture *tex_next = mb_hair->psys[psys_id].step_data[MB_NEXT].hair_pos_tx; grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL); DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]); DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]); DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]); DRW_shgroup_uniform_texture(grp, "prvBuffer", tex_prev); DRW_shgroup_uniform_texture(grp, "nxtBuffer", tex_next); DRW_shgroup_uniform_bool(grp, "useDeform", &mb_hair->use_deform, 1); } else { /* Store vertex position buffer. */ mb_hair->psys[psys_id].step_data[mb_step].hair_pos = DRW_hair_pos_buffer_get(ob, psys, md); mb_hair->use_deform = true; } } } void EEVEE_motion_blur_curves_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, Object *ob) { EEVEE_PassList *psl = vedata->psl; EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; if (!DRW_state_is_scene_render() || psl->velocity_hair == NULL) { return; } /* For now we assume curves objects are always moving. */ EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( &effects->motion_blur, ob, false); if (mb_data == NULL) { return; } int mb_step = effects->motion_blur_step; /* Store transform. */ copy_m4_m4(mb_data->obmat[mb_step], ob->obmat); EEVEE_HairMotionData *mb_curves = EEVEE_motion_blur_curves_data_get(mb_data); if (mb_step == MB_CURR) { /* Fill missing matrices if the object was hidden in previous or next frame. */ if (is_zero_m4(mb_data->obmat[MB_PREV])) { copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]); } if (is_zero_m4(mb_data->obmat[MB_NEXT])) { copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]); } GPUTexture *tex_prev = mb_curves->psys[0].step_data[MB_PREV].hair_pos_tx; GPUTexture *tex_next = mb_curves->psys[0].step_data[MB_NEXT].hair_pos_tx; DRWShadingGroup *grp = DRW_shgroup_curves_create_sub(ob, effects->motion_blur.hair_grp, NULL); DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]); DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]); DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]); DRW_shgroup_uniform_texture(grp, "prvBuffer", tex_prev); DRW_shgroup_uniform_texture(grp, "nxtBuffer", tex_next); DRW_shgroup_uniform_bool(grp, "useDeform", &mb_curves->use_deform, 1); } else { /* Store vertex position buffer. */ mb_curves->psys[0].step_data[mb_step].hair_pos = DRW_curves_pos_buffer_get(ob); mb_curves->use_deform = true; } } void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, Object *ob) { EEVEE_PassList *psl = vedata->psl; EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; DRWShadingGroup *grp = NULL; if (!DRW_state_is_scene_render() || psl->velocity_object == NULL) { return; } RigidBodyOb *rbo = ob->rigidbody_object; /* active rigidbody objects only, as only those are affected by sim. */ const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE)); #if 0 /* For now we assume dupli objects are moving. */ const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0; const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true); #else /* BKE_object_moves_in_time does not work in some cases. * Better detect non moving object after evaluation. */ const bool object_moves = true; #endif const bool is_deform = BKE_object_is_deform_modified(DRW_context_state_get()->scene, ob) || (has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0); if (!(object_moves || is_deform)) { return; } EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( &effects->motion_blur, ob, false); if (mb_data) { int mb_step = effects->motion_blur_step; /* Store transform. */ copy_m4_m4(mb_data->obmat[mb_step], ob->obmat); EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(mb_data); if (mb_step == MB_CURR) { GPUBatch *batch = DRW_cache_object_surface_get(ob); if (batch == NULL) { return; } /* Fill missing matrices if the object was hidden in previous or next frame. */ if (is_zero_m4(mb_data->obmat[MB_PREV])) { copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]); } if (is_zero_m4(mb_data->obmat[MB_NEXT])) { copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]); } if (mb_geom->use_deform) { /* Keep to modify later (after init). */ mb_geom->batch = batch; } /* Avoid drawing object that has no motions since object_moves is always true. */ if (!mb_geom->use_deform && /* Object deformation can happen without transform. */ equals_m4m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]) && equals_m4m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR])) { return; } grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_object_sh_get(), psl->velocity_object); DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]); DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]); DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]); DRW_shgroup_uniform_bool(grp, "useDeform", &mb_geom->use_deform, 1); DRW_shgroup_call(grp, batch, ob); } else if (is_deform) { /* Store vertex position buffer. */ mb_geom->vbo[mb_step] = DRW_cache_object_pos_vertbuf_get(ob); mb_geom->use_deform = (mb_geom->vbo[mb_step] != NULL); } else { mb_geom->vbo[mb_step] = NULL; mb_geom->use_deform = false; } } } static void motion_blur_remove_vbo_reference_from_batch(GPUBatch *batch, GPUVertBuf *vbo1, GPUVertBuf *vbo2) { for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN; i++) { if (ELEM(batch->verts[i], vbo1, vbo2)) { /* Avoid double reference of the VBOs. */ batch->verts[i] = NULL; } } } void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata) { EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; GHashIterator ghi; if ((effects->enabled_effects & EFFECT_MOTION_BLUR) == 0) { return; } int mb_step = effects->motion_blur_step; if (mb_step != MB_CURR) { /* Push instances attributes to the GPU. */ DRW_render_instance_buffer_finish(); /* Need to be called after #DRW_render_instance_buffer_finish() */ /* Also we weed to have a correct FBO bound for #DRW_curves_update. */ GPU_framebuffer_bind(vedata->fbl->main_fb); DRW_curves_update(); DRW_cache_restart(); } for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); BLI_ghashIterator_done(&ghi) == false; BLI_ghashIterator_step(&ghi)) { EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi); EEVEE_HairMotionData *mb_hair = mb_data->hair_data; EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data; if (mb_hair != NULL && mb_hair->use_deform) { if (mb_step == MB_CURR) { /* TODO(fclem): Check if vertex count mismatch. */ mb_hair->use_deform = true; } else { for (int i = 0; i < mb_hair->psys_len; i++) { GPUVertBuf *vbo = mb_hair->psys[i].step_data[mb_step].hair_pos; if (vbo == NULL) { continue; } EEVEE_HairMotionStepData **step_data_cache_ptr; if (!BLI_ghash_ensure_p(effects->motion_blur.hair_motion_step_cache[mb_step], vbo, (void ***)&step_data_cache_ptr)) { EEVEE_HairMotionStepData *new_step_data = MEM_callocN(sizeof(EEVEE_HairMotionStepData), __func__); /* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */ new_step_data->hair_pos = GPU_vertbuf_duplicate(vbo); /* Create vbo immediately to bind to texture buffer. */ GPU_vertbuf_use(new_step_data->hair_pos); new_step_data->hair_pos_tx = GPU_texture_create_from_vertbuf("hair_pos_motion_blur", new_step_data->hair_pos); *step_data_cache_ptr = new_step_data; } mb_hair->psys[i].step_data[mb_step] = **step_data_cache_ptr; } } } if (mb_geom != NULL && mb_geom->use_deform) { if (mb_step == MB_CURR) { /* Modify batch to have data from adjacent frames. */ GPUBatch *batch = mb_geom->batch; for (int i = 0; i < MB_CURR; i++) { GPUVertBuf *vbo = mb_geom->vbo[i]; if (vbo && batch) { if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) { /* Vertex count mismatch, disable deform motion blur. */ mb_geom->use_deform = false; } if (mb_geom->use_deform == false) { motion_blur_remove_vbo_reference_from_batch( batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); break; } /* Avoid adding the same vbo more than once when the batch is used by multiple * instances. */ if (!GPU_batch_vertbuf_has(batch, vbo)) { /* Currently, the code assumes that all objects that share the same mesh in the * current frame also share the same mesh on other frames. */ GPU_batch_vertbuf_add_ex(batch, vbo, false); } } } } else { GPUVertBuf *vbo = mb_geom->vbo[mb_step]; if (vbo) { /* Use the vbo to perform the copy on the GPU. */ GPU_vertbuf_use(vbo); /* Perform a copy to avoid losing it after RE_engine_frame_set(). */ GPUVertBuf **vbo_cache_ptr; if (!BLI_ghash_ensure_p(effects->motion_blur.position_vbo_cache[mb_step], vbo, (void ***)&vbo_cache_ptr)) { /* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */ GPUVertBuf *duplicated_vbo = GPU_vertbuf_duplicate(vbo); *vbo_cache_ptr = duplicated_vbo; /* Find and replace "pos" attrib name. */ GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(duplicated_vbo); int attrib_id = GPU_vertformat_attr_id_get(format, "pos"); GPU_vertformat_attr_rename(format, attrib_id, (mb_step == MB_PREV) ? "prv" : "nxt"); } mb_geom->vbo[mb_step] = vbo = *vbo_cache_ptr; } else { /* This might happen if the object visibility has been animated. */ mb_geom->use_deform = false; } } } } } void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata) { EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; GHashIterator ghi; BLI_assert((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0); /* Camera Data. */ effects->motion_blur.camera[MB_PREV] = effects->motion_blur.camera[MB_NEXT]; /* Swap #position_vbo_cache pointers. */ if (effects->motion_blur.position_vbo_cache[MB_PREV]) { BLI_ghash_free(effects->motion_blur.position_vbo_cache[MB_PREV], NULL, (GHashValFreeFP)GPU_vertbuf_discard); } effects->motion_blur.position_vbo_cache[MB_PREV] = effects->motion_blur.position_vbo_cache[MB_NEXT]; effects->motion_blur.position_vbo_cache[MB_NEXT] = NULL; /* Swap #hair_motion_step_cache pointers. */ if (effects->motion_blur.hair_motion_step_cache[MB_PREV]) { BLI_ghash_free(effects->motion_blur.hair_motion_step_cache[MB_PREV], NULL, (GHashValFreeFP)EEVEE_motion_hair_step_free); } effects->motion_blur.hair_motion_step_cache[MB_PREV] = effects->motion_blur.hair_motion_step_cache[MB_NEXT]; effects->motion_blur.hair_motion_step_cache[MB_NEXT] = NULL; /* Rename attributes in #position_vbo_cache. */ for (BLI_ghashIterator_init(&ghi, effects->motion_blur.position_vbo_cache[MB_PREV]); !BLI_ghashIterator_done(&ghi); BLI_ghashIterator_step(&ghi)) { GPUVertBuf *vbo = BLI_ghashIterator_getValue(&ghi); GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo); int attrib_id = GPU_vertformat_attr_id_get(format, "nxt"); GPU_vertformat_attr_rename(format, attrib_id, "prv"); } /* Object Data. */ for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); !BLI_ghashIterator_done(&ghi); BLI_ghashIterator_step(&ghi)) { EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi); EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data; EEVEE_HairMotionData *mb_hair = mb_data->hair_data; copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]); if (mb_hair != NULL) { for (int i = 0; i < mb_hair->psys_len; i++) { mb_hair->psys[i].step_data[MB_PREV].hair_pos = mb_hair->psys[i].step_data[MB_NEXT].hair_pos; mb_hair->psys[i].step_data[MB_PREV].hair_pos_tx = mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx; mb_hair->psys[i].step_data[MB_NEXT].hair_pos = NULL; mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx = NULL; } } if (mb_geom != NULL) { if (mb_geom->batch != NULL) { motion_blur_remove_vbo_reference_from_batch( mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); } mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT]; mb_geom->vbo[MB_NEXT] = NULL; } } } void EEVEE_motion_blur_draw(EEVEE_Data *vedata) { EEVEE_PassList *psl = vedata->psl; EEVEE_TextureList *txl = vedata->txl; EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; /* Motion Blur */ if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) { /* Create velocity max tiles in 2 passes. One for each dimension. */ GPU_framebuffer_bind(fbl->velocity_tiles_fb[0]); DRW_draw_pass(psl->velocity_tiles_x); GPU_framebuffer_bind(fbl->velocity_tiles_fb[1]); DRW_draw_pass(psl->velocity_tiles); /* Expand the tiles by reading the neighborhood. Do as many passes as required. */ int buf = 0; for (int i = effects->motion_blur_max; i > 0; i -= EEVEE_VELOCITY_TILE_SIZE) { GPU_framebuffer_bind(fbl->velocity_tiles_fb[buf]); /* Change viewport to avoid invoking more pixel shaders than necessary since in one of the * buffer the texture is way bigger in height. This avoid creating another texture and * reduce VRAM usage. */ int w = GPU_texture_width(effects->velocity_tiles_tx); int h = GPU_texture_height(effects->velocity_tiles_tx); GPU_framebuffer_viewport_set(fbl->velocity_tiles_fb[buf], 0, 0, w, h); DRW_draw_pass(psl->velocity_tiles_expand[buf]); GPU_framebuffer_viewport_reset(fbl->velocity_tiles_fb[buf]); buf = buf ? 0 : 1; } GPU_framebuffer_bind(effects->target_buffer); DRW_draw_pass(psl->motion_blur); SWAP_BUFFERS(); } }