Realtime Compositor: Rewrite Pixelate node #117243
@ -188,6 +188,7 @@ set(GLSL_SRC
|
||||
shaders/compositor_movie_distortion.glsl
|
||||
shaders/compositor_normalize.glsl
|
||||
shaders/compositor_parallel_reduction.glsl
|
||||
shaders/compositor_pixelate.glsl
|
||||
shaders/compositor_plane_deform.glsl
|
||||
shaders/compositor_plane_deform_motion_blur.glsl
|
||||
shaders/compositor_premultiply_alpha.glsl
|
||||
@ -311,6 +312,7 @@ set(SRC_SHADER_CREATE_INFOS
|
||||
shaders/infos/compositor_movie_distortion_info.hh
|
||||
shaders/infos/compositor_normalize_info.hh
|
||||
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
|
||||
|
@ -0,0 +1,24 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* 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);
|
||||
|
||||
ivec2 start = (texel / ivec2(pixel_size)) * ivec2(pixel_size);
|
||||
ivec2 end = min(start + ivec2(pixel_size), texture_size(input_tx));
|
||||
|
||||
vec4 accumulated_color = vec4(0.0);
|
||||
for (int y = start.y; y < end.y; y++) {
|
||||
for (int x = start.x; x < end.x; x++) {
|
||||
accumulated_color += texture_load_unbound(input_tx, ivec2(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
ivec2 size = end - start;
|
||||
int count = size.x * size.y;
|
||||
imageStore(output_img, texel, accumulated_color / count);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_pixelate)
|
||||
.local_group_size(16, 16)
|
||||
.push_constant(Type::INT, "pixel_size")
|
||||
.sampler(0, ImageType::FLOAT_2D, "input_tx")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.compute_source("compositor_pixelate.glsl")
|
||||
.do_static_compilation(true);
|
@ -28,6 +28,12 @@ vec4 texture_load(sampler2D sampler_2d, ivec2 texel)
|
||||
return texelFetch(sampler_2d, clamp(texel, ivec2(0), texture_bounds), 0);
|
||||
}
|
||||
|
||||
/* A shorthand for 2D texelFetch with zero LOD. */
|
||||
vec4 texture_load_unbound(sampler2D sampler_2d, ivec2 texel)
|
||||
{
|
||||
return texelFetch(sampler_2d, texel, 0);
|
||||
}
|
||||
|
||||
/* A shorthand for 2D texelFetch with zero LOD and a fallback value for out-of-bound access. */
|
||||
vec4 texture_load(sampler2D sampler_2d, ivec2 texel, vec4 fallback)
|
||||
{
|
||||
|
@ -6,9 +6,10 @@
|
||||
* \ingroup cmpnodes
|
||||
*/
|
||||
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "GPU_shader.h"
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
@ -21,7 +22,7 @@ namespace blender::nodes::node_composite_pixelate_cc {
|
||||
|
||||
static void cmp_node_pixelate_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>("Color");
|
||||
b.add_input<decl::Color>("Color").compositor_domain_priority(0);
|
||||
b.add_output<decl::Color>("Color");
|
||||
}
|
||||
|
||||
@ -43,36 +44,34 @@ class PixelateOperation : public NodeOperation {
|
||||
|
||||
void execute() override
|
||||
{
|
||||
/* It might seems strange that the input is passed through without any processing, but note
|
||||
* that the actual processing happens inside the domain realization input processor of the
|
||||
* input. Indeed, the pixelate node merely realizes its input on a smaller-sized domain that
|
||||
* matches its apparent size, that is, its size after the domain transformation. The pixelate
|
||||
* node has no effect if the input is scaled-up. See the compute_domain method for more
|
||||
* information. */
|
||||
Result &result = get_result("Color");
|
||||
get_input("Color").pass_through(result);
|
||||
Result &input_image = get_input("Color");
|
||||
Result &output_image = get_result("Color");
|
||||
if (input_image.is_single_value()) {
|
||||
input_image.pass_through(output_image);
|
||||
return;
|
||||
}
|
||||
|
||||
result.get_realization_options().interpolation = Interpolation::Nearest;
|
||||
GPUShader *shader = context().get_shader("compositor_pixelate");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
GPU_shader_uniform_1i(shader, "pixel_size", get_pixel_size());
|
||||
|
||||
input_image.bind_as_texture(shader, "input_tx");
|
||||
|
||||
const Domain domain = compute_domain();
|
||||
output_image.allocate_texture(domain);
|
||||
output_image.bind_as_image(shader, "output_img");
|
||||
|
||||
compute_dispatch_threads_at_least(shader, domain.size);
|
||||
|
||||
GPU_shader_unbind();
|
||||
output_image.unbind_as_image();
|
||||
input_image.unbind_as_texture();
|
||||
}
|
||||
|
||||
/* Compute a smaller-sized domain that matches the apparent size of the input while having a unit
|
||||
* scale transformation, see the execute method for more information. */
|
||||
Domain compute_domain() override
|
||||
float get_pixel_size()
|
||||
{
|
||||
Domain domain = get_input("Color").domain();
|
||||
|
||||
/* Get the scaling component of the domain transformation, but make sure it doesn't exceed 1,
|
||||
* because pixelation should only happen if the input is scaled down. */
|
||||
const float2 scale = math::min(float2(1.0f), math::to_scale(float2x2(domain.transformation)));
|
||||
|
||||
/* Multiply the size of the domain by its scale to match its apparent size, but make sure it is
|
||||
* at least 1 pixel in both axis. */
|
||||
domain.size = math::max(int2(float2(domain.size) * scale), int2(1));
|
||||
|
||||
/* Reset the scale of the transformation by transforming it with the inverse of the scale. */
|
||||
domain.transformation *= math::from_scale<float3x3>(math::safe_divide(float2(1.0f), scale));
|
||||
|
||||
return domain;
|
||||
return bnode().custom1;
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user