Compositor: Unify plane anti-aliasing between CPU and GPU #118853

Merged
Omar Emara merged 3 commits from OmarEmaraDev/blender:unify-plane-deform-anti-aliasing into main 2024-02-29 12:30:25 +01:00
18 changed files with 300 additions and 145 deletions

View File

@ -412,14 +412,20 @@ void MemoryBuffer::add_pixel(int x, int y, const float color[4])
}
}
static void read_ewa_elem(void *userdata, int x, int y, float result[4])
static void read_ewa_elem_checked(void *userdata, int x, int y, float result[4])
{
const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
buffer->read_elem_checked(x, y, result);
}
static void read_ewa_elem_clamped(void *userdata, int x, int y, float result[4])
{
const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
buffer->read_elem_clamped(x, y, result);
}
void MemoryBuffer::read_elem_filtered(
const float x, const float y, float dx[2], float dy[2], float *out) const
const float x, const float y, float dx[2], float dy[2], bool extend_boundary, float *out) const
{
BLI_assert(datatype_ == DataType::Color);
@ -441,7 +447,7 @@ void MemoryBuffer::read_elem_filtered(
uv_normal,
du_normal,
dv_normal,
read_ewa_elem,
extend_boundary ? read_ewa_elem_clamped : read_ewa_elem_checked,
const_cast<MemoryBuffer *>(this),
out);
}

View File

@ -263,7 +263,8 @@ class MemoryBuffer {
}
}
void read_elem_filtered(float x, float y, float dx[2], float dy[2], float *out) const;
void read_elem_filtered(
float x, float y, float dx[2], float dy[2], bool extend_boundary, float *out) const;
/**
* Get channel value at given coordinates.

View File

@ -5,6 +5,7 @@
#include "COM_CornerPinNode.h"
#include "COM_PlaneCornerPinOperation.h"
#include "COM_SMAAOperation.h"
namespace blender::compositor {
@ -13,7 +14,36 @@ CornerPinNode::CornerPinNode(bNode *editor_node) : Node(editor_node) {}
void CornerPinNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
NodeInput *input_image = this->get_input_socket(0);
PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation();
converter.add_operation(plane_mask_operation);
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_edge_detection->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(1), smaa_neighborhood->get_output_socket());
PlaneCornerPinWarpImageOperation *warp_image_operation = new PlaneCornerPinWarpImageOperation();
converter.add_operation(warp_image_operation);
converter.map_input_socket(this->get_input_socket(0), warp_image_operation->get_input_socket(0));
/* NOTE: socket order differs between UI node and operations:
* bNode uses intuitive order following top-down layout:
* upper-left, upper-right, lower-left, lower-right
@ -21,23 +51,20 @@ void CornerPinNode::convert_to_operations(NodeConverter &converter,
* lower-left, lower-right, upper-right, upper-left
*/
const int node_corner_index[4] = {3, 4, 2, 1};
NodeOutput *output_warped_image = this->get_output_socket(0);
NodeOutput *output_plane = this->get_output_socket(1);
PlaneCornerPinWarpImageOperation *warp_image_operation = new PlaneCornerPinWarpImageOperation();
converter.add_operation(warp_image_operation);
PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation();
converter.add_operation(plane_mask_operation);
converter.map_input_socket(input_image, warp_image_operation->get_input_socket(0));
for (int i = 0; i < 4; i++) {
NodeInput *corner_input = get_input_socket(node_corner_index[i]);
converter.map_input_socket(corner_input, warp_image_operation->get_input_socket(i + 1));
converter.map_input_socket(corner_input, plane_mask_operation->get_input_socket(i));
}
converter.map_output_socket(output_warped_image, warp_image_operation->get_output_socket());
converter.map_output_socket(output_plane, plane_mask_operation->get_output_socket());
SetAlphaMultiplyOperation *set_alpha_operation = new SetAlphaMultiplyOperation();
converter.add_operation(set_alpha_operation);
converter.add_link(warp_image_operation->get_output_socket(),
set_alpha_operation->get_input_socket(0));
converter.add_link(smaa_neighborhood->get_output_socket(),
set_alpha_operation->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(0),
set_alpha_operation->get_output_socket());
}
} // namespace blender::compositor

View File

@ -5,6 +5,7 @@
#include "COM_PlaneTrackDeformNode.h"
#include "COM_PlaneTrackOperation.h"
#include "COM_SMAAOperation.h"
namespace blender::compositor {
@ -22,9 +23,39 @@ void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
int frame_number = context.get_framenumber();
NodeInput *input_image = this->get_input_socket(0);
NodeOutput *output_warped_image = this->get_output_socket(0);
NodeOutput *output_plane = this->get_output_socket(1);
PlaneTrackMaskOperation *plane_mask_operation = new PlaneTrackMaskOperation();
plane_mask_operation->set_movie_clip(clip);
plane_mask_operation->set_tracking_object(data->tracking_object);
plane_mask_operation->set_plane_track_name(data->plane_track_name);
plane_mask_operation->set_framenumber(frame_number);
if (data->flag & CMP_NODE_PLANE_TRACK_DEFORM_FLAG_MOTION_BLUR) {
plane_mask_operation->set_motion_blur_samples(data->motion_blur_samples);
plane_mask_operation->set_motion_blur_shutter(data->motion_blur_shutter);
}
converter.add_operation(plane_mask_operation);
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_edge_detection->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(1), smaa_neighborhood->get_output_socket());
PlaneTrackWarpImageOperation *warp_image_operation = new PlaneTrackWarpImageOperation();
warp_image_operation->set_movie_clip(clip);
@ -37,21 +68,16 @@ void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
}
converter.add_operation(warp_image_operation);
converter.map_input_socket(input_image, warp_image_operation->get_input_socket(0));
converter.map_output_socket(output_warped_image, warp_image_operation->get_output_socket());
converter.map_input_socket(this->get_input_socket(0), warp_image_operation->get_input_socket(0));
PlaneTrackMaskOperation *plane_mask_operation = new PlaneTrackMaskOperation();
plane_mask_operation->set_movie_clip(clip);
plane_mask_operation->set_tracking_object(data->tracking_object);
plane_mask_operation->set_plane_track_name(data->plane_track_name);
plane_mask_operation->set_framenumber(frame_number);
if (data->flag & CMP_NODE_PLANE_TRACK_DEFORM_FLAG_MOTION_BLUR) {
plane_mask_operation->set_motion_blur_samples(data->motion_blur_samples);
plane_mask_operation->set_motion_blur_shutter(data->motion_blur_shutter);
}
converter.add_operation(plane_mask_operation);
converter.map_output_socket(output_plane, plane_mask_operation->get_output_socket());
SetAlphaMultiplyOperation *set_alpha_operation = new SetAlphaMultiplyOperation();
converter.add_operation(set_alpha_operation);
converter.add_link(warp_image_operation->get_output_socket(),
set_alpha_operation->get_input_socket(0));
converter.add_link(smaa_neighborhood->get_output_socket(),
set_alpha_operation->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(0),
set_alpha_operation->get_output_socket());
}
} // namespace blender::compositor

View File

@ -152,7 +152,7 @@ void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
else {
/* EWA filtering (without nearest it gets blurry with NO distortion). */
input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], false, it.out);
}
}
}

View File

@ -144,7 +144,7 @@ void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
else {
/* EWA filtering. */
input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], false, it.out);
/* UV to alpha threshold. */
const float threshold = alpha_ * 0.05f;

View File

@ -99,7 +99,7 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer *
if (motion_blur_samples_ == 1) {
for (; !it.is_end(); ++it) {
warp_coord(it.x, it.y, samples_[0].perspective_matrix, uv, deriv);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], true, it.out);
}
}
else {
@ -108,7 +108,7 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer *
for (const int sample : IndexRange(motion_blur_samples_)) {
float color[4];
warp_coord(it.x, it.y, samples_[sample].perspective_matrix, uv, deriv);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], color);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], true, color);
add_v4_v4(it.out, color);
}
mul_v4_fl(it.out, 1.0f / float(motion_blur_samples_));
@ -175,14 +175,6 @@ void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx,
PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation()
{
add_output_socket(DataType::Value);
/* Currently hardcoded to 8 samples. */
osa_ = 8;
}
void PlaneDistortMaskOperation::init_execution()
{
BLI_jitter_init(jitter_, osa_);
}
void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *output,
@ -190,37 +182,22 @@ void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *outpu
Span<MemoryBuffer *> /*inputs*/)
{
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
int inside_count = 0;
float accumulated_mask = 0.0f;
const float2 point = float2(it.x, it.y);
for (const int motion_sample : IndexRange(motion_blur_samples_)) {
MotionSample &sample = samples_[motion_sample];
inside_count += get_jitter_samples_inside_count(it.x, it.y, sample);
const bool is_inside_plane = isect_point_tri_v2(point,
sample.frame_space_corners[0],
sample.frame_space_corners[1],
sample.frame_space_corners[2]) ||
isect_point_tri_v2(point,
sample.frame_space_corners[0],
sample.frame_space_corners[2],
sample.frame_space_corners[3]);
accumulated_mask += is_inside_plane ? 1.0f : 0.0f;
}
*it.out = float(inside_count) / (osa_ * motion_blur_samples_);
*it.out = accumulated_mask / motion_blur_samples_;
}
}
int PlaneDistortMaskOperation::get_jitter_samples_inside_count(int x,
int y,
MotionSample &sample_data)
{
float point[2];
int inside_count = 0;
for (int sample = 0; sample < osa_; sample++) {
point[0] = x + jitter_[sample][0];
point[1] = y + jitter_[sample][1];
if (isect_point_tri_v2(point,
sample_data.frame_space_corners[0],
sample_data.frame_space_corners[1],
sample_data.frame_space_corners[2]) ||
isect_point_tri_v2(point,
sample_data.frame_space_corners[0],
sample_data.frame_space_corners[2],
sample_data.frame_space_corners[3]))
{
inside_count++;
}
}
return inside_count;
}
} // namespace blender::compositor

View File

@ -60,21 +60,12 @@ class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation {
};
class PlaneDistortMaskOperation : public PlaneDistortBaseOperation {
protected:
int osa_;
float jitter_[32][2];
public:
PlaneDistortMaskOperation();
void init_execution() override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
private:
int get_jitter_samples_inside_count(int x, int y, MotionSample &sample_data);
};
} // namespace blender::compositor

View File

@ -191,7 +191,9 @@ set(GLSL_SRC
shaders/compositor_parallel_reduction.glsl
shaders/compositor_pixelate.glsl
shaders/compositor_plane_deform.glsl
shaders/compositor_plane_deform_mask.glsl
shaders/compositor_plane_deform_motion_blur.glsl
shaders/compositor_plane_deform_motion_blur_mask.glsl
shaders/compositor_premultiply_alpha.glsl
shaders/compositor_projector_lens_distortion.glsl
shaders/compositor_read_input.glsl
@ -315,7 +317,6 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/compositor_parallel_reduction_info.hh
shaders/infos/compositor_pixelate_info.hh
shaders/infos/compositor_plane_deform_info.hh
shaders/infos/compositor_plane_deform_motion_blur_info.hh
shaders/infos/compositor_premultiply_alpha_info.hh
shaders/infos/compositor_projector_lens_distortion_info.hh
shaders/infos/compositor_read_input_info.hh

View File

@ -8,8 +8,6 @@ void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
/* Add 0.5 to evaluate the input sampler at the center of the pixel and divide by the image size
* to get the coordinates into the sampler's expected [0, 1] range. */
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(texture_size(input_tx));
vec3 transformed_coordinates = mat3(homography_matrix) * vec3(coordinates, 1.0);
@ -23,14 +21,8 @@ void main()
vec4 sampled_color = textureGrad(input_tx, projected_coordinates, x_gradient, y_gradient);
/* The plane mask is 1 if it is inside the plane and 0 otherwise. However, we use the alpha value
* of the sampled color for pixels outside of the plane to utilize the anti-aliasing effect of
* the anisotropic filtering. Therefore, the input_tx sampler should use anisotropic filtering
* and be clamped to zero border color. */
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
float mask_value = is_inside_plane ? 1.0 : sampled_color.a;
/* Premultiply the mask value as an alpha. */
vec4 plane_color = sampled_color * texture_load(mask_tx, texel).x;
imageStore(output_img, texel, sampled_color);
imageStore(mask_img, texel, vec4(mask_value));
imageStore(output_img, texel, plane_color);
}

View File

@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(mask_img));
vec3 transformed_coordinates = mat3(homography_matrix) * vec3(coordinates, 1.0);
vec2 projected_coordinates = transformed_coordinates.xy / transformed_coordinates.z;
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
float mask_value = is_inside_plane ? 1.0 : 0.0;
imageStore(mask_img, texel, vec4(mask_value));
}

View File

@ -2,17 +2,14 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
/* Add 0.5 to evaluate the sampler at the center of the pixel and divide by the size to get the
* coordinates into the sampler's expected [0, 1] range. We choose the maximum between both
* output sizes because one of the outputs might be a dummy 1x1 image. */
ivec2 output_size = max(imageSize(output_img), imageSize(mask_img));
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(output_size);
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(output_img));
float accumulated_mask = 0.0;
vec4 accumulated_color = vec4(0.0);
for (int i = 0; i < number_of_motion_blur_samples; i++) {
mat3 homography_matrix = mat3(homography_matrices[i]);
@ -28,19 +25,12 @@ void main()
vec4 sampled_color = textureGrad(input_tx, projected_coordinates, x_gradient, y_gradient);
accumulated_color += sampled_color;
/* The plane mask is 1 if it is inside the plane and 0 otherwise. However, we use the alpha
* value of the sampled color for pixels outside of the plane to utilize the anti-aliasing
* effect of the anisotropic filtering. Therefore, the input_tx sampler should use anisotropic
* filtering and be clamped to zero border color. */
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
accumulated_mask += is_inside_plane ? 1.0 : sampled_color.a;
}
accumulated_mask /= number_of_motion_blur_samples;
accumulated_color /= number_of_motion_blur_samples;
imageStore(output_img, texel, accumulated_color);
imageStore(mask_img, texel, vec4(accumulated_mask));
/* Premultiply the mask value as an alpha. */
vec4 plane_color = accumulated_color * texture_load(mask_tx, texel).x;
imageStore(output_img, texel, plane_color);
}

View File

@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(mask_img));
float accumulated_mask = 0.0;
for (int i = 0; i < number_of_motion_blur_samples; i++) {
mat3 homography_matrix = mat3(homography_matrices[i]);
vec3 transformed_coordinates = homography_matrix * vec3(coordinates, 1.0);
vec2 projected_coordinates = transformed_coordinates.xy / transformed_coordinates.z;
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
accumulated_mask += is_inside_plane ? 1.0 : 0.0;
}
accumulated_mask /= number_of_motion_blur_samples;
imageStore(mask_img, texel, vec4(accumulated_mask));
}

View File

@ -4,11 +4,36 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_plane_deform_mask)
.local_group_size(16, 16)
.push_constant(Type::MAT4, "homography_matrix")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform_mask.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_plane_deform)
.local_group_size(16, 16)
.push_constant(Type::MAT4, "homography_matrix")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.sampler(1, ImageType::FLOAT_2D, "mask_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_plane_deform_motion_blur_mask)
.local_group_size(16, 16)
.push_constant(Type::INT, "number_of_motion_blur_samples")
.uniform_buf(0, "mat4", "homography_matrices[64]")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform_motion_blur_mask.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_plane_deform_motion_blur)
.local_group_size(16, 16)
.push_constant(Type::INT, "number_of_motion_blur_samples")
.uniform_buf(0, "mat4", "homography_matrices[64]")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.sampler(1, ImageType::FLOAT_2D, "mask_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.compute_source("compositor_plane_deform_motion_blur.glsl")
.do_static_compilation(true);

View File

@ -1,15 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_plane_deform_motion_blur)
.local_group_size(16, 16)
.push_constant(Type::INT, "number_of_motion_blur_samples")
.uniform_buf(0, "mat4", "homography_matrices[64]")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform_motion_blur.glsl")
.do_static_compilation(true);

View File

@ -15,6 +15,7 @@
#include "BKE_tracking.h"
#include "COM_algorithm_smaa.hh"
#include "COM_node_operation.hh"
#include "COM_utilities.hh"
@ -75,31 +76,71 @@ class CornerPinOperation : public NodeOperation {
return;
}
Result plane_mask = compute_plane_mask(homography_matrix);
Result anti_aliased_plane_mask = context().create_temporary_result(ResultType::Float);
smaa(context(), plane_mask, anti_aliased_plane_mask);
plane_mask.release();
if (output_image.should_compute()) {
compute_plane(homography_matrix, anti_aliased_plane_mask);
}
if (output_mask.should_compute()) {
output_mask.steal_data(anti_aliased_plane_mask);
}
else {
anti_aliased_plane_mask.release();
}
}
void compute_plane(const float3x3 &homography_matrix, Result &plane_mask)
{
GPUShader *shader = context().get_shader("compositor_plane_deform");
GPU_shader_bind(shader);
GPU_shader_uniform_mat3_as_mat4(shader, "homography_matrix", homography_matrix.ptr());
Result &input_image = get_input("Image");
GPU_texture_mipmap_mode(input_image.texture(), true, true);
GPU_texture_anisotropic_filter(input_image.texture(), true);
GPU_texture_extend_mode(input_image.texture(), GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
GPU_texture_extend_mode(input_image.texture(), GPU_SAMPLER_EXTEND_MODE_EXTEND);
input_image.bind_as_texture(shader, "input_tx");
plane_mask.bind_as_texture(shader, "mask_tx");
const Domain domain = compute_domain();
Result &output_image = get_result("Image");
output_image.allocate_texture(domain);
output_image.bind_as_image(shader, "output_img");
output_mask.allocate_texture(domain);
output_mask.bind_as_image(shader, "mask_img");
compute_dispatch_threads_at_least(shader, domain.size);
input_image.unbind_as_texture();
plane_mask.unbind_as_texture();
output_image.unbind_as_image();
output_mask.unbind_as_image();
GPU_shader_unbind();
}
Result compute_plane_mask(const float3x3 &homography_matrix)
{
GPUShader *shader = context().get_shader("compositor_plane_deform_mask");
GPU_shader_bind(shader);
GPU_shader_uniform_mat3_as_mat4(shader, "homography_matrix", homography_matrix.ptr());
const Domain domain = compute_domain();
Result plane_mask = context().create_temporary_result(ResultType::Float);
plane_mask.allocate_texture(domain);
plane_mask.bind_as_image(shader, "mask_img");
compute_dispatch_threads_at_least(shader, domain.size);
plane_mask.unbind_as_image();
GPU_shader_unbind();
return plane_mask;
}
float3x3 compute_homography_matrix()
{
float2 lower_left = get_input("Lower Left").get_vector_value_default(float4(0.0f)).xy();

View File

@ -30,6 +30,7 @@
#include "GPU_texture.h"
#include "GPU_uniform_buffer.h"
#include "COM_algorithm_smaa.hh"
#include "COM_node_operation.hh"
#include "COM_utilities.hh"
@ -144,40 +145,87 @@ class PlaneTrackDeformOperation : public NodeOperation {
}
const Array<float4x4> homography_matrices = compute_homography_matrices(plane_track);
GPUUniformBuf *homography_matrices_buffer = GPU_uniformbuf_create_ex(
homography_matrices.size() * sizeof(float4x4),
homography_matrices.data(),
"Plane Track Deform Homography Matrices");
Result plane_mask = compute_plane_mask(homography_matrices, homography_matrices_buffer);
Result anti_aliased_plane_mask = context().create_temporary_result(ResultType::Float);
smaa(context(), plane_mask, anti_aliased_plane_mask);
plane_mask.release();
if (output_image.should_compute()) {
compute_plane(homography_matrices, homography_matrices_buffer, anti_aliased_plane_mask);
}
if (output_mask.should_compute()) {
output_mask.steal_data(anti_aliased_plane_mask);
}
else {
anti_aliased_plane_mask.release();
}
GPU_uniformbuf_free(homography_matrices_buffer);
}
void compute_plane(const Array<float4x4> &homography_matrices,
GPUUniformBuf *homography_matrices_buffer,
Result &plane_mask)
{
GPUShader *shader = context().get_shader("compositor_plane_deform_motion_blur");
GPU_shader_bind(shader);
GPU_shader_uniform_1i(shader, "number_of_motion_blur_samples", homography_matrices.size());
GPUUniformBuf *matrices_buffer = GPU_uniformbuf_create_ex(
homography_matrices.size() * sizeof(float4x4),
homography_matrices.data(),
"Plane Track Deform Homography Matrices");
const int ubo_location = GPU_shader_get_ubo_binding(shader, "homography_matrices");
GPU_uniformbuf_bind(matrices_buffer, ubo_location);
GPU_uniformbuf_bind(homography_matrices_buffer, ubo_location);
Result &input_image = get_input("Image");
GPU_texture_mipmap_mode(input_image.texture(), true, true);
GPU_texture_anisotropic_filter(input_image.texture(), true);
GPU_texture_extend_mode(input_image.texture(), GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
GPU_texture_extend_mode(input_image.texture(), GPU_SAMPLER_EXTEND_MODE_EXTEND);
input_image.bind_as_texture(shader, "input_tx");
plane_mask.bind_as_texture(shader, "mask_tx");
const Domain domain = compute_domain();
Result &output_image = get_result("Image");
output_image.allocate_texture(domain);
output_image.bind_as_image(shader, "output_img");
output_mask.allocate_texture(domain);
output_mask.bind_as_image(shader, "mask_img");
compute_dispatch_threads_at_least(shader, domain.size);
input_image.unbind_as_texture();
plane_mask.unbind_as_texture();
output_image.unbind_as_image();
output_mask.unbind_as_image();
GPU_uniformbuf_unbind(homography_matrices_buffer);
GPU_shader_unbind();
}
Result compute_plane_mask(const Array<float4x4> &homography_matrices,
GPUUniformBuf *homography_matrices_buffer)
{
GPUShader *shader = context().get_shader("compositor_plane_deform_motion_blur_mask");
GPU_shader_bind(shader);
GPU_shader_uniform_1i(shader, "number_of_motion_blur_samples", homography_matrices.size());
const int ubo_location = GPU_shader_get_ubo_binding(shader, "homography_matrices");
GPU_uniformbuf_bind(homography_matrices_buffer, ubo_location);
const Domain domain = compute_domain();
Result plane_mask = context().create_temporary_result(ResultType::Float);
plane_mask.allocate_texture(domain);
plane_mask.bind_as_image(shader, "mask_img");
compute_dispatch_threads_at_least(shader, domain.size);
plane_mask.unbind_as_image();
GPU_uniformbuf_unbind(homography_matrices_buffer);
GPU_shader_unbind();
GPU_uniformbuf_unbind(matrices_buffer);
GPU_uniformbuf_free(matrices_buffer);
return plane_mask;
}
Domain compute_domain() override

@ -1 +1 @@
Subproject commit 3a0fc9f3bae1f1a838ed9e2aac0648fdad291817
Subproject commit 15c20a4b5d40dfbb1f64c65d898ea22ab00b1f1a