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:
Diffstat (limited to 'source/blender/draw/engines/eevee/eevee_velocity.cc')
-rw-r--r--source/blender/draw/engines/eevee/eevee_velocity.cc356
1 files changed, 356 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_velocity.cc b/source/blender/draw/engines/eevee/eevee_velocity.cc
new file mode 100644
index 00000000000..6c221b66aec
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_velocity.cc
@@ -0,0 +1,356 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * The velocity pass outputs motion vectors to use for either
+ * temporal re-projection or motion blur.
+ *
+ * It is the module that tracks the objects between frames updates.
+ */
+
+#include "BKE_duplilist.h"
+#include "BKE_object.h"
+#include "BLI_map.hh"
+#include "DEG_depsgraph_query.h"
+#include "DNA_rigidbody_types.h"
+
+#include "eevee_instance.hh"
+#include "eevee_renderpasses.hh"
+#include "eevee_shader.hh"
+#include "eevee_shader_shared.hh"
+#include "eevee_velocity.hh"
+#include "eevee_wrapper.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name VelocityModule
+ *
+ * \{ */
+
+void VelocityModule::init(void)
+{
+ if (inst_.is_viewport()) {
+ /* For viewport we sync when object is evaluated and we swap at init time.
+ * Use next step to store the current position. This one will become the previous step after
+ * next swapping. */
+ step_ = STEP_NEXT;
+ step_swap();
+ }
+
+ if (inst_.render && (inst_.render_passes.vector != nullptr)) {
+ /* No motion blur and the vector pass was requested. Do the step sync here. */
+ const Scene *scene = inst_.scene;
+ float initial_time = scene->r.cfra + scene->r.subframe;
+ step_sync(STEP_PREVIOUS, initial_time - 1.0f);
+ step_sync(STEP_NEXT, initial_time + 1.0f);
+ inst_.set_time(initial_time);
+ }
+}
+
+static void step_object_sync_render(void *velocity,
+ Object *ob,
+ RenderEngine *UNUSED(engine),
+ Depsgraph *UNUSED(depsgraph))
+{
+ ObjectKey object_key(ob);
+ reinterpret_cast<VelocityModule *>(velocity)->step_object_sync(ob, object_key);
+}
+
+void VelocityModule::step_sync(eStep step, float time)
+{
+ inst_.set_time(time);
+ step_ = step;
+ step_camera_sync();
+ DRW_render_object_iter(this, inst_.render, inst_.depsgraph, step_object_sync_render);
+}
+
+void VelocityModule::step_camera_sync()
+{
+ if (!inst_.is_viewport()) {
+ inst_.camera.sync();
+ }
+
+ if (step_ == STEP_NEXT) {
+ camera_step.next = inst_.camera.data_get();
+ }
+ else if (step_ == STEP_PREVIOUS) {
+ camera_step.prev = inst_.camera.data_get();
+ }
+}
+
+/* Gather motion data from all objects in the scene. */
+void VelocityModule::step_object_sync(Object *ob, ObjectKey &object_key)
+{
+ if (!object_has_velocity(ob) && !object_is_deform(ob)) {
+ return;
+ }
+
+ auto add_cb = [&]() {
+ inst_.sampling.reset();
+ return new VelocityObjectBuf();
+ };
+ auto data = objects_steps.lookup_or_add_cb(object_key, add_cb);
+
+ if (step_ == STEP_NEXT) {
+ copy_m4_m4(data->next_object_mat, ob->obmat);
+ }
+ else if (step_ == STEP_PREVIOUS) {
+ copy_m4_m4(data->prev_object_mat, ob->obmat);
+ }
+}
+
+/* Moves next frame data to previous frame data. Nullify next frame data. */
+void VelocityModule::step_swap(void)
+{
+ for (VelocityObjectBuf *data : objects_steps.values()) {
+ copy_m4_m4(data->prev_object_mat, data->next_object_mat);
+ /* Important: This let us known if object is missing from the next time step. */
+ zero_m4(data->next_object_mat);
+ }
+ camera_step.prev = static_cast<CameraData>(camera_step.next);
+}
+
+void VelocityModule::begin_sync(void)
+{
+ if (inst_.is_viewport()) {
+ step_camera_sync();
+ }
+}
+
+/* This is the end of the current frame sync. Not the step_sync. */
+void VelocityModule::end_sync(void)
+{
+ Vector<ObjectKey, 1> deleted_keys;
+
+ for (auto item : objects_steps.items()) {
+ /* Detect object deletion. Only do this on viewport as STEP_NEXT means current step. */
+ if (inst_.is_viewport() && is_zero_m4(item.value->next_object_mat)) {
+ deleted_keys.append(item.key);
+ delete item.value;
+ }
+ else {
+ item.value->push_update();
+ }
+ }
+
+ if (deleted_keys.size() > 0) {
+ inst_.sampling.reset();
+ }
+
+ for (auto key : deleted_keys) {
+ objects_steps.remove(key);
+ }
+
+ camera_step.prev.push_update();
+ camera_step.next.push_update();
+}
+
+bool VelocityModule::object_has_velocity(const Object *ob)
+{
+#if 0
+ RigidBodyOb *rbo = ob->rigidbody_object;
+ /* Active rigidbody objects only, as only those are affected by sim. */
+ const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
+ /* For now we assume dupli objects are moving. */
+ const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0;
+ const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true);
+#else
+ UNUSED_VARS(ob);
+ /* BKE_object_moves_in_time does not work in some cases.
+ * Better detect non moving object after evaluation. */
+ const bool object_moves = true;
+#endif
+ return object_moves;
+}
+
+bool VelocityModule::object_is_deform(const Object *ob)
+{
+ RigidBodyOb *rbo = ob->rigidbody_object;
+ /* Active rigidbody objects only, as only those are affected by sim. */
+ const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
+ const bool is_deform = BKE_object_is_deform_modified(inst_.scene, (Object *)ob) ||
+ (has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0);
+
+ return is_deform;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name VelocityPass
+ *
+ * Draws velocity data from VelocityModule module to a framebuffer / texture.
+ * \{ */
+
+void VelocityPass::sync(void)
+{
+ VelocityModule &velocity = inst_.velocity;
+ {
+ /* Outputs camera motion vector. */
+ /* TODO(fclem) Ideally, we should run this only where the motion vectors were not written.
+ * But without imageLoadStore, we cannot do that without another buffer. */
+ DRWState state = DRW_STATE_WRITE_COLOR;
+ DRW_PASS_CREATE(camera_ps_, state);
+ GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_CAMERA);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, camera_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
+ DRW_shgroup_uniform_block(grp, "camera_prev_block", velocity.camera_step.prev.ubo_get());
+ DRW_shgroup_uniform_block(grp, "camera_next_block", velocity.camera_step.next.ubo_get());
+ DRW_shgroup_uniform_block(grp, "camera_curr_block", inst_.camera.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+ {
+ /* Animated objects are rendered and output the correct motion vector. */
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL;
+ DRW_PASS_CREATE(object_ps_, state);
+ {
+ GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_MESH);
+ DRWShadingGroup *grp = mesh_grp_ = DRW_shgroup_create(sh, object_ps_);
+ DRW_shgroup_uniform_block(grp, "camera_prev_block", velocity.camera_step.prev.ubo_get());
+ DRW_shgroup_uniform_block(grp, "camera_next_block", velocity.camera_step.next.ubo_get());
+ DRW_shgroup_uniform_block(grp, "camera_curr_block", inst_.camera.ubo_get());
+ }
+ }
+}
+
+void VelocityPass::mesh_add(Object *ob, ObjectHandle &handle)
+{
+ if (inst_.is_viewport()) {
+ /* FIXME(fclem) As we are using original objects pointers, there is a chance the previous
+ * object key matches a totally different object if the scene was changed by user or python
+ * callback. In this case, we cannot correctly match objects between updates.
+ * What this means is that there will be incorrect motion vectors for these objects.
+ * We live with that until we have a correct way of identifying new objects. */
+ if (handle.recalc & ID_RECALC_TRANSFORM) {
+ inst_.velocity.step_object_sync(ob, handle.object_key);
+ }
+ }
+
+ VelocityObjectBuf **data_ptr = inst_.velocity.objects_steps.lookup_ptr(handle.object_key);
+
+ if (data_ptr == nullptr) {
+ return;
+ }
+
+ VelocityObjectBuf *data = *data_ptr;
+
+ GPUBatch *geom = DRW_cache_object_surface_get(ob);
+ if (geom == NULL) {
+ return;
+ }
+
+ if (!inst_.is_viewport()) {
+ /* Fill missing matrices if the object was hidden in previous or next frame. */
+ if (is_zero_m4(data->prev_object_mat)) {
+ copy_m4_m4(data->prev_object_mat, ob->obmat);
+ }
+
+ /* Avoid drawing object that has no motions since object_moves is always true. */
+ if (/* !mb_geom->use_deform && */ /* Object deformation can happen without transform. */
+ equals_m4m4(data->prev_object_mat, ob->obmat)) {
+ return;
+ }
+ }
+ else {
+ /* Fill missing matrices if the object was hidden in previous or next frame. */
+ if (is_zero_m4(data->prev_object_mat)) {
+ copy_m4_m4(data->prev_object_mat, ob->obmat);
+ }
+ if (is_zero_m4(data->next_object_mat)) {
+ copy_m4_m4(data->next_object_mat, ob->obmat);
+ }
+
+ // if (mb_geom->use_deform) {
+ // /* Keep to modify later (after init). */
+ // mb_geom->batch = geom;
+ // }
+
+ /* Avoid drawing object that has no motions since object_moves is always true. */
+ if (/* !mb_geom->use_deform && */ /* Object deformation can happen without transform. */
+ equals_m4m4(data->prev_object_mat, ob->obmat) &&
+ equals_m4m4(data->next_object_mat, ob->obmat)) {
+ return;
+ }
+ }
+
+ /* TODO(fclem) Use the same layout as modelBlock from draw so we can reuse the same offset
+ * and avoid the overhead of 1 shading group and one UBO per object. */
+ DRWShadingGroup *grp = DRW_shgroup_create_sub(mesh_grp_);
+ DRW_shgroup_uniform_block(grp, "object_block", data->ubo_get());
+ DRW_shgroup_call(grp, geom, ob);
+}
+
+void VelocityPass::render_objects(void)
+{
+ DRW_draw_pass(object_ps_);
+}
+
+void VelocityPass::resolve_camera_motion(GPUTexture *depth_tx)
+{
+ input_depth_tx_ = depth_tx;
+ DRW_draw_pass(camera_ps_);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name VelocityPass
+ *
+ * Draws velocity data from VelocityModule module to a framebuffer / texture.
+ * \{ */
+
+void Velocity::sync(int extent[2])
+{
+ /* HACK: View name should be unique and static.
+ * With this, we can reuse the same texture across views. */
+ DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
+
+ /* TODO(fclem) Only allocate if needed. RG16F when only doing reprojection. */
+ velocity_camera_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), GPU_RGBA16F, owner);
+ /* TODO(fclem) Only allocate if needed. RG16F when only doing motion blur post fx in
+ * panoramic camera. */
+ velocity_view_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), GPU_RGBA16F, owner);
+
+ velocity_only_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(velocity_camera_tx_),
+ GPU_ATTACHMENT_TEXTURE(velocity_view_tx_));
+}
+
+void Velocity::render(GPUTexture *depth_tx)
+{
+ DRW_stats_group_start("Velocity");
+
+ GPU_framebuffer_bind(velocity_only_fb_);
+ inst_.shading_passes.velocity.resolve_camera_motion(depth_tx);
+
+ velocity_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx),
+ GPU_ATTACHMENT_TEXTURE(velocity_camera_tx_),
+ GPU_ATTACHMENT_TEXTURE(velocity_view_tx_));
+
+ GPU_framebuffer_bind(velocity_fb_);
+ inst_.shading_passes.velocity.render_objects();
+
+ DRW_stats_group_end();
+}
+
+/** \} */
+
+} // namespace blender::eevee