Fix #121639: Glare shifts the colors of highlights

The Glare node shifts the color of the highlights when the threshold is
high. That's because the thresholding algorithm simply subtracts the
threshold from the RGB data, which is not expected to retain the same hue
of the color.

To fix this, we do the thresholding only on the luminance of the color
in HSV color space. This eliminates the color shifting and also helps to
smooth the edges of the highlights.

This is a breaking change, but it is more of a fix rather than a change
of behavior.

Pull Request: https://projects.blender.org/blender/blender/pulls/122570
This commit is contained in:
Omar Emara 2024-06-04 20:59:32 +02:00 committed by Omar Emara
parent 9ad2c7df0b
commit 4f21b26675
5 changed files with 24 additions and 25 deletions

@ -2,6 +2,10 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_base.hh"
#include "BLI_math_color.h"
#include "BLI_utildefines.h"
#include "COM_GlareThresholdOperation.h"
#include "IMB_colormanagement.hh"
@ -31,17 +35,14 @@ void GlareThresholdOperation::update_memory_buffer_partial(MemoryBuffer *output,
{
const float threshold = settings_->threshold;
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const float *color = it.in(0);
if (IMB_colormanagement_get_luminance(color) >= threshold) {
it.out[0] = color[0] - threshold;
it.out[1] = color[1] - threshold;
it.out[2] = color[2] - threshold;
float4 hsva;
rgb_to_hsv_v(it.in(0), hsva);
CLAMP3_MIN(it.out, 0.0f);
}
else {
zero_v3(it.out);
}
hsva.z = math::max(0.0f, hsva.z - threshold);
hsv_to_rgb_v(hsva, it.out);
CLAMP3_MIN(it.out, 0.0f);
it.out[3] = 1.0f;
}
}

@ -2,6 +2,8 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void main()
{
/* The dispatch domain covers the output image size, which might be a fraction of the input image
@ -13,14 +15,16 @@ void main()
* to get the coordinates into the sampler's expected [0, 1] range. */
vec2 normalized_coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(output_img));
vec4 input_color = texture(input_tx, normalized_coordinates);
float luminance = dot(input_color.rgb, luminance_coefficients);
vec4 hsva;
rgb_to_hsv(texture(input_tx, normalized_coordinates), hsva);
/* The pixel whose luminance is less than the threshold luminance is not considered part of the
* highlights and is given a value of zero. Otherwise, the pixel is considered part of the
* highlights, whose value is the difference to the threshold value clamped to zero. */
bool is_highlights = luminance >= threshold;
vec3 highlights = is_highlights ? max(vec3(0.0), input_color.rgb - threshold) : vec3(0.0);
/* The pixel whose luminance value is less than the threshold luminance is not considered part of
* the highlights and is given a value of zero. Otherwise, the pixel is considered part of the
* highlights, whose luminance value is the difference to the threshold. */
hsva.z = max(0.0, hsva.z - threshold);
imageStore(output_img, texel, vec4(highlights, 1.0));
vec4 rgba;
hsv_to_rgb(hsva, rgba);
imageStore(output_img, texel, vec4(rgba.rgb, 1.0));
}

@ -11,7 +11,6 @@
GPU_SHADER_CREATE_INFO(compositor_glare_highlights)
.local_group_size(16, 16)
.push_constant(Type::FLOAT, "threshold")
.push_constant(Type::VEC3, "luminance_coefficients")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.compute_source("compositor_glare_highlights.glsl")

@ -30,8 +30,6 @@
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "IMB_colormanagement.hh"
#include "GPU_shader.hh"
#include "GPU_state.hh"
#include "GPU_texture.hh"
@ -182,9 +180,6 @@ class GlareOperation : public NodeOperation {
GPUShader *shader = context().get_shader("compositor_glare_highlights");
GPU_shader_bind(shader);
float luminance_coefficients[3];
IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
GPU_shader_uniform_3fv(shader, "luminance_coefficients", luminance_coefficients);
GPU_shader_uniform_1f(shader, "threshold", node_storage(bnode()).threshold);
const Result &input_image = get_input("Image");

@ -1 +1 @@
Subproject commit 90695d0404b7cb675e1da9f778aa51b00028dd61
Subproject commit 363d42173a72ff8e9d0bc7c3be17b9739559b74c