Realtime Compositor: Implement Sun Beams node

This patch implements the Sun Beams node for the realtime compositor.
The implementation is not identical to the existing CPU implementation,
but is very close. The new implementation is a higher quality one and
resolves some of the artefacts in the existing implementation. This is
achieved by doing a simple line integration toward the source pixel,
while having a number of integration steps that is invariant of the
angle to the source.

Pull Request: https://projects.blender.org/blender/blender/pulls/108718
This commit is contained in:
Omar Emara
2023-06-08 15:36:35 +02:00
committed by Omar Emara
parent f6249cc93b
commit 9ddc8504fa
4 changed files with 77 additions and 5 deletions

View File

@@ -152,6 +152,7 @@ set(GLSL_SRC
shaders/compositor_smaa_edge_detection.glsl
shaders/compositor_smaa_neighborhood_blending.glsl
shaders/compositor_split_viewer.glsl
shaders/compositor_sun_beams.glsl
shaders/compositor_symmetric_blur.glsl
shaders/compositor_symmetric_blur_variable_size.glsl
shaders/compositor_symmetric_separable_blur.glsl
@@ -252,6 +253,7 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/compositor_screen_lens_distortion_info.hh
shaders/infos/compositor_smaa_info.hh
shaders/infos/compositor_split_viewer_info.hh
shaders/infos/compositor_sun_beams_info.hh
shaders/infos/compositor_symmetric_blur_info.hh
shaders/infos/compositor_symmetric_blur_variable_size_info.hh
shaders/infos/compositor_symmetric_separable_blur_info.hh

View File

@@ -0,0 +1,32 @@
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
ivec2 input_size = texture_size(input_tx);
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(input_size);
vec2 vector_to_source = source - coordinates;
float distance_to_source = length(vector_to_source);
vec2 direction_to_source = vector_to_source / distance_to_source;
/* We integrate from the current pixel to the source pixel, but up until the user specified
* maximum ray length. The number of integration steps is roughly equivalent to the number of
* pixels along the integration path. Assume a minimum number of steps of 1 to avoid zero
* division handling and return source pixels as is. */
float integration_length = min(distance_to_source, max_ray_length);
float integration_length_in_pixels = length(input_size) * integration_length;
int steps = max(1, int(integration_length_in_pixels));
vec2 step_vector = (direction_to_source * integration_length) / steps;
vec4 accumulated_color = vec4(0.0);
for (int i = 0; i < steps; i++) {
/* Attenuate the contributions of pixels that are further away from the source using a
* quadratic falloff. */
float weight = pow(1.0f - i / integration_length_in_pixels, 2.0);
accumulated_color += texture(input_tx, coordinates + i * step_vector) * weight;
}
imageStore(output_img, texel, accumulated_color / steps);
}

View File

@@ -0,0 +1,14 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_sun_beams)
.local_group_size(16, 16)
.push_constant(Type::VEC2, "source")
.push_constant(Type::FLOAT, "max_ray_length")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.compute_source("compositor_sun_beams.glsl")
.do_static_compilation(true);

View File

@@ -9,15 +9,22 @@
#include "UI_interface.h"
#include "UI_resources.h"
#include "GPU_shader.h"
#include "COM_node_operation.hh"
#include "COM_utilities.hh"
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_sunbeams_cc {
NODE_STORAGE_FUNCS(NodeSunBeams)
static void cmp_node_sunbeams_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Color>("Image")
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_domain_priority(0);
b.add_output<decl::Color>("Image");
}
@@ -49,8 +56,27 @@ class SunBeamsOperation : public NodeOperation {
void execute() override
{
get_input("Image").pass_through(get_result("Image"));
context().set_info_message("Viewport compositor setup not fully supported");
GPUShader *shader = shader_manager().get("compositor_sun_beams");
GPU_shader_bind(shader);
GPU_shader_uniform_2fv(shader, "source", node_storage(bnode()).source);
GPU_shader_uniform_1f(shader, "max_ray_length", node_storage(bnode()).ray_length);
const Result &input_image = get_input("Image");
GPU_texture_filter_mode(input_image.texture(), true);
GPU_texture_extend_mode(input_image.texture(), GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
input_image.bind_as_texture(shader, "input_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");
compute_dispatch_threads_at_least(shader, domain.size);
GPU_shader_unbind();
output_image.unbind_as_image();
input_image.unbind_as_texture();
}
};
@@ -74,8 +100,6 @@ void register_node_type_cmp_sunbeams()
node_type_storage(
&ntype, "NodeSunBeams", node_free_standard_storage, node_copy_standard_storage);
ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.realtime_compositor_unsupported_message = N_(
"Node not supported in the Viewport compositor");
nodeRegisterType(&ntype);
}