Compositor: Implement sampled motion blur for plane track deform node

Quite striaghtforward change, and in theory we can even try supporting motion
blur for the corner pin node (which is tricky because coordinates actually
coming from sockets, but with some black magic should be doable).
This commit is contained in:
Sergey Sharybin 2015-02-04 01:16:28 +05:00
parent c69458985c
commit 1dddcfbaff
Notes: blender-bot 2023-02-14 09:33:11 +01:00
Referenced by issue #43476, 2.73 crashes on startup
11 changed files with 233 additions and 82 deletions

View File

@ -542,4 +542,21 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
part->child_flag |= PART_CHILD_USE_ROUGH_CURVE;
}
}
if (!DNA_struct_elem_find(fd->filesdna, "NodePlaneTrackDeformData", "char", "flag")) {
FOREACH_NODETREE(main, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
bNode *node;
for (node = ntree->nodes.first; node; node = node->next) {
if (ELEM(node->type, CMP_NODE_PLANETRACKDEFORM)) {
NodePlaneTrackDeformData *data = node->storage;
data->flag = 0;
data->motion_blur_samples = 16;
data->motion_blur_shutter = 0.5f;
}
}
}
}
FOREACH_NODETREE_END
}
}

View File

@ -54,6 +54,10 @@ void PlaneTrackDeformNode::convertToOperations(NodeConverter &converter, const C
warp_image_operation->setTrackingObject(data->tracking_object);
warp_image_operation->setPlaneTrackName(data->plane_track_name);
warp_image_operation->setFramenumber(frame_number);
if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) {
warp_image_operation->setMotionBlurSamples(data->motion_blur_samples);
warp_image_operation->setMotionBlurShutter(data->motion_blur_shutter);
}
converter.addOperation(warp_image_operation);
converter.mapInputSocket(input_image, warp_image_operation->getInputSocket(0));
@ -64,6 +68,10 @@ void PlaneTrackDeformNode::convertToOperations(NodeConverter &converter, const C
plane_mask_operation->setTrackingObject(data->tracking_object);
plane_mask_operation->setPlaneTrackName(data->plane_track_name);
plane_mask_operation->setFramenumber(frame_number);
if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) {
plane_mask_operation->setMotionBlurSamples(data->motion_blur_samples);
plane_mask_operation->setMotionBlurShutter(data->motion_blur_shutter);
}
converter.addOperation(plane_mask_operation);
converter.mapOutputSocket(output_plane, plane_mask_operation->getOutputSocket());

View File

@ -135,7 +135,7 @@ void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect)
getInputSocketReader(3) };
float corners[4][2];
readCornersFromSockets(rect, readers, corners);
calculateCorners(corners, true);
calculateCorners(corners, true, 0);
m_corners_ready = true;
}
@ -194,8 +194,7 @@ void *PlaneCornerPinWarpImageOperation::initializeTileData(rcti *rect)
getInputSocketReader(4) };
float corners[4][2];
readCornersFromSockets(rect, readers, corners);
calculateCorners(corners, true);
calculatePerspectiveMatrix();
calculateCorners(corners, true, 0);
m_corners_ready = true;
}

View File

@ -56,36 +56,38 @@ PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() :
this->addInputSocket(COM_DT_COLOR, COM_SC_NO_RESIZE);
this->addOutputSocket(COM_DT_COLOR);
this->m_pixelReader = NULL;
this->m_motion_blur_samples = 1;
this->m_motion_blur_shutter = 0.5f;
this->setComplex(true);
}
void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2], bool normalized)
{
if (normalized) {
for (int i = 0; i < 4; i++) {
this->m_frameSpaceCorners[i][0] = corners[i][0] * this->getWidth();
this->m_frameSpaceCorners[i][1] = corners[i][1] * this->getHeight();
}
}
else {
for (int i = 0; i < 4; i++) {
this->m_frameSpaceCorners[i][0] = corners[i][0];
this->m_frameSpaceCorners[i][1] = corners[i][1];
}
}
}
void PlaneDistortWarpImageOperation::calculatePerspectiveMatrix()
void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2],
bool normalized,
int sample)
{
BLI_assert(sample < this->m_motion_blur_samples);
const int width = this->m_pixelReader->getWidth();
const int height = this->m_pixelReader->getHeight();
float frame_corners[4][2] = {{0.0f, 0.0f},
{(float) width, 0.0f},
{(float) width, (float) height},
{0.0f, (float) height}};
BKE_tracking_homography_between_two_quads(this->m_frameSpaceCorners,
MotionSample *sample_data = &this->m_samples[sample];
if (normalized) {
for (int i = 0; i < 4; i++) {
sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth();
sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight();
}
}
else {
for (int i = 0; i < 4; i++) {
sample_data->frameSpaceCorners[i][0] = corners[i][0];
sample_data->frameSpaceCorners[i][1] = corners[i][1];
}
}
BKE_tracking_homography_between_two_quads(sample_data->frameSpaceCorners,
frame_corners,
this->m_perspectiveMatrix);
sample_data->perspectiveMatrix);
}
void PlaneDistortWarpImageOperation::initExecution()
@ -100,35 +102,47 @@ void PlaneDistortWarpImageOperation::deinitExecution()
void PlaneDistortWarpImageOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float xy[2] = {x, y};
float uv[2];
float deriv[2][2];
pixelTransform(xy, uv, deriv);
m_pixelReader->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1], COM_PS_BILINEAR);
}
void PlaneDistortWarpImageOperation::pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2])
{
warpCoord(xy[0], xy[1], m_perspectiveMatrix, r_uv, r_deriv);
if (this->m_motion_blur_samples == 1) {
warpCoord(x, y, this->m_samples[0].perspectiveMatrix, uv, deriv);
m_pixelReader->readFiltered(output,
uv[0], uv[1],
deriv[0], deriv[1],
COM_PS_BILINEAR);
}
else {
zero_v4(output);
for (int sample = 0; sample < this->m_motion_blur_samples; ++sample) {
float color[4];
warpCoord(x, y, this->m_samples[sample].perspectiveMatrix, uv, deriv);
m_pixelReader->readFiltered(color,
uv[0], uv[1],
deriv[0], deriv[1],
COM_PS_BILINEAR);
add_v4_v4(output, color);
}
mul_v4_fl(output, 1.0f / (float)this->m_motion_blur_samples);
}
}
bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
{
float UVs[4][2];
float deriv[2][2];
/* TODO(sergey): figure out proper way to do this. */
warpCoord(input->xmin - 2, input->ymin - 2, this->m_perspectiveMatrix, UVs[0], deriv);
warpCoord(input->xmax + 2, input->ymin - 2, this->m_perspectiveMatrix, UVs[1], deriv);
warpCoord(input->xmax + 2, input->ymax + 2, this->m_perspectiveMatrix, UVs[2], deriv);
warpCoord(input->xmin - 2, input->ymax + 2, this->m_perspectiveMatrix, UVs[3], deriv);
float min[2], max[2];
INIT_MINMAX2(min, max);
for (int i = 0; i < 4; i++) {
minmax_v2v2_v2(min, max, UVs[i]);
for (int sample = 0; sample < this->m_motion_blur_samples; ++sample) {
float UVs[4][2];
float deriv[2][2];
MotionSample *sample_data = &this->m_samples[sample];
/* TODO(sergey): figure out proper way to do this. */
warpCoord(input->xmin - 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv);
warpCoord(input->xmax + 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv);
warpCoord(input->xmax + 2, input->ymax + 2, sample_data->perspectiveMatrix, UVs[2], deriv);
warpCoord(input->xmin - 2, input->ymax + 2, sample_data->perspectiveMatrix, UVs[3], deriv);
for (int i = 0; i < 4; i++) {
minmax_v2v2_v2(min, max, UVs[i]);
}
}
rcti newInput;
@ -151,20 +165,26 @@ PlaneDistortMaskOperation::PlaneDistortMaskOperation() :
/* Currently hardcoded to 8 samples. */
m_osa = 8;
this->m_motion_blur_samples = 1;
this->m_motion_blur_shutter = 0.5f;
}
void PlaneDistortMaskOperation::calculateCorners(const float corners[4][2], bool normalized)
void PlaneDistortMaskOperation::calculateCorners(const float corners[4][2],
bool normalized,
int sample)
{
BLI_assert(sample < this->m_motion_blur_samples);
MotionSample *sample_data = &this->m_samples[sample];
if (normalized) {
for (int i = 0; i < 4; i++) {
this->m_frameSpaceCorners[i][0] = corners[i][0] * this->getWidth();
this->m_frameSpaceCorners[i][1] = corners[i][1] * this->getHeight();
sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth();
sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight();
}
}
else {
for (int i = 0; i < 4; i++) {
this->m_frameSpaceCorners[i][0] = corners[i][0];
this->m_frameSpaceCorners[i][1] = corners[i][1];
sample_data->frameSpaceCorners[i][0] = corners[i][0];
sample_data->frameSpaceCorners[i][1] = corners[i][1];
}
}
}
@ -177,18 +197,44 @@ void PlaneDistortMaskOperation::initExecution()
void PlaneDistortMaskOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float point[2];
int inside_counter = 0;
for (int sample = 0; sample < this->m_osa; sample++) {
point[0] = x + this->m_jitter[sample][0];
point[1] = y + this->m_jitter[sample][1];
if (isect_point_tri_v2(point, this->m_frameSpaceCorners[0], this->m_frameSpaceCorners[1], this->m_frameSpaceCorners[2]) ||
isect_point_tri_v2(point, this->m_frameSpaceCorners[0], this->m_frameSpaceCorners[2], this->m_frameSpaceCorners[3]))
{
inside_counter++;
if (this->m_motion_blur_samples == 1) {
MotionSample *sample_data = &this->m_samples[0];
for (int sample = 0; sample < this->m_osa; sample++) {
point[0] = x + this->m_jitter[sample][0];
point[1] = y + this->m_jitter[sample][1];
if (isect_point_tri_v2(point, sample_data->frameSpaceCorners[0],
sample_data->frameSpaceCorners[1],
sample_data->frameSpaceCorners[2]) ||
isect_point_tri_v2(point, sample_data->frameSpaceCorners[0],
sample_data->frameSpaceCorners[2],
sample_data->frameSpaceCorners[3]))
{
inside_counter++;
}
}
output[0] = (float)inside_counter / this->m_osa;
}
else {
for (int motion_sample = 0;
motion_sample < this->m_motion_blur_samples;
++motion_sample)
{
MotionSample *sample_data = &this->m_samples[motion_sample];
for (int osa_sample = 0; osa_sample < this->m_osa; ++osa_sample) {
point[0] = x + this->m_jitter[osa_sample][0];
point[1] = y + this->m_jitter[osa_sample][1];
if (isect_point_tri_v2(point, sample_data->frameSpaceCorners[0],
sample_data->frameSpaceCorners[1],
sample_data->frameSpaceCorners[2]) ||
isect_point_tri_v2(point, sample_data->frameSpaceCorners[0],
sample_data->frameSpaceCorners[2],
sample_data->frameSpaceCorners[3]))
{
inside_counter++;
}
}
}
output[0] = (float)inside_counter / (this->m_osa * this->m_motion_blur_samples);
}
output[0] = (float) inside_counter / this->m_osa;
}

View File

@ -33,43 +33,68 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
#define PLANE_DISTORT_MAX_SAMPLES 64
class PlaneDistortWarpImageOperation : public NodeOperation {
protected:
struct MotionSample {
float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */
float perspectiveMatrix[3][3];
};
SocketReader *m_pixelReader;
float m_frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */
float m_perspectiveMatrix[3][3];
MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES];
int m_motion_blur_samples;
float m_motion_blur_shutter;
public:
PlaneDistortWarpImageOperation();
void calculateCorners(const float corners[4][2], bool normalized);
void calculatePerspectiveMatrix();
void calculateCorners(const float corners[4][2],
bool normalized,
int sample);
void initExecution();
void deinitExecution();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
void pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2]);
bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output);
void setMotionBlurSamples(int samples) {
BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES);
this->m_motion_blur_samples = samples;
}
void setMotionBlurShutter(float shutter) { this->m_motion_blur_shutter = shutter; }
};
class PlaneDistortMaskOperation : public NodeOperation {
protected:
struct MotionSample {
float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */
};
int m_osa;
MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES];
float m_jitter[32][2];
float m_frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */
int m_motion_blur_samples;
float m_motion_blur_shutter;
public:
PlaneDistortMaskOperation();
void calculateCorners(const float corners[4][2], bool normalized);
void calculateCorners(const float corners[4][2],
bool normalized,
int sample);
void initExecution();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
void setMotionBlurSamples(int samples) {
BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES);
this->m_motion_blur_samples = samples;
}
void setMotionBlurShutter(float shutter) { this->m_motion_blur_shutter = shutter; }
};
#endif

View File

@ -46,7 +46,7 @@ PlaneTrackCommon::PlaneTrackCommon()
this->m_planeTrackName[0] = '\0';
}
void PlaneTrackCommon::readCornersFromTrack(float corners[4][2])
void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame)
{
MovieTracking *tracking;
MovieTrackingObject *object;
@ -64,13 +64,13 @@ void PlaneTrackCommon::readCornersFromTrack(float corners[4][2])
if (plane_track) {
MovieTrackingPlaneMarker *plane_marker;
int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, this->m_framenumber);
float clip_framenr =
BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip,
frame);
plane_marker = BKE_tracking_plane_marker_get(plane_track, clip_framenr);
copy_v2_v2(corners[0], plane_marker->corners[0]);
copy_v2_v2(corners[1], plane_marker->corners[1]);
copy_v2_v2(corners[2], plane_marker->corners[2]);
copy_v2_v2(corners[3], plane_marker->corners[3]);
BKE_tracking_plane_marker_get_subframe_corners(plane_track,
clip_framenr,
corners);
}
}
}
@ -98,10 +98,21 @@ void PlaneTrackCommon::determineResolution(unsigned int resolution[2], unsigned
void PlaneTrackMaskOperation::initExecution()
{
PlaneDistortMaskOperation::initExecution();
float corners[4][2];
readCornersFromTrack(corners);
calculateCorners(corners, true);
if (this->m_motion_blur_samples == 1) {
readCornersFromTrack(corners, this->m_framenumber);
calculateCorners(corners, true, 0);
}
else {
const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter;
const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples;
float frame_iter = frame;
for (int sample = 0; sample < this->m_motion_blur_samples; ++sample) {
readCornersFromTrack(corners, frame_iter);
calculateCorners(corners, true, sample);
frame_iter += frame_step;
}
}
}
/* ******** PlaneTrackWarpImageOperation ******** */
@ -109,9 +120,20 @@ void PlaneTrackMaskOperation::initExecution()
void PlaneTrackWarpImageOperation::initExecution()
{
PlaneDistortWarpImageOperation::initExecution();
/* TODO(sergey): De-duplicate with mask operation. */
float corners[4][2];
readCornersFromTrack(corners);
calculateCorners(corners, true);
calculatePerspectiveMatrix();
if (this->m_motion_blur_samples == 1) {
readCornersFromTrack(corners, this->m_framenumber);
calculateCorners(corners, true, 0);
}
else {
const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter;
const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples;
float frame_iter = frame;
for (int sample = 0; sample < this->m_motion_blur_samples; ++sample) {
readCornersFromTrack(corners, frame_iter);
calculateCorners(corners, true, sample);
frame_iter += frame_step;
}
}
}

View File

@ -43,7 +43,7 @@ protected:
/* note: this class is not an operation itself (to prevent virtual inheritance issues)
* implementation classes must make wrappers to use these methods, see below.
*/
void readCornersFromTrack(float corners[4][2]);
void readCornersFromTrack(float corners[4][2], float frame);
void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
public:

View File

@ -2314,6 +2314,7 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN
static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
bNode *node = ptr->data;
NodePlaneTrackDeformData *data = node->storage;
uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL);
@ -2342,6 +2343,12 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P
uiItemR(layout, ptr, "plane_track_name", 0, "", ICON_ANIM_DATA);
}
}
uiItemR(layout, ptr, "use_motion_blur", 0, NULL, ICON_NONE);
if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) {
uiItemR(layout, ptr, "motion_blur_samples", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "motion_blur_shutter", 0, NULL, ICON_NONE);
}
}
static void node_composit_buts_cornerpin(uiLayout *UNUSED(layout), bContext *UNUSED(C), PointerRNA *UNUSED(ptr))

View File

@ -825,6 +825,10 @@ typedef struct NodeTranslateData {
typedef struct NodePlaneTrackDeformData {
char tracking_object[64];
char plane_track_name[64];
char flag;
char motion_blur_samples;
char pad[2];
float motion_blur_shutter;
} NodePlaneTrackDeformData;
typedef struct NodeShaderScript {
@ -1080,4 +1084,11 @@ enum {
/* viewer and cmposite output */
#define CMP_NODE_OUTPUT_IGNORE_ALPHA 1
/* Plane track deform node */
enum {
CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR = 1,
};
#define CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX 64
#endif

View File

@ -6231,6 +6231,21 @@ static void def_cmp_planetrackdeform(StructRNA *srna)
RNA_def_property_string_sdna(prop, NULL, "plane_track_name");
RNA_def_property_ui_text(prop, "Plane Track", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "use_motion_blur", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR);
RNA_def_property_ui_text(prop, "Motion Blur", "Use multi-sampled motion blur of the mask");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "motion_blur_samples", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 1, CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX);
RNA_def_property_ui_text(prop, "Samples", "Number of motion blur samples");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "motion_blur_shutter", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0, 1.0f);
RNA_def_property_ui_text(prop, "Shutter", "Exposure for motion blur as a factor of FPS");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_sunbeams(StructRNA *srna)

View File

@ -47,7 +47,8 @@ static bNodeSocketTemplate cmp_node_planetrackdeform_out[] = {
static void init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodePlaneTrackDeformData *data = MEM_callocN(sizeof(NodePlaneTrackDeformData), "node plane track deform data");
data->motion_blur_samples = 16;
data->motion_blur_shutter = 0.5f;
node->storage = data;
}