Cycles: Support user-defined shutter curve

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.
This commit is contained in:
Sergey Sharybin 2015-10-27 13:16:04 +05:00
parent c07c066685
commit 6a529e14f4
6 changed files with 65 additions and 10 deletions

@ -290,10 +290,13 @@ ccl_device void camera_sample(KernelGlobals *kg, int x, int y, float filter_u, f
#ifdef __CAMERA_MOTION__ #ifdef __CAMERA_MOTION__
/* motion blur */ /* motion blur */
if(kernel_data.cam.shuttertime == -1.0f) if(kernel_data.cam.shuttertime == -1.0f) {
ray->time = TIME_INVALID; 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 #endif
/* sample */ /* sample */

@ -39,6 +39,7 @@ CCL_NAMESPACE_BEGIN
#define LIGHT_SIZE 5 #define LIGHT_SIZE 5
#define FILTER_TABLE_SIZE 256 #define FILTER_TABLE_SIZE 256
#define RAMP_TABLE_SIZE 256 #define RAMP_TABLE_SIZE 256
#define SHUTTER_TABLE_SIZE 256
#define PARTICLE_SIZE 5 #define PARTICLE_SIZE 5
#define TIME_INVALID FLT_MAX #define TIME_INVALID FLT_MAX
@ -813,6 +814,9 @@ typedef struct KernelCamera {
* Used for camera zoom motion blur, * Used for camera zoom motion blur,
*/ */
PerspectiveMotionTransform perspective_motion; PerspectiveMotionTransform perspective_motion;
int shutter_table_offset;
int pad;
} KernelCamera; } KernelCamera;
typedef struct KernelFilm { typedef struct KernelFilm {

@ -18,18 +18,36 @@
#include "mesh.h" #include "mesh.h"
#include "object.h" #include "object.h"
#include "scene.h" #include "scene.h"
#include "tables.h"
#include "device.h" #include "device.h"
#include "util_foreach.h" #include "util_foreach.h"
#include "util_function.h"
#include "util_math_cdf.h"
#include "util_vector.h" #include "util_vector.h"
CCL_NAMESPACE_BEGIN 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() Camera::Camera()
{ {
shuttertime = 1.0f; shuttertime = 1.0f;
motion_position = MOTION_POSITION_CENTER; motion_position = MOTION_POSITION_CENTER;
shutter_table_offset = TABLE_OFFSET_INVALID;
aperturesize = 0.0f; aperturesize = 0.0f;
focaldistance = 10.0f; focaldistance = 10.0f;
@ -85,6 +103,12 @@ Camera::Camera()
need_device_update = true; need_device_update = true;
need_flags_update = true; need_flags_update = true;
previous_need_motion = -1; 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() Camera::~Camera()
@ -279,6 +303,23 @@ void Camera::device_update(Device *device, DeviceScene *dscene, Scene *scene)
/* motion blur */ /* motion blur */
#ifdef __CAMERA_MOTION__ #ifdef __CAMERA_MOTION__
kcam->shuttertime = (need_motion == Scene::MOTION_BLUR) ? shuttertime: -1.0f; 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 #else
kcam->shuttertime = -1.0f; kcam->shuttertime = -1.0f;
#endif #endif
@ -342,9 +383,14 @@ void Camera::device_update_volume(Device * /*device*/,
need_flags_update = false; 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) bool Camera::modified(const Camera& cam)

@ -50,6 +50,8 @@ public:
/* motion blur */ /* motion blur */
float shuttertime; float shuttertime;
MotionPosition motion_position; MotionPosition motion_position;
float shutter_curve[RAMP_TABLE_SIZE];
size_t shutter_table_offset;
/* depth of field */ /* depth of field */
float focaldistance; float focaldistance;
@ -132,7 +134,7 @@ public:
void device_update(Device *device, DeviceScene *dscene, Scene *scene); void device_update(Device *device, DeviceScene *dscene, Scene *scene);
void device_update_volume(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 modified(const Camera& cam);
bool motion_modified(const Camera& cam); bool motion_modified(const Camera& cam);

@ -97,7 +97,7 @@ void Scene::free_memory(bool final)
particle_systems.clear(); particle_systems.clear();
if(device) { if(device) {
camera->device_free(device, &dscene); camera->device_free(device, &dscene, this);
film->device_free(device, &dscene, this); film->device_free(device, &dscene, this);
background->device_free(device, &dscene); background->device_free(device, &dscene);
integrator->device_free(device, &dscene); integrator->device_free(device, &dscene);

@ -37,7 +37,7 @@ void util_cdf_invert(const int resolution,
float x = i / (float)half_size; float x = i / (float)half_size;
int index = upper_bound(cdf.begin(), cdf.end(), x) - cdf.begin(); int index = upper_bound(cdf.begin(), cdf.end(), x) - cdf.begin();
float t; float t;
if(index < cdf.size()) { if(index < cdf.size() - 1) {
t = (x - cdf[index])/(cdf[index+1] - cdf[index]); t = (x - cdf[index])/(cdf[index+1] - cdf[index]);
} else { } else {
t = 0.0f; t = 0.0f;
@ -49,11 +49,11 @@ void util_cdf_invert(const int resolution,
} }
} }
else { else {
for(int i = 0; i <= resolution; i++) { for(int i = 0; i < resolution; i++) {
float x = from + range * (float)i * inv_resolution; float x = from + range * (float)i * inv_resolution;
int index = upper_bound(cdf.begin(), cdf.end(), x) - cdf.begin(); int index = upper_bound(cdf.begin(), cdf.end(), x) - cdf.begin();
float t; float t;
if(index < cdf.size()) { if(index < cdf.size() - 1) {
t = (x - cdf[index])/(cdf[index+1] - cdf[index]); t = (x - cdf[index])/(cdf[index+1] - cdf[index]);
} else { } else {
t = 0.0f; t = 0.0f;