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
diff options
context:
space:
mode:
authorJulian Eisel <julian@blender.org>2020-07-01 18:25:04 +0300
committerJulian Eisel <julian@blender.org>2020-07-01 18:25:04 +0300
commit889c4ca9c26d770af02cedef972d65eb173bdb5e (patch)
tree6f46156146db1b3f09d92c6eaa1f44ba8a584ed4 /source/blender/draw/engines/eevee
parent87df15190210eb84ef52e5dccc2932918f912da5 (diff)
parent0829cebeb024095c268f190c34daa8ae9a5a224c (diff)
Merge branch 'asset-uuid--archived' into asset-engine--archivedasset-engine--archived
Diffstat (limited to 'source/blender/draw/engines/eevee')
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c144
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c29
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c137
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.c1
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobes.c4
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c16
-rw-r--r--source/blender/draw/engines/eevee/eevee_mist.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c598
-rw-r--r--source/blender/draw/engines/eevee/eevee_occlusion.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h117
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c81
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c49
-rw-r--r--source/blender/draw/engines/eevee/eevee_screen_raytrace.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_subsurface.c25
-rw-r--r--source/blender/draw/engines/eevee/eevee_temporal_sampling.c41
-rw-r--r--source/blender/draw/engines/eevee/eevee_volumes.c2
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl15
-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.glsl54
24 files changed, 1493 insertions, 290 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
index a19af77124f..a4aa0e10198 100644
--- a/source/blender/draw/engines/eevee/eevee_data.c
+++ b/source/blender/draw/engines/eevee/eevee_data.c
@@ -24,11 +24,153 @@
#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;
+ switch (geom_mb->type) {
+ case EEVEE_HAIR_GEOM_MOTION_DATA:
+ for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) {
+ GPU_VERTBUF_DISCARD_SAFE(geom_mb->hair_pos[i]);
+ DRW_TEXTURE_FREE_SAFE(geom_mb->hair_pos_tx[i]);
+ }
+ break;
+
+ case EEVEE_MESH_GEOM_MOTION_DATA:
+ for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) {
+ GPU_VERTBUF_DISCARD_SAFE(geom_mb->vbo[i]);
+ }
+ break;
+ }
+ 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,
+ bool hair)
+{
+ if (mb->object == NULL) {
+ return NULL;
+ }
+
+ EEVEE_ObjectKey key, *key_p;
+ /* Small hack to avoid another comparisson. */
+ key.ob = (Object *)((char *)ob + hair);
+ 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,
+ bool hair)
+{
+ if (mb->geom == NULL) {
+ return NULL;
+ }
+
+ /* Use original data as key to ensure matching accross update. */
+ Object *ob_orig = DEG_get_original_object(ob);
+
+ void *key = (char *)ob_orig->data + hair;
+ EEVEE_GeometryMotionData *geom_step = BLI_ghash_lookup(mb->geom, key);
+ if (geom_step == NULL) {
+ geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__);
+ geom_step->type = (hair) ? EEVEE_HAIR_GEOM_MOTION_DATA : EEVEE_MESH_GEOM_MOTION_DATA;
+ BLI_ghash_insert(mb->geom, key, geom_step);
+ }
+
+ return geom_step;
+}
+
+/* View Layer data. */
+
void EEVEE_view_layer_data_free(void *storage)
{
EEVEE_ViewLayerData *sldata = (EEVEE_ViewLayerData *)storage;
@@ -105,6 +247,8 @@ static void eevee_object_data_init(DrawData *dd)
{
EEVEE_ObjectEngineData *eevee_data = (EEVEE_ObjectEngineData *)dd;
eevee_data->shadow_caster_id = -1;
+ eevee_data->need_update = false;
+ eevee_data->geom_update = false;
}
EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob)
diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c
index ab846fe0f11..8c48ae65d9b 100644
--- a/source/blender/draw/engines/eevee/eevee_effects.c
+++ b/source/blender/draw/engines/eevee/eevee_effects.c
@@ -153,7 +153,7 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
effects->enabled_effects = 0;
effects->enabled_effects |= (G.debug_value == 9) ? EFFECT_VELOCITY_BUFFER : 0;
- effects->enabled_effects |= EEVEE_motion_blur_init(sldata, vedata, camera);
+ effects->enabled_effects |= EEVEE_motion_blur_init(sldata, vedata);
effects->enabled_effects |= EEVEE_bloom_init(sldata, vedata);
effects->enabled_effects |= EEVEE_depth_of_field_init(sldata, vedata, camera);
effects->enabled_effects |= EEVEE_temporal_sampling_init(sldata, vedata);
@@ -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 b698574f9d7..bac96ab1079 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -166,7 +166,9 @@ static void eevee_cache_finish(void *vedata)
EEVEE_materials_cache_finish(sldata, vedata);
EEVEE_lights_cache_finish(sldata, vedata);
EEVEE_lightprobes_cache_finish(sldata, vedata);
+ EEVEE_renderpasses_cache_finish(sldata, vedata);
+ EEVEE_subsurface_draw_init(sldata, vedata);
EEVEE_effects_draw_init(sldata, vedata);
EEVEE_volumes_draw_init(sldata, vedata);
@@ -213,6 +215,10 @@ static void eevee_draw_scene(void *vedata)
loop_len = MAX2(1, scene->eevee.taa_samples);
}
+ if (stl->effects->bypass_drawing) {
+ loop_len = 0;
+ }
+
while (loop_len--) {
float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float clear_depth = 1.0f;
@@ -351,11 +357,18 @@ static void eevee_draw_scene(void *vedata)
EEVEE_renderpasses_draw(sldata, vedata);
}
+ if (stl->effects->bypass_drawing) {
+ /* Restore the depth from sample 1. */
+ GPU_framebuffer_blit(fbl->double_buffer_depth_fb, 0, dfbl->default_fb, 0, GPU_DEPTH_BIT);
+ }
+
EEVEE_renderpasses_draw_debug(vedata);
EEVEE_volumes_free_smoke_textures();
stl->g_data->view_updated = false;
+
+ DRW_view_set_active(NULL);
}
static void eevee_view_update(void *vedata)
@@ -370,7 +383,7 @@ static void eevee_id_object_update(void *UNUSED(vedata), Object *object)
{
EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(object);
if (ped != NULL && ped->dd.recalc != 0) {
- ped->need_update = (ped->dd.recalc & (ID_RECALC_TRANSFORM)) != 0;
+ ped->need_update = (ped->dd.recalc & ID_RECALC_TRANSFORM) != 0;
ped->dd.recalc = 0;
}
EEVEE_LightEngineData *led = EEVEE_light_data_get(object);
@@ -381,6 +394,7 @@ static void eevee_id_object_update(void *UNUSED(vedata), Object *object)
EEVEE_ObjectEngineData *oedata = EEVEE_object_data_get(object);
if (oedata != NULL && oedata->dd.recalc != 0) {
oedata->need_update = true;
+ oedata->geom_update = (oedata->dd.recalc & (ID_RECALC_GEOMETRY)) != 0;
oedata->dd.recalc = 0;
}
}
@@ -390,6 +404,11 @@ static void eevee_id_world_update(void *vedata, World *wo)
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
LightCache *lcache = stl->g_data->light_cache;
+ if (lcache == NULL || lcache == stl->lookdev_lightcache) {
+ /* Avoid Lookdev viewport clearing the update flag (see T67741). */
+ return;
+ }
+
EEVEE_WorldEngineData *wedata = EEVEE_world_data_ensure(wo);
if (wedata != NULL && wedata->dd.recalc != 0) {
@@ -400,7 +419,7 @@ static void eevee_id_world_update(void *vedata, World *wo)
}
}
-static void eevee_id_update(void *vedata, ID *id)
+void eevee_id_update(void *vedata, ID *id)
{
/* Handle updates based on ID type. */
switch (GS(id->name)) {
@@ -416,23 +435,129 @@ static void eevee_id_update(void *vedata, ID *id)
}
}
+static void eevee_render_reset_passes(EEVEE_Data *vedata)
+{
+ /* Reset passlist. This is safe as they are stored into managed memory chunks. */
+ memset(vedata->psl, 0, sizeof(*vedata->psl));
+}
+
static void eevee_render_to_image(void *vedata,
RenderEngine *engine,
struct RenderLayer *render_layer,
const rcti *rect)
{
+ EEVEE_Data *ved = (EEVEE_Data *)vedata;
const DRWContextState *draw_ctx = DRW_context_state_get();
+ Depsgraph *depsgraph = draw_ctx->depsgraph;
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
+ const bool do_motion_blur = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0;
+ const bool do_motion_blur_fx = do_motion_blur && (scene->eevee.motion_blur_max > 0);
- if (!EEVEE_render_init(vedata, engine, draw_ctx->depsgraph)) {
+ if (!EEVEE_render_init(vedata, engine, depsgraph)) {
return;
}
+ EEVEE_PrivateData *g_data = ved->stl->g_data;
+
+ int steps = max_ii(1, scene->eevee.motion_blur_steps);
+ int time_steps_tot = (do_motion_blur) ? steps : 1;
+ g_data->render_tot_samples = divide_ceil_u(scene->eevee.taa_render_samples, time_steps_tot);
+ /* Centered on frame for now. */
+ float time = CFRA - scene->eevee.motion_blur_shutter / 2.0f;
+ float time_step = scene->eevee.motion_blur_shutter / time_steps_tot;
+ for (int i = 0; i < time_steps_tot && !RE_engine_test_break(engine); i++) {
+ float time_prev = time;
+ float time_curr = time + time_step * 0.5f;
+ float time_next = time + time_step;
+ time += time_step;
+
+ /* Previous motion step. */
+ if (do_motion_blur_fx) {
+ if (i > 0) {
+ /* The previous step of this iteration N is exactly the next step of iteration N - 1.
+ * So we just swap the resources to avoid too much re-evaluation. */
+ EEVEE_motion_blur_swap_data(vedata);
+ }
+ else {
+ EEVEE_motion_blur_step_set(ved, MB_PREV);
+ RE_engine_frame_set(engine, floorf(time_prev), fractf(time_prev));
+
+ EEVEE_render_view_sync(vedata, engine, depsgraph);
+ EEVEE_render_cache_init(sldata, vedata);
+
+ DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
+
+ EEVEE_motion_blur_cache_finish(vedata);
+ EEVEE_materials_cache_finish(sldata, vedata);
+ eevee_render_reset_passes(vedata);
+ }
+ }
+
+ /* Next motion step. */
+ if (do_motion_blur_fx) {
+ EEVEE_motion_blur_step_set(ved, MB_NEXT);
+ RE_engine_frame_set(engine, floorf(time_next), fractf(time_next));
- DRW_render_object_iter(vedata, engine, draw_ctx->depsgraph, EEVEE_render_cache);
+ EEVEE_render_view_sync(vedata, engine, depsgraph);
+ EEVEE_render_cache_init(sldata, vedata);
- /* Actually do the rendering. */
- EEVEE_render_draw(vedata, engine, render_layer, rect);
+ DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
+
+ EEVEE_motion_blur_cache_finish(vedata);
+ EEVEE_materials_cache_finish(sldata, vedata);
+ eevee_render_reset_passes(vedata);
+ }
+
+ /* Current motion step. */
+ {
+ if (do_motion_blur) {
+ EEVEE_motion_blur_step_set(ved, MB_CURR);
+ RE_engine_frame_set(engine, floorf(time_curr), fractf(time_curr));
+ }
+
+ EEVEE_render_view_sync(vedata, engine, depsgraph);
+ EEVEE_render_cache_init(sldata, vedata);
+
+ DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
+
+ EEVEE_motion_blur_cache_finish(vedata);
+ EEVEE_volumes_cache_finish(sldata, vedata);
+ EEVEE_materials_cache_finish(sldata, vedata);
+ EEVEE_lights_cache_finish(sldata, vedata);
+ EEVEE_lightprobes_cache_finish(sldata, vedata);
+ EEVEE_renderpasses_cache_finish(sldata, vedata);
+
+ EEVEE_subsurface_draw_init(sldata, vedata);
+ EEVEE_effects_draw_init(sldata, vedata);
+ EEVEE_volumes_draw_init(sldata, vedata);
+ }
+
+ /* Actual drawing. */
+ {
+ EEVEE_renderpasses_output_init(sldata, vedata, g_data->render_tot_samples * time_steps_tot);
+
+ EEVEE_temporal_sampling_create_view(vedata);
+ EEVEE_render_draw(vedata, engine, render_layer, rect);
+
+ if (i < time_steps_tot - 1) {
+ /* Don't reset after the last loop. Since EEVEE_render_read_result
+ * might need some DRWPasses. */
+ DRW_cache_restart();
+ }
+ }
+ }
EEVEE_volumes_free_smoke_textures();
+ EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur);
+
+ if (RE_engine_test_break(engine)) {
+ return;
+ }
+
+ EEVEE_render_read_result(vedata, engine, render_layer, rect);
+
+ /* Restore original viewport size. */
+ DRW_render_viewport_size_set((int[2]){g_data->size_orig[0], g_data->size_orig[1]});
}
static void eevee_engine_free(void)
diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c
index 4cdd166f09c..78d50a02fc7 100644
--- a/source/blender/draw/engines/eevee/eevee_lightcache.c
+++ b/source/blender/draw/engines/eevee/eevee_lightcache.c
@@ -849,6 +849,7 @@ static void eevee_lightbake_cache_create(EEVEE_Data *vedata, EEVEE_LightBake *lb
/* Disable volumetrics when baking. */
stl->effects->enabled_effects &= ~EFFECT_VOLUMETRIC;
+ EEVEE_subsurface_draw_init(sldata, vedata);
EEVEE_effects_draw_init(sldata, vedata);
EEVEE_volumes_draw_init(sldata, vedata);
diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c
index 83b2a9bb6d4..71c8294d123 100644
--- a/source/blender/draw/engines/eevee/eevee_lightprobes.c
+++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c
@@ -957,8 +957,8 @@ static void lightbake_render_scene_reflected(int layer, EEVEE_BakeRenderData *us
/* Slight modification: we handle refraction as normal
* shading and don't do SSRefraction. */
- DRW_draw_pass(psl->depth_ps);
- DRW_draw_pass(psl->depth_refract_ps);
+ DRW_draw_pass(psl->depth_clip_ps);
+ DRW_draw_pass(psl->depth_refract_clip_ps);
DRW_draw_pass(psl->probe_background);
EEVEE_create_minmax_buffer(vedata, tmp_planar_depth, layer);
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index cfc70baaf01..8537ad0e532 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -801,6 +801,8 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata,
*matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp);
*cast_shadow = true;
}
+
+ EEVEE_motion_blur_hair_cache_populate(sldata, vedata, ob, psys, md);
}
#define ADD_SHGROUP_CALL(shgrp, ob, geom, oedata) \
@@ -851,8 +853,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata,
*/
bool use_volume_material = (matcache[0].shading_gpumat &&
GPU_material_has_volume_output(matcache[0].shading_gpumat));
-
- if ((ob->dt >= OB_SOLID) || DRW_state_is_image_render()) {
+ if ((ob->dt >= OB_SOLID) || DRW_state_is_scene_render()) {
if (use_sculpt_pbvh) {
struct DRWShadingGroup **shgrps_array = BLI_array_alloca(shgrps_array, materials_len);
@@ -901,6 +902,9 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata,
}
}
}
+
+ /* Motion Blur Vectors. */
+ EEVEE_motion_blur_cache_populate(sldata, vedata, ob);
}
/* Volumetrics */
@@ -946,17 +950,15 @@ void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata,
eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, HAIR_MATERIAL_NR, cast_shadow);
}
-void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
+void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PrivateData *pd = vedata->stl->g_data;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
BLI_ghash_free(pd->material_hash, NULL, NULL);
+ pd->material_hash = NULL;
SET_FLAG_FROM_TEST(effects->enabled_effects, effects->sss_surface_count > 0, EFFECT_SSS);
-
- /* TODO(fclem) this is not really clean. Init should not be done in cache finish. */
- EEVEE_subsurface_draw_init(sldata, vedata);
}
void EEVEE_materials_free(void)
@@ -1015,7 +1017,7 @@ void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
/* Should be enough precision for many samples. */
const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_RGBA32F : GPU_RGBA16F;
- const bool do_clear = DRW_state_is_image_render() || (effects->taa_current_sample == 1);
+ const bool do_clear = (effects->taa_current_sample == 1);
/* Create FrameBuffer. */
GPU_framebuffer_ensure_config(&fbl->material_accum_fb,
{GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_LEAVE});
diff --git a/source/blender/draw/engines/eevee/eevee_mist.c b/source/blender/draw/engines/eevee/eevee_mist.c
index 7b942784ee9..1cedd334d67 100644
--- a/source/blender/draw/engines/eevee/eevee_mist.c
+++ b/source/blender/draw/engines/eevee/eevee_mist.c
@@ -75,7 +75,7 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->mist_accum)});
/* Clear texture. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1) {
+ if (effects->taa_current_sample == 1) {
GPU_framebuffer_bind(fbl->mist_accum_fb);
GPU_framebuffer_clear_color(fbl->mist_accum_fb, clear);
}
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
index a6e6b30a6b1..586ee780f1d 100644
--- a/source/blender/draw/engines/eevee/eevee_motion_blur.c
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c
@@ -24,12 +24,19 @@
#include "DRW_render.h"
+#include "BLI_rand.h"
+#include "BLI_string_utils.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_modifier_types.h"
+#include "DNA_particle_types.h"
#include "DNA_screen_types.h"
#include "ED_screen.h"
@@ -37,172 +44,517 @@
#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 *motion_blur_hair_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_hair_lib_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");
+
+ char *vert = BLI_string_joinN(datatoc_common_hair_lib_glsl, datatoc_object_motion_vert_glsl);
+ e_data.motion_blur_hair_sh = DRW_shader_create_with_lib(
+ vert, NULL, datatoc_object_motion_frag_glsl, datatoc_common_view_lib_glsl, "#define HAIR\n");
+ MEM_freeN(vert);
+}
- /* 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)
+{
+ 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;
+ }
+
+ 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 (!e_data.motion_blur_sh) {
+ eevee_create_shader_motion_blur();
+ }
- /* Compute winmat */
- CameraParams params;
- BKE_camera_params_init(&params);
+ 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);
+ }
- 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);
+ 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;
}
- 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);
+ 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);
}
- BKE_camera_params_compute_matrix(&params);
+ 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();
+ 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 = 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);
+
+ 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);
+
+ DRW_PASS_CREATE(psl->velocity_hair, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
+
+ mb_data->hair_grp = grp = DRW_shgroup_create(e_data.motion_blur_hair_sh, 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);
+ }
- /* 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);
+ EEVEE_motion_blur_data_init(mb_data);
+ }
+ else {
+ psl->motion_blur = NULL;
+ psl->velocity_object = NULL;
+ psl->velocity_hair = NULL;
+ }
}
-static void eevee_create_shader_motion_blur(void)
+void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
+ EEVEE_Data *vedata,
+ Object *ob,
+ ParticleSystem *psys,
+ ModifierData *md)
{
- e_data.motion_blur_sh = DRW_shader_create_fullscreen(datatoc_effect_motion_blur_frag_glsl, NULL);
+ 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_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(
+ &effects->motion_blur, ob, true);
+
+ 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]);
+ }
+
+ grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp);
+ 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", mb_geom->hair_pos_tx[MB_PREV]);
+ DRW_shgroup_uniform_texture(grp, "nxtBuffer", mb_geom->hair_pos_tx[MB_NEXT]);
+ DRW_shgroup_uniform_bool(grp, "useDeform", &mb_geom->use_deform, 1);
+ }
+ else {
+ /* Store vertex position buffer. */
+ mb_geom->hair_pos[mb_step] = DRW_hair_pos_buffer_get(ob, psys, md);
+ mb_geom->use_deform = true;
+ }
+ }
}
-int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, Object *camera)
+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;
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
- Scene *scene = draw_ctx->scene;
+ if (!DRW_state_is_scene_render() || psl->velocity_object == NULL) {
+ return;
+ }
- View3D *v3d = draw_ctx->v3d;
- RegionView3D *rv3d = draw_ctx->rv3d;
- ARegion *region = draw_ctx->region;
+ 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 (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);
+ if (!(object_moves || is_deform)) {
+ return;
+ }
- /* Viewport Matrix */
- /* Note: This does not have TAA jitter applied. */
- DRW_view_persmat_get(NULL, persmat, false);
+ EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
+ &effects->motion_blur, ob, false);
- bool view_is_valid = (stl->g_data->view_updated == 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(
+ &effects->motion_blur, ob, false);
+
+ if (mb_step == MB_CURR) {
+ GPUBatch *batch = DRW_cache_object_surface_get(ob);
+ if (batch == NULL) {
+ return;
+ }
- 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);
+ /* 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]);
}
- /* 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);
+ 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);
+
+ DRW_shgroup_call(grp, batch, ob);
+
+ if (mb_geom->use_deform) {
+ EEVEE_ObjectEngineData *oedata = EEVEE_object_data_ensure(ob);
+ if (!oedata->geom_update) {
+ /* FIXME(fclem) There can be false positive where the actual mesh is not updated.
+ * This avoids a crash but removes the motion blur from some object.
+ * Maybe an issue with depsgraph tagging. */
+ mb_geom->use_deform = false;
+ oedata->geom_update = false;
+
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]);
}
+ /* 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;
+ }
+ }
+}
+
+void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata)
+{
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_EffectsInfo *effects = stl->effects;
+ GHashIterator ghi;
- effects->motion_blur_mat_cached = view_is_valid && !DRW_state_is_image_render();
+ if ((effects->enabled_effects & EFFECT_MOTION_BLUR) == 0) {
+ return;
+ }
- /* 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);
- }
+ 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_hair_update */
+ GPU_framebuffer_bind(vedata->fbl->main_fb);
+ DRW_hair_update();
- /* 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);
+ DRW_cache_restart();
+ }
+
+ 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);
+
+ if (!mb_geom->use_deform) {
+ continue;
+ }
+
+ switch (mb_geom->type) {
+ case EEVEE_HAIR_GEOM_MOTION_DATA:
+ if (mb_step == MB_CURR) {
+ /* TODO(fclem) Check if vertex count mismatch. */
+ mb_geom->use_deform = true;
}
+ else {
+ mb_geom->hair_pos[mb_step] = GPU_vertbuf_duplicate(mb_geom->hair_pos[mb_step]);
- effects->motion_blur_mat_cached = true;
- effects->motion_blur_samples = scene_eval->eevee.motion_blur_samples;
+ /* Create vbo immediately to bind to texture buffer. */
+ GPU_vertbuf_use(mb_geom->hair_pos[mb_step]);
- if (!e_data.motion_blur_sh) {
- eevee_create_shader_motion_blur();
+ mb_geom->hair_pos_tx[mb_step] = GPU_texture_create_from_vertbuf(
+ mb_geom->hair_pos[mb_step]);
+ }
+ break;
+
+ case EEVEE_MESH_GEOM_MOTION_DATA:
+ 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;
+ }
+
+ if (mb_geom->use_deform == false) {
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]);
+ break;
+ }
+ else {
+ /* Modify the batch to include the previous & next position. */
+ if (i == MB_PREV) {
+ GPU_batch_vertbuf_add_ex(batch, vbo, true);
+ mb_geom->vbo[i] = NULL;
+ }
+ else {
+ /* This VBO can be reuse by next time step. Don't pass ownership. */
+ GPU_batch_vertbuf_add_ex(batch, vbo, false);
+ }
+ }
+ }
+ }
+ }
+ 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");
+ }
}
+ break;
- return EFFECT_MOTION_BLUR | EFFECT_POST_BUFFER;
- }
+ default:
+ BLI_assert(0);
+ break;
}
}
-
- return 0;
}
-void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
+void EEVEE_motion_blur_swap_data(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();
- struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
+ GHashIterator ghi;
- if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
- DRW_PASS_CREATE(psl->motion_blur, DRW_STATE_WRITE_COLOR);
+ BLI_assert((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0);
- 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);
+ /* Camera Data. */
+ effects->motion_blur.camera[MB_PREV] = effects->motion_blur.camera[MB_CURR];
+
+ /* Object Data. */
+ 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);
+
+ copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]);
+ }
+
+ /* Deformation Data. */
+ 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);
+
+ switch (mb_geom->type) {
+ case EEVEE_HAIR_GEOM_MOTION_DATA:
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->hair_pos[MB_PREV]);
+ DRW_TEXTURE_FREE_SAFE(mb_geom->hair_pos_tx[MB_PREV]);
+ mb_geom->hair_pos[MB_PREV] = mb_geom->hair_pos[MB_NEXT];
+ mb_geom->hair_pos_tx[MB_PREV] = mb_geom->hair_pos_tx[MB_NEXT];
+ break;
+
+ case EEVEE_MESH_GEOM_MOTION_DATA:
+ GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
+ mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT];
+
+ if (mb_geom->vbo[MB_NEXT]) {
+ GPUVertBuf *vbo = mb_geom->vbo[MB_NEXT];
+ int attrib_id = GPU_vertformat_attr_id_get(&vbo->format, "nxt");
+ GPU_vertformat_attr_rename(&vbo->format, attrib_id, "prv");
+ }
+ break;
+
+ default:
+ BLI_assert(0);
+ break;
+ }
}
}
@@ -216,6 +568,30 @@ void EEVEE_motion_blur_draw(EEVEE_Data *vedata)
/* 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]);
+
+ buf = buf ? 0 : 1;
+ }
+
GPU_framebuffer_bind(effects->target_buffer);
DRW_draw_pass(psl->motion_blur);
SWAP_BUFFERS();
@@ -225,4 +601,8 @@ 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.motion_blur_hair_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_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c
index f5ebbe08dd1..a075210967c 100644
--- a/source/blender/draw/engines/eevee/eevee_occlusion.c
+++ b/source/blender/draw/engines/eevee/eevee_occlusion.c
@@ -155,7 +155,7 @@ void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)});
/* Clear texture. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1) {
+ if (effects->taa_current_sample == 1) {
GPU_framebuffer_bind(fbl->ao_accum_fb);
GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
}
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 40008c5c364..5005c5a8ba9 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,12 @@ 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_hair;
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 +334,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 +565,58 @@ 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];
+ DRWShadingGroup *hair_grp;
+} 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 enum eEEVEEMotionData {
+ EEVEE_MESH_GEOM_MOTION_DATA = 0,
+ EEVEE_HAIR_GEOM_MOTION_DATA,
+} eEEVEEMotionData;
+
+typedef struct EEVEE_GeometryMotionData {
+ eEEVEEMotionData type;
+ int use_deform; /* To disable deform mb if vertcount mismatch. */
+ union {
+ struct {
+ /* Mesh */
+ struct GPUBatch *batch; /* Batch for time = t. */
+ struct GPUVertBuf *vbo[2]; /* Vbo for time = t +/- step. */
+ };
+ struct {
+ /* Hair */
+ struct GPUVertBuf *hair_pos[2]; /* Position buffer for time = t +/- step. */
+ struct GPUTexture *hair_pos_tx[2]; /* Buffer Texture of the corresponding VBO. */
+ };
+ };
+} EEVEE_GeometryMotionData;
+
/* ************ EFFECTS DATA ************* */
typedef enum EEVEE_EffectsFlag {
@@ -607,9 +668,10 @@ typedef struct EEVEE_EffectsInfo {
int taa_render_sample;
int taa_total_sample;
float taa_alpha;
+ bool bypass_drawing;
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 +679,24 @@ 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;
+ 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];
@@ -805,6 +876,7 @@ typedef struct EEVEE_ObjectEngineData {
bool ob_vis, ob_vis_dirty;
bool need_update;
+ bool geom_update;
uint shadow_caster_id;
} EEVEE_ObjectEngineData;
@@ -880,15 +952,25 @@ typedef struct EEVEE_PrivateData {
struct DRWView *world_views[6];
/** For rendering planar reflections. */
struct DRWView *planar_views[MAX_PLANAR];
+
+ int render_tot_samples;
} 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,
+ bool hair);
+EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb,
+ Object *ob,
+ bool hair);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob);
EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob);
@@ -896,6 +978,8 @@ EEVEE_LightEngineData *EEVEE_light_data_ensure(Object *ob);
EEVEE_WorldEngineData *EEVEE_world_data_get(World *wo);
EEVEE_WorldEngineData *EEVEE_world_data_ensure(World *wo);
+void eevee_id_update(void *vedata, ID *id);
+
/* eevee_materials.c */
struct GPUTexture *EEVEE_materials_get_util_tex(void); /* XXX */
void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
@@ -1112,8 +1196,17 @@ void EEVEE_subsurface_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data
void EEVEE_subsurface_free(void);
/* eevee_motion_blur.c */
-int EEVEE_motion_blur_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *camera);
+int EEVEE_motion_blur_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
+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_hair_cache_populate(EEVEE_ViewLayerData *sldata,
+ EEVEE_Data *vedata,
+ Object *ob,
+ struct ParticleSystem *psys,
+ struct ModifierData *md);
+void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata);
+void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata);
void EEVEE_motion_blur_draw(EEVEE_Data *vedata);
void EEVEE_motion_blur_free(void);
@@ -1127,6 +1220,7 @@ void EEVEE_renderpasses_init(EEVEE_Data *vedata);
void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
uint tot_samples);
+void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
bool post_effect);
@@ -1140,6 +1234,7 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata);
/* eevee_temporal_sampling.c */
void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata);
+void EEVEE_temporal_sampling_create_view(EEVEE_Data *vedata);
int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_temporal_sampling_offset_calc(const double ht_point[2],
const float filter_size,
@@ -1183,6 +1278,10 @@ void EEVEE_effects_free(void);
bool EEVEE_render_init(EEVEE_Data *vedata,
struct RenderEngine *engine,
struct Depsgraph *depsgraph);
+void EEVEE_render_view_sync(EEVEE_Data *vedata,
+ struct RenderEngine *engine,
+ struct Depsgraph *depsgraph);
+void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_render_cache(void *vedata,
struct Object *ob,
struct RenderEngine *engine,
@@ -1191,6 +1290,10 @@ void EEVEE_render_draw(EEVEE_Data *vedata,
struct RenderEngine *engine,
struct RenderLayer *render_layer,
const struct rcti *rect);
+void EEVEE_render_read_result(EEVEE_Data *vedata,
+ struct RenderEngine *engine,
+ struct RenderLayer *rl,
+ const rcti *rect);
void EEVEE_render_update_passes(struct RenderEngine *engine,
struct Scene *scene,
struct ViewLayer *view_layer);
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index 89a5ad2198a..f903fa905e8 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -131,6 +131,29 @@ bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *
&sldata->common_data);
}
+ EEVEE_render_view_sync(vedata, engine, depsgraph);
+
+ /* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
+ struct Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re));
+
+ DRWView *view = (DRWView *)DRW_view_default_get();
+ DRW_view_camtexco_set(view, camtexcofac);
+
+ /* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`.
+ * `EEVEE_effects_init` needs to go second for TAA. */
+ EEVEE_renderpasses_init(vedata);
+ EEVEE_effects_init(sldata, vedata, ob_camera_eval, false);
+ EEVEE_materials_init(sldata, vedata, stl, fbl);
+ EEVEE_shadows_init(sldata);
+ EEVEE_lightprobes_init(sldata, vedata);
+
+ return true;
+}
+
+void EEVEE_render_view_sync(EEVEE_Data *vedata, RenderEngine *engine, struct Depsgraph *depsgraph)
+{
+ EEVEE_PrivateData *g_data = vedata->stl->g_data;
+
/* Set the pers & view matrix. */
float winmat[4][4], viewmat[4][4], viewinv[4][4];
/* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
@@ -143,19 +166,13 @@ bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *
invert_m4_m4(viewmat, viewinv);
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);
+}
- /* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`.
- * `EEVEE_effects_init` needs to go second for TAA. */
- EEVEE_renderpasses_init(vedata);
- EEVEE_effects_init(sldata, vedata, ob_camera_eval, false);
- EEVEE_materials_init(sldata, vedata, stl, fbl);
- EEVEE_shadows_init(sldata);
- EEVEE_lightprobes_init(sldata, vedata);
-
- /* INIT CACHE */
+void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
+{
EEVEE_bloom_cache_init(sldata, vedata);
EEVEE_depth_of_field_cache_init(sldata, vedata);
EEVEE_effects_cache_init(sldata, vedata);
@@ -168,8 +185,6 @@ bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *
EEVEE_subsurface_cache_init(sldata, vedata);
EEVEE_temporal_sampling_cache_init(sldata, vedata);
EEVEE_volumes_cache_init(sldata, vedata);
-
- return true;
}
/* Used by light cache. in this case engine is NULL. */
@@ -182,6 +197,8 @@ void EEVEE_render_cache(void *vedata,
EEVEE_LightProbesInfo *pinfo = sldata->probes;
bool cast_shadow = false;
+ eevee_id_update(vedata, &ob->id);
+
if (pinfo->vis_data.collection) {
/* Used for rendering probe with visibility groups. */
bool ob_vis = BKE_collection_has_object_recursive(pinfo->vis_data.collection, ob);
@@ -478,27 +495,12 @@ static void eevee_render_draw_background(EEVEE_Data *vedata)
void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl, const rcti *rect)
{
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
const char *viewname = RE_GetActiveRenderView(engine->re);
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_FramebufferList *fbl = vedata->fbl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
- EEVEE_PrivateData *g_data = stl->g_data;
-
- /* FINISH CACHE */
- EEVEE_volumes_cache_finish(sldata, vedata);
- EEVEE_materials_cache_finish(sldata, vedata);
- EEVEE_lights_cache_finish(sldata, vedata);
- EEVEE_lightprobes_cache_finish(sldata, vedata);
-
- EEVEE_effects_draw_init(sldata, vedata);
- EEVEE_volumes_draw_init(sldata, vedata);
-
- /* Sort transparents before the loop. */
- DRW_pass_sort_shgroup_z(psl->transparent_pass);
/* Push instances attributes to the GPU. */
DRW_render_instance_buffer_finish();
@@ -508,20 +510,17 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
GPU_framebuffer_bind(fbl->main_fb);
DRW_hair_update();
- uint tot_sample = scene_eval->eevee.taa_render_samples;
+ /* Sort transparents before the loop. */
+ DRW_pass_sort_shgroup_z(psl->transparent_pass);
+
+ uint tot_sample = stl->g_data->render_tot_samples;
uint render_samples = 0;
/* SSR needs one iteration to start properly. */
- if (stl->effects->enabled_effects & EFFECT_SSR) {
+ if ((stl->effects->enabled_effects & EFFECT_SSR) && !stl->effects->ssr_was_valid_double_buffer) {
tot_sample += 1;
}
- EEVEE_renderpasses_output_init(sldata, vedata, tot_sample);
-
- if (RE_engine_test_break(engine)) {
- return;
- }
-
while (render_samples < tot_sample && !RE_engine_test_break(engine)) {
float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float clear_depth = 1.0f;
@@ -617,6 +616,15 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
RE_engine_update_progress(engine, (float)(render_samples++) / (float)tot_sample);
}
+}
+
+void EEVEE_render_read_result(EEVEE_Data *vedata,
+ RenderEngine *engine,
+ RenderLayer *rl,
+ const rcti *rect)
+{
+ const char *viewname = RE_GetActiveRenderView(engine->re);
+ EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
eevee_render_result_combined(rl, viewname, rect, vedata, sldata);
eevee_render_result_mist(rl, viewname, rect, vedata, sldata);
@@ -631,9 +639,6 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
eevee_render_result_bloom(rl, viewname, rect, vedata, sldata);
eevee_render_result_volume_scatter(rl, viewname, rect, vedata, sldata);
eevee_render_result_volume_transmittance(rl, viewname, rect, vedata, sldata);
-
- /* Restore original viewport size. */
- DRW_render_viewport_size_set((int[2]){g_data->size_orig[0], g_data->size_orig[1]});
}
void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c
index 9a47ca19e7b..be771d7cf42 100644
--- a/source/blender/draw/engines/eevee/eevee_renderpasses.c
+++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c
@@ -136,24 +136,13 @@ void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
- EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_PrivateData *g_data = stl->g_data;
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const bool needs_post_processing = (g_data->render_passes &
EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0;
if (needs_post_processing) {
- if (e_data.postprocess_sh == NULL) {
- char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_renderpass_postprocess_frag_glsl);
- e_data.postprocess_sh = DRW_shader_create_fullscreen(frag_str, NULL);
- MEM_freeN(frag_str);
- }
-
/* Create FrameBuffer. */
/* Should be enough to store the data needs for a single pass.
@@ -188,29 +177,51 @@ void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
EEVEE_volumes_output_init(sldata, vedata, tot_samples);
}
- /* Create Pass. */
- DRW_PASS_CREATE(psl->renderpass_pass, DRW_STATE_WRITE_COLOR);
- DRWShadingGroup *grp = DRW_shgroup_create(e_data.postprocess_sh, psl->renderpass_pass);
/* We set a default texture as not all post processes uses the inputBuffer. */
g_data->renderpass_input = txl->color;
g_data->renderpass_col_input = txl->color;
g_data->renderpass_light_input = txl->color;
+ }
+ else {
+ /* Free unneeded memory */
+ DRW_TEXTURE_FREE_SAFE(txl->renderpass);
+ GPU_FRAMEBUFFER_FREE_SAFE(fbl->renderpass_fb);
+ }
+}
+
+void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
+{
+ EEVEE_PassList *psl = vedata->psl;
+ EEVEE_PrivateData *g_data = vedata->stl->g_data;
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+
+ const bool needs_post_processing = (g_data->render_passes &
+ EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0;
+ if (needs_post_processing) {
+ if (e_data.postprocess_sh == NULL) {
+ char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
+ datatoc_common_uniforms_lib_glsl,
+ datatoc_bsdf_common_lib_glsl,
+ datatoc_renderpass_postprocess_frag_glsl);
+ e_data.postprocess_sh = DRW_shader_create_fullscreen(frag_str, NULL);
+ MEM_freeN(frag_str);
+ }
+
+ DRW_PASS_CREATE(psl->renderpass_pass, DRW_STATE_WRITE_COLOR);
+ DRWShadingGroup *grp = DRW_shgroup_create(e_data.postprocess_sh, psl->renderpass_pass);
DRW_shgroup_uniform_texture_ref(grp, "inputBuffer", &g_data->renderpass_input);
DRW_shgroup_uniform_texture_ref(grp, "inputColorBuffer", &g_data->renderpass_col_input);
DRW_shgroup_uniform_texture_ref(
grp, "inputSecondLightBuffer", &g_data->renderpass_light_input);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
+ DRW_shgroup_uniform_block_ref(grp, "common_block", &sldata->common_ubo);
+ DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &sldata->renderpass_ubo.combined);
DRW_shgroup_uniform_int(grp, "currentSample", &g_data->renderpass_current_sample, 1);
DRW_shgroup_uniform_int(grp, "renderpassType", &g_data->renderpass_type, 1);
DRW_shgroup_uniform_int(grp, "postProcessType", &g_data->renderpass_postprocess, 1);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
else {
- /* Free unneeded memory */
- DRW_TEXTURE_FREE_SAFE(txl->renderpass);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->renderpass_fb);
psl->renderpass_pass = NULL;
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
index cece67334c5..32d758dba4b 100644
--- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
+++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
@@ -358,7 +358,7 @@ void EEVEE_reflection_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ssr_accum)});
/* Clear texture. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1) {
+ if (effects->taa_current_sample == 1) {
GPU_framebuffer_bind(fbl->ssr_accum_fb);
GPU_framebuffer_clear_color(fbl->ssr_accum_fb, clear);
}
diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c
index 84c50a22ae6..d0e430e115f 100644
--- a/source/blender/draw/engines/eevee/eevee_shadows.c
+++ b/source/blender/draw/engines/eevee/eevee_shadows.c
@@ -412,7 +412,7 @@ void EEVEE_shadow_output_init(EEVEE_ViewLayerData *sldata,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->shadow_accum)});
/* Clear texture. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1) {
+ if (effects->taa_current_sample == 1) {
GPU_framebuffer_bind(fbl->shadow_accum_fb);
GPU_framebuffer_clear_color(fbl->shadow_accum_fb, clear);
}
diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c
index 7674148f76a..ef4588f4aca 100644
--- a/source/blender/draw/engines/eevee/eevee_subsurface.c
+++ b/source/blender/draw/engines/eevee/eevee_subsurface.c
@@ -75,18 +75,8 @@ static void eevee_create_shader_subsurface(void)
MEM_freeN(frag_str);
}
-void EEVEE_subsurface_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
+void EEVEE_subsurface_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *UNUSED(vedata))
{
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
-
- effects->sss_sample_count = 1 + scene_eval->eevee.sss_samples * 2;
- effects->sss_surface_count = 0;
- common_data->sss_jitter_threshold = scene_eval->eevee.sss_jitter_threshold;
}
void EEVEE_subsurface_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
@@ -197,22 +187,31 @@ void EEVEE_subsurface_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
* already higher than one. This is noticeable when loading a file that has the diffuse light
* pass in look dev mode active. `texture_created` will make sure that newly created textures
* are cleared. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1 || texture_created) {
+ if (effects->taa_current_sample == 1 || texture_created) {
float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_framebuffer_bind(fbl->sss_accum_fb);
GPU_framebuffer_clear_color(fbl->sss_accum_fb, clear);
}
}
-void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
+void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
+ EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
EEVEE_PassList *psl = vedata->psl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
+
/* Shaders */
if (!e_data.sss_sh[0]) {
eevee_create_shader_subsurface();
}
+ effects->sss_sample_count = 1 + scene_eval->eevee.sss_samples * 2;
+ effects->sss_surface_count = 0;
+ common_data->sss_jitter_threshold = scene_eval->eevee.sss_jitter_threshold;
+
/** Screen Space SubSurface Scattering overview
* TODO
*/
diff --git a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
index d57048f2c4e..714481c39f1 100644
--- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
+++ b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
@@ -186,6 +186,18 @@ void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata)
vedata->stl->effects->taa_current_sample = 1;
}
+void EEVEE_temporal_sampling_create_view(EEVEE_Data *vedata)
+{
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
+ /* Create a sub view to disable clipping planes (if any). */
+ const DRWView *default_view = DRW_view_default_get();
+ float viewmat[4][4], winmat[4][4];
+ DRW_view_viewmat_get(default_view, viewmat, false);
+ DRW_view_winmat_get(default_view, winmat, false);
+ effects->taa_view = DRW_view_create_sub(default_view, viewmat, winmat);
+ DRW_view_clip_planes_set(effects->taa_view, NULL, 0);
+}
+
int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
@@ -201,15 +213,9 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
* we accumulate the redraw inside the drawing loop in eevee_draw_scene().
**/
effects->taa_render_sample = 1;
- effects->taa_view = NULL;
+ effects->bypass_drawing = false;
- /* Create a sub view to disable clipping planes (if any). */
- const DRWView *default_view = DRW_view_default_get();
- float viewmat[4][4], winmat[4][4];
- DRW_view_viewmat_get(default_view, viewmat, false);
- DRW_view_winmat_get(default_view, winmat, false);
- effects->taa_view = DRW_view_create_sub(default_view, viewmat, winmat);
- DRW_view_clip_planes_set(effects->taa_view, NULL, 0);
+ EEVEE_temporal_sampling_create_view(vedata);
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
@@ -241,7 +247,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()) &&
@@ -251,7 +256,7 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
if (((effects->taa_total_sample == 0) ||
(effects->taa_current_sample < effects->taa_total_sample)) ||
- DRW_state_is_image_render()) {
+ (!view_is_valid) || DRW_state_is_image_render()) {
if (view_is_valid) {
/* Viewport rendering updates the matrices in `eevee_draw_scene` */
if (!DRW_state_is_image_render()) {
@@ -264,7 +269,7 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
}
}
else {
- effects->taa_current_sample = 1;
+ effects->bypass_drawing = true;
}
return repro_flag | EFFECT_TAA | EFFECT_DOUBLE_BUFFER | EFFECT_DEPTH_DOUBLE_BUFFER |
@@ -283,7 +288,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 +300,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);
@@ -359,10 +365,13 @@ void EEVEE_temporal_sampling_draw(EEVEE_Data *vedata)
effects->taa_current_sample += 1;
}
else {
- if ((effects->taa_total_sample == 0) ||
- (effects->taa_current_sample < effects->taa_total_sample)) {
+ if (!DRW_state_is_playback() &&
+ ((effects->taa_total_sample == 0) ||
+ (effects->taa_current_sample < effects->taa_total_sample))) {
DRW_viewport_request_redraw();
}
}
+
+ DRW_view_persmat_get(NULL, effects->prev_drw_persmat, false);
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c
index 90860e94270..17f53113939 100644
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ b/source/blender/draw/engines/eevee/eevee_volumes.c
@@ -894,7 +894,7 @@ void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
GPU_ATTACHMENT_TEXTURE(txl->volume_transmittance_accum)});
/* Clear texture. */
- if (DRW_state_is_image_render() || effects->taa_current_sample == 1) {
+ if (effects->taa_current_sample == 1) {
GPU_framebuffer_bind(fbl->volumetric_accum_fb);
GPU_framebuffer_clear_color(fbl->volumetric_accum_fb, clear);
}
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
index 402d306df45..393ecaf1fc5 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
@@ -877,6 +877,14 @@ Closure closure_mix(Closure cl1, Closure cl2, float fac)
{
Closure cl;
cl.holdout = mix(cl1.holdout, cl2.holdout, fac);
+
+ if (FLAG_TEST(cl1.flag, CLOSURE_HOLDOUT_FLAG)) {
+ fac = 1.0;
+ }
+ else if (FLAG_TEST(cl2.flag, CLOSURE_HOLDOUT_FLAG)) {
+ fac = 0.0;
+ }
+
cl.transmittance = mix(cl1.transmittance, cl2.transmittance, fac);
cl.radiance = mix(cl1.radiance, cl2.radiance, fac);
cl.flag = cl1.flag | cl2.flag;
@@ -958,7 +966,7 @@ void main()
{
Closure cl = nodetree_exec();
- float holdout = 1.0 - saturate(cl.holdout);
+ float holdout = saturate(1.0 - cl.holdout);
float transmit = saturate(avg(cl.transmittance));
float alpha = 1.0 - transmit;
@@ -972,8 +980,9 @@ void main()
* Since we do that using the blending pipeline we need to account for material transmittance. */
vol_scatter -= vol_scatter * cl.transmittance;
- outRadiance = vec4(cl.radiance * vol_transmit + vol_scatter, alpha * holdout);
- outTransmittance = vec4(cl.transmittance, transmit * holdout);
+ cl.radiance = cl.radiance * holdout * vol_transmit + vol_scatter;
+ outRadiance = vec4(cl.radiance, alpha * holdout);
+ outTransmittance = vec4(cl.transmittance, transmit) * holdout;
# else
outRadiance = vec4(cl.radiance, holdout);
ssrNormals = cl.ssr_normal;
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..95cd69ba310
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl
@@ -0,0 +1,54 @@
+
+uniform mat4 currModelMatrix;
+uniform mat4 prevModelMatrix;
+uniform mat4 nextModelMatrix;
+uniform bool useDeform;
+
+#ifdef HAIR
+uniform samplerBuffer prvBuffer; /* RGBA32F */
+uniform samplerBuffer nxtBuffer; /* RGBA32F */
+#else
+in vec3 pos;
+in vec3 prv; /* Previous frame position. */
+in vec3 nxt; /* Next frame position. */
+#endif
+
+out vec3 currWorldPos;
+out vec3 prevWorldPos;
+out vec3 nextWorldPos;
+
+void main()
+{
+#ifdef HAIR
+ bool is_persp = (ProjectionMatrix[3][3] == 0.0);
+ float time, thick_time, thickness;
+ vec3 tan, binor;
+ vec3 wpos;
+
+ hair_get_pos_tan_binor_time(is_persp,
+ ModelMatrixInverse,
+ ViewMatrixInverse[3].xyz,
+ ViewMatrixInverse[2].xyz,
+ wpos,
+ tan,
+ binor,
+ time,
+ thickness,
+ thick_time);
+
+ int id = hair_get_base_id();
+ vec3 pos = texelFetch(hairPointBuffer, id).point_position;
+ vec3 prv = texelFetch(prvBuffer, id).point_position;
+ vec3 nxt = texelFetch(nxtBuffer, id).point_position;
+#endif
+ 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. */
+#ifndef HAIR
+ gl_Position = ViewProjectionMatrix * vec4(currWorldPos, 1.0);
+#else
+ gl_Position = ViewProjectionMatrix * vec4(wpos, 1.0);
+#endif
+}