Cycles: Support user-defined shutter curve
authorSergey Sharybin <sergey.vfx@gmail.com>
Tue, 27 Oct 2015 08:16:04 +0000 (13:16 +0500)
committerSergey Sharybin <sergey.vfx@gmail.com>
Tue, 27 Oct 2015 21:43:06 +0000 (02:43 +0500)
Previously shutter was instantly opening, staying opened for the shutter time
period of time and then instantly closing. This isn't quite how real cameras
are working, where shutter is opening with some curve. Now it is possible to
define user curve for how much shutter is opened across the sampling period
of time.

This could be used for example to make motion blur trails softer.

intern/cycles/kernel/kernel_camera.h
intern/cycles/kernel/kernel_types.h
intern/cycles/render/camera.cpp
intern/cycles/render/camera.h
intern/cycles/render/scene.cpp
intern/cycles/util/util_math_cdf.cpp

index 8d439ceb0d7c636445d2385013cf6d46962e6602..01017eabde20cc00a18b2d766c49ed8359f56761 100644 (file)
@@ -290,10 +290,13 @@ ccl_device void camera_sample(KernelGlobals *kg, int x, int y, float filter_u, f
 
 #ifdef __CAMERA_MOTION__
        /* motion blur */
-       if(kernel_data.cam.shuttertime == -1.0f)
+       if(kernel_data.cam.shuttertime == -1.0f) {
                ray->time = TIME_INVALID;
-       else
-               ray->time = time;
+       }
+       else {
+               const int shutter_table_offset = kernel_data.cam.shutter_table_offset;
+               ray->time = lookup_table_read(kg, time, shutter_table_offset, SHUTTER_TABLE_SIZE);
+       }
 #endif
 
        /* sample */
index 740a5f1f5d3cf7d3d35ebd80670ee42ce1e7ceb3..1d71e6435dbb5091424c6ae890b6eedeb9894628 100644 (file)
@@ -39,6 +39,7 @@ CCL_NAMESPACE_BEGIN
 #define LIGHT_SIZE                     5
 #define FILTER_TABLE_SIZE      256
 #define RAMP_TABLE_SIZE                256
+#define SHUTTER_TABLE_SIZE             256
 #define PARTICLE_SIZE          5
 #define TIME_INVALID           FLT_MAX
 
@@ -813,6 +814,9 @@ typedef struct KernelCamera {
         * Used for camera zoom motion blur,
         */
        PerspectiveMotionTransform perspective_motion;
+
+       int shutter_table_offset;
+       int pad;
 } KernelCamera;
 
 typedef struct KernelFilm {
index 88ff7fbb70a93e32963442c6d864d527369530db..c59971996bcf9cd96658513c0c48efb57c67b2a2 100644 (file)
 #include "mesh.h"
 #include "object.h"
 #include "scene.h"
+#include "tables.h"
 
 #include "device.h"
 
 #include "util_foreach.h"
+#include "util_function.h"
+#include "util_math_cdf.h"
 #include "util_vector.h"
 
 CCL_NAMESPACE_BEGIN
 
+static float shutter_curve_eval(float x,
+                                float shutter_curve[RAMP_TABLE_SIZE])
+{
+       x *= RAMP_TABLE_SIZE;
+       int index = (int)x;
+       float frac = x - index;
+       if(index < RAMP_TABLE_SIZE - 1) {
+               return lerp(shutter_curve[index], shutter_curve[index + 1], frac);
+       }
+       else {
+               return shutter_curve[RAMP_TABLE_SIZE - 1];
+       }
+}
+
 Camera::Camera()
 {
        shuttertime = 1.0f;
        motion_position = MOTION_POSITION_CENTER;
+       shutter_table_offset = TABLE_OFFSET_INVALID;
 
        aperturesize = 0.0f;
        focaldistance = 10.0f;
@@ -85,6 +103,12 @@ Camera::Camera()
        need_device_update = true;
        need_flags_update = true;
        previous_need_motion = -1;
+
+       /* Initialize shutter curve. */
+       const int num_shutter_points = sizeof(shutter_curve) / sizeof(*shutter_curve);
+       for(int i = 0; i < num_shutter_points; ++i) {
+               shutter_curve[i] = 1.0f;
+       }
 }
 
 Camera::~Camera()
@@ -279,6 +303,23 @@ void Camera::device_update(Device *device, DeviceScene *dscene, Scene *scene)
        /* motion blur */
 #ifdef __CAMERA_MOTION__
        kcam->shuttertime = (need_motion == Scene::MOTION_BLUR) ? shuttertime: -1.0f;
+
+       if(need_motion == Scene::MOTION_BLUR) {
+               vector<float> shutter_table;
+               util_cdf_inverted(SHUTTER_TABLE_SIZE,
+                                 0.0f,
+                                 1.0f,
+                                 function_bind(shutter_curve_eval, _1, shutter_curve),
+                                 false,
+                                 shutter_table);
+               shutter_table_offset = scene->lookup_tables->add_table(dscene,
+                                                                      shutter_table);
+               kcam->shutter_table_offset = (int)shutter_table_offset;
+       }
+       else if(shutter_table_offset != TABLE_OFFSET_INVALID) {
+               scene->lookup_tables->remove_table(shutter_table_offset);
+               shutter_table_offset = TABLE_OFFSET_INVALID;
+       }
 #else
        kcam->shuttertime = -1.0f;
 #endif
@@ -342,9 +383,14 @@ void Camera::device_update_volume(Device * /*device*/,
        need_flags_update = false;
 }
 
-void Camera::device_free(Device * /*device*/, DeviceScene * /*dscene*/)
+void Camera::device_free(Device * /*device*/,
+                         DeviceScene * /*dscene*/,
+                         Scene *scene)
 {
-       /* nothing to free, only writing to constant memory */
+       if(shutter_table_offset != TABLE_OFFSET_INVALID) {
+               scene->lookup_tables->remove_table(shutter_table_offset);
+               shutter_table_offset = TABLE_OFFSET_INVALID;
+       }
 }
 
 bool Camera::modified(const Camera& cam)
index 53bd4f0bc1486db956942e75b2d1f42238ba118d..1c26afafeff11fae77cc6e3993e7d1a0f5f4d0c6 100644 (file)
@@ -50,6 +50,8 @@ public:
        /* motion blur */
        float shuttertime;
        MotionPosition motion_position;
+       float shutter_curve[RAMP_TABLE_SIZE];
+       size_t shutter_table_offset;
 
        /* depth of field */
        float focaldistance;
@@ -132,7 +134,7 @@ public:
 
        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);
+       void device_free(Device *device, DeviceScene *dscene, Scene *scene);
 
        bool modified(const Camera& cam);
        bool motion_modified(const Camera& cam);
index 19d715d834b45cc9ce2ddd164815412571f12849..25f812221ac33151020cfc1ada3144b24f7669b9 100644 (file)
@@ -97,7 +97,7 @@ void Scene::free_memory(bool final)
        particle_systems.clear();
 
        if(device) {
-               camera->device_free(device, &dscene);
+               camera->device_free(device, &dscene, this);
                film->device_free(device, &dscene, this);
                background->device_free(device, &dscene);
                integrator->device_free(device, &dscene);
index cafe4c5abeb7de0283f9cc3e8262fd70cb54aa44..ec78ca15d8848abf11018a69f944887028fb8568 100644 (file)
@@ -37,7 +37,7 @@ void util_cdf_invert(const int resolution,
                        float x = i / (float)half_size;
                        int index = upper_bound(cdf.begin(), cdf.end(), x) - cdf.begin();
                        float t;
-                       if(index < cdf.size()) {
+                       if(index < cdf.size() - 1) {
                                t = (x - cdf[index])/(cdf[index+1] - cdf[index]);
                        } else {
                                t = 0.0f;
@@ -49,11 +49,11 @@ void util_cdf_invert(const int resolution,
                }
        }
        else {
-               for(int i = 0; i <= resolution; i++) {
+               for(int i = 0; i < resolution; i++) {
                        float x = from + range * (float)i * inv_resolution;
                        int index = upper_bound(cdf.begin(), cdf.end(), x) - cdf.begin();
                        float t;
-                       if(index < cdf.size()) {
+                       if(index < cdf.size() - 1) {
                                t = (x - cdf[index])/(cdf[index+1] - cdf[index]);
                        } else {
                                t = 0.0f;