mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Cycles: Add support for motion vectors in the viewport
This patch introduces the capability for Cycles to track changes to camera, object and vertex positions, so that it can compute motion vectors when any such changes occur, during viewport camera movements, objects being dragged around or meshes being modified. When Cycles debug mode is enabled, the motion pass is added to the viewport passes selection for testing purposes. This patch also adds motion vectors for the background during camera rotations. Pull Request: https://projects.blender.org/blender/blender/pulls/156227
This commit is contained in:
parent
70626760a2
commit
ba033d2ff5
21 changed files with 320 additions and 48 deletions
|
|
@ -239,6 +239,8 @@ enum_view3d_shading_render_pass = (
|
|||
)
|
||||
|
||||
enum_view3d_debug_render_pass = (
|
||||
('MOTION', "Vector", "Show motion vectors"),
|
||||
|
||||
('VOLUME_SCATTER', "Volume Scatter", "Show the contribution of scattered ray in volume"),
|
||||
('VOLUME_TRANSMIT', "Volume Transmit", "Show the contribution of transmitted ray in volume"),
|
||||
('VOLUME_MAJORANT', "Volume Majorant", "Show the majorant transmittance of the volume")
|
||||
|
|
|
|||
|
|
@ -680,8 +680,11 @@ static void blender_camera_sync(Camera *cam,
|
|||
/* transform */
|
||||
cam->set_matrix(blender_camera_matrix(bcam->matrix, bcam->type, bcam->panorama_type));
|
||||
|
||||
array<Transform> motion;
|
||||
array<Transform> motion = cam->get_motion();
|
||||
motion.resize(bcam->motion_steps, cam->get_matrix());
|
||||
if (bcam->motion_steps != 0) {
|
||||
motion[bcam->motion_steps / 2] = cam->get_matrix();
|
||||
}
|
||||
cam->set_motion(motion);
|
||||
cam->set_use_perspective_motion(false);
|
||||
|
||||
|
|
@ -1166,6 +1169,7 @@ void BlenderSync::sync_view(blender::View3D *b_v3d,
|
|||
&bcam, *b_engine, b_render_settings, *b_scene, *b_data, b_v3d, b_rv3d, width, height);
|
||||
blender_camera_border(
|
||||
&bcam, *b_engine, b_render_settings, *b_scene, *b_data, b_v3d, b_rv3d, width, height);
|
||||
bcam.motion_steps = scene->need_motion() == Scene::MOTION_PASS_INTERACTIVE ? 2 : 0;
|
||||
blender::PointerRNA scene_rna_ptr = RNA_id_pointer_create(&b_scene->id);
|
||||
blender::PointerRNA cscene = RNA_pointer_get(&scene_rna_ptr, "cycles");
|
||||
blender_camera_sync(scene->camera, scene, &bcam, width, height, "", &cscene);
|
||||
|
|
|
|||
|
|
@ -1094,6 +1094,21 @@ void BlenderSync::sync_hair(BObjectInfo &b_ob_info, Hair *hair)
|
|||
free_object_to_mesh(b_ob_info, *b_mesh);
|
||||
}
|
||||
}
|
||||
|
||||
if (scene->need_motion() == Scene::MOTION_PASS_INTERACTIVE &&
|
||||
hair->num_keys() == new_hair.num_keys())
|
||||
{
|
||||
new_hair.set_motion_steps(2);
|
||||
|
||||
Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
Attribute *new_attr_mP = new_hair.attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
if (attr_mP) {
|
||||
new_attr_mP->set_data_from(std::move(*attr_mP));
|
||||
}
|
||||
else {
|
||||
new_hair.copy_center_to_motion_step(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* update original sockets */
|
||||
|
|
|
|||
|
|
@ -969,6 +969,21 @@ void BlenderSync::sync_mesh(BObjectInfo &b_ob_info, Mesh *mesh)
|
|||
|
||||
free_object_to_mesh(b_ob_info, const_cast<blender::Mesh &>(*b_mesh));
|
||||
}
|
||||
|
||||
if (scene->need_motion() == Scene::MOTION_PASS_INTERACTIVE &&
|
||||
mesh->num_verts() == new_mesh.num_verts())
|
||||
{
|
||||
new_mesh.set_motion_steps(2);
|
||||
|
||||
Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
Attribute *new_attr_mP = new_mesh.attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
if (attr_mP) {
|
||||
new_attr_mP->set_data_from(std::move(*attr_mP));
|
||||
}
|
||||
else {
|
||||
new_mesh.copy_center_to_motion_step(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* update original sockets */
|
||||
|
|
|
|||
|
|
@ -120,8 +120,7 @@ void BlenderSync::sync_object_motion_init(blender::Object &b_parent,
|
|||
{
|
||||
/* Initialize motion blur for object, detecting if it's enabled and creating motion
|
||||
* steps array if so. */
|
||||
array<Transform> motion;
|
||||
object->set_motion(motion);
|
||||
array<Transform> motion = object->get_motion();
|
||||
|
||||
Geometry *geom = object->get_geometry();
|
||||
if (!geom) {
|
||||
|
|
@ -156,6 +155,9 @@ void BlenderSync::sync_object_motion_init(blender::Object &b_parent,
|
|||
motion_times.insert(object->motion_time(step));
|
||||
}
|
||||
}
|
||||
else {
|
||||
object->set_motion(motion);
|
||||
}
|
||||
}
|
||||
|
||||
Object *BlenderSync::sync_object(blender::ViewLayer &b_view_layer,
|
||||
|
|
@ -584,19 +586,15 @@ void BlenderSync::sync_objects(blender::Depsgraph &b_depsgraph,
|
|||
}
|
||||
}
|
||||
|
||||
void BlenderSync::sync_motion(blender::RenderData &b_render,
|
||||
blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
const int width,
|
||||
const int height,
|
||||
void **python_thread_state)
|
||||
void BlenderSync::sync_objects_and_motion(blender::RenderData &b_render,
|
||||
blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
const int width,
|
||||
const int height,
|
||||
void **python_thread_state)
|
||||
{
|
||||
if (scene->need_motion() == Scene::MOTION_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* get camera object here to deal with camera switch */
|
||||
blender::Object *b_cam = get_camera_object(b_v3d, b_rv3d);
|
||||
|
||||
|
|
@ -604,7 +602,7 @@ void BlenderSync::sync_motion(blender::RenderData &b_render,
|
|||
const float subframe_center = b_scene->r.subframe;
|
||||
float frame_center_delta = 0.0f;
|
||||
|
||||
if (scene->need_motion() != Scene::MOTION_PASS &&
|
||||
if (scene->need_motion() == Scene::MOTION_BLUR &&
|
||||
scene->camera->get_motion_position() != MOTION_POSITION_CENTER)
|
||||
{
|
||||
const float shuttertime = scene->camera->get_shuttertime();
|
||||
|
|
@ -625,7 +623,20 @@ void BlenderSync::sync_motion(blender::RenderData &b_render,
|
|||
if (b_cam) {
|
||||
sync_camera_motion(b_render, b_cam, width, height, 0.0f);
|
||||
}
|
||||
sync_objects(b_depsgraph, b_screen, b_v3d);
|
||||
}
|
||||
|
||||
sync_objects(b_depsgraph, b_screen, b_v3d);
|
||||
|
||||
/* In the viewport, only motion between previous frame and current frame is of interest, which is
|
||||
* kept updated separately. */
|
||||
if (b_v3d) {
|
||||
assert(scene->need_motion() == Scene::MOTION_NONE ||
|
||||
scene->need_motion() == Scene::MOTION_PASS_INTERACTIVE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (scene->need_motion() == Scene::MOTION_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Insert motion times from camera. Motion times from other objects
|
||||
|
|
|
|||
|
|
@ -225,9 +225,25 @@ void BlenderSync::sync_pointcloud(PointCloud *pointcloud, BObjectInfo &b_ob_info
|
|||
0.0f;
|
||||
export_pointcloud(scene, &new_pointcloud, *b_pointcloud, need_motion, motion_scale);
|
||||
|
||||
pointcloud->clear_non_sockets();
|
||||
if (scene->need_motion() == Scene::MOTION_PASS_INTERACTIVE &&
|
||||
pointcloud->num_points() == new_pointcloud.num_points())
|
||||
{
|
||||
new_pointcloud.set_motion_steps(2);
|
||||
|
||||
Attribute *attr_mP = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
Attribute *new_attr_mP = new_pointcloud.attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
if (attr_mP) {
|
||||
new_attr_mP->set_data_from(std::move(*attr_mP));
|
||||
}
|
||||
else {
|
||||
new_pointcloud.copy_center_to_motion_step(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update original sockets. */
|
||||
|
||||
pointcloud->clear_non_sockets();
|
||||
|
||||
for (const SocketType &socket : new_pointcloud.type->inputs) {
|
||||
/* Those sockets are updated in sync_object, so do not modify them. */
|
||||
if (socket.name == "use_motion_blur" || socket.name == "used_shaders") {
|
||||
|
|
|
|||
|
|
@ -326,12 +326,8 @@ void BlenderSync::sync_data(blender::RenderData &b_render,
|
|||
|
||||
geometry_synced.clear(); /* use for objects and motion sync */
|
||||
|
||||
if (scene->need_motion() == Scene::MOTION_NONE || scene->need_motion() == Scene::MOTION_PASS ||
|
||||
scene->camera->get_motion_position() == MOTION_POSITION_CENTER)
|
||||
{
|
||||
sync_objects(b_depsgraph, b_screen, b_v3d);
|
||||
}
|
||||
sync_motion(b_render, b_depsgraph, b_screen, b_v3d, b_rv3d, width, height, python_thread_state);
|
||||
sync_objects_and_motion(
|
||||
b_render, b_depsgraph, b_screen, b_v3d, b_rv3d, width, height, python_thread_state);
|
||||
|
||||
geometry_synced.clear();
|
||||
|
||||
|
|
|
|||
|
|
@ -126,14 +126,14 @@ class BlenderSync {
|
|||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
const float motion_time = 0.0f);
|
||||
void sync_motion(blender::RenderData &b_render,
|
||||
blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
const int width,
|
||||
const int height,
|
||||
void **python_thread_state);
|
||||
void sync_objects_and_motion(blender::RenderData &b_render,
|
||||
blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
const int width,
|
||||
const int height,
|
||||
void **python_thread_state);
|
||||
void sync_film(blender::ViewLayer &b_view_layer,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d);
|
||||
|
|
|
|||
|
|
@ -598,4 +598,66 @@ ccl_device_inline float3 camera_world_to_ndc(KernelGlobals kg,
|
|||
return P;
|
||||
}
|
||||
|
||||
/* Motion vector for motion pass */
|
||||
|
||||
ccl_device_forceinline float4 camera_motion_vector_direction(KernelGlobals kg, const float3 D)
|
||||
{
|
||||
Transform tfm;
|
||||
float3 motion_center;
|
||||
float3 motion_pre;
|
||||
float3 motion_post;
|
||||
|
||||
/* Camera motion, for perspective/orthographic motion.pre/post will be a
|
||||
* world-to-raster matrix, for panorama it's world-to-camera, for custom
|
||||
* we fall back to the world position until we have inverse mapping for it */
|
||||
if (kernel_data.cam.type == CAMERA_CUSTOM) {
|
||||
/* TODO: Custom cameras don't have inverse mappings yet, so we fall back to
|
||||
* camera-space vectors here for now. */
|
||||
tfm = kernel_data.cam.worldtocamera;
|
||||
motion_center = normalize(transform_direction(&tfm, D));
|
||||
|
||||
tfm = kernel_data.cam.motion_pass_pre;
|
||||
motion_pre = normalize(transform_direction(&tfm, D));
|
||||
|
||||
tfm = kernel_data.cam.motion_pass_post;
|
||||
motion_post = normalize(transform_direction(&tfm, D));
|
||||
}
|
||||
else if (kernel_data.cam.type != CAMERA_PANORAMA) {
|
||||
/* Perspective and orthographics camera use the world-to-raster matrix. */
|
||||
ProjectionTransform projection = kernel_data.cam.worldtoraster;
|
||||
motion_center = transform_perspective_direction(&projection, D);
|
||||
|
||||
projection = kernel_data.cam.perspective_pre;
|
||||
motion_pre = transform_perspective_direction(&projection, D);
|
||||
|
||||
projection = kernel_data.cam.perspective_post;
|
||||
motion_post = transform_perspective_direction(&projection, D);
|
||||
}
|
||||
else {
|
||||
/* Panorama cameras have their own inverse mappings. */
|
||||
tfm = kernel_data.cam.worldtocamera;
|
||||
motion_center = normalize(transform_direction(&tfm, D));
|
||||
motion_center = make_float3(direction_to_panorama(&kernel_data.cam, motion_center));
|
||||
motion_center.x *= kernel_data.cam.width;
|
||||
motion_center.y *= kernel_data.cam.height;
|
||||
|
||||
tfm = kernel_data.cam.motion_pass_pre;
|
||||
motion_pre = normalize(transform_direction(&tfm, D));
|
||||
motion_pre = make_float3(direction_to_panorama(&kernel_data.cam, motion_pre));
|
||||
motion_pre.x *= kernel_data.cam.width;
|
||||
motion_pre.y *= kernel_data.cam.height;
|
||||
|
||||
tfm = kernel_data.cam.motion_pass_post;
|
||||
motion_post = normalize(transform_direction(&tfm, D));
|
||||
motion_post = make_float3(direction_to_panorama(&kernel_data.cam, motion_post));
|
||||
motion_post.x *= kernel_data.cam.width;
|
||||
motion_post.y *= kernel_data.cam.height;
|
||||
}
|
||||
|
||||
motion_pre = motion_pre - motion_center;
|
||||
motion_post = motion_center - motion_post;
|
||||
|
||||
return make_float4(motion_pre.x, motion_pre.y, motion_post.x, motion_post.y);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -211,6 +211,12 @@ ccl_device_inline void film_write_data_passes_background(
|
|||
film_overwrite_pass_float3(buffer + kernel_data.film.pass_position, zero_float3());
|
||||
}
|
||||
}
|
||||
|
||||
if (flag & PASSMASK(MOTION)) {
|
||||
const float4 speed = camera_motion_vector_direction(kg, INTEGRATOR_STATE(state, ray, D));
|
||||
film_write_pass_float4(buffer + kernel_data.film.pass_motion, speed);
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_motion_weight, 1.0f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,13 +226,20 @@ ccl_device_forceinline void primitive_motion_data_without_camera(KernelGlobals k
|
|||
|
||||
if (is_attribute_found(desc)) {
|
||||
/* get motion info */
|
||||
const int numverts = kernel_data_fetch(objects, sd->object).numverts;
|
||||
const ccl_global KernelObject *kobject = &kernel_data_fetch(objects, sd->object);
|
||||
const int numverts = kobject->numverts;
|
||||
const int num_motion_steps = kobject->num_geom_steps;
|
||||
|
||||
#if defined(__HAIR__) || defined(__POINTCLOUD__)
|
||||
if (is_curve_or_point) {
|
||||
*motion_pre = make_float3(primitive_surface_attribute<float4>(kg, sd, desc));
|
||||
desc.offset += numverts;
|
||||
*motion_post = make_float3(primitive_surface_attribute<float4>(kg, sd, desc));
|
||||
if (num_motion_steps > 2) {
|
||||
desc.offset += numverts;
|
||||
*motion_post = make_float3(primitive_surface_attribute<float4>(kg, sd, desc));
|
||||
}
|
||||
else {
|
||||
object_inverse_position_transform(kg, sd, motion_post);
|
||||
}
|
||||
|
||||
/* Curve */
|
||||
if ((sd->object_flag & SD_OBJECT_HAS_VERTEX_MOTION) == 0) {
|
||||
|
|
@ -246,8 +253,13 @@ ccl_device_forceinline void primitive_motion_data_without_camera(KernelGlobals k
|
|||
{
|
||||
/* Triangle */
|
||||
*motion_pre = triangle_attribute<float3>(kg, sd, desc);
|
||||
desc.offset += numverts;
|
||||
*motion_post = triangle_attribute<float3>(kg, sd, desc);
|
||||
if (num_motion_steps > 2) {
|
||||
desc.offset += numverts;
|
||||
*motion_post = triangle_attribute<float3>(kg, sd, desc);
|
||||
}
|
||||
else {
|
||||
object_inverse_position_transform(kg, sd, motion_post);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -361,7 +361,7 @@ void Camera::update(Scene *scene)
|
|||
have_motion = have_motion || motion[i] != matrix;
|
||||
}
|
||||
|
||||
if (need_motion == Scene::MOTION_PASS) {
|
||||
if (need_motion == Scene::MOTION_PASS || need_motion == Scene::MOTION_PASS_INTERACTIVE) {
|
||||
if (have_motion) {
|
||||
kcam->motion_pass_pre = transform_inverse(motion[0]);
|
||||
kcam->motion_pass_post = transform_inverse(motion[motion.size() - 1]);
|
||||
|
|
@ -506,6 +506,20 @@ void Camera::update(Scene *scene)
|
|||
previous_need_motion = need_motion;
|
||||
}
|
||||
|
||||
void Camera::update_interactive_motion()
|
||||
{
|
||||
array<Transform> motion = get_motion();
|
||||
if (!motion.empty()) {
|
||||
motion[0] = matrix;
|
||||
|
||||
/* Trigger another update if there was motion compared to previous frame, so that last viewport
|
||||
* camera movement does not stick around. */
|
||||
set_motion(motion);
|
||||
}
|
||||
|
||||
set_fov_pre(fov);
|
||||
}
|
||||
|
||||
void Camera::device_update(Device * /*device*/, DeviceScene *dscene, Scene *scene)
|
||||
{
|
||||
update(scene);
|
||||
|
|
|
|||
|
|
@ -208,6 +208,8 @@ class Camera : public Node {
|
|||
|
||||
void update(Scene *scene);
|
||||
|
||||
void update_interactive_motion();
|
||||
|
||||
void device_update(Device *device, DeviceScene *dscene, Scene *scene);
|
||||
void device_update_volume(Device *device, DeviceScene *dscene, Scene *scene);
|
||||
void device_free(Device *device, DeviceScene *dscene, Scene *scene);
|
||||
|
|
|
|||
|
|
@ -245,7 +245,8 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
|
|||
|
||||
/* Can't do motion pass if no motion vectors are available. */
|
||||
if (pass->get_type() == PASS_MOTION || pass->get_type() == PASS_MOTION_WEIGHT) {
|
||||
if (scene->need_motion() != Scene::MOTION_PASS) {
|
||||
const Scene::MotionType need_motion = scene->need_motion();
|
||||
if (need_motion != Scene::MOTION_PASS && need_motion != Scene::MOTION_PASS_INTERACTIVE) {
|
||||
kfilm->pass_stride += pass->get_info().num_components;
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,6 +173,56 @@ GeometryManager::GeometryManager()
|
|||
|
||||
GeometryManager::~GeometryManager() = default;
|
||||
|
||||
void GeometryManager::update_interactive_motion(Scene *scene)
|
||||
{
|
||||
bool update = false;
|
||||
|
||||
parallel_for(blocked_range<size_t>(0, scene->geometry.size(), 32),
|
||||
[&](const blocked_range<size_t> &r) {
|
||||
for (size_t i = r.begin(); i != r.end(); i++) {
|
||||
Geometry *geom = scene->geometry[i];
|
||||
|
||||
Attribute *attr_mP = geom->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
if (attr_mP) {
|
||||
if (geom->is_mesh()) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (std::memcmp(mesh->get_verts().data(),
|
||||
attr_mP->data_float3(),
|
||||
sizeof(float3) * mesh->num_verts()) != 0)
|
||||
{
|
||||
mesh->copy_center_to_motion_step(0);
|
||||
attr_mP->modified = update = true;
|
||||
}
|
||||
}
|
||||
else if (geom->is_hair()) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
if (std::memcmp(hair->get_curve_keys().data(),
|
||||
attr_mP->data_float3(),
|
||||
sizeof(float3) * hair->num_keys()) != 0)
|
||||
{
|
||||
hair->copy_center_to_motion_step(0);
|
||||
attr_mP->modified = update = true;
|
||||
}
|
||||
}
|
||||
else if (geom->is_pointcloud()) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
|
||||
if (std::memcmp(pointcloud->get_points().data(),
|
||||
attr_mP->data_float3(),
|
||||
sizeof(float3) * pointcloud->num_points()) != 0)
|
||||
{
|
||||
pointcloud->copy_center_to_motion_step(0);
|
||||
attr_mP->modified = update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (update) {
|
||||
tag_update(scene, TRANSFORM_MODIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryManager::update_osl_globals(Device *device, Scene *scene)
|
||||
{
|
||||
#ifdef WITH_OSL
|
||||
|
|
|
|||
|
|
@ -251,6 +251,8 @@ class GeometryManager {
|
|||
GeometryManager();
|
||||
~GeometryManager();
|
||||
|
||||
void update_interactive_motion(Scene *scene);
|
||||
|
||||
/* Device Updates */
|
||||
void device_update_preprocess(Device *device, Scene *scene, Progress &progress);
|
||||
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
|
||||
|
|
|
|||
|
|
@ -74,6 +74,10 @@ class Mesh : public Geometry {
|
|||
return tri;
|
||||
}
|
||||
|
||||
size_t num_verts() const
|
||||
{
|
||||
return verts.size();
|
||||
}
|
||||
size_t num_triangles() const
|
||||
{
|
||||
return triangles.size() / 3;
|
||||
|
|
|
|||
|
|
@ -143,6 +143,13 @@ void Object::update_motion()
|
|||
motion.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (have_motion && i == (motion.size() - 1)) {
|
||||
/* Remove last motion when it is not actually set. */
|
||||
motion.resize(motion.size() - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise just copy center motion. */
|
||||
motion[i] = tfm;
|
||||
}
|
||||
|
|
@ -496,6 +503,40 @@ ObjectManager::ObjectManager()
|
|||
|
||||
ObjectManager::~ObjectManager() = default;
|
||||
|
||||
void ObjectManager::update_interactive_motion(Scene *scene)
|
||||
{
|
||||
bool update = false;
|
||||
|
||||
parallel_for(blocked_range<size_t>(0, scene->objects.size(), 32),
|
||||
[&](const blocked_range<size_t> &r) {
|
||||
for (size_t i = r.begin(); i != r.end(); i++) {
|
||||
Object *ob = scene->objects[i];
|
||||
|
||||
const bool use_motion = ob->use_motion();
|
||||
|
||||
array<Transform> motion = ob->get_motion();
|
||||
if (motion.empty()) {
|
||||
/* Can always store current matrix in motion array with a single element,
|
||||
* since that still causes 'use_motion()' to return false. */
|
||||
motion.resize(1);
|
||||
}
|
||||
motion[0] = ob->tfm;
|
||||
|
||||
/* Trigger another update if there was motion compared to previous frame, so
|
||||
* that last movement does not stick around. */
|
||||
ob->set_motion(motion);
|
||||
|
||||
if (use_motion && ob->motion_is_modified()) {
|
||||
update = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (update) {
|
||||
tag_update(scene, TRANSFORM_MODIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
static float object_volume_density(const Transform &tfm, Geometry *geom)
|
||||
{
|
||||
if (geom->is_volume()) {
|
||||
|
|
@ -596,7 +637,9 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
|
|||
}
|
||||
}
|
||||
|
||||
if (state->need_motion == Scene::MOTION_PASS) {
|
||||
if (state->need_motion == Scene::MOTION_PASS ||
|
||||
state->need_motion == Scene::MOTION_PASS_INTERACTIVE)
|
||||
{
|
||||
/* Clear motion array if there is no actual motion. */
|
||||
ob->update_motion();
|
||||
|
||||
|
|
@ -752,7 +795,9 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
|
|||
state.object_motion = nullptr;
|
||||
state.object_motion_pass = nullptr;
|
||||
|
||||
if (state.need_motion == Scene::MOTION_PASS) {
|
||||
if (state.need_motion == Scene::MOTION_PASS ||
|
||||
state.need_motion == Scene::MOTION_PASS_INTERACTIVE)
|
||||
{
|
||||
state.object_motion_pass = dscene->object_motion_pass.alloc(OBJECT_MOTION_PASS_SIZE *
|
||||
scene->objects.size());
|
||||
}
|
||||
|
|
@ -801,7 +846,9 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
|
|||
}
|
||||
|
||||
dscene->objects.copy_to_device_if_modified();
|
||||
if (state.need_motion == Scene::MOTION_PASS) {
|
||||
if (state.need_motion == Scene::MOTION_PASS ||
|
||||
state.need_motion == Scene::MOTION_PASS_INTERACTIVE)
|
||||
{
|
||||
dscene->object_motion_pass.copy_to_device();
|
||||
}
|
||||
else if (state.need_motion == Scene::MOTION_BLUR) {
|
||||
|
|
@ -1092,7 +1139,8 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, P
|
|||
map<Geometry *, int> geometry_users;
|
||||
const Scene::MotionType need_motion = scene->need_motion();
|
||||
const bool motion_blur = need_motion == Scene::MOTION_BLUR;
|
||||
const bool apply_to_motion = need_motion != Scene::MOTION_PASS;
|
||||
const bool apply_to_motion = need_motion != Scene::MOTION_PASS &&
|
||||
need_motion != Scene::MOTION_PASS_INTERACTIVE;
|
||||
int i = 0;
|
||||
|
||||
for (Object *object : scene->objects) {
|
||||
|
|
|
|||
|
|
@ -167,6 +167,8 @@ class ObjectManager {
|
|||
ObjectManager();
|
||||
~ObjectManager();
|
||||
|
||||
void update_interactive_motion(Scene *scene);
|
||||
|
||||
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
|
||||
void device_update_transforms(DeviceScene *dscene, Scene *scene, Progress &progress);
|
||||
void device_update_prim_offsets(Device *device, DeviceScene *dscene, Scene *scene);
|
||||
|
|
|
|||
|
|
@ -395,6 +395,13 @@ void Scene::device_update(Device *device_, Progress &progress)
|
|||
|
||||
device->optimize_for_scene(this);
|
||||
|
||||
if (need_motion() == MOTION_PASS_INTERACTIVE) {
|
||||
/* Swap current camera/object/vertex positions to previous positions for next frame. */
|
||||
camera->update_interactive_motion();
|
||||
object_manager->update_interactive_motion(this);
|
||||
geometry_manager->update_interactive_motion(this);
|
||||
}
|
||||
|
||||
if (print_stats) {
|
||||
const size_t mem_used = util_guarded_get_mem_used();
|
||||
const size_t mem_peak = util_guarded_get_mem_peak();
|
||||
|
|
@ -412,17 +419,20 @@ Scene::MotionType Scene::need_motion() const
|
|||
if (integrator->get_motion_blur()) {
|
||||
return MOTION_BLUR;
|
||||
}
|
||||
if (Pass::contains(passes, PASS_MOTION) ||
|
||||
Pass::contains(passes, PASS_DENOISING_BACKWARD_MOTION))
|
||||
const bool denoiser_motion = integrator->get_use_denoise() &&
|
||||
(integrator->get_denoiser_passes() &
|
||||
(DENOISER_PASS_MOTION | DENOISER_PASS_BACKWARD_MOTION)) != 0;
|
||||
if (denoiser_motion || (Pass::contains(passes, PASS_MOTION) ||
|
||||
Pass::contains(passes, PASS_DENOISING_BACKWARD_MOTION)))
|
||||
{
|
||||
return MOTION_PASS;
|
||||
return params.background ? MOTION_PASS : MOTION_PASS_INTERACTIVE;
|
||||
}
|
||||
return MOTION_NONE;
|
||||
}
|
||||
|
||||
float Scene::motion_shutter_time()
|
||||
{
|
||||
if (need_motion() == Scene::MOTION_PASS) {
|
||||
if (need_motion() == Scene::MOTION_PASS || need_motion() == Scene::MOTION_PASS_INTERACTIVE) {
|
||||
return 2.0f;
|
||||
}
|
||||
return camera->get_shuttertime();
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ class Scene : public NodeOwner {
|
|||
bool need_global_attribute(AttributeStandard std) const;
|
||||
void need_global_attributes(AttributeRequestSet &attributes);
|
||||
|
||||
enum MotionType { MOTION_NONE = 0, MOTION_PASS, MOTION_BLUR };
|
||||
enum MotionType { MOTION_NONE = 0, MOTION_PASS, MOTION_BLUR, MOTION_PASS_INTERACTIVE };
|
||||
MotionType need_motion() const;
|
||||
float motion_shutter_time();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue