Realtime Compositor: Implement normalize node
This patch implements the normalize node for the realtime compositor. Differential Revision: https://developer.blender.org/D16279 Reviewed By: Clement Foucault
This commit is contained in:
parent
9d710374bd
commit
58e25f11ae
|
@ -70,6 +70,17 @@ float sum_luminance_squared_difference(Context &context,
|
|||
* coefficients to compute the luminance. */
|
||||
float maximum_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients);
|
||||
|
||||
/* Computes the maximum float of all pixels in the given float texture, limited to the given range.
|
||||
* Values outside of the given range are ignored. If non of the pixel values are in the range, the
|
||||
* lower bound of the range is returned. For instance, if the given range is [-10, 10] and the
|
||||
* image contains the values {2, 5, 11}, the maximum will be 5, since 11 is outside of the range.
|
||||
* This is particularly useful for Z Depth normalization, since Z Depth can contain near infinite
|
||||
* values, so enforcing an upper bound is beneficial. */
|
||||
float maximum_float_in_range(Context &context,
|
||||
GPUTexture *texture,
|
||||
float lower_bound,
|
||||
float upper_bound);
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Minimum Reductions.
|
||||
*/
|
||||
|
@ -78,4 +89,15 @@ float maximum_luminance(Context &context, GPUTexture *texture, float3 luminance_
|
|||
* coefficients to compute the luminance. */
|
||||
float minimum_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients);
|
||||
|
||||
/* Computes the minimum float of all pixels in the given float texture, limited to the given range.
|
||||
* Values outside of the given range are ignored. If non of the pixel values are in the range, the
|
||||
* upper bound of the range is returned. For instance, if the given range is [-10, 10] and the
|
||||
* image contains the values {-11, 2, 5}, the minimum will be 2, since -11 is outside of the range.
|
||||
* This is particularly useful for Z Depth normalization, since Z Depth can contain near infinite
|
||||
* values, so enforcing a lower bound is beneficial. */
|
||||
float minimum_float_in_range(Context &context,
|
||||
GPUTexture *texture,
|
||||
float lower_bound,
|
||||
float upper_bound);
|
||||
|
||||
} // namespace blender::realtime_compositor
|
||||
|
|
|
@ -249,6 +249,25 @@ float maximum_luminance(Context &context, GPUTexture *texture, float3 luminance_
|
|||
return maximum;
|
||||
}
|
||||
|
||||
float maximum_float_in_range(Context &context,
|
||||
GPUTexture *texture,
|
||||
float lower_bound,
|
||||
float upper_bound)
|
||||
{
|
||||
GPUShader *shader = context.shader_manager().get("compositor_maximum_float_in_range");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
GPU_shader_uniform_1f(shader, "lower_bound", lower_bound);
|
||||
GPU_shader_uniform_1f(shader, "upper_bound", upper_bound);
|
||||
|
||||
float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F);
|
||||
const float maximum = *reduced_value;
|
||||
MEM_freeN(reduced_value);
|
||||
GPU_shader_unbind();
|
||||
|
||||
return maximum;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Minimum Reductions.
|
||||
*/
|
||||
|
@ -268,4 +287,23 @@ float minimum_luminance(Context &context, GPUTexture *texture, float3 luminance_
|
|||
return minimum;
|
||||
}
|
||||
|
||||
float minimum_float_in_range(Context &context,
|
||||
GPUTexture *texture,
|
||||
float lower_bound,
|
||||
float upper_bound)
|
||||
{
|
||||
GPUShader *shader = context.shader_manager().get("compositor_minimum_float_in_range");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
GPU_shader_uniform_1f(shader, "lower_bound", lower_bound);
|
||||
GPU_shader_uniform_1f(shader, "upper_bound", upper_bound);
|
||||
|
||||
float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F);
|
||||
const float minimum = *reduced_value;
|
||||
MEM_freeN(reduced_value);
|
||||
GPU_shader_unbind();
|
||||
|
||||
return minimum;
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
||||
|
|
|
@ -349,6 +349,7 @@ set(GLSL_SRC
|
|||
shaders/compositor/compositor_morphological_distance_feather.glsl
|
||||
shaders/compositor/compositor_morphological_distance_threshold.glsl
|
||||
shaders/compositor/compositor_morphological_step.glsl
|
||||
shaders/compositor/compositor_normalize.glsl
|
||||
shaders/compositor/compositor_parallel_reduction.glsl
|
||||
shaders/compositor/compositor_projector_lens_distortion.glsl
|
||||
shaders/compositor/compositor_realize_on_domain.glsl
|
||||
|
@ -633,6 +634,7 @@ set(SRC_SHADER_CREATE_INFOS
|
|||
shaders/compositor/infos/compositor_morphological_distance_info.hh
|
||||
shaders/compositor/infos/compositor_morphological_distance_threshold_info.hh
|
||||
shaders/compositor/infos/compositor_morphological_step_info.hh
|
||||
shaders/compositor/infos/compositor_normalize_info.hh
|
||||
shaders/compositor/infos/compositor_parallel_reduction_info.hh
|
||||
shaders/compositor/infos/compositor_projector_lens_distortion_info.hh
|
||||
shaders/compositor/infos/compositor_realize_on_domain_info.hh
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
float value = texture_load(input_tx, texel).x;
|
||||
float normalized_value = (value - minimum) * scale;
|
||||
float clamped_value = clamp(normalized_value, 0.0, 1.0);
|
||||
imageStore(output_img, texel, vec4(clamped_value));
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_normalize)
|
||||
.local_group_size(16, 16)
|
||||
.push_constant(Type::FLOAT, "minimum")
|
||||
.push_constant(Type::FLOAT, "scale")
|
||||
.sampler(0, ImageType::FLOAT_2D, "input_tx")
|
||||
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.compute_source("compositor_normalize.glsl")
|
||||
.do_static_compilation(true);
|
|
@ -108,6 +108,18 @@ GPU_SHADER_CREATE_INFO(compositor_maximum_luminance)
|
|||
.define("REDUCE(lhs, rhs)", "max(lhs, rhs)")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_maximum_float_in_range)
|
||||
.additional_info("compositor_parallel_reduction_shared")
|
||||
.image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.push_constant(Type::FLOAT, "lower_bound")
|
||||
.push_constant(Type::FLOAT, "upper_bound")
|
||||
.define("TYPE", "float")
|
||||
.define("IDENTITY", "vec4(lower_bound)")
|
||||
.define("INITIALIZE(v)", "((v.x <= upper_bound) && (v.x >= lower_bound)) ? v.x : lower_bound")
|
||||
.define("LOAD(value)", "value.x")
|
||||
.define("REDUCE(lhs, rhs)", "((rhs > lhs) && (rhs <= upper_bound)) ? rhs : lhs")
|
||||
.do_static_compilation(true);
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Minimum Reductions.
|
||||
*/
|
||||
|
@ -123,3 +135,15 @@ GPU_SHADER_CREATE_INFO(compositor_minimum_luminance)
|
|||
.define("LOAD(value)", "value.x")
|
||||
.define("REDUCE(lhs, rhs)", "min(lhs, rhs)")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_minimum_float_in_range)
|
||||
.additional_info("compositor_parallel_reduction_shared")
|
||||
.image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.push_constant(Type::FLOAT, "lower_bound")
|
||||
.push_constant(Type::FLOAT, "upper_bound")
|
||||
.define("TYPE", "float")
|
||||
.define("IDENTITY", "vec4(upper_bound)")
|
||||
.define("INITIALIZE(v)", "((v.x <= upper_bound) && (v.x >= lower_bound)) ? v.x : upper_bound")
|
||||
.define("LOAD(value)", "value.x")
|
||||
.define("REDUCE(lhs, rhs)", "((rhs < lhs) && (rhs >= lower_bound)) ? rhs : lhs")
|
||||
.do_static_compilation(true);
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
* \ingroup cmpnodes
|
||||
*/
|
||||
|
||||
#include "COM_algorithm_parallel_reduction.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
|
@ -15,19 +17,60 @@ namespace blender::nodes::node_composite_normalize_cc {
|
|||
|
||||
static void cmp_node_normalize_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
|
||||
b.add_input<decl::Float>(N_("Value"))
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.compositor_domain_priority(0);
|
||||
b.add_output<decl::Float>(N_("Value"));
|
||||
}
|
||||
|
||||
using namespace blender::realtime_compositor;
|
||||
|
||||
class NormalizeOperation : public NodeOperation {
|
||||
private:
|
||||
/* The normalize operation is specifically designed to normalize Z Depth information. But since Z
|
||||
* Depth can contain near infinite values, normalization is limited to [-range_, range], meaning
|
||||
* that values outside of that range will be ignored when computing the maximum and minimum for
|
||||
* normalization and will eventually be 0 or 1 if they are less than or larger than the range
|
||||
* respectively. */
|
||||
constexpr static float range_ = 10000.0f;
|
||||
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
get_input("Value").pass_through(get_result("Value"));
|
||||
Result &input_image = get_input("Value");
|
||||
Result &output_image = get_result("Value");
|
||||
if (input_image.is_single_value()) {
|
||||
input_image.pass_through(output_image);
|
||||
return;
|
||||
}
|
||||
|
||||
const float maximum = maximum_float_in_range(
|
||||
context(), input_image.texture(), -range_, range_);
|
||||
const float minimum = minimum_float_in_range(
|
||||
context(), input_image.texture(), -range_, range_);
|
||||
const float scale = (maximum != minimum) ? (1.0f / (maximum - minimum)) : 0.0f;
|
||||
|
||||
GPUShader *shader = shader_manager().get("compositor_normalize");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
GPU_shader_uniform_1f(shader, "minimum", minimum);
|
||||
GPU_shader_uniform_1f(shader, "scale", scale);
|
||||
|
||||
input_image.bind_as_texture(shader, "input_tx");
|
||||
|
||||
const Domain domain = compute_domain();
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue