Blender V4.5
node_composite_sunbeams.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2014 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_math_base.hh"
10#include "BLI_math_vector.hh"
12
13#include "UI_interface.hh"
14#include "UI_resources.hh"
15
16#include "GPU_shader.hh"
17
18#include "COM_node_operation.hh"
19#include "COM_utilities.hh"
20
22
24
26{
27 b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
28 b.add_input<decl::Vector>("Source")
29 .subtype(PROP_FACTOR)
30 .dimensions(2)
31 .default_value({0.5f, 0.5f})
32 .min(0.0f)
33 .max(1.0f)
34 .description(
35 "The position of the source of the rays in normalized coordinates. 0 means lower left "
36 "corner and 1 means upper right corner")
37 .compositor_expects_single_value();
38 b.add_input<decl::Float>("Length")
39 .subtype(PROP_FACTOR)
40 .min(0.0f)
41 .max(1.0f)
42 .default_value(0.2f)
43 .description(
44 "The length of rays relative to the size of the image. 0 means no rays and 1 means the "
45 "rays cover the full extent of the image")
46 .compositor_expects_single_value();
47
48 b.add_output<decl::Color>("Image");
49}
50
51using namespace blender::compositor;
52
54 public:
56
57 void execute() override
58 {
59 const Result &input_image = this->get_input("Image");
60
61 const int2 input_size = input_image.domain().size;
62 const int max_steps = int(this->get_length() * math::length(input_size));
63 if (max_steps == 0) {
64 Result &output_image = this->get_result("Image");
65 output_image.share_data(input_image);
66 return;
67 }
68
69 if (this->context().use_gpu()) {
70 this->execute_gpu(max_steps);
71 }
72 else {
73 this->execute_cpu(max_steps);
74 }
75 }
76
77 void execute_gpu(const int max_steps)
78 {
79 GPUShader *shader = context().get_shader("compositor_sun_beams");
80 GPU_shader_bind(shader);
81
82 GPU_shader_uniform_2fv(shader, "source", this->get_source());
83 GPU_shader_uniform_1i(shader, "max_steps", max_steps);
84
85 Result &input_image = get_input("Image");
86 GPU_texture_filter_mode(input_image, true);
88 input_image.bind_as_texture(shader, "input_tx");
89
90 Result &output_image = get_result("Image");
91 const Domain domain = compute_domain();
92 output_image.allocate_texture(domain);
93 output_image.bind_as_image(shader, "output_img");
94
96
98 output_image.unbind_as_image();
99 input_image.unbind_as_texture();
100 }
101
102 void execute_cpu(const int max_steps)
103 {
104 const float2 source = this->get_source();
105
106 Result &input = get_input("Image");
107
108 const Domain domain = compute_domain();
109 Result &output = get_result("Image");
110 output.allocate_texture(domain);
111
112 const int2 input_size = domain.size;
113 parallel_for(input_size, [&](const int2 texel) {
114 /* The number of steps is the distance in pixels from the source to the current texel. With
115 * at least a single step and at most the user specified maximum ray length, which is
116 * proportional to the diagonal pixel count. */
117 float unbounded_steps = math::max(
118 1.0f, math::distance(float2(texel), source * float2(input_size)));
119 int steps = math::min(max_steps, int(unbounded_steps));
120
121 /* We integrate from the current pixel to the source pixel, so compute the start coordinates
122 * and step vector in the direction to source. Notice that the step vector is still computed
123 * from the unbounded steps, such that the total integration length becomes limited by the
124 * bounded steps, and thus by the maximum ray length. */
125 float2 coordinates = (float2(texel) + float2(0.5f)) / float2(input_size);
126 float2 vector_to_source = source - coordinates;
127 float2 step_vector = vector_to_source / unbounded_steps;
128
129 float accumulated_weight = 0.0f;
130 float4 accumulated_color = float4(0.0f);
131 for (int i = 0; i <= steps; i++) {
132 float2 position = coordinates + i * step_vector;
133
134 /* We are already past the image boundaries, and any future steps are also past the image
135 * boundaries, so break. */
136 if (position.x < 0.0f || position.y < 0.0f || position.x > 1.0f || position.y > 1.0f) {
137 break;
138 }
139
140 float4 sample_color = input.sample_bilinear_zero(position);
141
142 /* Attenuate the contributions of pixels that are further away from the source using a
143 * quadratic falloff. */
144 float weight = math::square(1.0f - i / float(steps));
145
146 accumulated_weight += weight;
147 accumulated_color += sample_color * weight;
148 }
149
150 accumulated_color /= accumulated_weight != 0.0f ? accumulated_weight : 1.0f;
151 output.store_pixel(texel, accumulated_color);
152 });
153 }
154
156 {
157 return this->get_input("Source").get_single_value_default(float3(0.5f, 0.5f, 0.0f)).xy();
158 }
159
161 {
162 return math::clamp(this->get_input("Length").get_single_value_default(0.2f), 0.0f, 1.0f);
163 }
164};
165
167{
168 return new SunBeamsOperation(context, node);
169}
170
171} // namespace blender::nodes::node_composite_sunbeams_cc
172
174{
176
177 static blender::bke::bNodeType ntype;
178
179 cmp_node_type_base(&ntype, "CompositorNodeSunBeams", CMP_NODE_SUNBEAMS);
180 ntype.ui_name = "Sun Beams";
181 ntype.ui_description = "Create sun beams based on image brightness";
182 ntype.enum_name_legacy = "SUNBEAMS";
184 ntype.declare = file_ns::cmp_node_sunbeams_declare;
185 ntype.get_compositor_operation = file_ns::get_compositor_operation;
186
188}
#define NODE_CLASS_OP_FILTER
Definition BKE_node.hh:437
#define CMP_NODE_SUNBEAMS
void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2])
void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value)
void GPU_shader_bind(GPUShader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_unbind()
void GPU_texture_extend_mode(GPUTexture *texture, GPUSamplerExtendMode extend_mode)
@ GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER
void GPU_texture_filter_mode(GPUTexture *texture, bool use_filter)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_FACTOR
Definition RNA_types.hh:239
GPUShader * get_shader(const char *info_name, ResultPrecision precision)
NodeOperation(Context &context, DNode node)
Result & get_result(StringRef identifier)
Definition operation.cc:39
Result & get_input(StringRef identifier) const
Definition operation.cc:138
virtual Domain compute_domain()
Definition operation.cc:56
void share_data(const Result &source)
Definition result.cc:401
T get_single_value_default(const T &default_value) const
void allocate_texture(Domain domain, bool from_pool=true)
Definition result.cc:309
void unbind_as_texture() const
Definition result.cc:389
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:365
const Domain & domain() const
void bind_as_image(GPUShader *shader, const char *image_name, bool read=false) const
Definition result.cc:376
void unbind_as_image() const
Definition result.cc:395
#define input
#define output
void node_register_type(bNodeType &ntype)
Definition node.cc:2748
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:170
void parallel_for(const int2 range, const Function &function)
T clamp(const T &a, const T &min, const T &max)
T distance(const T &a, const T &b)
T length(const VecBase< T, Size > &a)
T min(const T &a, const T &b)
T square(const T &a)
T max(const T &a, const T &b)
static void cmp_node_sunbeams_declare(NodeDeclarationBuilder &b)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
static void register_node_type_cmp_sunbeams()
void cmp_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
static const int steps
#define min(a, b)
Definition sort.cc:36
Defines a node type.
Definition BKE_node.hh:226
std::string ui_description
Definition BKE_node.hh:232
NodeGetCompositorOperationFunction get_compositor_operation
Definition BKE_node.hh:336
const char * enum_name_legacy
Definition BKE_node.hh:235
NodeDeclareFunction declare
Definition BKE_node.hh:355
i
Definition text_draw.cc:230