17#if defined(WITH_FFTW3)
48#define MAX_GLARE_ITERATIONS 5
60 {0,
nullptr, 0,
nullptr,
nullptr},
67 {0,
nullptr, 0,
nullptr,
nullptr},
80 N_(
"The kernel is a float and will be convolved with all input channels")},
85 N_(
"The kernel is a color and each channel of the kernel will be convolved with each "
86 "respective channel in the input")},
87 {0,
nullptr, 0,
nullptr,
nullptr},
92 b.use_custom_socket_order();
93 b.allow_any_socket_order();
96 .default_value({1.0f, 1.0f, 1.0f, 1.0f})
100 .structure_type(StructureType::Dynamic)
101 .
description(
"The image with the generated glare added")
102 .align_with_previous();
105 .structure_type(StructureType::Dynamic)
108 .structure_type(StructureType::Dynamic)
109 .
description(
"The extracted highlights from which the glare was generated");
125 "The brightness level at which pixels are considered part of the highlights that "
132 .description(
"The smoothness of the extracted highlights");
137 .default_value(
false)
141 .default_value(10.0f)
144 "Clamp bright highlights such that their brightness are not larger than this value");
152 .description(
"Adjusts the brightness of the glare");
158 .description(
"Adjusts the saturation of the glare");
160 .default_value({1.0f, 1.0f, 1.0f, 1.0f})
161 .description(
"Tints the glare. Consider desaturating the glare to more accurate tinting");
169 .usage_by_menu(
"Type",
172 "The size of the glare relative to the image. 1 means the glare covers the entire "
173 "image, 0.5 means the glare covers half the image, and so on");
184 .description(
"The angle that the first streak makes with the horizontal axis");
189 .usage_by_menu(
"Type",
192 "The number of ghosts for Ghost glare or the quality and spread of Glare for Streaks "
200 .description(
"Streak fade-out factor");
207 .description(
"Modulates colors of streaks and ghosts for a spectral dispersion effect");
215 .default_value({0.5f, 0.5f})
220 "The position of the source of the rays in normalized coordinates. 0 means lower left "
221 "corner and 1 means upper right corner");
229 "The amount of jitter to introduce while computing rays, higher jitter can be faster "
230 "but can produce grainy or noisy results");
260 bNode &node =
params.add_node(
"CompositorNodeGlare");
263 params.update_and_connect_available_socket(node,
"Image");
270 if (!
params.node_tree().typeinfo->validate_link(from_socket_type,
SOCK_RGBA)) {
340 if (this->
context().use_gpu()) {
371 return highlights_result;
384 output.allocate_texture(highlights_size);
387 const int2 input_size =
input.domain().size;
404 color =
input.sample_bilinear_extended(normalized_coordinates);
416 float4 lower_left_color =
input.sample_bilinear_extended(lower_left_coordinates);
420 float4 lower_right_color =
input.sample_bilinear_extended(lower_right_coordinates);
424 float4 upper_left_color =
input.sample_bilinear_extended(upper_left_coordinates);
428 float4 upper_right_color =
input.sample_bilinear_extended(upper_right_coordinates);
430 color = (upper_left_color + upper_right_color + lower_left_color + lower_right_color) /
449 hsva.z, threshold, max_brightness, highlights_smoothness);
452 hsva.z = clamped_brightness - threshold;
467 return std::numeric_limits<float>::max();
483 float smooth_min(
const float a,
const float b,
const float smoothness)
485 if (smoothness == 0.0f) {
489 return math::min(a,
b) - h * h * smoothness * (1.0f / 4.0f);
492 float smooth_max(
const float a,
const float b,
const float smoothness)
500 const float min_value,
501 const float max_value,
502 const float min_smoothness,
503 const float max_smoothness)
506 max_value, this->
smooth_max(min_value,
x, min_smoothness), max_smoothness);
522 const float min_value,
523 const float max_value,
524 const float smoothness)
526 const float range_distance =
math::distance(min_value, max_value);
527 const float distance_from_min_to_zero =
math::distance(min_value, 0.0f);
528 const float distance_from_max_to_zero =
math::distance(max_value, 0.0f);
530 const float max_safe_smoothness_for_min =
math::min(distance_from_min_to_zero, range_distance);
531 const float max_safe_smoothness_for_max =
math::min(distance_from_max_to_zero, range_distance);
533 const float min_smoothness =
math::min(smoothness, max_safe_smoothness_for_min);
534 const float max_smoothness =
math::min(smoothness, max_safe_smoothness_for_max);
536 return this->
smooth_clamp(x, min_value, max_value, min_smoothness, max_smoothness);
541 return math::max(0.0f, this->
get_input(
"Highlights Threshold").get_single_value_default(1.0f));
547 this->
get_input(
"Highlights Smoothness").get_single_value_default(0.1f));
557 return math::max(0.0f, this->
get_input(
"Maximum Highlights").get_single_value_default(0.0f));
564 if (this->
context().use_gpu()) {
584 output.bind_as_image(shader,
"output_img");
660 horizontal_pass_result);
661 horizontal_pass_result.
release();
662 return vertical_pass_result;
666 const Result &horizontal_pass_result)
668 if (this->
context().use_gpu()) {
675 const Result &horizontal_pass_result)
696 const int width =
size.x;
703 return vertical_pass_result;
707 const Result &horizontal_pass_result)
719 const float fade_factor = this->
get_fade();
722 const int width =
size.x;
729 for (
int i = 0;
i < iterations;
i++) {
734 for (
int y = 0;
y < height;
y++) {
740 float4 neighbor_average = (previous_output + next_input) / 2.0f;
742 output.store_pixel(texel, causal_output);
749 for (
int y = height - 1;
y >= 0;
y--) {
755 float4 neighbor_average = (previous_output + next_input) / 2.0f;
757 current_input, neighbor_average, fade_factor);
758 output.store_pixel(texel, non_causal_output);
764 for (
int y = 0;
y < height;
y++) {
768 float4 combined = horizontal + vertical;
769 output.store_pixel(texel,
float4(combined.xyz(), 1.0f));
779 if (this->
context().use_gpu()) {
801 horizontal_pass_result.
bind_as_image(shader,
"horizontal_img");
809 return horizontal_pass_result;
824 const float fade_factor = this->
get_fade();
827 const int width =
size.x;
829 for (const int64_t y : sub_range) {
832 for (int i = 0; i < iterations; i++) {
837 for (int x = 0; x < width; x++) {
838 int2 texel = int2(x, y);
839 float4 previous_output = horizontal_pass_result.load_pixel_zero<float4>(texel -
841 float4 current_input = horizontal_pass_result.load_pixel<float4>(texel);
842 float4 next_input = horizontal_pass_result.load_pixel_zero<float4>(texel + int2(i, 0));
844 float4 neighbor_average = (previous_output + next_input) / 2.0f;
845 float4 causal_output = math::interpolate(current_input, neighbor_average, fade_factor);
846 horizontal_pass_result.store_pixel(texel, causal_output);
853 for (int x = width - 1; x >= 0; x--) {
854 int2 texel = int2(x, y);
855 float4 previous_output = horizontal_pass_result.load_pixel_zero<float4>(texel +
857 float4 current_input = horizontal_pass_result.load_pixel<float4>(texel);
858 float4 next_input = horizontal_pass_result.load_pixel_zero<float4>(texel - int2(i, 0));
860 float4 neighbor_average = (previous_output + next_input) / 2.0f;
861 float4 non_causal_output = math::interpolate(
862 current_input, neighbor_average, fade_factor);
863 horizontal_pass_result.store_pixel(texel, non_causal_output);
869 return horizontal_pass_result;
876 highlights, diagonal_pass_result);
877 diagonal_pass_result.
release();
878 return anti_diagonal_pass_result;
882 const Result &diagonal_pass_result)
884 if (this->
context().use_gpu()) {
891 const Result &diagonal_pass_result)
901 gpu::Shader *shader =
context().get_shader(
"compositor_glare_simple_star_anti_diagonal_pass");
909 anti_diagonal_pass_result.
bind_as_image(shader,
"anti_diagonal_img");
918 return anti_diagonal_pass_result;
922 const Result &diagonal_pass_result)
934 const float fade_factor = this->
get_fade();
939 for (
const int64_t index : sub_range) {
943 int2 end = start + (anti_diagonal_length - 1) * direction;
947 for (
int i = 0;
i < iterations;
i++) {
952 for (
int j = 0; j < anti_diagonal_length; j++) {
953 int2 texel = start + j * direction;
958 float4 neighbor_average = (previous_output + next_input) / 2.0f;
960 output.store_pixel(texel, causal_output);
967 for (
int j = 0; j < anti_diagonal_length; j++) {
968 int2 texel = end - j * direction;
973 float4 neighbor_average = (previous_output + next_input) / 2.0f;
975 current_input, neighbor_average, fade_factor);
976 output.store_pixel(texel, non_causal_output);
982 for (
int j = 0; j < anti_diagonal_length; j++) {
983 int2 texel = start + j * direction;
986 float4 combined = horizontal + vertical;
987 output.store_pixel(texel,
float4(combined.xyz(), 1.0f));
997 if (this->
context().use_gpu()) {
1013 gpu::Shader *shader =
context().get_shader(
"compositor_glare_simple_star_diagonal_pass");
1027 return diagonal_pass_result;
1042 const float fade_factor = this->
get_fade();
1047 for (
const int64_t index : sub_range) {
1051 int2 end = start + (diagonal_length - 1) * direction;
1055 for (
int i = 0;
i < iterations;
i++) {
1060 for (
int j = 0; j < diagonal_length; j++) {
1061 int2 texel = start + j * direction;
1068 float4 neighbor_average = (previous_output + next_input) / 2.0f;
1070 diagonal_pass_result.
store_pixel(texel, causal_output);
1077 for (
int j = 0; j < diagonal_length; j++) {
1078 int2 texel = end - j * direction;
1085 float4 neighbor_average = (previous_output + next_input) / 2.0f;
1087 current_input, neighbor_average, fade_factor);
1088 diagonal_pass_result.
store_pixel(texel, non_causal_output);
1094 return diagonal_pass_result;
1099 return this->
get_input(
"Diagonal Star").get_single_value_default(
true);
1112 if (this->
context().use_gpu()) {
1131 return accumulated_streaks_result;
1136 if (this->
context().use_gpu()) {
1161 for (
const int iteration : iterations_range) {
1165 const float2 streak_vector = streak_direction * iteration_magnitude;
1175 output_streak_result.
bind_as_image(shader,
"output_streak_img");
1185 if (iteration != iterations_range.
last()) {
1191 input_streak_result.
release();
1194 return output_streak_result;
1214 for (
const int iteration : iterations_range) {
1218 const float2 streak_vector = streak_direction * iteration_magnitude;
1230 neighbors[0] =
input.sample_bilinear_zero(coordinates +
vector);
1231 neighbors[1] =
input.sample_bilinear_zero(coordinates +
vector * 2.0f);
1232 neighbors[2] =
input.sample_bilinear_zero(coordinates +
vector * 3.0f);
1237 neighbors[0] *=
float4(1.0f, color_modulator, color_modulator, 1.0f);
1238 neighbors[1] *=
float4(color_modulator, color_modulator, 1.0f, 1.0f);
1239 neighbors[2] *=
float4(color_modulator, 1.0f, color_modulator, 1.0f);
1244 for (
int i = 0;
i < 3;
i++) {
1245 weighted_neighbors_sum += fade_factors[
i] * neighbors[
i];
1251 float4 center_color =
input.sample_bilinear_zero(coordinates);
1252 float4 output_color = (center_color + weighted_neighbors_sum) / 2.0f;
1253 output.store_pixel(texel, output_color);
1259 if (iteration != iterations_range.
last()) {
1272 if (this->
context().use_gpu()) {
1282 gpu::Shader *shader = this->
context().get_shader(
"compositor_glare_streaks_accumulate");
1289 accumulated_streaks_result.
bind_as_image(shader,
"accumulated_streaks_img",
true);
1306 float4 combined_streaks = current_accumulated_streaks + attenuated_streak;
1330 const float angle = start_angle + (
float(streak_index) / number_of_streaks) * (
M_PI * 2.0f);
1361 const float fade_factor = std::pow(this->
get_fade(), iteration_magnitude);
1362 return float3(fade_factor, std::pow(fade_factor, 2.0f), std::pow(fade_factor, 3.0f));
1373 return std::pow(4.0f, iteration);
1383 return this->
get_input(
"Streaks Angle").get_single_value_default(0.0f);
1394 if (this->
context().use_gpu()) {
1402 return accumulated_ghosts_result;
1414 color_modulators.size(),
1415 (
const float (*)[4])color_modulators.data());
1434 for (
const int i : iterations_range) {
1439 accumulated_ghosts_result.
bind_as_image(shader,
"accumulated_ghost_img",
true);
1449 if (
i != iterations_range.
last()) {
1484 for (
const int i : iterations_range) {
1495 for (
int i = 0;
i < 4;
i++) {
1496 float scale = scales[
i];
1497 float4 color_modulator = color_modulators[
i];
1501 float2 scaled_coordinates = (coordinates - 0.5f) * scale + 0.5f;
1508 float attenuator =
math::max(0.0f, 1.0f - distance_to_center *
math::abs(scale)) / 4.0f;
1511 float4 multiplier = attenuator * color_modulator;
1512 accumulated_ghost +=
input.sample_bilinear_zero(scaled_coordinates) * multiplier;
1516 float4 combined_ghost = current_accumulated_ghost + accumulated_ghost;
1517 accumulated_ghosts_result.
store_pixel(texel,
float4(combined_ghost.xyz(), 1.0f));
1523 if (
i != iterations_range.last()) {
1550 if (this->
context().use_gpu()) {
1560 return base_ghost_result;
1564 const Result &big_ghost_result,
1565 Result &base_ghost_result)
1579 base_ghost_result.
bind_as_image(shader,
"combined_ghost_img");
1590 const Result &big_ghost_result,
1607 float small_ghost_scale = 2.13f;
1608 float big_ghost_scale = -0.97f;
1613 float2 small_ghost_coordinates = (coordinates - 0.5f) * small_ghost_scale + 0.5f;
1614 float2 big_ghost_coordinates = (coordinates - 0.5f) * big_ghost_scale + 0.5f;
1620 float small_ghost_attenuator =
math::max(0.0f,
1621 1.0f - distance_to_center * small_ghost_scale);
1623 0.0f, 1.0f - distance_to_center *
math::abs(big_ghost_scale));
1626 small_ghost_attenuator;
1628 big_ghost_attenuator;
1630 combined_ghost.
store_pixel(texel, small_ghost + big_ghost);
1644 std::array<float4, 4> color_modulators;
1645 color_modulators[0] =
float4(1.0f);
1646 color_modulators[1] =
float4(1.0f, color_modulation_factor, color_modulation_factor, 1.0f);
1647 color_modulators[2] =
float4(color_modulation_factor, color_modulation_factor, 1.0f, 1.0f);
1648 color_modulators[3] =
float4(color_modulation_factor, 1.0f, color_modulation_factor, 1.0f);
1650 return color_modulators;
1670 std::array<float, 4> scales;
1673 const int global_i = iteration * 4 +
i;
1677 scales[
i] = 2.1f * (1.0f - progression);
1682 scales[
i] = -0.99f / scales[
i];
1748 if (chain_length < 2) {
1751 if (this->
context().use_gpu()) {
1756 bloom_result.store_pixel(texel, highlights.load_pixel<float4>(texel));
1759 return bloom_result;
1765 const IndexRange upsample_passes_range(chain_length - 1);
1767 for (
const int i : upsample_passes_range) {
1770 if (this->
context().use_gpu()) {
1779 return downsample_chain[0];
1788 input.bind_as_texture(shader,
"input_tx");
1790 output.bind_as_image(shader,
"output_img",
true);
1794 input.unbind_as_texture();
1795 output.unbind_as_image();
1824 upsampled += (4.0f / 16.0f) *
input.sample_bilinear_extended(coordinates);
1825 upsampled += (2.0f / 16.0f) *
1826 input.sample_bilinear_extended(coordinates + pixel_size *
float2(-1.0f, 0.0f));
1827 upsampled += (2.0f / 16.0f) *
1828 input.sample_bilinear_extended(coordinates + pixel_size *
float2(0.0f, 1.0f));
1829 upsampled += (2.0f / 16.0f) *
1830 input.sample_bilinear_extended(coordinates + pixel_size *
float2(1.0f, 0.0f));
1831 upsampled += (2.0f / 16.0f) *
1832 input.sample_bilinear_extended(coordinates + pixel_size *
float2(0.0f, -1.0f));
1833 upsampled += (1.0f / 16.0f) *
1834 input.sample_bilinear_extended(coordinates + pixel_size *
float2(-1.0f, -1.0f));
1835 upsampled += (1.0f / 16.0f) *
1836 input.sample_bilinear_extended(coordinates + pixel_size *
float2(-1.0f, 1.0f));
1837 upsampled += (1.0f / 16.0f) *
1838 input.sample_bilinear_extended(coordinates + pixel_size *
float2(1.0f, -1.0f));
1839 upsampled += (1.0f / 16.0f) *
1840 input.sample_bilinear_extended(coordinates + pixel_size *
float2(1.0f, 1.0f));
1856 Array<Result> downsample_chain(chain_length, downsampled_result);
1860 Result &base_layer = downsample_chain[0];
1862 if (this->
context().use_gpu()) {
1867 base_layer.store_pixel(texel, highlights.load_pixel<float4>(texel));
1873 const IndexRange downsample_passes_range(chain_length - 1);
1875 for (
const int i : downsample_passes_range) {
1880 const bool use_karis_average =
i == downsample_passes_range.
first();
1881 if (this->
context().use_gpu()) {
1883 downsample_chain[
i], downsample_chain[
i + 1], use_karis_average);
1886 if (use_karis_average) {
1895 return downsample_chain;
1900 const bool use_karis_average)
1903 use_karis_average ?
"compositor_glare_bloom_downsample_karis_average" :
1904 "compositor_glare_bloom_downsample_simple_average");
1908 input.bind_as_texture(shader,
"input_tx");
1911 output.bind_as_image(shader,
"output_img");
1915 input.unbind_as_texture();
1916 output.unbind_as_image();
1920 template<
bool UseKarisAverage>
1948 float4 center =
input.sample_bilinear_extended(coordinates);
1949 float4 upper_left_near =
input.sample_bilinear_extended(coordinates +
1950 pixel_size *
float2(-1.0f, 1.0f));
1951 float4 upper_right_near =
input.sample_bilinear_extended(coordinates +
1952 pixel_size *
float2(1.0f, 1.0f));
1953 float4 lower_left_near =
input.sample_bilinear_extended(coordinates +
1954 pixel_size *
float2(-1.0f, -1.0f));
1955 float4 lower_right_near =
input.sample_bilinear_extended(coordinates +
1956 pixel_size *
float2(1.0f, -1.0f));
1957 float4 left_far =
input.sample_bilinear_extended(coordinates +
1958 pixel_size *
float2(-2.0f, 0.0f));
1959 float4 right_far =
input.sample_bilinear_extended(coordinates +
1960 pixel_size *
float2(2.0f, 0.0f));
1961 float4 upper_far =
input.sample_bilinear_extended(coordinates +
1962 pixel_size *
float2(0.0f, 2.0f));
1963 float4 lower_far =
input.sample_bilinear_extended(coordinates +
1964 pixel_size *
float2(0.0f, -2.0f));
1965 float4 upper_left_far =
input.sample_bilinear_extended(coordinates +
1966 pixel_size *
float2(-2.0f, 2.0f));
1967 float4 upper_right_far =
input.sample_bilinear_extended(coordinates +
1968 pixel_size *
float2(2.0f, 2.0f));
1969 float4 lower_left_far =
input.sample_bilinear_extended(coordinates +
1970 pixel_size *
float2(-2.0f, -2.0f));
1971 float4 lower_right_far =
input.sample_bilinear_extended(coordinates +
1972 pixel_size *
float2(2.0f, -2.0f));
1975 if constexpr (!UseKarisAverage) {
1984 result = (4.0f / 32.0f) * center +
1986 (upper_left_near + upper_right_near + lower_left_near + lower_right_near) +
1987 (2.0f / 32.0f) * (left_far + right_far + upper_far + lower_far) +
1989 (upper_left_far + upper_right_far + lower_left_far + lower_right_far);
1999 upper_left_near, upper_right_near, lower_right_near, lower_left_near);
2001 upper_left_far, upper_far, center, left_far);
2003 upper_far, upper_right_far, right_far, center);
2005 center, right_far, lower_right_far, lower_far);
2007 left_far, center, lower_far, lower_left_far);
2014 result = (4.0f / 8.0f) * center_weighted_sum +
2015 (1.0f / 8.0f) * (upper_left_weighted_sum + upper_right_weighted_sum +
2016 lower_left_weighted_sum + lower_right_weighted_sum);
2038 float4 weights = 1.0f / (brightness + 1.0f);
2039 return (color1 * weights.x + color2 * weights.y + color3 * weights.z + color4 * weights.w) *
2053 const float scaled_dimension = smaller_dimension * this->
get_size();
2054 return int(std::log2(
math::max(1.0f, scaled_dimension)));
2063#if defined(WITH_FFTW3)
2070 const int needed_padding_amount = kernel_size;
2072 const int2 needed_spatial_size = image_size + needed_padding_amount - 1;
2079 const int2 frequency_size =
int2(spatial_size.x / 2 + 1, spatial_size.y);
2082 const int channels_count = 3;
2083 const int image_channels_count = 4;
2084 const int64_t spatial_pixels_per_channel =
int64_t(spatial_size.x) * spatial_size.y;
2085 const int64_t frequency_pixels_per_channel =
int64_t(frequency_size.x) * frequency_size.y;
2086 const int64_t spatial_pixels_count = spatial_pixels_per_channel * channels_count;
2087 const int64_t frequency_pixels_count = frequency_pixels_per_channel * channels_count;
2089 float *image_spatial_domain = fftwf_alloc_real(spatial_pixels_count);
2090 std::complex<float> *image_frequency_domain =
reinterpret_cast<std::complex<float> *
>(
2091 fftwf_alloc_complex(frequency_pixels_count));
2094 fftwf_plan forward_plan = fftwf_plan_dft_r2c_2d(
2097 image_spatial_domain,
2098 reinterpret_cast<fftwf_complex *
>(image_frequency_domain),
2101 const float *highlights_buffer =
nullptr;
2102 if (this->
context().use_gpu()) {
2104 highlights_buffer =
static_cast<const float *
>(
2108 highlights_buffer =
static_cast<const float *
>(highlights.
cpu_data().
data());
2114 for (const int64_t y : sub_y_range) {
2115 for (const int64_t x : IndexRange(spatial_size.x)) {
2116 const bool is_inside_image = x < image_size.x && y < image_size.y;
2117 for (const int64_t channel : IndexRange(channels_count)) {
2118 const int64_t base_index = y * spatial_size.x + x;
2119 const int64_t output_index = base_index + spatial_pixels_per_channel * channel;
2120 if (is_inside_image) {
2121 const int64_t image_index = (y * image_size.x + x) * image_channels_count + channel;
2122 image_spatial_domain[output_index] = highlights_buffer[image_index];
2125 image_spatial_domain[output_index] = 0.0f;
2133 for (
const int64_t channel : sub_range) {
2134 fftwf_execute_dft_r2c(forward_plan,
2135 image_spatial_domain + spatial_pixels_per_channel * channel,
2136 reinterpret_cast<fftwf_complex *
>(image_frequency_domain) +
2137 frequency_pixels_per_channel * channel);
2141 const FogGlowKernel &fog_glow_kernel = context().cache_manager().fog_glow_kernels.get(
2142 kernel_size, spatial_size, this->compute_fog_glow_field_of_view());
2149 const float normalization_scale =
float(spatial_size.x) * spatial_size.y *
2150 fog_glow_kernel.normalization_factor();
2152 for (const int64_t channel : IndexRange(channels_count)) {
2153 for (const int64_t y : sub_y_range) {
2154 for (const int64_t x : IndexRange(frequency_size.x)) {
2155 const int64_t base_index = x + y * frequency_size.x;
2156 const int64_t output_index = base_index + frequency_pixels_per_channel * channel;
2157 const std::complex<float> kernel_value = fog_glow_kernel.frequencies()[base_index];
2158 image_frequency_domain[output_index] *= kernel_value / normalization_scale;
2165 fftwf_plan backward_plan = fftwf_plan_dft_c2r_2d(
2168 reinterpret_cast<fftwf_complex *
>(image_frequency_domain),
2169 image_spatial_domain,
2173 for (
const int64_t channel : sub_range) {
2174 fftwf_execute_dft_c2r(backward_plan,
2175 reinterpret_cast<fftwf_complex *
>(image_frequency_domain) +
2176 frequency_pixels_per_channel * channel,
2177 image_spatial_domain + spatial_pixels_per_channel * channel);
2182 fog_glow_result.allocate_texture(highlights.domain());
2187 const_cast<float *
>(highlights_buffer) :
2188 static_cast<
float *>(fog_glow_result.cpu_data().
data());
2192 for (const int64_t y : sub_y_range) {
2193 for (const int64_t x : IndexRange(image_size.x)) {
2194 for (const int64_t channel : IndexRange(channels_count)) {
2195 const int64_t output_index = (x + y * image_size.x) * image_channels_count;
2196 const int64_t base_index = x + y * spatial_size.x;
2197 const int64_t input_index = base_index + spatial_pixels_per_channel * channel;
2198 output[output_index + channel] = image_spatial_domain[input_index];
2199 output[output_index + 3] = highlights_buffer[output_index + 3];
2205 if (this->
context().use_gpu()) {
2211 fftwf_destroy_plan(forward_plan);
2212 fftwf_destroy_plan(backward_plan);
2213 fftwf_free(image_spatial_domain);
2214 fftwf_free(image_frequency_domain);
2218 if (this->
context().use_gpu()) {
2223 fog_glow_result.store_pixel(texel, highlights.load_pixel<float4>(texel));
2228 return fog_glow_result;
2256 if (max_steps == 0) {
2259 if (this->
context().use_gpu()) {
2264 sun_beams_result.store_pixel(texel, highlights.load_pixel<float4>(texel));
2267 return sun_beams_result;
2270 if (this->
context().use_gpu()) {
2301 return output_image;
2307 return "compositor_glare_sun_beams_jitter";
2309 return "compositor_glare_sun_beams";
2328 int steps =
math::min(max_steps,
int(unbounded_steps));
2335 float2 vector_to_source = source - coordinates;
2336 float2 step_vector = vector_to_source / unbounded_steps;
2338 float accumulated_weight = 0.0f;
2341 int number_of_steps = (1.0f - jitter_factor) * steps;
2344 for (
int i = 0;
i <= number_of_steps;
i++) {
2346 i, use_jitter, jitter_factor, random_offset);
2347 float2 position = coordinates + position_index * step_vector;
2351 if (position.x < 0.0f || position.y < 0.0f || position.x > 1.0f || position.y > 1.0f) {
2359 float weight =
math::square(1.0f - position_index /
float(steps));
2361 accumulated_weight += weight;
2362 accumulated_color += sample_color * weight;
2365 if (accumulated_weight != 0.0f) {
2366 accumulated_color /= accumulated_weight;
2371 output.store_pixel(texel, accumulated_color);
2386 const bool use_jitter,
2387 float jitter_factor,
2388 const float random_offset)
2417 if (this->
context().use_gpu()) {
2422 kernel_result.store_pixel(texel, highlights.load_pixel<float4>(texel));
2425 return kernel_result;
2428 return kernel_result;
2447 const MenuValue menu_value =
input.get_single_value_default(default_menu_value);
2462 if (this->
context().use_gpu()) {
2506 output.allocate_texture(domain);
2511 float4 input_color = math::max(float4(0.0f), input.load_pixel<float4>(texel));
2513 float2 normalized_coordinates = (float2(texel) + float2(0.5f)) / float2(input.domain().size);
2514 float4 glare_color = glare_result.sample_bilinear_extended(normalized_coordinates);
2518 rgb_to_hsv_v(glare_color, glare_hsva);
2519 glare_hsva.y = math::clamp(glare_hsva.y * saturation, 0.0f, 1.0f);
2521 hsv_to_rgb_v(glare_hsva, glare_rgba);
2523 float3 combined_color = input_color.xyz() + glare_rgba.xyz() * tint;
2525 output.store_pixel(texel, float4(combined_color, input_color.w));
2534 if (this->
context().use_gpu()) {
2544 gpu::Shader *shader = this->
context().get_shader(
"compositor_glare_write_glare_output");
2557 output.bind_as_image(shader,
"output_img");
2562 output.unbind_as_image();
2583 glare_hsva.y =
math::clamp(glare_hsva.y * saturation, 0.0f, 1.0f);
2587 float3 adjusted_glare_value = glare_rgba.
xyz() * tint;
2588 output.store_pixel(texel,
float4(adjusted_glare_value, 1.0f));
2633 const MenuValue menu_value =
input.get_single_value_default(default_menu_value);
2644 return math::max(0.0f, this->
get_input(
"Saturation").get_single_value_default(1.0f));
2670 this->
get_input(
"Color Modulation").get_single_value_default(0.25f), 0.0f, 1.0f);
2675 return this->
get_input(
"Sun Position").get_single_value_default(
float2(0.5f));
2706 const MenuValue menu_value =
input.get_single_value_default(default_menu_value);
2726 ntype.
ui_description =
"Add lens flares, fog and glows around bright parts of the image";
2729 ntype.
declare = file_ns::cmp_node_glare_declare;
2730 ntype.
initfunc = file_ns::node_composit_init_glare;
#define NODE_CLASS_OP_FILTER
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_QUALITY_LOW
@ CMP_NODE_GLARE_QUALITY_MEDIUM
@ CMP_NODE_GLARE_QUALITY_HIGH
@ CMP_NODE_GLARE_SIMPLE_STAR
@ CMP_NODE_GLARE_SUN_BEAMS
@ CMP_NODE_GLARE_FOG_GLOW
void GPU_shader_uniform_4fv_array(blender::gpu::Shader *sh, const char *name, int len, const float(*val)[4])
void GPU_shader_uniform_1f(blender::gpu::Shader *sh, const char *name, float value)
void GPU_shader_uniform_4fv(blender::gpu::Shader *sh, const char *name, const float data[4])
void GPU_shader_uniform_3fv(blender::gpu::Shader *sh, const char *name, const float data[3])
void GPU_shader_bind(blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_uniform_1i(blender::gpu::Shader *sh, const char *name, int value)
void GPU_shader_uniform_2fv(blender::gpu::Shader *sh, const char *name, const float data[2])
@ GPU_BARRIER_TEXTURE_UPDATE
void GPU_memory_barrier(GPUBarrier barrier)
void GPU_texture_clear(blender::gpu::Texture *texture, eGPUDataFormat data_format, const void *data)
void GPU_texture_copy(blender::gpu::Texture *dst, blender::gpu::Texture *src)
void GPU_texture_extend_mode(blender::gpu::Texture *texture, GPUSamplerExtendMode extend_mode)
@ GPU_SAMPLER_EXTEND_MODE_EXTEND
@ GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER
void * GPU_texture_read(blender::gpu::Texture *texture, eGPUDataFormat data_format, int mip_level)
void GPU_texture_filter_mode(blender::gpu::Texture *texture, bool use_filter)
void GPU_texture_update(blender::gpu::Texture *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)
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)
gpu::Shader * get_shader(const char *info_name, ResultPrecision precision)
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 store_pixel(const int2 &texel, const T &pixel_value)
void allocate_texture(const Domain domain, const bool from_pool=true, const std::optional< ResultStorageType > storage_type=std::nullopt)
void unbind_as_texture() const
T load_pixel_zero(const int2 &texel) const
float4 sample_bilinear_zero(const float2 &coordinates) const
void bind_as_texture(gpu::Shader *shader, const char *texture_name) const
const Domain & domain() const
T load_pixel(const int2 &texel) const
void unbind_as_image() const
float4 sample_bilinear_extended(const float2 &coordinates) const
void bind_as_image(gpu::Shader *shader, const char *image_name, bool read=false) 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="")
Self & default_closed(bool closed)
StructureType structure_type
void write_highlights_output_cpu(const Result &highlights)
void execute_mix_cpu(const Result &glare_result)
Result execute_sun_beams_gpu(Result &highlights, const int max_steps)
float get_small_ghost_radius()
CMPNodeGlareType get_type()
float get_jitter_factor()
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 execute_sun_beams_cpu(Result &highlights, const int max_steps)
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)
float2 get_sun_position()
float compute_streak_attenuation_factor()
Result execute_simple_star_axis_aligned(const Result &highlights)
math::AngleRadian compute_fog_glow_field_of_view()
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)
CMPNodeGlareQuality get_quality()
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_sun_beams(Result &highlights)
Result execute_simple_star_diagonal(const Result &highlights)
const char * get_compositor_sun_beams_shader()
Array< Result > compute_bloom_downsample_chain(const Result &highlights, int chain_length)
const Result & get_kernel_input()
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)
float get_sample_position(const int i, const bool use_jitter, float jitter_factor, const float random_offset)
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)
Result execute_kernel(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()
KernelDataType get_kernel_data_type()
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_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)
void symmetric_separable_blur(Context &context, const Result &input, Result &output, const float2 &radius, const int filter_type=R_FILTER_GAUSS)
int2 compute_anti_diagonal_start(const int2 &size, const int index)
void compute_dispatch_threads_at_least(gpu::Shader *shader, int2 threads_range, int2 local_size=int2(16))
int compute_anti_diagonal_length(const int2 &size, const int diagonal_index)
int compute_number_of_diagonals(const int2 &size)
void convolve(Context &context, const Result &input, const Result &kernel, Result &output, const bool normalize_kernel)
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)
AngleRadianBase< float > AngleRadian
T cos(const AngleRadianBase< T > &a)
T pow(const T &x, const T &power)
T clamp(const T &a, const T &min, const T &max)
T safe_divide(const T &a, const T &b)
T reduce_max(const VecBase< T, Size > &a)
T distance(const T &a, const T &b)
VecBase< T, Size > divide_ceil(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
T length(const VecBase< T, Size > &a)
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 const EnumPropertyItem type_items[]
static void cmp_node_glare_declare(NodeDeclarationBuilder &b)
static const EnumPropertyItem quality_items[]
static void gather_link_searches(GatherLinkSearchOpParams ¶ms)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
static const EnumPropertyItem kernel_data_type_items[]
static void node_composit_init_glare(bNodeTree *, bNode *node)
float hash_to_float(uint32_t kx)
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)
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
static AngleRadianBase from_degree(const float °rees)
static pxr::UsdShadeInput get_input(const pxr::UsdShadeShader &usd_shader, const pxr::TfToken &input_name)