16#if defined(WITH_FFTW3)
48#define MAX_GLARE_ITERATIONS 5
56 b.use_custom_socket_order();
61 .
description(
"The extracted highlights from which the glare was generated");
67 layout->
label(
RPT_(
"Disabled, built without FFTW"), ICON_ERROR);
77 .compositor_domain_priority(0);
84 "The brightness level at which pixels are considered part of the highlights that "
86 .compositor_expects_single_value();
92 .description(
"The smoothness of the extracted highlights")
93 .compositor_expects_single_value();
96 highlights_panel.
add_panel(
"Clamp").default_closed(
true);
100 .description(
"Clamp bright highlights")
101 .compositor_expects_single_value();
106 "Clamp bright highlights such that their brightness are not larger than this value")
107 .compositor_expects_single_value();
115 .description(
"Adjusts the brightness of the glare")
116 .compositor_expects_single_value();
122 .description(
"Adjusts the saturation of the glare")
123 .compositor_expects_single_value();
126 .description(
"Tints the glare. Consider desaturating the glare to more accurate tinting")
127 .compositor_expects_single_value();
136 "The size of the glare relative to the image. 1 means the glare covers the entire "
137 "image, 0.5 means the glare covers half the image, and so on")
138 .compositor_expects_single_value();
143 .description(
"The number of streaks")
144 .compositor_expects_single_value();
148 .description(
"The angle that the first streak makes with the horizontal axis")
149 .compositor_expects_single_value();
155 "The number of ghosts for Ghost glare or the quality and spread of Glare for Streaks "
157 .compositor_expects_single_value();
163 .description(
"Streak fade-out factor")
164 .compositor_expects_single_value();
170 .description(
"Modulates colors of streaks and ghosts for a spectral dispersion effect")
171 .compositor_expects_single_value();
174 .description(
"Align the star diagonally")
175 .compositor_expects_single_value();
207 *color_modulation_input,
228 bNode &node =
params.add_node(
"CompositorNodeGlare");
229 node_storage(node).type = this->type;
230 params.update_and_connect_available_socket(node,
"Image");
237 if (!
params.node_tree().typeinfo->validate_link(from_socket_type,
SOCK_RGBA)) {
305 if (this->
context().use_gpu()) {
335 return highlights_result;
348 output.allocate_texture(highlights_size);
366 hsva.z, threshold, max_brightness, highlights_smoothness);
369 hsva.z = clamped_brightness - threshold;
384 return std::numeric_limits<float>::max();
400 float smooth_min(
const float a,
const float b,
const float smoothness)
402 if (smoothness == 0.0f) {
406 return math::min(a,
b) - h * h * smoothness * (1.0f / 4.0f);
409 float smooth_max(
const float a,
const float b,
const float smoothness)
417 const float min_value,
418 const float max_value,
419 const float min_smoothness,
420 const float max_smoothness)
423 max_value, this->
smooth_max(min_value,
x, min_smoothness), max_smoothness);
439 const float min_value,
440 const float max_value,
441 const float smoothness)
443 const float range_distance =
math::distance(min_value, max_value);
444 const float distance_from_min_to_zero =
math::distance(min_value, 0.0f);
445 const float distance_from_max_to_zero =
math::distance(max_value, 0.0f);
447 const float max_safe_smoothness_for_min =
math::min(distance_from_min_to_zero, range_distance);
448 const float max_safe_smoothness_for_max =
math::min(distance_from_max_to_zero, range_distance);
450 const float min_smoothness =
math::min(smoothness, max_safe_smoothness_for_min);
451 const float max_smoothness =
math::min(smoothness, max_safe_smoothness_for_max);
453 return this->
smooth_clamp(x, min_value, max_value, min_smoothness, max_smoothness);
458 return math::max(0.0f, this->
get_input(
"Highlights Threshold").get_single_value_default(1.0f));
464 this->
get_input(
"Highlights Smoothness").get_single_value_default(0.1f));
474 return math::max(0.0f, this->
get_input(
"Maximum Highlights").get_single_value_default(0.0f));
481 if (this->
context().use_gpu()) {
491 GPUShader *shader = this->
context().
get_shader(
"compositor_glare_write_highlights_output");
501 output.bind_as_image(shader,
"output_img");
533 switch (node_storage(
bnode()).type) {
574 horizontal_pass_result);
575 horizontal_pass_result.
release();
576 return vertical_pass_result;
580 const Result &horizontal_pass_result)
582 if (this->
context().use_gpu()) {
589 const Result &horizontal_pass_result)
599 GPUShader *shader =
context().
get_shader(
"compositor_glare_simple_star_vertical_pass");
610 const int width =
size.x;
617 return vertical_pass_result;
621 const Result &horizontal_pass_result)
633 const float fade_factor = this->
get_fade();
636 const int width =
size.x;
643 for (
int i = 0;
i < iterations;
i++) {
648 for (
int y = 0;
y < height;
y++) {
654 float4 neighbor_average = (previous_output + next_input) / 2.0f;
656 output.store_pixel(texel, causal_output);
663 for (
int y = height - 1;
y >= 0;
y--) {
669 float4 neighbor_average = (previous_output + next_input) / 2.0f;
671 current_input, neighbor_average, fade_factor);
672 output.store_pixel(texel, non_causal_output);
678 for (
int y = 0;
y < height;
y++) {
682 float4 combined = horizontal + vertical;
683 output.store_pixel(texel,
float4(combined.xyz(), 1.0f));
693 if (this->
context().use_gpu()) {
709 GPUShader *shader =
context().
get_shader(
"compositor_glare_simple_star_horizontal_pass");
715 horizontal_pass_result.
bind_as_image(shader,
"horizontal_img");
723 return horizontal_pass_result;
738 const float fade_factor = this->
get_fade();
741 const int width =
size.x;
743 for (const int64_t y : sub_range) {
746 for (int i = 0; i < iterations; i++) {
751 for (int x = 0; x < width; x++) {
752 int2 texel = int2(x, y);
753 float4 previous_output = horizontal_pass_result.load_pixel_zero<float4>(texel -
755 float4 current_input = horizontal_pass_result.load_pixel<float4>(texel);
756 float4 next_input = horizontal_pass_result.load_pixel_zero<float4>(texel + int2(i, 0));
758 float4 neighbor_average = (previous_output + next_input) / 2.0f;
759 float4 causal_output = math::interpolate(current_input, neighbor_average, fade_factor);
760 horizontal_pass_result.store_pixel(texel, causal_output);
767 for (int x = width - 1; x >= 0; x--) {
768 int2 texel = int2(x, y);
769 float4 previous_output = horizontal_pass_result.load_pixel_zero<float4>(texel +
771 float4 current_input = horizontal_pass_result.load_pixel<float4>(texel);
772 float4 next_input = horizontal_pass_result.load_pixel_zero<float4>(texel - int2(i, 0));
774 float4 neighbor_average = (previous_output + next_input) / 2.0f;
775 float4 non_causal_output = math::interpolate(
776 current_input, neighbor_average, fade_factor);
777 horizontal_pass_result.store_pixel(texel, non_causal_output);
783 return horizontal_pass_result;
790 highlights, diagonal_pass_result);
791 diagonal_pass_result.
release();
792 return anti_diagonal_pass_result;
796 const Result &diagonal_pass_result)
798 if (this->
context().use_gpu()) {
805 const Result &diagonal_pass_result)
815 GPUShader *shader =
context().get_shader(
"compositor_glare_simple_star_anti_diagonal_pass");
823 anti_diagonal_pass_result.
bind_as_image(shader,
"anti_diagonal_img");
832 return anti_diagonal_pass_result;
836 const Result &diagonal_pass_result)
848 const float fade_factor = this->
get_fade();
853 for (
const int64_t index : sub_range) {
857 int2 end = start + (anti_diagonal_length - 1) * direction;
861 for (
int i = 0;
i < iterations;
i++) {
866 for (
int j = 0; j < anti_diagonal_length; j++) {
867 int2 texel = start + j * direction;
872 float4 neighbor_average = (previous_output + next_input) / 2.0f;
874 output.store_pixel(texel, causal_output);
881 for (
int j = 0; j < anti_diagonal_length; j++) {
882 int2 texel = end - j * direction;
887 float4 neighbor_average = (previous_output + next_input) / 2.0f;
889 current_input, neighbor_average, fade_factor);
890 output.store_pixel(texel, non_causal_output);
896 for (
int j = 0; j < anti_diagonal_length; j++) {
897 int2 texel = start + j * direction;
900 float4 combined = horizontal + vertical;
901 output.store_pixel(texel,
float4(combined.xyz(), 1.0f));
911 if (this->
context().use_gpu()) {
927 GPUShader *shader =
context().get_shader(
"compositor_glare_simple_star_diagonal_pass");
941 return diagonal_pass_result;
956 const float fade_factor = this->
get_fade();
961 for (
const int64_t index : sub_range) {
965 int2 end = start + (diagonal_length - 1) * direction;
969 for (
int i = 0;
i < iterations;
i++) {
974 for (
int j = 0; j < diagonal_length; j++) {
975 int2 texel = start + j * direction;
982 float4 neighbor_average = (previous_output + next_input) / 2.0f;
984 diagonal_pass_result.
store_pixel(texel, causal_output);
991 for (
int j = 0; j < diagonal_length; j++) {
992 int2 texel = end - j * direction;
999 float4 neighbor_average = (previous_output + next_input) / 2.0f;
1001 current_input, neighbor_average, fade_factor);
1002 diagonal_pass_result.
store_pixel(texel, non_causal_output);
1008 return diagonal_pass_result;
1013 return this->
get_input(
"Diagonal Star").get_single_value_default(
true);
1026 if (this->
context().use_gpu()) {
1045 return accumulated_streaks_result;
1050 if (this->
context().use_gpu()) {
1058 GPUShader *shader =
context().get_shader(
"compositor_glare_streaks_filter");
1075 for (
const int iteration : iterations_range) {
1079 const float2 streak_vector = streak_direction * iteration_magnitude;
1089 output_streak_result.
bind_as_image(shader,
"output_streak_img");
1099 if (iteration != iterations_range.
last()) {
1105 input_streak_result.
release();
1108 return output_streak_result;
1128 for (
const int iteration : iterations_range) {
1132 const float2 streak_vector = streak_direction * iteration_magnitude;
1144 neighbors[0] =
input.sample_bilinear_zero(coordinates +
vector);
1145 neighbors[1] =
input.sample_bilinear_zero(coordinates +
vector * 2.0f);
1146 neighbors[2] =
input.sample_bilinear_zero(coordinates +
vector * 3.0f);
1151 neighbors[0] *=
float4(1.0f, color_modulator, color_modulator, 1.0f);
1152 neighbors[1] *=
float4(color_modulator, color_modulator, 1.0f, 1.0f);
1153 neighbors[2] *=
float4(color_modulator, 1.0f, color_modulator, 1.0f);
1158 for (
int i = 0;
i < 3;
i++) {
1159 weighted_neighbors_sum += fade_factors[
i] * neighbors[
i];
1165 float4 center_color =
input.sample_bilinear_zero(coordinates);
1166 float4 output_color = (center_color + weighted_neighbors_sum) / 2.0f;
1167 output.store_pixel(texel, output_color);
1173 if (iteration != iterations_range.
last()) {
1186 if (this->
context().use_gpu()) {
1196 GPUShader *shader = this->
context().get_shader(
"compositor_glare_streaks_accumulate");
1203 accumulated_streaks_result.
bind_as_image(shader,
"accumulated_streaks_img",
true);
1220 float4 combined_streaks = current_accumulated_streaks + attenuated_streak;
1244 const float angle = start_angle + (float(streak_index) / number_of_streaks) * (
M_PI * 2.0f);
1275 const float fade_factor = std::pow(this->
get_fade(), iteration_magnitude);
1276 return float3(fade_factor, std::pow(fade_factor, 2.0f), std::pow(fade_factor, 3.0f));
1287 return std::pow(4.0f, iteration);
1297 return this->
get_input(
"Streaks Angle").get_single_value_default(0.0f);
1308 if (this->
context().use_gpu()) {
1316 return accumulated_ghosts_result;
1321 GPUShader *shader =
context().get_shader(
"compositor_glare_ghost_accumulate");
1328 color_modulators.size(),
1329 (
const float(*)[4])color_modulators.data());
1348 for (
const int i : iterations_range) {
1353 accumulated_ghosts_result.
bind_as_image(shader,
"accumulated_ghost_img",
true);
1363 if (
i != iterations_range.
last()) {
1398 for (
const int i : iterations_range) {
1409 for (
int i = 0;
i < 4;
i++) {
1410 float scale = scales[
i];
1411 float4 color_modulator = color_modulators[
i];
1415 float2 scaled_coordinates = (coordinates - 0.5f) * scale + 0.5f;
1422 float attenuator =
math::max(0.0f, 1.0f - distance_to_center *
math::abs(scale)) / 4.0f;
1425 float4 multiplier = attenuator * color_modulator;
1426 accumulated_ghost +=
input.sample_bilinear_zero(scaled_coordinates) * multiplier;
1430 float4 combined_ghost = current_accumulated_ghost + accumulated_ghost;
1431 accumulated_ghosts_result.
store_pixel(texel,
float4(combined_ghost.xyz(), 1.0f));
1437 if (
i != iterations_range.last()) {
1469 if (this->
context().use_gpu()) {
1479 return base_ghost_result;
1483 const Result &big_ghost_result,
1484 Result &base_ghost_result)
1486 GPUShader *shader =
context().get_shader(
"compositor_glare_ghost_base");
1498 base_ghost_result.
bind_as_image(shader,
"combined_ghost_img");
1509 const Result &big_ghost_result,
1526 float small_ghost_scale = 2.13f;
1527 float big_ghost_scale = -0.97f;
1532 float2 small_ghost_coordinates = (coordinates - 0.5f) * small_ghost_scale + 0.5f;
1533 float2 big_ghost_coordinates = (coordinates - 0.5f) * big_ghost_scale + 0.5f;
1539 float small_ghost_attenuator =
math::max(0.0f,
1540 1.0f - distance_to_center * small_ghost_scale);
1542 0.0f, 1.0f - distance_to_center *
math::abs(big_ghost_scale));
1545 small_ghost_attenuator;
1547 big_ghost_attenuator;
1549 combined_ghost.
store_pixel(texel, small_ghost + big_ghost);
1563 std::array<float4, 4> color_modulators;
1564 color_modulators[0] =
float4(1.0f);
1565 color_modulators[1] =
float4(1.0f, color_modulation_factor, color_modulation_factor, 1.0f);
1566 color_modulators[2] =
float4(color_modulation_factor, color_modulation_factor, 1.0f, 1.0f);
1567 color_modulators[3] =
float4(color_modulation_factor, 1.0f, color_modulation_factor, 1.0f);
1569 return color_modulators;
1589 std::array<float, 4> scales;
1592 const int global_i = iteration * 4 +
i;
1596 scales[
i] = 2.1f * (1.0f - progression);
1601 scales[
i] = -0.99f / scales[
i];
1667 if (chain_length < 2) {
1670 if (this->
context().use_gpu()) {
1675 bloom_result.store_pixel(texel, highlights.load_pixel<float4>(texel));
1678 return bloom_result;
1684 const IndexRange upsample_passes_range(chain_length - 1);
1686 for (
const int i : upsample_passes_range) {
1689 if (this->
context().use_gpu()) {
1698 return downsample_chain[0];
1703 GPUShader *shader =
context().get_shader(
"compositor_glare_bloom_upsample");
1707 input.bind_as_texture(shader,
"input_tx");
1709 output.bind_as_image(shader,
"output_img",
true);
1713 input.unbind_as_texture();
1714 output.unbind_as_image();
1743 upsampled += (4.0f / 16.0f) *
input.sample_bilinear_extended(coordinates);
1744 upsampled += (2.0f / 16.0f) *
1745 input.sample_bilinear_extended(coordinates + pixel_size *
float2(-1.0f, 0.0f));
1746 upsampled += (2.0f / 16.0f) *
1747 input.sample_bilinear_extended(coordinates + pixel_size *
float2(0.0f, 1.0f));
1748 upsampled += (2.0f / 16.0f) *
1749 input.sample_bilinear_extended(coordinates + pixel_size *
float2(1.0f, 0.0f));
1750 upsampled += (2.0f / 16.0f) *
1751 input.sample_bilinear_extended(coordinates + pixel_size *
float2(0.0f, -1.0f));
1752 upsampled += (1.0f / 16.0f) *
1753 input.sample_bilinear_extended(coordinates + pixel_size *
float2(-1.0f, -1.0f));
1754 upsampled += (1.0f / 16.0f) *
1755 input.sample_bilinear_extended(coordinates + pixel_size *
float2(-1.0f, 1.0f));
1756 upsampled += (1.0f / 16.0f) *
1757 input.sample_bilinear_extended(coordinates + pixel_size *
float2(1.0f, -1.0f));
1758 upsampled += (1.0f / 16.0f) *
1759 input.sample_bilinear_extended(coordinates + pixel_size *
float2(1.0f, 1.0f));
1775 Array<Result> downsample_chain(chain_length, downsampled_result);
1779 Result &base_layer = downsample_chain[0];
1781 if (this->
context().use_gpu()) {
1786 base_layer.store_pixel(texel, highlights.load_pixel<float4>(texel));
1792 const IndexRange downsample_passes_range(chain_length - 1);
1794 for (
const int i : downsample_passes_range) {
1799 const bool use_karis_average =
i == downsample_passes_range.
first();
1800 if (this->
context().use_gpu()) {
1802 downsample_chain[
i], downsample_chain[
i + 1], use_karis_average);
1805 if (use_karis_average) {
1814 return downsample_chain;
1819 const bool use_karis_average)
1821 GPUShader *shader =
context().get_shader(
1822 use_karis_average ?
"compositor_glare_bloom_downsample_karis_average" :
1823 "compositor_glare_bloom_downsample_simple_average");
1827 input.bind_as_texture(shader,
"input_tx");
1830 output.bind_as_image(shader,
"output_img");
1834 input.unbind_as_texture();
1835 output.unbind_as_image();
1839 template<
bool UseKarisAverage>
1867 float4 center =
input.sample_bilinear_extended(coordinates);
1868 float4 upper_left_near =
input.sample_bilinear_extended(coordinates +
1869 pixel_size *
float2(-1.0f, 1.0f));
1870 float4 upper_right_near =
input.sample_bilinear_extended(coordinates +
1871 pixel_size *
float2(1.0f, 1.0f));
1872 float4 lower_left_near =
input.sample_bilinear_extended(coordinates +
1873 pixel_size *
float2(-1.0f, -1.0f));
1874 float4 lower_right_near =
input.sample_bilinear_extended(coordinates +
1875 pixel_size *
float2(1.0f, -1.0f));
1876 float4 left_far =
input.sample_bilinear_extended(coordinates +
1877 pixel_size *
float2(-2.0f, 0.0f));
1878 float4 right_far =
input.sample_bilinear_extended(coordinates +
1879 pixel_size *
float2(2.0f, 0.0f));
1880 float4 upper_far =
input.sample_bilinear_extended(coordinates +
1881 pixel_size *
float2(0.0f, 2.0f));
1882 float4 lower_far =
input.sample_bilinear_extended(coordinates +
1883 pixel_size *
float2(0.0f, -2.0f));
1884 float4 upper_left_far =
input.sample_bilinear_extended(coordinates +
1885 pixel_size *
float2(-2.0f, 2.0f));
1886 float4 upper_right_far =
input.sample_bilinear_extended(coordinates +
1887 pixel_size *
float2(2.0f, 2.0f));
1888 float4 lower_left_far =
input.sample_bilinear_extended(coordinates +
1889 pixel_size *
float2(-2.0f, -2.0f));
1890 float4 lower_right_far =
input.sample_bilinear_extended(coordinates +
1891 pixel_size *
float2(2.0f, -2.0f));
1894 if constexpr (!UseKarisAverage) {
1903 result = (4.0f / 32.0f) * center +
1905 (upper_left_near + upper_right_near + lower_left_near + lower_right_near) +
1906 (2.0f / 32.0f) * (left_far + right_far + upper_far + lower_far) +
1908 (upper_left_far + upper_right_far + lower_left_far + lower_right_far);
1918 upper_left_near, upper_right_near, lower_right_near, lower_left_near);
1920 upper_left_far, upper_far, center, left_far);
1922 upper_far, upper_right_far, right_far, center);
1924 center, right_far, lower_right_far, lower_far);
1926 left_far, center, lower_far, lower_left_far);
1933 result = (4.0f / 8.0f) * center_weighted_sum +
1934 (1.0f / 8.0f) * (upper_left_weighted_sum + upper_right_weighted_sum +
1935 lower_left_weighted_sum + lower_right_weighted_sum);
1957 float4 weights = 1.0f / (brightness + 1.0f);
1958 return (color1 * weights.x + color2 * weights.y + color3 * weights.z + color4 * weights.w) *
1972 const float scaled_dimension = smaller_dimension * this->
get_size();
1973 return int(std::log2(
math::max(1.0f, scaled_dimension)));
1982#if defined(WITH_FFTW3)
1989 const int needed_padding_amount = kernel_size / 2;
1991 const int2 needed_spatial_size = image_size + needed_padding_amount;
1998 const int2 frequency_size =
int2(spatial_size.x / 2 + 1, spatial_size.y);
2001 const int channels_count = 3;
2002 const int image_channels_count = 4;
2003 const int64_t spatial_pixels_per_channel =
int64_t(spatial_size.x) * spatial_size.y;
2004 const int64_t frequency_pixels_per_channel =
int64_t(frequency_size.x) * frequency_size.y;
2005 const int64_t spatial_pixels_count = spatial_pixels_per_channel * channels_count;
2006 const int64_t frequency_pixels_count = frequency_pixels_per_channel * channels_count;
2008 float *image_spatial_domain = fftwf_alloc_real(spatial_pixels_count);
2009 std::complex<float> *image_frequency_domain =
reinterpret_cast<std::complex<float> *
>(
2010 fftwf_alloc_complex(frequency_pixels_count));
2013 fftwf_plan forward_plan = fftwf_plan_dft_r2c_2d(
2016 image_spatial_domain,
2017 reinterpret_cast<fftwf_complex *
>(image_frequency_domain),
2020 const float *highlights_buffer =
nullptr;
2021 if (this->
context().use_gpu()) {
2023 highlights_buffer =
static_cast<const float *
>(
2027 highlights_buffer =
static_cast<const float *
>(highlights.
cpu_data().
data());
2033 for (const int64_t y : sub_y_range) {
2034 for (const int64_t x : IndexRange(spatial_size.x)) {
2035 const bool is_inside_image = x < image_size.x && y < image_size.y;
2036 for (const int64_t channel : IndexRange(channels_count)) {
2037 const int64_t base_index = y * spatial_size.x + x;
2038 const int64_t output_index = base_index + spatial_pixels_per_channel * channel;
2039 if (is_inside_image) {
2040 const int64_t image_index = (y * image_size.x + x) * image_channels_count + channel;
2041 image_spatial_domain[output_index] = highlights_buffer[image_index];
2044 image_spatial_domain[output_index] = 0.0f;
2052 for (
const int64_t channel : sub_range) {
2053 fftwf_execute_dft_r2c(forward_plan,
2054 image_spatial_domain + spatial_pixels_per_channel * channel,
2055 reinterpret_cast<fftwf_complex *
>(image_frequency_domain) +
2056 frequency_pixels_per_channel * channel);
2060 const FogGlowKernel &fog_glow_kernel = context().cache_manager().fog_glow_kernels.get(
2061 kernel_size, spatial_size);
2068 const float normalization_scale = float(spatial_size.x) * spatial_size.y *
2069 fog_glow_kernel.normalization_factor();
2071 for (const int64_t channel : IndexRange(channels_count)) {
2072 for (const int64_t y : sub_y_range) {
2073 for (const int64_t x : IndexRange(frequency_size.x)) {
2074 const int64_t base_index = x + y * frequency_size.x;
2075 const int64_t output_index = base_index + frequency_pixels_per_channel * channel;
2076 const std::complex<float> kernel_value = fog_glow_kernel.frequencies()[base_index];
2077 image_frequency_domain[output_index] *= kernel_value / normalization_scale;
2084 fftwf_plan backward_plan = fftwf_plan_dft_c2r_2d(
2087 reinterpret_cast<fftwf_complex *
>(image_frequency_domain),
2088 image_spatial_domain,
2092 for (
const int64_t channel : sub_range) {
2093 fftwf_execute_dft_c2r(backward_plan,
2094 reinterpret_cast<fftwf_complex *
>(image_frequency_domain) +
2095 frequency_pixels_per_channel * channel,
2096 image_spatial_domain + spatial_pixels_per_channel * channel);
2101 fog_glow_result.allocate_texture(highlights.domain());
2106 const_cast<float *
>(highlights_buffer) :
2107 static_cast<float *>(fog_glow_result.cpu_data().
data());
2111 for (const int64_t y : sub_y_range) {
2112 for (const int64_t x : IndexRange(image_size.x)) {
2113 for (const int64_t channel : IndexRange(channels_count)) {
2114 const int64_t output_index = (x + y * image_size.x) * image_channels_count;
2115 const int64_t base_index = x + y * spatial_size.x;
2116 const int64_t input_index = base_index + spatial_pixels_per_channel * channel;
2117 output[output_index + channel] = image_spatial_domain[input_index];
2118 output[output_index + 3] = highlights_buffer[output_index + 3];
2124 if (this->
context().use_gpu()) {
2130 fftwf_destroy_plan(forward_plan);
2131 fftwf_destroy_plan(backward_plan);
2132 fftwf_free(image_spatial_domain);
2133 fftwf_free(image_frequency_domain);
2137 if (this->
context().use_gpu()) {
2142 fog_glow_result.store_pixel(texel, highlights.load_pixel<float4>(texel));
2147 return fog_glow_result;
2163 const bool is_even = safe_size % 2 == 0;
2164 const int odd_size = safe_size + (is_even ? 1 : 0);
2180 if (this->
context().use_gpu()) {
2190 GPUShader *shader =
context().get_shader(
"compositor_glare_mix");
2224 output.allocate_texture(domain);
2229 float4 input_color = math::max(float4(0.0f), input.load_pixel<float4>(texel));
2231 float2 normalized_coordinates = (float2(texel) + float2(0.5f)) / float2(input.domain().size);
2232 float4 glare_color = glare_result.sample_bilinear_extended(normalized_coordinates);
2236 rgb_to_hsv_v(glare_color, glare_hsva);
2237 glare_hsva.y = math::clamp(glare_hsva.y * saturation, 0.0f, 1.0f);
2239 hsv_to_rgb_v(glare_hsva, glare_rgba);
2241 float3 combined_color = input_color.xyz() + glare_rgba.xyz() * tint;
2243 output.store_pixel(texel, float4(combined_color, input_color.w));
2252 if (this->
context().use_gpu()) {
2262 GPUShader *shader = this->
context().get_shader(
"compositor_glare_write_glare_output");
2275 output.bind_as_image(shader,
"output_img");
2280 output.unbind_as_image();
2301 glare_hsva.y =
math::clamp(glare_hsva.y * saturation, 0.0f, 1.0f);
2305 float3 adjusted_glare_value = glare_rgba.
xyz() * tint;
2306 output.store_pixel(texel,
float4(adjusted_glare_value, 1.0f));
2351 return math::max(0.0f, this->
get_input(
"Saturation").get_single_value_default(1.0f));
2377 this->
get_input(
"Color Modulation").get_single_value_default(0.25f), 0.0f, 1.0f);
2401 return 1 << node_storage(
bnode()).quality;
2420 ntype.
ui_description =
"Add lens flares, fog and glows around bright parts of the image";
2423 ntype.
declare = file_ns::cmp_node_glare_declare;
2425 ntype.
initfunc = file_ns::node_composit_init_glare;
#define NODE_STORAGE_FUNCS(StorageT)
#define NODE_CLASS_OP_FILTER
#define BLI_assert_unreachable()
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
@ CMP_NODE_GLARE_SIMPLE_STAR
@ CMP_NODE_GLARE_FOG_GLOW
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_uniform_1f(GPUShader *sh, const char *name, float value)
void GPU_shader_bind(GPUShader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3])
void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float(*val)[4])
void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4])
void GPU_memory_barrier(eGPUBarrier barrier)
@ GPU_BARRIER_TEXTURE_UPDATE
void GPU_texture_clear(GPUTexture *texture, eGPUDataFormat data_format, const void *data)
void GPU_texture_copy(GPUTexture *dst, GPUTexture *src)
void * GPU_texture_read(GPUTexture *texture, eGPUDataFormat data_format, int mip_level)
void GPU_texture_extend_mode(GPUTexture *texture, GPUSamplerExtendMode extend_mode)
@ GPU_SAMPLER_EXTEND_MODE_EXTEND
@ GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER
void GPU_texture_filter_mode(GPUTexture *texture, bool use_filter)
void GPU_texture_update(GPUTexture *texture, eGPUDataFormat data_format, const void *data)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Read Guarded memory(de)allocation.
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ UI_ITEM_R_SPLIT_EMPTY_NAME
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr IndexRange drop_front(int64_t n) const
Result create_result(ResultType type, ResultPrecision precision)
GPUShader * get_shader(const char *info_name, ResultPrecision precision)
const bNode & bnode() const
NodeOperation(Context &context, DNode node)
Result & get_result(StringRef identifier)
Context & context() const
Result & get_input(StringRef identifier) const
virtual Domain compute_domain()
void share_data(const Result &source)
T get_single_value_default(const T &default_value) const
void allocate_texture(Domain domain, bool from_pool=true)
void store_pixel(const int2 &texel, const T &pixel_value)
void unbind_as_texture() const
void bind_as_texture(GPUShader *shader, const char *texture_name) const
T load_pixel_zero(const int2 &texel) const
float4 sample_bilinear_zero(const float2 &coordinates) const
const Domain & domain() const
T load_pixel(const int2 &texel) const
void bind_as_image(GPUShader *shader, const char *image_name, bool read=false) const
void unbind_as_image() const
float4 sample_bilinear_extended(const float2 &coordinates) const
bool is_single_value() const
void steal_data(Result &source)
PanelDeclarationBuilder & add_panel(StringRef name, int identifier=-1)
DeclType::Builder & add_input(StringRef name, StringRef identifier="")
ColorGeometry4f default_value
void write_highlights_output_cpu(const Result &highlights)
void execute_mix_cpu(const Result &glare_result)
float get_small_ghost_radius()
void write_glare_output(const Result &glare)
void write_highlights_output_gpu(const Result &highlights)
void compute_bloom_upsample_gpu(const Result &input, Result &output)
Result execute_fog_glow(const Result &highlights)
float get_ghost_color_modulation_factor()
float3 compute_streak_fade_factors(float iteration_magnitude)
int get_number_of_streaks()
Result apply_streak_filter(const Result &highlights, const float2 &streak_direction)
float adaptive_smooth_clamp(const float x, const float min_value, const float max_value, const float smoothness)
Result compute_glare(Result &highlights_result)
int2 get_glare_image_size()
void accumulate_streak_gpu(const Result &streak_result, Result &accumulated_streaks_result)
Result execute_highlights_cpu()
Result execute_highlights_gpu()
bool get_clamp_highlights()
void write_highlights_output(const Result &highlights)
Result execute_simple_star_anti_diagonal_pass(const Result &highlights, const Result &diagonal_pass_result)
float3 get_corrected_tint()
void write_glare_output_gpu(const Result &glare)
float smooth_min(const float a, const float b, const float smoothness)
Result compute_highlights()
void compute_base_ghost_gpu(const Result &small_ghost_result, const Result &big_ghost_result, Result &base_ghost_result)
float compute_streak_attenuation_factor()
Result execute_simple_star_axis_aligned(const Result &highlights)
Result execute_simple_star_horizontal_pass_gpu(const Result &highlights)
Result apply_streak_filter_gpu(const Result &highlights, const float2 &streak_direction)
float smooth_clamp(const float x, const float min_value, const float max_value, const float min_smoothness, const float max_smoothness)
float get_maximum_brightness()
void execute_mix(const Result &glare_result)
std::array< float4, 4 > compute_ghost_color_modulators()
Result execute_simple_star_horizontal_pass(const Result &highlights)
Result execute_simple_star_diagonal_pass_cpu(const Result &highlights)
Result execute_simple_star_anti_diagonal_pass_gpu(const Result &highlights, const Result &diagonal_pass_result)
void compute_bloom_downsample_cpu(const Result &input, Result &output)
void accumulate_ghosts_cpu(const Result &base_ghost, Result &accumulated_ghosts_result)
Result execute_simple_star_vertical_pass_cpu(const Result &highlights, const Result &horizontal_pass_result)
float get_normalization_scale()
int compute_bloom_chain_length()
Result execute_simple_star_horizontal_pass_cpu(const Result &highlights)
float compute_streak_iteration_magnitude(int iteration)
Result execute_simple_star_vertical_pass(const Result &highlights, const Result &horizontal_pass_result)
void compute_bloom_upsample_cpu(const Result &input, Result &output)
float smooth_max(const float a, const float b, const float smoothness)
Result execute_simple_star_diagonal_pass(const Result &highlights)
Result execute_simple_star_vertical_pass_gpu(const Result &highlights, const Result &horizontal_pass_result)
float compute_streak_color_modulator(int iteration)
Result execute_simple_star(const Result &highlights)
Result execute_bloom(Result &highlights)
Result compute_base_ghost(const Result &highlights)
void write_glare_output_cpu(const Result &glare)
bool should_compute_glare()
Result execute_simple_star_diagonal_pass_gpu(const Result &highlights)
Result execute_streaks(const Result &highlights)
void accumulate_streak(const Result &streak_result, Result &accumulated_streaks_result)
void accumulate_streak_cpu(const Result &streak, Result &accumulated_streaks)
Result execute_simple_star_diagonal(const Result &highlights)
Array< Result > compute_bloom_downsample_chain(const Result &highlights, int chain_length)
float get_big_ghost_radius()
Result apply_streak_filter_cpu(const Result &highlights, const float2 &streak_direction)
float get_highlights_smoothness()
void execute_mix_gpu(const Result &glare_result)
float get_color_modulation()
void compute_base_ghost_cpu(const Result &small_ghost_result, const Result &big_ghost_result, Result &combined_ghost)
float2 compute_streak_direction(int streak_index)
void compute_bloom_downsample_gpu(const Result &input, Result &output, const bool use_karis_average)
float get_streaks_angle()
Result execute_simple_star_anti_diagonal_pass_cpu(const Result &highlights, const Result &diagonal_pass_result)
float4 karis_brightness_weighted_sum(const float4 &color1, const float4 &color2, const float4 &color3, const float4 &color4)
Result execute_ghost(const Result &highlights)
float get_max_highlights()
NodeOperation(Context &context, DNode node)
int compute_fog_glow_kernel_size(const Result &highlights)
std::array< float, 4 > compute_ghost_scales(int iteration)
void accumulate_ghosts_gpu(const Result &base_ghost_result, Result &accumulated_ghosts_result)
int get_number_of_iterations()
void operator()(LinkSearchOpParams ¶ms)
VecBase< float, 2 > float2
VecBase< float, 4 > float4
void * MEM_callocN(size_t len, const char *str)
void MEM_freeN(void *vmemh)
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
void node_register_type(bNodeType &ntype)
void node_set_socket_availability(bNodeTree &ntree, bNodeSocket &sock, bool is_available)
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
int compute_diagonal_length(const int2 &size, const int diagonal_index)
int2 compute_anti_diagonal_start(const int2 &size, const int index)
int compute_anti_diagonal_length(const int2 &size, const int diagonal_index)
int compute_number_of_diagonals(const int2 &size)
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
void symmetric_separable_blur(Context &context, const Result &input, Result &output, const float2 &radius, const int filter_type=R_FILTER_GAUSS, const bool extend_bounds=false)
int2 compute_diagonal_start(const int2 &size, const int index)
int2 get_diagonal_direction()
int2 get_anti_diagonal_direction()
void parallel_for(const int2 range, const Function &function)
int context(const bContext *C, const char *member, bContextDataResult *result)
int optimal_size_for_real_transform(int size)
T cos(const AngleRadianBase< T > &a)
T clamp(const T &a, const T &min, const T &max)
T reduce_max(const VecBase< T, Size > &a)
T distance(const T &a, const T &b)
T reduce_min(const VecBase< T, Size > &a)
T min(const T &a, const T &b)
T interpolate(const T &a, const T &b, const FactorT &t)
T sin(const AngleRadianBase< T > &a)
T max(const T &a, const T &b)
T reduce_add(const VecBase< T, Size > &a)
static void cmp_node_glare_declare(NodeDeclarationBuilder &b)
static void node_update(bNodeTree *ntree, bNode *node)
static void gather_link_searches(GatherLinkSearchOpParams ¶ms)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
static void node_composit_init_glare(bNodeTree *, bNode *node)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
static void register_node_type_cmp_glare()
#define MAX_GLARE_ITERATIONS
void cmp_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
int RNA_enum_get(PointerRNA *ptr, const char *name)
VecBase< T, 3 > xyz() const
std::string ui_description
NodeGetCompositorOperationFunction get_compositor_operation
void(* initfunc)(bNodeTree *ntree, bNode *node)
const char * enum_name_legacy
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
NodeDeclareFunction declare
void(* updatefunc)(bNodeTree *ntree, bNode *node)
void label(blender::StringRef name, int icon)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
static pxr::UsdShadeInput get_input(const pxr::UsdShadeShader &usd_shader, const pxr::TfToken &input_name)