diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 463590ba011..c30b2c4ed4a 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -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(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(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(this), out); } diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 2ad308a3d92..6fe0e854254 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -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. diff --git a/source/blender/compositor/nodes/COM_CornerPinNode.cc b/source/blender/compositor/nodes/COM_CornerPinNode.cc index 94b124c1419..9e9f8227763 100644 --- a/source/blender/compositor/nodes/COM_CornerPinNode.cc +++ b/source/blender/compositor/nodes/COM_CornerPinNode.cc @@ -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 diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc index 2583e881141..2f3d8a76a52 100644 --- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc +++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc @@ -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 diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc index d71030f182a..7be21ee796d 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc @@ -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); } } } diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc index 556c23cedd0..59121a478c9 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cc +++ b/source/blender/compositor/operations/COM_MapUVOperation.cc @@ -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; diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index 6a1c48beed2..e62eda1809a 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -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 /*inputs*/) { for (BuffersIterator 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 diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h index d995edc31a9..269a26261d6 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h @@ -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 inputs) override; - - private: - int get_jitter_samples_inside_count(int x, int y, MotionSample &sample_data); }; } // namespace blender::compositor diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt index da11d37809b..12313b895a4 100644 --- a/source/blender/compositor/realtime_compositor/CMakeLists.txt +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -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 diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform.glsl index 54e1b19b419..ced3b24691f 100644 --- a/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform.glsl +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform.glsl @@ -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); } diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform_mask.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform_mask.glsl new file mode 100644 index 00000000000..4762ab8c3c3 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform_mask.glsl @@ -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)); +} diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform_motion_blur.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform_motion_blur.glsl index 199f517c631..e4981d6dacd 100644 --- a/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform_motion_blur.glsl +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform_motion_blur.glsl @@ -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); } diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform_motion_blur_mask.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform_motion_blur_mask.glsl new file mode 100644 index 00000000000..9f41bf6aa88 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_plane_deform_motion_blur_mask.glsl @@ -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)); +} diff --git a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_plane_deform_info.hh b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_plane_deform_info.hh index 118c9aac1ab..445896c8b06 100644 --- a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_plane_deform_info.hh +++ b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_plane_deform_info.hh @@ -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); diff --git a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_plane_deform_motion_blur_info.hh b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_plane_deform_motion_blur_info.hh deleted file mode 100644 index 0f1c3ad377f..00000000000 --- a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_plane_deform_motion_blur_info.hh +++ /dev/null @@ -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); diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc index 4b36639cc18..609d3b40336 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc @@ -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(); diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc index c077ec292fe..3c6959646bc 100644 --- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc +++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc @@ -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 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 &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 &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 diff --git a/tests/data b/tests/data index 3a0fc9f3bae..15c20a4b5d4 160000 --- a/tests/data +++ b/tests/data @@ -1 +1 @@ -Subproject commit 3a0fc9f3bae1f1a838ed9e2aac0648fdad291817 +Subproject commit 15c20a4b5d40dfbb1f64c65d898ea22ab00b1f1a