Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2020-06-19 18:02:55 +0300
committerClément Foucault <foucault.clem@gmail.com>2020-06-19 18:05:49 +0300
commitf84414d6e14c42bf0f96b128c35d29bc2da59087 (patch)
tree27bfac79fd3371b3cc58305cb72f1ce39ddbc28a /source
parente18d6f520693e31a46226821cad1226e400eb0bc (diff)
EEEVEE: Object Motion Blur: Initial Implementation
This adds object motion blur vectors for EEVEE as well as better noise reduction for it. For TAA reprojection we just compute the motion vector on the fly based on camera motion and depth buffer. This makes possible to store another motion vector only for the blurring which is not useful for TAA history fetching. Motion Data is saved per object & per geometry if using deformation blur. We support deformation motion blur by saving previous VBO and modifying the actual GPUBatch for the geometry to include theses VBOs. We store Previous and Next frame motion in the same motion vector buffer (RG for prev and BA for next). This makes non linear motion blur (like rotating objects) less prone to outward/inward blur. We also improve the motion blur post process to expand outside the objects border. We use a tile base approach and the max size of the blur is set via a new render setting. We use a background reconstruction method that needs another setting (Background Separation). Sampling is done using a fixed 8 dithered samples per direction. The final render samples will clear the noise like other stochastic effects. One caveat is that hair particles are not yet supported. Support will come in another patch. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D7297
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenloader/intern/versioning_290.c8
-rw-r--r--source/blender/draw/CMakeLists.txt3
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c124
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c27
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c58
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c3
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c426
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h74
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c7
-rw-r--r--source/blender/draw/engines/eevee/eevee_temporal_sampling.c10
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl247
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl19
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl18
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl151
-rw-r--r--source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl27
-rw-r--r--source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl23
-rw-r--r--source/blender/draw/intern/DRW_render.h6
-rw-r--r--source/blender/draw/intern/draw_cache.c26
-rw-r--r--source/blender/draw/intern/draw_cache.h2
-rw-r--r--source/blender/draw/intern/draw_cache_impl.h5
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.c10
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c17
-rw-r--r--source/blender/draw/intern/draw_cache_impl_metaball.c12
-rw-r--r--source/blender/draw/intern/draw_cache_inline.h24
-rw-r--r--source/blender/draw/intern/draw_manager.c11
-rw-r--r--source/blender/draw/intern/draw_manager_data.c20
-rw-r--r--source/blender/gpu/GPU_vertex_buffer.h2
-rw-r--r--source/blender/gpu/GPU_vertex_format.h4
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer.c29
-rw-r--r--source/blender/gpu/intern/gpu_vertex_format.c14
-rw-r--r--source/blender/makesdna/DNA_scene_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_scene_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_scene.c23
33 files changed, 1218 insertions, 218 deletions
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index c5628b43960..8d57ef25c4d 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -279,5 +279,13 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /* EEVEE Motion blur new parameters. */
+ if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "motion_blur_depth_scale")) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ scene->eevee.motion_blur_depth_scale = 100.0f;
+ scene->eevee.motion_blur_max = 32;
+ }
+ }
}
}
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index a26c150cb51..d18717c85e7 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -214,6 +214,7 @@ data_to_c_simple(engines/eevee/shaders/effect_downsample_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_downsample_cube_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_gtao_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_velocity_resolve_frag.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/effect_velocity_tile_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_minmaxz_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_mist_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_motion_blur_frag.glsl SRC)
@@ -224,6 +225,8 @@ data_to_c_simple(engines/eevee/shaders/effect_temporal_aa.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/object_motion_frag.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/object_motion_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/prepass_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/prepass_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/shadow_accum_frag.glsl SRC)
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
index a19af77124f..5ca24d296c5 100644
--- a/source/blender/draw/engines/eevee/eevee_data.c
+++ b/source/blender/draw/engines/eevee/eevee_data.c
@@ -24,11 +24,135 @@
#include "DRW_render.h"
+#include "BLI_ghash.h"
#include "BLI_memblock.h"
+#include "BKE_duplilist.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "GPU_vertex_buffer.h"
+
#include "eevee_lightcache.h"
#include "eevee_private.h"
+/* Motion Blur data. */
+
+static void eevee_motion_blur_mesh_data_free(void *val)
+{
+ EEVEE_GeometryMotionData *geom_mb = (EEVEE_GeometryMotionData *)val;
+ for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) {
+ GPU_VERTBUF_DISCARD_SAFE(geom_mb->vbo[i]);
+ }
+ MEM_freeN(val);
+}
+
+static uint eevee_object_key_hash(const void *key)
+{
+ EEVEE_ObjectKey *ob_key = (EEVEE_ObjectKey *)key;
+ uint hash = BLI_ghashutil_ptrhash(ob_key->ob);
+ hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_ptrhash(ob_key->parent));
+ for (int i = 0; i < 16; i++) {
+ if (ob_key->id[i] != 0) {
+ hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_inthash(ob_key->id[i]));
+ }
+ else {
+ break;
+ }
+ }
+ return hash;
+}
+
+/* Return false if equal. */
+static bool eevee_object_key_cmp(const void *a, const void *b)
+{
+ EEVEE_ObjectKey *key_a = (EEVEE_ObjectKey *)a;
+ EEVEE_ObjectKey *key_b = (EEVEE_ObjectKey *)b;
+
+ if (key_a->ob != key_b->ob) {
+ return true;
+ }
+ if (key_a->parent != key_b->parent) {
+ return true;
+ }
+ if (memcmp(key_a->id, key_b->id, sizeof(key_a->id)) != 0) {
+ return true;
+ }
+ return false;
+}
+
+void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb)
+{
+ if (mb->object == NULL) {
+ mb->object = BLI_ghash_new(eevee_object_key_hash, eevee_object_key_cmp, "EEVEE Object Motion");
+ }
+ if (mb->geom == NULL) {
+ mb->geom = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE Mesh Motion");
+ }
+}
+
+void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb)
+{
+ if (mb->object) {
+ BLI_ghash_free(mb->object, MEM_freeN, MEM_freeN);
+ mb->object = NULL;
+ }
+ if (mb->geom) {
+ BLI_ghash_free(mb->geom, NULL, eevee_motion_blur_mesh_data_free);
+ mb->geom = NULL;
+ }
+}
+
+EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob)
+{
+ if (mb->object == NULL) {
+ return NULL;
+ }
+
+ EEVEE_ObjectKey key, *key_p;
+ key.ob = ob;
+ DupliObject *dup = DRW_object_get_dupli(ob);
+ if (dup) {
+ key.parent = DRW_object_get_dupli_parent(ob);
+ memcpy(key.id, dup->persistent_id, sizeof(key.id));
+ }
+ else {
+ key.parent = key.ob;
+ memset(key.id, 0, sizeof(key.id));
+ }
+
+ EEVEE_ObjectMotionData *ob_step = BLI_ghash_lookup(mb->object, &key);
+ if (ob_step == NULL) {
+ key_p = MEM_mallocN(sizeof(*key_p), __func__);
+ memcpy(key_p, &key, sizeof(*key_p));
+
+ ob_step = MEM_callocN(sizeof(EEVEE_ObjectMotionData), __func__);
+
+ BLI_ghash_insert(mb->object, key_p, ob_step);
+ }
+ return ob_step;
+}
+
+EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, Object *ob)
+{
+ if (mb->geom == NULL) {
+ return NULL;
+ }
+
+ /* Use original data as key to ensure matching accross update. */
+ Object *ob_orig = DEG_get_original_object(ob);
+
+ EEVEE_GeometryMotionData *geom_step = BLI_ghash_lookup(mb->geom, ob_orig->data);
+ if (geom_step == NULL) {
+ geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__);
+ BLI_ghash_insert(mb->geom, ob_orig->data, geom_step);
+ }
+
+ return geom_step;
+}
+
+/* View Layer data. */
+
void EEVEE_view_layer_data_free(void *storage)
{
EEVEE_ViewLayerData *sldata = (EEVEE_ViewLayerData *)storage;
diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c
index ab846fe0f11..6826b645971 100644
--- a/source/blender/draw/engines/eevee/eevee_effects.c
+++ b/source/blender/draw/engines/eevee/eevee_effects.c
@@ -225,10 +225,13 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
*/
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
effects->velocity_tx = DRW_texture_pool_query_2d(
- size_fs[0], size_fs[1], GPU_RG16, &draw_engine_eevee_type);
+ size_fs[0], size_fs[1], GPU_RGBA16, &draw_engine_eevee_type);
- /* TODO output objects velocity during the mainpass. */
- // GPU_framebuffer_texture_attach(fbl->main_fb, effects->velocity_tx, 1, 0);
+ GPU_framebuffer_ensure_config(&fbl->velocity_fb,
+ {
+ GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+ GPU_ATTACHMENT_TEXTURE(effects->velocity_tx),
+ });
GPU_framebuffer_ensure_config(
&fbl->velocity_resolve_fb,
@@ -328,14 +331,18 @@ void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
}
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
+ EEVEE_MotionBlurData *mb_data = &effects->motion_blur;
+
/* This pass compute camera motions to the non moving objects. */
DRW_PASS_CREATE(psl->velocity_resolve, DRW_STATE_WRITE_COLOR);
grp = DRW_shgroup_create(EEVEE_shaders_velocity_resolve_sh_get(), psl->velocity_resolve);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
- DRW_shgroup_uniform_mat4(grp, "currPersinv", effects->velocity_curr_persinv);
- DRW_shgroup_uniform_mat4(grp, "pastPersmat", effects->velocity_past_persmat);
+
+ DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat);
+ DRW_shgroup_uniform_mat4(grp, "currViewProjMatrixInv", mb_data->camera[MB_CURR].persinv);
+ DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat);
DRW_shgroup_call(grp, quad, NULL);
}
}
@@ -501,17 +508,19 @@ static void EEVEE_velocity_resolve(EEVEE_Data *vedata)
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
- struct DRWView *view = effects->taa_view;
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
e_data.depth_src = dtxl->depth;
- DRW_view_persmat_get(view, effects->velocity_curr_persinv, true);
GPU_framebuffer_bind(fbl->velocity_resolve_fb);
DRW_draw_pass(psl->velocity_resolve);
+
+ if (psl->velocity_object) {
+ GPU_framebuffer_bind(fbl->velocity_fb);
+ DRW_draw_pass(psl->velocity_object);
+ }
}
- DRW_view_persmat_get(view, effects->velocity_past_persmat, false);
}
void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
@@ -529,6 +538,7 @@ void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
effects->target_buffer = fbl->effect_color_fb; /* next target to render to */
/* Post process stack (order matters) */
+ EEVEE_velocity_resolve(vedata);
EEVEE_motion_blur_draw(vedata);
EEVEE_depth_of_field_draw(vedata);
@@ -537,7 +547,6 @@ void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
* Velocity resolve use a hack to exclude lookdev
* spheres from creating shimmering re-projection vectors. */
EEVEE_lookdev_draw(vedata);
- EEVEE_velocity_resolve(vedata);
EEVEE_temporal_sampling_draw(vedata);
EEVEE_bloom_draw(vedata);
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index 083d0a82214..93f66b0ab04 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -421,18 +421,76 @@ static void eevee_render_to_image(void *vedata,
struct RenderLayer *render_layer,
const rcti *rect)
{
+ EEVEE_Data *ved = (EEVEE_Data *)vedata;
const DRWContextState *draw_ctx = DRW_context_state_get();
+ if (EEVEE_render_do_motion_blur(draw_ctx->depsgraph)) {
+ Scene *scene = DEG_get_evaluated_scene(draw_ctx->depsgraph);
+
+ float shutter = scene->eevee.motion_blur_shutter * 0.5f;
+ float time = CFRA;
+ /* Centered on frame for now. */
+ float start_time = time - shutter;
+ float end_time = time + shutter;
+
+ {
+ EEVEE_motion_blur_step_set(ved, MB_PREV);
+ RE_engine_frame_set(engine, floorf(start_time), fractf(start_time));
+
+ if (!EEVEE_render_init(vedata, engine, draw_ctx->depsgraph)) {
+ return;
+ }
+
+ if (RE_engine_test_break(engine)) {
+ return;
+ }
+
+ DRW_render_object_iter(vedata, engine, draw_ctx->depsgraph, EEVEE_render_cache);
+ EEVEE_motion_blur_cache_finish(vedata);
+ /* Reset passlist. This is safe as they are stored into managed memory chunks. */
+ memset(ved->psl, 0, sizeof(*ved->psl));
+ /* Fix memleak */
+ BLI_ghash_free(ved->stl->g_data->material_hash, NULL, NULL);
+ ved->stl->g_data->material_hash = NULL;
+ }
+
+ {
+ EEVEE_motion_blur_step_set(ved, MB_NEXT);
+ RE_engine_frame_set(engine, floorf(end_time), fractf(end_time));
+
+ EEVEE_render_init(vedata, engine, draw_ctx->depsgraph);
+
+ DRW_render_object_iter(vedata, engine, draw_ctx->depsgraph, EEVEE_render_cache);
+ EEVEE_motion_blur_cache_finish(vedata);
+ /* Reset passlist. This is safe as they are stored into managed memory chunks. */
+ memset(ved->psl, 0, sizeof(*ved->psl));
+ /* Fix memleak */
+ BLI_ghash_free(ved->stl->g_data->material_hash, NULL, NULL);
+ ved->stl->g_data->material_hash = NULL;
+ }
+
+ /* Current frame. */
+ EEVEE_motion_blur_step_set(ved, MB_CURR);
+ RE_engine_frame_set(engine, time, 0.0f);
+ }
+
if (!EEVEE_render_init(vedata, engine, draw_ctx->depsgraph)) {
return;
}
+ if (RE_engine_test_break(engine)) {
+ return;
+ }
+
DRW_render_object_iter(vedata, engine, draw_ctx->depsgraph, EEVEE_render_cache);
+ EEVEE_motion_blur_cache_finish(vedata);
+
/* Actually do the rendering. */
EEVEE_render_draw(vedata, engine, render_layer, rect);
EEVEE_volumes_free_smoke_textures();
+ EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur);
}
static void eevee_engine_free(void)
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index cfc70baaf01..06ed3764eaf 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -901,6 +901,9 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata,
}
}
}
+
+ /* Motion Blur Vectors. */
+ EEVEE_motion_blur_cache_populate(sldata, vedata, ob);
}
/* Volumetrics */
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
index a6e6b30a6b1..b90d575b80c 100644
--- a/source/blender/draw/engines/eevee/eevee_motion_blur.c
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c
@@ -24,12 +24,16 @@
#include "DRW_render.h"
+#include "BLI_rand.h"
+
#include "BKE_animsys.h"
#include "BKE_camera.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_screen_types.h"
#include "ED_screen.h"
@@ -37,172 +41,333 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+#include "GPU_batch.h"
#include "GPU_texture.h"
#include "eevee_private.h"
static struct {
/* Motion Blur */
struct GPUShader *motion_blur_sh;
+ struct GPUShader *motion_blur_object_sh;
+ struct GPUShader *velocity_tiles_sh;
+ struct GPUShader *velocity_tiles_expand_sh;
} e_data = {NULL}; /* Engine data */
+extern char datatoc_effect_velocity_tile_frag_glsl[];
extern char datatoc_effect_motion_blur_frag_glsl[];
+extern char datatoc_object_motion_frag_glsl[];
+extern char datatoc_object_motion_vert_glsl[];
+extern char datatoc_common_view_lib_glsl[];
-static void eevee_motion_blur_camera_get_matrix_at_time(Scene *scene,
- ARegion *region,
- RegionView3D *rv3d,
- View3D *v3d,
- Object *camera,
- float time,
- float r_mat[4][4])
-{
- float obmat[4][4];
+#define EEVEE_VELOCITY_TILE_SIZE 32
- /* HACK */
- Object cam_cpy = *camera;
- Camera camdata_cpy = *(Camera *)(camera->data);
- cam_cpy.data = &camdata_cpy;
+static void eevee_create_shader_motion_blur(void)
+{
+ e_data.motion_blur_sh = DRW_shader_create_fullscreen(
+ datatoc_effect_motion_blur_frag_glsl,
+ "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n");
+ e_data.motion_blur_object_sh = DRW_shader_create_with_lib(datatoc_object_motion_vert_glsl,
+ NULL,
+ datatoc_object_motion_frag_glsl,
+ datatoc_common_view_lib_glsl,
+ NULL);
+ e_data.velocity_tiles_sh = DRW_shader_create_fullscreen(
+ datatoc_effect_velocity_tile_frag_glsl,
+ "#define TILE_GATHER\n"
+ "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n");
+ e_data.velocity_tiles_expand_sh = DRW_shader_create_fullscreen(
+ datatoc_effect_velocity_tile_frag_glsl,
+ "#define TILE_EXPANSION\n"
+ "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n");
+}
- /* Reset original pointers, so direct evaluation does not attempt to flush
- * animation back to the original object: otherwise viewport with motion
- * blur enabled will always loose non-keyed changes. */
- cam_cpy.id.orig_id = NULL;
- camdata_cpy.id.orig_id = NULL;
+int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, Object *camera)
+{
+ 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;
- /* Past matrix */
- /* FIXME : This is a temporal solution that does not take care of parent animations */
- /* Recalc Anim manually */
- BKE_animsys_evaluate_animdata(&camdata_cpy.id, camdata_cpy.adt, time, ADT_RECALC_ALL, false);
- BKE_object_where_is_calc_time(draw_ctx->depsgraph, scene, &cam_cpy, time);
+ /* Viewport not supported for now. */
+ if (!DRW_state_is_scene_render()) {
+ return 0;
+ }
- /* Compute winmat */
- CameraParams params;
- BKE_camera_params_init(&params);
+ if (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) {
+ if (!e_data.motion_blur_sh) {
+ eevee_create_shader_motion_blur();
+ }
- if (v3d != NULL) {
- BKE_camera_params_from_view3d(&params, draw_ctx->depsgraph, v3d, rv3d);
- BKE_camera_params_compute_viewplane(&params, region->winx, region->winy, 1.0f, 1.0f);
- }
- else {
- BKE_camera_params_from_object(&params, &cam_cpy);
- BKE_camera_params_compute_viewplane(
- &params, scene->r.xsch, scene->r.ysch, scene->r.xasp, scene->r.yasp);
- }
+ 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);
+ }
- BKE_camera_params_compute_matrix(&params);
+ if (camera != NULL) {
+ Camera *cam = camera->data;
+ effects->motion_blur_near_far[0] = cam->clip_start;
+ effects->motion_blur_near_far[1] = cam->clip_end;
+ }
+ else {
+ /* Not supported yet. */
+ BLI_assert(0);
+ }
- /* FIXME Should be done per view (MULTIVIEW) */
- normalize_m4_m4(obmat, cam_cpy.obmat);
- invert_m4(obmat);
- mul_m4_m4m4(r_mat, params.winmat, obmat);
+ effects->motion_blur_max = max_ii(0, scene->eevee.motion_blur_max);
+ const float *fs_size = DRW_viewport_size_get();
+ 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;
}
-static void eevee_create_shader_motion_blur(void)
+void EEVEE_motion_blur_step_set(EEVEE_Data *vedata, int step)
{
- e_data.motion_blur_sh = DRW_shader_create_fullscreen(datatoc_effect_motion_blur_frag_glsl, NULL);
+ BLI_assert(step < 3);
+ /* Meh, code duplication. Could be avoided if render init would not contain cache init. */
+ if (vedata->stl->effects == NULL) {
+ vedata->stl->effects = MEM_callocN(sizeof(*vedata->stl->effects), __func__);
+ }
+ vedata->stl->effects->motion_blur_step = step;
}
-int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, Object *camera)
+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();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
Scene *scene = draw_ctx->scene;
- View3D *v3d = draw_ctx->v3d;
- RegionView3D *rv3d = draw_ctx->rv3d;
- ARegion *region = draw_ctx->region;
+ if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
+ const float *fs_size = DRW_viewport_size_get();
+ int tx_size[2] = {GPU_texture_width(effects->velocity_tiles_tx),
+ GPU_texture_height(effects->velocity_tiles_tx)};
+ 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 = e_data.velocity_tiles_sh;
+ 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 = e_data.velocity_tiles_expand_sh;
+ 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(e_data.motion_blur_sh, 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);
- if (scene_eval->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) {
- /* Update Motion Blur Matrices */
- if (camera && (camera->type == OB_CAMERA) && (camera->data != NULL)) {
- float persmat[4][4];
- float ctime = DEG_get_ctime(draw_ctx->depsgraph);
- float delta = scene_eval->eevee.motion_blur_shutter;
- Object *ob_camera_eval = DEG_get_evaluated_object(draw_ctx->depsgraph, camera);
+ grp = DRW_shgroup_create(e_data.motion_blur_object_sh, 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);
+ }
- /* Viewport Matrix */
- /* Note: This does not have TAA jitter applied. */
- DRW_view_persmat_get(NULL, persmat, false);
+ EEVEE_motion_blur_data_init(mb_data);
+ }
+ else {
+ psl->motion_blur = NULL;
+ psl->velocity_object = NULL;
+ }
+}
- bool view_is_valid = (stl->g_data->view_updated == false);
+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 (draw_ctx->evil_C != NULL) {
- struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C);
- view_is_valid = view_is_valid && (ED_screen_animation_no_scrub(wm) == NULL);
- }
+ if (!DRW_state_is_scene_render() || psl->velocity_object == NULL) {
+ return;
+ }
- /* The view is jittered by the oglrenderer. So avoid testing in this case. */
- if (!DRW_state_is_image_render()) {
- view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN);
- /* WATCH: assume TAA init code runs last. */
- if (scene_eval->eevee.taa_samples == 1) {
- /* Only if TAA is disabled. If not, TAA will update prev_drw_persmat itself. */
- copy_m4_m4(effects->prev_drw_persmat, persmat);
- }
- }
+ const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0;
+ /* For now we assume dupli objects are moving. */
+ const bool object_moves = is_dupli || BKE_object_moves_in_time(ob, true);
+ const bool is_deform = BKE_object_is_deform_modified(DRW_context_state_get()->scene, ob);
+
+ if (!(object_moves || is_deform)) {
+ return;
+ }
+
+ EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob);
+
+ if (mb_data) {
+ int mb_step = effects->motion_blur_step;
+ /* Store transform */
+ copy_m4_m4(mb_data->obmat[mb_step], ob->obmat);
- effects->motion_blur_mat_cached = view_is_valid && !DRW_state_is_image_render();
+ EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(&effects->motion_blur,
+ ob);
- /* Current matrix */
- if (effects->motion_blur_mat_cached == false) {
- eevee_motion_blur_camera_get_matrix_at_time(
- scene, region, rv3d, v3d, ob_camera_eval, ctime, effects->current_world_to_ndc);
+ if (mb_step == MB_CURR) {
+ GPUBatch *batch = DRW_cache_object_surface_get(ob);
+ if (batch == NULL) {
+ return;
}
- /* Only continue if camera is not being keyed */
- if (DRW_state_is_image_render() ||
- compare_m4m4(persmat, effects->current_world_to_ndc, 0.0001f)) {
- /* Past matrix */
- if (effects->motion_blur_mat_cached == false) {
- eevee_motion_blur_camera_get_matrix_at_time(
- scene, region, rv3d, v3d, ob_camera_eval, ctime - delta, effects->past_world_to_ndc);
-
-#if 0 /* for future high quality blur */
- /* Future matrix */
- eevee_motion_blur_camera_get_matrix_at_time(
- scene, region, rv3d, v3d, ob_camera_eval, ctime + delta, effects->future_world_to_ndc);
-#endif
- invert_m4_m4(effects->current_ndc_to_world, effects->current_world_to_ndc);
- }
+ /* 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]);
+ }
- effects->motion_blur_mat_cached = true;
- effects->motion_blur_samples = scene_eval->eevee.motion_blur_samples;
+ grp = DRW_shgroup_create(e_data.motion_blur_object_sh, 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);
- if (!e_data.motion_blur_sh) {
- eevee_create_shader_motion_blur();
- }
+ DRW_shgroup_call(grp, batch, ob);
- return EFFECT_MOTION_BLUR | EFFECT_POST_BUFFER;
+ if (mb_geom->use_deform) {
+ /* Keep to modify later (after init). */
+ mb_geom->batch = batch;
}
}
+ 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;
+ }
}
-
- return 0;
}
-void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
+void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata)
{
- EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ GHashIterator ghi;
- struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
+ if ((effects->enabled_effects & EFFECT_MOTION_BLUR) == 0) {
+ return;
+ }
- if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
- DRW_PASS_CREATE(psl->motion_blur, DRW_STATE_WRITE_COLOR);
-
- DRWShadingGroup *grp = DRW_shgroup_create(e_data.motion_blur_sh, psl->motion_blur);
- DRW_shgroup_uniform_int(grp, "samples", &effects->motion_blur_samples, 1);
- DRW_shgroup_uniform_mat4(grp, "currInvViewProjMatrix", effects->current_ndc_to_world);
- DRW_shgroup_uniform_mat4(grp, "pastViewProjMatrix", effects->past_world_to_ndc);
- DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->source_buffer);
- DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
- DRW_shgroup_call(grp, quad, NULL);
+ for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom);
+ BLI_ghashIterator_done(&ghi) == false;
+ BLI_ghashIterator_step(&ghi)) {
+ EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi);
+
+ int mb_step = effects->motion_blur_step;
+
+ if (!mb_geom->use_deform) {
+ continue;
+ }
+
+ 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 (vbo->vertex_len != batch->verts[0]->vertex_len) {
+ /* Vertex count mismatch, disable deform motion blur. */
+ mb_geom->use_deform = false;
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]);
+ break;
+ }
+ /* Modify the batch to include the previous position. */
+ GPU_batch_vertbuf_add_ex(batch, vbo, true);
+ /* TODO(fclem) keep the vbo around for next (sub)frames. */
+ /* Only do once. */
+ mb_geom->vbo[i] = NULL;
+ }
+ }
+ }
+ else {
+ GPUVertBuf *vbo = mb_geom->vbo[mb_step];
+ /* If this assert fails, it means that different EEVEE_GeometryMotionDatas
+ * has been used for each motion blur step. */
+ BLI_assert(vbo);
+ if (vbo) {
+ /* Use the vbo to perform the copy on the GPU. */
+ GPU_vertbuf_use(vbo);
+ /* Perform a copy to avoid loosing it after RE_engine_frame_set(). */
+ mb_geom->vbo[mb_step] = vbo = GPU_vertbuf_duplicate(vbo);
+ /* Find and replace "pos" attrib name. */
+ int attrib_id = GPU_vertformat_attr_id_get(&vbo->format, "pos");
+ GPU_vertformat_attr_rename(&vbo->format, attrib_id, (mb_step == MB_PREV) ? "prv" : "nxt");
+ }
+ }
}
}
@@ -216,6 +381,36 @@ void EEVEE_motion_blur_draw(EEVEE_Data *vedata)
/* Motion Blur */
if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
+ int sample = DRW_state_is_image_render() ? effects->taa_render_sample :
+ effects->taa_current_sample;
+ double r;
+ BLI_halton_1d(2, 0.0, sample - 1, &r);
+ effects->motion_blur_sample_offset = r;
+
+ /* 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]);
+
+ buf = buf ? 0 : 1;
+ }
+
GPU_framebuffer_bind(effects->target_buffer);
DRW_draw_pass(psl->motion_blur);
SWAP_BUFFERS();
@@ -225,4 +420,7 @@ void EEVEE_motion_blur_draw(EEVEE_Data *vedata)
void EEVEE_motion_blur_free(void)
{
DRW_SHADER_FREE_SAFE(e_data.motion_blur_sh);
+ DRW_SHADER_FREE_SAFE(e_data.motion_blur_object_sh);
+ DRW_SHADER_FREE_SAFE(e_data.velocity_tiles_sh);
+ DRW_SHADER_FREE_SAFE(e_data.velocity_tiles_expand_sh);
}
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 40008c5c364..a55b15e7d13 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -29,6 +29,8 @@
#include "DNA_lightprobe_types.h"
+#include "BKE_camera.h"
+
struct EEVEE_ShadowCasterBuffer;
struct GPUFrameBuffer;
struct Object;
@@ -256,7 +258,11 @@ typedef struct EEVEE_PassList {
struct DRWPass *sss_translucency_ps;
struct DRWPass *color_downsample_ps;
struct DRWPass *color_downsample_cube_ps;
+ struct DRWPass *velocity_object;
struct DRWPass *velocity_resolve;
+ struct DRWPass *velocity_tiles_x;
+ struct DRWPass *velocity_tiles;
+ struct DRWPass *velocity_tiles_expand[2];
struct DRWPass *taa_resolve;
struct DRWPass *alpha_checker;
@@ -327,6 +333,8 @@ typedef struct EEVEE_FramebufferList {
struct GPUFrameBuffer *renderpass_fb;
struct GPUFrameBuffer *ao_accum_fb;
struct GPUFrameBuffer *velocity_resolve_fb;
+ struct GPUFrameBuffer *velocity_fb;
+ struct GPUFrameBuffer *velocity_tiles_fb[2];
struct GPUFrameBuffer *update_noise_fb;
@@ -556,6 +564,41 @@ enum {
PROBE_UPDATE_ALL = 0xFFFFFF,
};
+/* ************** MOTION BLUR ************ */
+
+#define MB_PREV 0
+#define MB_NEXT 1
+#define MB_CURR 2
+
+typedef struct EEVEE_MotionBlurData {
+ struct GHash *object;
+ struct GHash *geom;
+ struct {
+ float viewmat[4][4];
+ float persmat[4][4];
+ float persinv[4][4];
+ } camera[3];
+} EEVEE_MotionBlurData;
+
+typedef struct EEVEE_ObjectKey {
+ /** Object or source object for duplis */
+ struct Object *ob;
+ /** Parent object for duplis */
+ struct Object *parent;
+ /** Dupli objects recursive unique identifier */
+ int id[16]; /* 2*MAX_DUPLI_RECUR */
+} EEVEE_ObjectKey;
+
+typedef struct EEVEE_ObjectMotionData {
+ float obmat[3][4][4];
+} EEVEE_ObjectMotionData;
+
+typedef struct EEVEE_GeometryMotionData {
+ struct GPUBatch *batch; /* Batch for time = t. */
+ struct GPUVertBuf *vbo[2]; /* Vbo for time = t +/- step. */
+ int use_deform; /* To disable deform mb if vertcount mismatch. */
+} EEVEE_GeometryMotionData;
+
/* ************ EFFECTS DATA ************* */
typedef enum EEVEE_EffectsFlag {
@@ -609,7 +652,7 @@ typedef struct EEVEE_EffectsInfo {
float taa_alpha;
bool prev_drw_support;
bool prev_is_navigating;
- float prev_drw_persmat[4][4];
+ float prev_drw_persmat[4][4]; /* Used for checking view validity and reprojection. */
struct DRWView *taa_view;
/* Ambient Occlusion */
int ao_depth_layer;
@@ -617,15 +660,25 @@ typedef struct EEVEE_EffectsInfo {
struct GPUTexture *gtao_horizons; /* Textures from pool */
struct GPUTexture *gtao_horizons_debug;
/* Motion Blur */
- float current_world_to_ndc[4][4];
float current_ndc_to_world[4][4];
+ float current_world_to_ndc[4][4];
+ float current_world_to_view[4][4];
float past_world_to_ndc[4][4];
- int motion_blur_samples;
- bool motion_blur_mat_cached;
+ float past_world_to_view[4][4];
+ CameraParams past_cam_params;
+ CameraParams current_cam_params;
+ float motion_blur_sample_offset;
+ char motion_blur_step; /* Which step we are evaluating. */
+ int motion_blur_max; /* Maximum distance in pixels a motion blured pixel can cover. */
+ float motion_blur_near_far[2]; /* Camera near/far clip distances (positive). */
+ bool cam_params_init;
+ /* TODO(fclem) Only used in render mode for now.
+ * This is because we are missing a per scene persistent place to hold this. */
+ struct EEVEE_MotionBlurData motion_blur;
/* Velocity Pass */
- float velocity_curr_persinv[4][4];
- float velocity_past_persmat[4][4];
struct GPUTexture *velocity_tx; /* Texture from pool */
+ struct GPUTexture *velocity_tiles_x_tx;
+ struct GPUTexture *velocity_tiles_tx;
/* Depth Of Field */
float dof_near_far[2];
float dof_params[2];
@@ -883,12 +936,17 @@ typedef struct EEVEE_PrivateData {
} EEVEE_PrivateData; /* Transient data */
/* eevee_data.c */
+void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb);
+void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb);
void EEVEE_view_layer_data_free(void *sldata);
EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void);
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer);
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void);
EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob);
EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob);
+EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob);
+EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb,
+ Object *ob);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob);
EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob);
@@ -1113,7 +1171,10 @@ void EEVEE_subsurface_free(void);
/* eevee_motion_blur.c */
int EEVEE_motion_blur_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *camera);
+void EEVEE_motion_blur_step_set(EEVEE_Data *vedata, int step);
void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
+void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob);
+void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata);
void EEVEE_motion_blur_draw(EEVEE_Data *vedata);
void EEVEE_motion_blur_free(void);
@@ -1194,6 +1255,7 @@ void EEVEE_render_draw(EEVEE_Data *vedata,
void EEVEE_render_update_passes(struct RenderEngine *engine,
struct Scene *scene,
struct ViewLayer *view_layer);
+bool EEVEE_render_do_motion_blur(const struct Depsgraph *depsgraph);
/** eevee_lookdev.c */
void EEVEE_lookdev_cache_init(EEVEE_Data *vedata,
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index 89a5ad2198a..00b42130e43 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -46,6 +46,12 @@
#include "eevee_private.h"
+bool EEVEE_render_do_motion_blur(const struct Depsgraph *depsgraph)
+{
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ return (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0;
+}
+
/* Return true if init properly. */
bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *depsgraph)
{
@@ -144,6 +150,7 @@ bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *
DRWView *view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL);
DRW_view_camtexco_set(view, camtexcofac);
+ DRW_view_reset();
DRW_view_default_set(view);
DRW_view_set_active(view);
diff --git a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
index d57048f2c4e..d348c5aea8e 100644
--- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
+++ b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
@@ -241,7 +241,6 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
DRW_view_persmat_get(NULL, persmat, false);
view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN);
- copy_m4_m4(effects->prev_drw_persmat, persmat);
/* Prevent ghosting from probe data. */
view_is_valid = view_is_valid && (effects->prev_drw_support == DRW_state_draw_support()) &&
@@ -283,7 +282,7 @@ void EEVEE_temporal_sampling_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
- if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) {
+ if (effects->enabled_effects & EFFECT_TAA) {
struct GPUShader *sh = EEVEE_shaders_taa_resolve_sh_get(effects->enabled_effects);
DRW_PASS_CREATE(psl->taa_resolve, DRW_STATE_WRITE_COLOR);
@@ -295,8 +294,9 @@ void EEVEE_temporal_sampling_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
if (effects->enabled_effects & EFFECT_TAA_REPROJECT) {
- // DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- DRW_shgroup_uniform_texture_ref(grp, "velocityBuffer", &effects->velocity_tx);
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
+ DRW_shgroup_uniform_mat4(grp, "prevViewProjectionMatrix", effects->prev_drw_persmat);
}
else {
DRW_shgroup_uniform_float(grp, "alpha", &effects->taa_alpha, 1);
@@ -364,5 +364,7 @@ void EEVEE_temporal_sampling_draw(EEVEE_Data *vedata)
DRW_viewport_request_redraw();
}
}
+
+ DRW_view_persmat_get(NULL, effects->prev_drw_persmat, false);
}
}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl
index b7935235d06..fbf507a2e40 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl
@@ -1,64 +1,235 @@
+/*
+ * Based on:
+ * A Fast and Stable Feature-Aware Motion Blur Filter
+ * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai
+ *
+ * With modification from the presentation:
+ * Next Generation Post Processing in Call of Duty Advanced Warfare
+ * by Jorge Jimenez
+ */
uniform sampler2D colorBuffer;
uniform sampler2D depthBuffer;
+uniform sampler2D velocityBuffer;
+uniform sampler2D tileMaxBuffer;
-/* current frame */
-uniform mat4 currInvViewProjMatrix;
+#define KERNEL 8
-/* past frame frame */
-uniform mat4 pastViewProjMatrix;
+/* TODO(fclem) deduplicate this code. */
+uniform sampler2DArray utilTex;
+#define LUT_SIZE 64
+#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
+
+uniform float depthScale;
+uniform ivec2 tileBufferSize;
+uniform vec2 viewportSize;
+uniform vec2 viewportSizeInv;
+uniform bool isPerspective;
+uniform vec2 nearFar; /* Near & far view depths values */
+
+#define linear_depth(z) \
+ ((isPerspective) ? (nearFar.x * nearFar.y) / (z * (nearFar.x - nearFar.y) + nearFar.y) : \
+ z * (nearFar.y - nearFar.x) + nearFar.x) /* Only true for camera view! */
in vec4 uvcoordsvar;
-out vec4 FragColor;
+out vec4 fragColor;
-#define MAX_SAMPLE 64
+#define saturate(a) clamp(a, 0.0, 1.0)
-uniform int samples;
+vec2 spread_compare(float center_motion_length, float sample_motion_length, float offset_length)
+{
+ return saturate(vec2(center_motion_length, sample_motion_length) - offset_length + 1.0);
+}
-float wang_hash_noise(uint s)
+vec2 depth_compare(float center_depth, float sample_depth)
{
- uint seed = (uint(gl_FragCoord.x) * 1664525u + uint(gl_FragCoord.y)) + s;
+ return saturate(0.5 + vec2(depthScale, -depthScale) * (sample_depth - center_depth));
+}
- seed = (seed ^ 61u) ^ (seed >> 16u);
- seed *= 9u;
- seed = seed ^ (seed >> 4u);
- seed *= 0x27d4eb2du;
- seed = seed ^ (seed >> 15u);
+/* Kill contribution if not going the same direction. */
+float dir_compare(vec2 offset, vec2 sample_motion, float sample_motion_length)
+{
+ if (sample_motion_length < 0.5) {
+ return 1.0;
+ }
+ return (dot(offset, sample_motion) > 0.0) ? 1.0 : 0.0;
+}
- float value = float(seed);
- value *= 1.0 / 4294967296.0;
- return fract(value);
+/* Return background (x) and foreground (y) weights. */
+vec2 sample_weights(float center_depth,
+ float sample_depth,
+ float center_motion_length,
+ float sample_motion_length,
+ float offset_length)
+{
+ /* Clasify foreground/background. */
+ vec2 depth_weight = depth_compare(center_depth, sample_depth);
+ /* Weight if sample is overlapping or under the center pixel. */
+ vec2 spread_weight = spread_compare(center_motion_length, sample_motion_length, offset_length);
+ return depth_weight * spread_weight;
}
-void main()
+vec4 decode_velocity(vec4 velocity)
{
- vec3 ndc_pos;
- ndc_pos.xy = uvcoordsvar.xy;
- ndc_pos.z = texture(depthBuffer, uvcoordsvar.xy).x;
+ velocity = velocity * 2.0 - 1.0;
+ /* Needed to match cycles. Can't find why... (fclem) */
+ velocity *= 0.5;
+ /* Transpose to pixelspace. */
+ velocity *= viewportSize.xyxy;
+ return velocity;
+}
- float inv_samples = 1.0 / float(samples);
- float noise = 2.0 * wang_hash_noise(0u) * inv_samples;
+vec4 sample_velocity(vec2 uv)
+{
+ vec4 data = texture(velocityBuffer, uv);
+ return decode_velocity(data);
+}
- /* Normalize Device Coordinates are [-1, +1]. */
- ndc_pos = ndc_pos * 2.0 - 1.0;
+vec2 sample_velocity(vec2 uv, const bool next)
+{
+ vec4 data = sample_velocity(uv);
+ data.xy = (next ? data.zw : data.xy);
+ return data.xy;
+}
- vec4 p = currInvViewProjMatrix * vec4(ndc_pos, 1.0);
- vec3 world_pos = p.xyz / p.w; /* Perspective divide */
+void gather_sample(vec2 screen_uv,
+ float center_depth,
+ float center_motion_len,
+ vec2 offset,
+ float offset_len,
+ const bool next,
+ inout vec4 accum,
+ inout vec4 accum_bg,
+ inout vec3 w_accum)
+{
+ vec2 sample_uv = screen_uv - offset * viewportSizeInv;
+ vec2 sample_motion = sample_velocity(sample_uv, next);
+ float sample_motion_len = length(sample_motion);
+ float sample_depth = linear_depth(texture(depthBuffer, sample_uv).r);
+ vec4 col = textureLod(colorBuffer, sample_uv, 0.0);
+
+ vec3 weights;
+ weights.xy = sample_weights(
+ center_depth, sample_depth, center_motion_len, sample_motion_len, offset_len);
+ weights.z = dir_compare(offset, sample_motion, sample_motion_len);
+ weights.xy *= weights.z;
+
+ accum += col * weights.y;
+ accum_bg += col * weights.x;
+ w_accum += weights;
+}
- /* Now find where was this pixel position
- * inside the past camera viewport */
- vec4 old_ndc = pastViewProjMatrix * vec4(world_pos, 1.0);
- old_ndc.xyz /= old_ndc.w; /* Perspective divide */
+void gather_blur(vec2 screen_uv,
+ vec2 center_motion,
+ float center_depth,
+ vec2 max_motion,
+ float ofs,
+ const bool next,
+ inout vec4 accum,
+ inout vec4 accum_bg,
+ inout vec3 w_accum)
+{
+ float center_motion_len = length(center_motion);
+ float max_motion_len = length(max_motion);
+
+ /* Tile boundaries randomization can fetch a tile where there is less motion than this pixel.
+ * Fix this by overriding the max_motion. */
+ if (max_motion_len < center_motion_len) {
+ max_motion_len = center_motion_len;
+ max_motion = center_motion;
+ }
- vec2 motion = (ndc_pos.xy - old_ndc.xy) * 0.25; /* 0.25 fit cycles ref */
+ if (max_motion_len < 0.5) {
+ return;
+ }
- float inc = 2.0 * inv_samples;
- float i = -1.0 + noise;
+ int i;
+ float t, inc = 1.0 / float(KERNEL);
+ for (i = 0, t = ofs * inc; i < KERNEL; i++, t += inc) {
+ gather_sample(screen_uv,
+ center_depth,
+ center_motion_len,
+ max_motion * t,
+ max_motion_len * t,
+ next,
+ accum,
+ accum_bg,
+ w_accum);
+ }
- FragColor = vec4(0.0);
- for (int j = 0; j < samples && j < MAX_SAMPLE; j++) {
- FragColor += textureLod(colorBuffer, uvcoordsvar.xy + motion * i, 0.0) * inv_samples;
- i += inc;
+ if (center_motion_len < 0.5) {
+ return;
}
+
+ for (i = 0, t = ofs * inc; i < KERNEL; i++, t += inc) {
+ /* Also sample in center motion direction.
+ * Allow to recover motion where there is conflicting
+ * motion between foreground and background. */
+ gather_sample(screen_uv,
+ center_depth,
+ center_motion_len,
+ center_motion * t,
+ center_motion_len * t,
+ next,
+ accum,
+ accum_bg,
+ w_accum);
+ }
+}
+
+void main()
+{
+ vec2 uv = uvcoordsvar.xy;
+
+ /* Data of the center pixel of the gather (target). */
+ float center_depth = linear_depth(texture(depthBuffer, uv).r);
+ vec4 center_motion = sample_velocity(uv);
+ vec4 center_color = textureLod(colorBuffer, uv, 0.0);
+
+ vec2 rand = texelfetch_noise_tex(gl_FragCoord.xy).xy;
+
+ /* Randomize tile boundary to avoid ugly discontinuities. Randomize 1/4th of the tile.
+ * Note this randomize only in one direction but in practice it's enough. */
+ rand.x = rand.x * 2.0 - 1.0;
+ ivec2 tile = ivec2(gl_FragCoord.xy + rand.x * float(EEVEE_VELOCITY_TILE_SIZE) * 0.25) /
+ EEVEE_VELOCITY_TILE_SIZE;
+ tile = clamp(tile, ivec2(0), tileBufferSize - 1);
+ vec4 max_motion = decode_velocity(texelFetch(tileMaxBuffer, tile, 0));
+
+ /* First (center) sample: time = T */
+ /* x: Background, y: Foreground, z: dir. */
+ vec3 w_accum = vec3(0.0, 0.0, 1.0);
+ vec4 accum_bg = vec4(0.0);
+ vec4 accum = vec4(0.0);
+ /* First linear gather. time = [T - delta, T] */
+ gather_blur(
+ uv, center_motion.xy, center_depth, max_motion.xy, rand.y, false, accum, accum_bg, w_accum);
+ /* Second linear gather. time = [T, T + delta] */
+ gather_blur(
+ uv, center_motion.zw, center_depth, max_motion.zw, rand.y, true, accum, accum_bg, w_accum);
+
+#if 1
+ /* Avoid division by 0.0. */
+ float w = 1.0 / (50.0 * float(KERNEL) * 4.0);
+ accum_bg += center_color * w;
+ w_accum.x += w;
+ /* Note: In Jimenez's presentation, they used center sample.
+ * We use background color as it contains more informations for foreground
+ * elements that have not enough weights.
+ * Yield beter blur in complex motion. */
+ center_color = accum_bg / w_accum.x;
+#endif
+ /* Merge background. */
+ accum += accum_bg;
+ w_accum.y += w_accum.x;
+ /* Balance accumulation for failled samples.
+ * We replace the missing foreground by the background. */
+ float blend_fac = saturate(1.0 - w_accum.y / w_accum.z);
+ fragColor = (accum / w_accum.z) + center_color * blend_fac;
+
+#if 0 /* For debugging. */
+ fragColor.rgb = fragColor.ggg;
+ fragColor.rg += max_motion.xy;
+#endif
}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl b/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl
index 428318e3c68..b44645174bd 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl
@@ -1,6 +1,6 @@
uniform sampler2D colorHistoryBuffer;
-uniform sampler2D velocityBuffer;
+uniform mat4 prevViewProjectionMatrix;
out vec4 FragColor;
@@ -38,16 +38,19 @@ vec3 clip_to_aabb(vec3 color, vec3 minimum, vec3 maximum, vec3 average)
*/
void main()
{
+ vec2 screen_res = vec2(textureSize(colorBuffer, 0).xy);
+ vec2 uv = gl_FragCoord.xy / screen_res;
ivec2 texel = ivec2(gl_FragCoord.xy);
- vec2 motion = texelFetch(velocityBuffer, texel, 0).rg;
-
- /* Decode from unsigned normalized 16bit texture. */
- motion = motion * 2.0 - 1.0;
/* Compute pixel position in previous frame. */
- vec2 screen_res = vec2(textureSize(colorBuffer, 0).xy);
- vec2 uv = gl_FragCoord.xy / screen_res;
- vec2 uv_history = uv - motion;
+ float depth = textureLod(depthBuffer, uv, 0.0).r;
+ vec3 pos = get_world_space_from_depth(uv, depth);
+ vec2 uv_history = project_point(prevViewProjectionMatrix, pos).xy * 0.5 + 0.5;
+
+ /* HACK: Reject lookdev spheres from TAA reprojection. */
+ if (depth == 0.0) {
+ uv_history = uv;
+ }
ivec2 texel_history = ivec2(uv_history * screen_res);
vec4 color_history = textureLod(colorHistoryBuffer, uv_history, 0.0);
diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl
index 7d701bce5cb..d927fd78d30 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl
@@ -1,8 +1,9 @@
-uniform mat4 currPersinv;
-uniform mat4 pastPersmat;
+uniform mat4 prevViewProjMatrix;
+uniform mat4 currViewProjMatrixInv;
+uniform mat4 nextViewProjMatrix;
-out vec2 outData;
+out vec4 outData;
void main()
{
@@ -12,13 +13,12 @@ void main()
float depth = texelFetch(depthBuffer, texel, 0).r;
- vec3 world_position = project_point(currPersinv, vec3(uv, depth) * 2.0 - 1.0);
- vec2 uv_history = project_point(pastPersmat, world_position).xy * 0.5 + 0.5;
+ vec3 world_position = project_point(currViewProjMatrixInv, vec3(uv, depth) * 2.0 - 1.0);
+ vec2 uv_prev = project_point(prevViewProjMatrix, world_position).xy * 0.5 + 0.5;
+ vec2 uv_next = project_point(nextViewProjMatrix, world_position).xy * 0.5 + 0.5;
- outData = uv - uv_history;
-
- /* HACK: Reject lookdev spheres from TAA reprojection. */
- outData = (depth > 0.0) ? outData : vec2(0.0);
+ outData.xy = uv_prev - uv;
+ outData.zw = uv_next - uv;
/* Encode to unsigned normalized 16bit texture. */
outData = outData * 0.5 + 0.5;
diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl
new file mode 100644
index 00000000000..0eb598521af
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl
@@ -0,0 +1,151 @@
+/**
+ * Shaders that down-sample velocity buffer,
+ *
+ * Based on:
+ * A Fast and Stable Feature-Aware Motion Blur Filter
+ * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai
+ *
+ * Adapted from G3D Innovation Engine implementation.
+ */
+
+uniform sampler2D velocityBuffer;
+uniform vec2 viewportSize;
+uniform vec2 viewportSizeInv;
+uniform ivec2 velocityBufferSize;
+
+out vec4 tileMaxVelocity;
+
+vec4 sample_velocity(ivec2 texel)
+{
+ texel = clamp(texel, ivec2(0), velocityBufferSize - 1);
+ vec4 data = texelFetch(velocityBuffer, texel, 0);
+ /* Decode data. */
+ return (data * 2.0 - 1.0) * viewportSize.xyxy;
+}
+
+vec4 encode_velocity(vec4 velocity)
+{
+ return velocity * viewportSizeInv.xyxy * 0.5 + 0.5;
+}
+
+#ifdef TILE_GATHER
+
+uniform ivec2 gatherStep;
+
+void main()
+{
+ vec4 max_motion = vec4(0.0);
+ float max_motion_len_sqr_prev = 0.0;
+ float max_motion_len_sqr_next = 0.0;
+ ivec2 texel = ivec2(gl_FragCoord.xy);
+ texel = texel * gatherStep.yx + texel * EEVEE_VELOCITY_TILE_SIZE * gatherStep;
+
+ for (int i = 0; i < EEVEE_VELOCITY_TILE_SIZE; ++i) {
+ vec4 motion = sample_velocity(texel + i * gatherStep);
+ float motion_len_sqr_prev = dot(motion.xy, motion.xy);
+ float motion_len_sqr_next = dot(motion.zw, motion.zw);
+
+ if (motion_len_sqr_prev > max_motion_len_sqr_prev) {
+ max_motion_len_sqr_prev = motion_len_sqr_prev;
+ max_motion.xy = motion.xy;
+ }
+ if (motion_len_sqr_next > max_motion_len_sqr_next) {
+ max_motion_len_sqr_next = motion_len_sqr_next;
+ max_motion.zw = motion.zw;
+ }
+ }
+
+ tileMaxVelocity = encode_velocity(max_motion);
+}
+
+#else /* TILE_EXPANSION */
+
+bool neighbor_affect_this_tile(ivec2 offset, vec2 velocity)
+{
+ /* Manhattan distance to the tiles, which is used for
+ * differentiating corners versus middle blocks */
+ float displacement = float(abs(offset.x) + abs(offset.y));
+ /**
+ * Relative sign on each axis of the offset compared
+ * to the velocity for that tile. In order for a tile
+ * to affect the center tile, it must have a
+ * neighborhood velocity in which x and y both have
+ * identical or both have opposite signs relative to
+ * offset. If the offset coordinate is zero then
+ * velocity is irrelevant.
+ **/
+ vec2 point = sign(offset * velocity);
+
+ float dist = (point.x + point.y);
+ /**
+ * Here's an example of the logic for this code.
+ * In this diagram, the upper-left tile has offset = (-1, -1).
+ * V1 is velocity = (1, -2). point in this case = (-1, 1), and therefore dist = 0,
+ * so the upper-left tile does not affect the center.
+ *
+ * Now, look at another case. V2 = (-1, -2). point = (1, 1), so dist = 2 and the tile
+ * does affect the center.
+ *
+ * V2(-1,-2) V1(1, -2)
+ * \ /
+ * \ /
+ * \/___ ____ ____
+ * (-1, -1)| | | |
+ * |____|____|____|
+ * | | | |
+ * |____|____|____|
+ * | | | |
+ * |____|____|____|
+ **/
+ return (abs(dist) == displacement);
+}
+
+/**
+ * Only gather neighborhood velocity into tiles that could be affected by it.
+ * In the general case, only six of the eight neighbors contribute:
+ *
+ * This tile can't possibly be affected by the center one
+ * |
+ * v
+ * ____ ____ ____
+ * | | ///|/// |
+ * |____|////|//__|
+ * | |////|/ |
+ * |___/|////|____|
+ * | //|////| | <--- This tile can't possibly be affected by the center one
+ * |_///|///_|____|
+ **/
+void main()
+{
+ vec4 max_motion = vec4(0.0);
+ float max_motion_len_sqr_prev = -1.0;
+ float max_motion_len_sqr_next = -1.0;
+
+ ivec2 tile = ivec2(gl_FragCoord.xy);
+ ivec2 offset = ivec2(0);
+ for (offset.y = -1; offset.y <= 1; ++offset.y) {
+ for (offset.x = -1; offset.x <= 1; ++offset.x) {
+ vec4 motion = sample_velocity(tile + offset);
+ float motion_len_sqr_prev = dot(motion.xy, motion.xy);
+ float motion_len_sqr_next = dot(motion.zw, motion.zw);
+
+ if (motion_len_sqr_prev > max_motion_len_sqr_prev) {
+ if (neighbor_affect_this_tile(offset, motion.xy)) {
+ max_motion_len_sqr_prev = motion_len_sqr_prev;
+ max_motion.xy = motion.xy;
+ }
+ }
+
+ if (motion_len_sqr_next > max_motion_len_sqr_next) {
+ if (neighbor_affect_this_tile(offset, motion.zw)) {
+ max_motion_len_sqr_next = motion_len_sqr_next;
+ max_motion.zw = motion.zw;
+ }
+ }
+ }
+ }
+
+ tileMaxVelocity = encode_velocity(max_motion);
+}
+
+#endif \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl b/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl
new file mode 100644
index 00000000000..66b098ef6c5
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl
@@ -0,0 +1,27 @@
+
+uniform mat4 prevViewProjMatrix;
+uniform mat4 currViewProjMatrix;
+uniform mat4 nextViewProjMatrix;
+
+in vec3 prevWorldPos;
+in vec3 currWorldPos;
+in vec3 nextWorldPos;
+
+out vec4 outData;
+
+void main()
+{
+ vec4 prev_wpos = prevViewProjMatrix * vec4(prevWorldPos, 1.0);
+ vec4 curr_wpos = currViewProjMatrix * vec4(currWorldPos, 1.0);
+ vec4 next_wpos = nextViewProjMatrix * vec4(nextWorldPos, 1.0);
+
+ vec2 prev_uv = (prev_wpos.xy / prev_wpos.w);
+ vec2 curr_uv = (curr_wpos.xy / curr_wpos.w);
+ vec2 next_uv = (next_wpos.xy / next_wpos.w);
+
+ outData.xy = prev_uv - curr_uv;
+ outData.zw = next_uv - curr_uv;
+
+ /* Encode to unsigned normalized 16bit texture. */
+ outData = outData * 0.5 + 0.5;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl
new file mode 100644
index 00000000000..f9011f5ae4e
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl
@@ -0,0 +1,23 @@
+
+uniform mat4 currModelMatrix;
+uniform mat4 prevModelMatrix;
+uniform mat4 nextModelMatrix;
+uniform bool useDeform;
+
+in vec3 pos;
+in vec3 prv; /* Previous frame position. */
+in vec3 nxt; /* Next frame position. */
+
+out vec3 currWorldPos;
+out vec3 prevWorldPos;
+out vec3 nextWorldPos;
+
+void main()
+{
+ prevWorldPos = (prevModelMatrix * vec4(useDeform ? prv : pos, 1.0)).xyz;
+ currWorldPos = (currModelMatrix * vec4(pos, 1.0)).xyz;
+ nextWorldPos = (nextModelMatrix * vec4(useDeform ? nxt : pos, 1.0)).xyz;
+ /* Use jittered projmatrix to be able to match exact sample depth (depth equal test).
+ * Note that currModelMatrix needs to also be equal to ModelMatrix for the samples to match. */
+ gl_Position = ViewProjectionMatrix * vec4(currWorldPos, 1.0);
+}
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 89dd6fa210c..7f5b4f5e42b 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -462,6 +462,10 @@ void DRW_shgroup_uniform_texture_ex(DRWShadingGroup *shgroup,
const char *name,
const struct GPUTexture *tex,
eGPUSamplerState sampler_state);
+void DRW_shgroup_uniform_texture_ref_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ GPUTexture **tex,
+ eGPUSamplerState sampler_state);
void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup,
const char *name,
const struct GPUTexture *tex);
@@ -569,7 +573,7 @@ void DRW_view_update_sub(DRWView *view, const float viewmat[4][4], const float w
const DRWView *DRW_view_default_get(void);
void DRW_view_default_set(DRWView *view);
-
+void DRW_view_reset(void);
void DRW_view_set_active(DRWView *view);
void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len);
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index af5b9cd05dd..f5540362041 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -888,6 +888,32 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob)
}
}
+/* Returns the vertbuf used by shaded surface batch. */
+GPUVertBuf *DRW_cache_object_pos_vertbuf_get(Object *ob)
+{
+ Mesh *me = BKE_object_get_evaluated_mesh(ob);
+ short type = (me != NULL) ? OB_MESH : ob->type;
+
+ switch (type) {
+ case OB_MESH:
+ return DRW_mesh_batch_cache_pos_vertbuf_get((me != NULL) ? me : ob->data);
+ case OB_CURVE:
+ case OB_SURF:
+ case OB_FONT:
+ return DRW_curve_batch_cache_pos_vertbuf_get(ob->data);
+ case OB_MBALL:
+ return DRW_mball_batch_cache_pos_vertbuf_get(ob);
+ case OB_HAIR:
+ return NULL;
+ case OB_POINTCLOUD:
+ return NULL;
+ case OB_VOLUME:
+ return NULL;
+ default:
+ return NULL;
+ }
+}
+
int DRW_cache_object_material_count_get(struct Object *ob)
{
Mesh *me = BKE_object_get_evaluated_mesh(ob);
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index 221fb89612f..5703f616ad1 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -63,6 +63,8 @@ struct GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob,
struct GPUBatch *DRW_cache_object_face_wireframe_get(struct Object *ob);
int DRW_cache_object_material_count_get(struct Object *ob);
+struct GPUVertBuf *DRW_cache_object_pos_vertbuf_get(struct Object *ob);
+
/* Empties */
struct GPUBatch *DRW_cache_plain_axes_get(void);
struct GPUBatch *DRW_cache_single_arrow_get(void);
diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h
index 191d75342d0..f670343140a 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -199,6 +199,11 @@ struct GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_uv_edges(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_edit_mesh_analysis(struct Mesh *me);
+/* For direct data access. */
+struct GPUVertBuf *DRW_mesh_batch_cache_pos_vertbuf_get(struct Mesh *me);
+struct GPUVertBuf *DRW_curve_batch_cache_pos_vertbuf_get(struct Curve *cu);
+struct GPUVertBuf *DRW_mball_batch_cache_pos_vertbuf_get(struct Object *ob);
+
int DRW_mesh_material_count_get(struct Mesh *me);
/* See 'common_globals_lib.glsl' for duplicate defines. */
diff --git a/source/blender/draw/intern/draw_cache_impl_curve.c b/source/blender/draw/intern/draw_cache_impl_curve.c
index 8798549a416..73e0ff7ef83 100644
--- a/source/blender/draw/intern/draw_cache_impl_curve.c
+++ b/source/blender/draw/intern/draw_cache_impl_curve.c
@@ -903,6 +903,16 @@ GPUBatch **DRW_curve_batch_cache_get_surface_shaded(struct Curve *cu,
return cache->surf_per_mat;
}
+GPUVertBuf *DRW_curve_batch_cache_pos_vertbuf_get(struct Curve *cu)
+{
+ CurveBatchCache *cache = curve_batch_cache_get(cu);
+ /* Request surface to trigger the vbo filling. Otherwise it may do nothing. */
+ DRW_batch_request(&cache->batch.surfaces);
+
+ DRW_vbo_request(NULL, &cache->ordered.loop_pos_nor);
+ return cache->ordered.loop_pos_nor;
+}
+
GPUBatch *DRW_curve_batch_cache_get_wireframes_face(Curve *cu)
{
CurveBatchCache *cache = curve_batch_cache_get(cu);
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 99e285a18f1..7207345f13c 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -810,6 +810,23 @@ int DRW_mesh_material_count_get(Mesh *me)
/** \name Edit Mode API
* \{ */
+GPUVertBuf *DRW_mesh_batch_cache_pos_vertbuf_get(Mesh *me)
+{
+ MeshBatchCache *cache = mesh_batch_cache_get(me);
+ /* Request surface to trigger the vbo filling. Otherwise it may do nothing. */
+ mesh_batch_cache_add_request(cache, MBC_SURFACE);
+ DRW_batch_request(&cache->batch.surface);
+
+ DRW_vbo_request(NULL, &cache->final.vbo.pos_nor);
+ return cache->final.vbo.pos_nor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Edit Mode API
+ * \{ */
+
GPUBatch *DRW_mesh_batch_cache_get_edit_triangles(Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
diff --git a/source/blender/draw/intern/draw_cache_impl_metaball.c b/source/blender/draw/intern/draw_cache_impl_metaball.c
index c14e66c2b47..076d32ffe1f 100644
--- a/source/blender/draw/intern/draw_cache_impl_metaball.c
+++ b/source/blender/draw/intern/draw_cache_impl_metaball.c
@@ -274,6 +274,18 @@ struct GPUBatch *DRW_metaball_batch_cache_get_edge_detection(struct Object *ob,
return cache->edge_detection;
}
+struct GPUVertBuf *DRW_mball_batch_cache_pos_vertbuf_get(Object *ob)
+{
+ if (!BKE_mball_is_basis(ob)) {
+ return NULL;
+ }
+
+ MetaBall *mb = ob->data;
+ MetaBallBatchCache *cache = metaball_batch_cache_get(mb);
+
+ return mball_batch_cache_get_pos_and_normals(ob, cache);
+}
+
int DRW_metaball_material_count_get(MetaBall *mb)
{
return max_ii(1, mb->totcol);
diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h
index a067434f3bb..67f44b5fb0c 100644
--- a/source/blender/draw/intern/draw_cache_inline.h
+++ b/source/blender/draw/intern/draw_cache_inline.h
@@ -90,17 +90,19 @@ BLI_INLINE void DRW_vbo_request(GPUBatch *batch, GPUVertBuf **vbo)
if (*vbo == NULL) {
*vbo = MEM_callocN(sizeof(GPUVertBuf), "GPUVertBuf");
}
- /* HACK set first vbo if not init. */
- if (batch->verts[0] == NULL) {
- GPU_batch_vao_cache_clear(batch);
- batch->verts[0] = *vbo;
- }
- else {
- /* HACK: bypass assert */
- int vbo_vert_len = (*vbo)->vertex_len;
- (*vbo)->vertex_len = batch->verts[0]->vertex_len;
- GPU_batch_vertbuf_add(batch, *vbo);
- (*vbo)->vertex_len = vbo_vert_len;
+ if (batch != NULL) {
+ /* HACK set first vbo if not init. */
+ if (batch->verts[0] == NULL) {
+ GPU_batch_vao_cache_clear(batch);
+ batch->verts[0] = *vbo;
+ }
+ else {
+ /* HACK: bypass assert */
+ int vbo_vert_len = (*vbo)->vertex_len;
+ (*vbo)->vertex_len = batch->verts[0]->vertex_len;
+ GPU_batch_vertbuf_add(batch, *vbo);
+ (*vbo)->vertex_len = vbo_vert_len;
+ }
}
}
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 16dd17590f8..55dc1d6c5d7 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -1635,13 +1635,6 @@ bool DRW_render_check_grease_pencil(Depsgraph *depsgraph)
return false;
}
-static void drw_view_reset(void)
-{
- DST.view_default = NULL;
- DST.view_active = NULL;
- DST.view_previous = NULL;
-}
-
static void DRW_render_gpencil_to_image(RenderEngine *engine,
struct RenderLayer *render_layer,
const rcti *rect)
@@ -1719,7 +1712,7 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph
for (RenderView *render_view = render_result->views.first; render_view != NULL;
render_view = render_view->next) {
RE_SetActiveRenderView(render, render_view->name);
- drw_view_reset();
+ DRW_view_reset();
DST.buffer_finish_called = false;
DRW_render_gpencil_to_image(engine, render_layer, &render_rect);
}
@@ -1827,7 +1820,7 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
for (RenderView *render_view = render_result->views.first; render_view != NULL;
render_view = render_view->next) {
RE_SetActiveRenderView(render, render_view->name);
- drw_view_reset();
+ DRW_view_reset();
engine_type->draw_engine->render_to_image(data, engine, render_layer, &render_rect);
DST.buffer_finish_called = false;
}
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 07fb97236fb..01cecc338c7 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -262,11 +262,19 @@ void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, con
DRW_shgroup_uniform_texture_ex(shgroup, name, tex, GPU_SAMPLER_MAX);
}
-void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, const char *name, GPUTexture **tex)
+void DRW_shgroup_uniform_texture_ref_ex(DRWShadingGroup *shgroup,
+ const char *name,
+ GPUTexture **tex,
+ eGPUSamplerState sampler_state)
{
BLI_assert(tex != NULL);
int loc = GPU_shader_get_texture_binding(shgroup->shader, name);
- drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_TEXTURE_REF, tex, GPU_SAMPLER_MAX, 0, 1);
+ drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_TEXTURE_REF, tex, sampler_state, 0, 1);
+}
+
+void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, const char *name, GPUTexture **tex)
+{
+ DRW_shgroup_uniform_texture_ref_ex(shgroup, name, tex, GPU_SAMPLER_MAX);
}
void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup,
@@ -1786,6 +1794,14 @@ const DRWView *DRW_view_default_get(void)
return DST.view_default;
}
+/* WARNING: Only use in render AND only if you are going to set view_default again. */
+void DRW_view_reset(void)
+{
+ DST.view_default = NULL;
+ DST.view_active = NULL;
+ DST.view_previous = NULL;
+}
+
/* MUST only be called once per render and only in render mode. Sets default view. */
void DRW_view_default_set(DRWView *view)
{
diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h
index fe45ec7b78b..f9bdf726930 100644
--- a/source/blender/gpu/GPU_vertex_buffer.h
+++ b/source/blender/gpu/GPU_vertex_buffer.h
@@ -80,6 +80,8 @@ void GPU_vertbuf_init_with_format_ex(GPUVertBuf *, const GPUVertFormat *, GPUUsa
#define GPU_vertbuf_init_with_format(verts, format) \
GPU_vertbuf_init_with_format_ex(verts, format, GPU_USAGE_STATIC)
+GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts);
+
uint GPU_vertbuf_size_get(const GPUVertBuf *);
void GPU_vertbuf_data_alloc(GPUVertBuf *, uint v_len);
void GPU_vertbuf_data_resize(GPUVertBuf *, uint v_len);
diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h
index 61b14a4c5c0..34bfbb27823 100644
--- a/source/blender/gpu/GPU_vertex_format.h
+++ b/source/blender/gpu/GPU_vertex_format.h
@@ -124,6 +124,10 @@ BLI_INLINE const char *GPU_vertformat_attr_name_get(const GPUVertFormat *format,
return format->names + attr->names[n_idx];
}
+/* WARNING: Can only rename using a string with same character count.
+ * WARNING: This removes all other aliases of this attrib */
+void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr, const char *new_name);
+
void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uint max_len);
/* format conversion */
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.c b/source/blender/gpu/intern/gpu_vertex_buffer.c
index 25daabe601d..3b4d469542c 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer.c
+++ b/source/blender/gpu/intern/gpu_vertex_buffer.c
@@ -85,6 +85,35 @@ void GPU_vertbuf_init_with_format_ex(GPUVertBuf *verts,
}
}
+GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts)
+{
+ GPUVertBuf *verts_dst = GPU_vertbuf_create(GPU_USAGE_STATIC);
+ /* Full copy. */
+ *verts_dst = *verts;
+ GPU_vertformat_copy(&verts_dst->format, &verts->format);
+
+ if (verts->vbo_id) {
+ uint buffer_sz = GPU_vertbuf_size_get(verts);
+
+ verts_dst->vbo_id = GPU_buf_alloc();
+
+ glBindBuffer(GL_COPY_READ_BUFFER, verts->vbo_id);
+ glBindBuffer(GL_COPY_WRITE_BUFFER, verts_dst->vbo_id);
+
+ glBufferData(GL_COPY_WRITE_BUFFER, buffer_sz, NULL, convert_usage_type_to_gl(verts->usage));
+
+ glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, buffer_sz);
+#if VRAM_USAGE
+ vbo_memory_usage += GPU_vertbuf_size_get(verts);
+#endif
+ }
+
+ if (verts->data) {
+ verts_dst->data = MEM_dupallocN(verts->data);
+ }
+ return verts_dst;
+}
+
/** Same as discard but does not free. */
void GPU_vertbuf_clear(GPUVertBuf *verts)
{
diff --git a/source/blender/gpu/intern/gpu_vertex_format.c b/source/blender/gpu/intern/gpu_vertex_format.c
index b84a7e0f554..585a22277b2 100644
--- a/source/blender/gpu/intern/gpu_vertex_format.c
+++ b/source/blender/gpu/intern/gpu_vertex_format.c
@@ -262,6 +262,20 @@ int GPU_vertformat_attr_id_get(const GPUVertFormat *format, const char *name)
return -1;
}
+void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr_id, const char *new_name)
+{
+ BLI_assert(attr_id > -1 && attr_id < format->attr_len);
+ GPUVertAttr *attr = &format->attrs[attr_id];
+ char *attr_name = (char *)GPU_vertformat_attr_name_get(format, attr, 0);
+ BLI_assert(strlen(attr_name) == strlen(new_name));
+ int i = 0;
+ while (attr_name[i] != '\0') {
+ attr_name[i] = new_name[i];
+ i++;
+ }
+ attr->name_len = 1;
+}
+
/* Encode 8 original bytes into 11 safe bytes. */
static void safe_bytes(char out[11], const char data[8])
{
diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h
index 4b6f079aa28..3f8730e21db 100644
--- a/source/blender/makesdna/DNA_scene_defaults.h
+++ b/source/blender/makesdna/DNA_scene_defaults.h
@@ -220,6 +220,8 @@
\
.motion_blur_samples = 8, \
.motion_blur_shutter = 0.5f, \
+ .motion_blur_depth_scale = 100.0f, \
+ .motion_blur_max = 32, \
\
.shadow_cube_size = 512, \
.shadow_cascade_size = 1024, \
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index c50e48982b3..7329365805c 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1628,8 +1628,10 @@ typedef struct SceneEEVEE {
float bloom_radius;
float bloom_clamp;
- int motion_blur_samples;
+ int motion_blur_samples DNA_DEPRECATED;
+ int motion_blur_max;
float motion_blur_shutter;
+ float motion_blur_depth_scale;
int shadow_method DNA_DEPRECATED;
int shadow_cube_size;
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index edbf4c90524..ed0e422cc20 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -7150,12 +7150,6 @@ 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, NULL);
- prop = RNA_def_property(srna, "motion_blur_samples", PROP_INT, PROP_UNSIGNED);
- RNA_def_property_ui_text(prop, "Samples", "Number of samples to take with motion blur");
- RNA_def_property_range(prop, 1, 64);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
-
prop = RNA_def_property(srna, "motion_blur_shutter", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_ui_text(prop, "Shutter", "Time taken in frames between shutter open and close");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
@@ -7163,6 +7157,23 @@ 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, NULL);
+ prop = RNA_def_property(srna, "motion_blur_depth_scale", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_ui_text(prop,
+ "Background Separation",
+ "Lower values will reduce background"
+ " bleeding onto foreground elements");
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.01f, 1000.0f, 1, 2);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "motion_blur_max", PROP_INT, PROP_PIXEL);
+ RNA_def_property_ui_text(prop, "Max Blur", "Maximum blur distance a pixel can spread over");
+ RNA_def_property_range(prop, 1, 2048);
+ RNA_def_property_ui_range(prop, 1, 512, 1, -1);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
/* Shadows */
prop = RNA_def_property(srna, "shadow_cube_size", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, eevee_shadow_size_items);