Realtime Compositor: Add basic output nodes
This patch implements the following nodes for the realtime compositor: - Composite node. - Viewer node. - Split viewer node. Differential Revision: https://developer.blender.org/D15226 Reviewed By: Clement Foucault
This commit is contained in:
parent
624b0ac656
commit
365fbb447e
|
@ -325,6 +325,8 @@ set(GLSL_SRC
|
|||
|
||||
shaders/compositor/compositor_convert.glsl
|
||||
shaders/compositor/compositor_realize_on_domain.glsl
|
||||
shaders/compositor/compositor_set_alpha.glsl
|
||||
shaders/compositor/compositor_split_viewer.glsl
|
||||
|
||||
shaders/compositor/library/gpu_shader_compositor_main.glsl
|
||||
shaders/compositor/library/gpu_shader_compositor_store_output.glsl
|
||||
|
@ -538,6 +540,8 @@ set(SRC_SHADER_CREATE_INFOS
|
|||
|
||||
shaders/compositor/infos/compositor_convert_info.hh
|
||||
shaders/compositor/infos/compositor_realize_on_domain_info.hh
|
||||
shaders/compositor/infos/compositor_set_alpha_info.hh
|
||||
shaders/compositor/infos/compositor_split_viewer_info.hh
|
||||
)
|
||||
|
||||
set(SHADER_CREATE_INFOS_CONTENT "")
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
vec4 color = vec4(texture_load(image_tx, texel).rgb, texture_load(alpha_tx, texel).x);
|
||||
imageStore(output_img, texel, color);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
#if defined(SPLIT_HORIZONTAL)
|
||||
bool condition = (view_size.x * split_ratio) < texel.x;
|
||||
#elif defined(SPLIT_VERTICAL)
|
||||
bool condition = (view_size.y * split_ratio) < texel.y;
|
||||
#endif
|
||||
vec4 color = condition ? texture_load(first_image_tx, texel) :
|
||||
texture_load(second_image_tx, texel);
|
||||
imageStore(output_img, texel, color);
|
||||
}
|
|
@ -61,3 +61,9 @@ GPU_SHADER_CREATE_INFO(compositor_convert_float_to_half_float)
|
|||
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.define("CONVERT_EXPRESSION(value)", "vec4(value.r, vec3(0.0))")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_convert_color_to_opaque)
|
||||
.additional_info("compositor_convert_shared")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.define("CONVERT_EXPRESSION(value)", "vec4(value.rgb, 1.0)")
|
||||
.do_static_compilation(true);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_set_alpha)
|
||||
.local_group_size(16, 16)
|
||||
.sampler(0, ImageType::FLOAT_2D, "image_tx")
|
||||
.sampler(1, ImageType::FLOAT_2D, "alpha_tx")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.compute_source("compositor_set_alpha.glsl")
|
||||
.do_static_compilation(true);
|
|
@ -0,0 +1,22 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_split_viewer_shared)
|
||||
.local_group_size(16, 16)
|
||||
.push_constant(Type::FLOAT, "split_ratio")
|
||||
.push_constant(Type::IVEC2, "view_size")
|
||||
.sampler(0, ImageType::FLOAT_2D, "first_image_tx")
|
||||
.sampler(1, ImageType::FLOAT_2D, "second_image_tx")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.compute_source("compositor_split_viewer.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_split_viewer_horizontal)
|
||||
.additional_info("compositor_split_viewer_shared")
|
||||
.define("SPLIT_HORIZONTAL")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_split_viewer_vertical)
|
||||
.additional_info("compositor_split_viewer_shared")
|
||||
.define("SPLIT_VERTICAL")
|
||||
.do_static_compilation(true);
|
|
@ -1838,6 +1838,12 @@ enum {
|
|||
/* viewer and composite output. */
|
||||
#define CMP_NODE_OUTPUT_IGNORE_ALPHA 1
|
||||
|
||||
/* Split Viewer Node. Stored in custom2. */
|
||||
typedef enum CMPNodeSplitViewerAxis {
|
||||
CMP_NODE_SPLIT_VIEWER_HORIZONTAL = 0,
|
||||
CMP_NODE_SPLIT_VIEWER_VERTICAL = 1,
|
||||
} CMPNodeSplitViewerAxis;
|
||||
|
||||
/* Plane track deform node. */
|
||||
|
||||
enum {
|
||||
|
|
|
@ -10,11 +10,13 @@ set(INC
|
|||
../../blenlib
|
||||
../../blentranslation
|
||||
../../depsgraph
|
||||
../../gpu
|
||||
../../imbuf
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../render
|
||||
../../windowmanager
|
||||
../../compositor/realtime_compositor
|
||||
../../../../intern/guardedalloc
|
||||
|
||||
# dna_type_offsets.h
|
||||
|
@ -120,6 +122,10 @@ set(SRC
|
|||
node_composite_util.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
bf_realtime_compositor
|
||||
)
|
||||
|
||||
if(WITH_IMAGE_OPENEXR)
|
||||
add_definitions(-DWITH_OPENEXR)
|
||||
endif()
|
||||
|
|
|
@ -5,9 +5,18 @@
|
|||
* \ingroup cmpnodes
|
||||
*/
|
||||
|
||||
#include "BLI_math_vec_types.hh"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_state.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
/* **************** COMPOSITE ******************** */
|
||||
|
@ -26,6 +35,125 @@ static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C),
|
|||
uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
|
||||
}
|
||||
|
||||
using namespace blender::realtime_compositor;
|
||||
|
||||
class CompositeOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
const Result &image = get_input("Image");
|
||||
const Result &alpha = get_input("Alpha");
|
||||
|
||||
if (image.is_single_value() && alpha.is_single_value()) {
|
||||
execute_clear();
|
||||
}
|
||||
else if (ignore_alpha()) {
|
||||
execute_ignore_alpha();
|
||||
}
|
||||
else if (!node().input_by_identifier("Alpha")->is_logically_linked()) {
|
||||
execute_copy();
|
||||
}
|
||||
else {
|
||||
execute_set_alpha();
|
||||
}
|
||||
}
|
||||
|
||||
/* Executes when all inputs are single values, in which case, the output texture can just be
|
||||
* cleared to the appropriate color. */
|
||||
void execute_clear()
|
||||
{
|
||||
const Result &image = get_input("Image");
|
||||
const Result &alpha = get_input("Alpha");
|
||||
|
||||
float4 color = image.get_color_value();
|
||||
if (ignore_alpha()) {
|
||||
color.w = 1.0f;
|
||||
}
|
||||
else if (node().input_by_identifier("Alpha")->is_logically_linked()) {
|
||||
color.w = alpha.get_float_value();
|
||||
}
|
||||
|
||||
GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color);
|
||||
}
|
||||
|
||||
/* Executes when the alpha channel of the image is ignored. */
|
||||
void execute_ignore_alpha()
|
||||
{
|
||||
GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
const Result &image = get_input("Image");
|
||||
image.bind_as_texture(shader, "input_tx");
|
||||
|
||||
GPUTexture *output_texture = context().get_output_texture();
|
||||
const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
|
||||
GPU_texture_image_bind(output_texture, image_unit);
|
||||
|
||||
compute_dispatch_threads_at_least(shader, compute_domain().size);
|
||||
|
||||
image.unbind_as_texture();
|
||||
GPU_texture_image_unbind(output_texture);
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
/* Executes when the image texture is written with no adjustments and can thus be copied directly
|
||||
* to the output texture. */
|
||||
void execute_copy()
|
||||
{
|
||||
const Result &image = get_input("Image");
|
||||
|
||||
/* Make sure any prior writes to the texture are reflected before copying it. */
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
|
||||
|
||||
GPU_texture_copy(context().get_output_texture(), image.texture());
|
||||
}
|
||||
|
||||
/* Executes when the alpha channel of the image is set as the value of the input alpha. */
|
||||
void execute_set_alpha()
|
||||
{
|
||||
GPUShader *shader = shader_manager().get("compositor_set_alpha");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
const Result &image = get_input("Image");
|
||||
image.bind_as_texture(shader, "image_tx");
|
||||
|
||||
const Result &alpha = get_input("Alpha");
|
||||
alpha.bind_as_texture(shader, "alpha_tx");
|
||||
|
||||
GPUTexture *output_texture = context().get_output_texture();
|
||||
const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
|
||||
GPU_texture_image_bind(output_texture, image_unit);
|
||||
|
||||
compute_dispatch_threads_at_least(shader, compute_domain().size);
|
||||
|
||||
image.unbind_as_texture();
|
||||
alpha.unbind_as_texture();
|
||||
GPU_texture_image_unbind(output_texture);
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
/* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the
|
||||
* alpha channel of the image is retained, but only if the alpha input is not linked. If the
|
||||
* alpha input is linked, it the value of that input will be used as the alpha of the image. */
|
||||
bool ignore_alpha()
|
||||
{
|
||||
return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA;
|
||||
}
|
||||
|
||||
/* The operation domain have the same dimensions of the output without any transformations. */
|
||||
Domain compute_domain() override
|
||||
{
|
||||
return Domain(context().get_output_size());
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, DNode node)
|
||||
{
|
||||
return new CompositeOperation(context, node);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_composite_composite_cc
|
||||
|
||||
void register_node_type_cmp_composite()
|
||||
|
@ -37,6 +165,7 @@ void register_node_type_cmp_composite()
|
|||
cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT);
|
||||
ntype.declare = file_ns::cmp_node_composite_declare;
|
||||
ntype.draw_buttons = file_ns::node_composit_buts_composite;
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
ntype.flag |= NODE_PREVIEW;
|
||||
ntype.no_muting = true;
|
||||
|
||||
|
|
|
@ -11,6 +11,12 @@
|
|||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
/* **************** SPLIT VIEWER ******************** */
|
||||
|
@ -43,6 +49,70 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C)
|
|||
uiItemR(col, ptr, "factor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
|
||||
}
|
||||
|
||||
using namespace blender::realtime_compositor;
|
||||
|
||||
class ViewerOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
GPUShader *shader = get_split_viewer_shader();
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
const int2 size = compute_domain().size;
|
||||
|
||||
GPU_shader_uniform_1f(shader, "split_ratio", get_split_ratio());
|
||||
GPU_shader_uniform_2iv(shader, "view_size", size);
|
||||
|
||||
const Result &first_image = get_input("Image");
|
||||
first_image.bind_as_texture(shader, "first_image_tx");
|
||||
const Result &second_image = get_input("Image_001");
|
||||
second_image.bind_as_texture(shader, "second_image_tx");
|
||||
|
||||
GPUTexture *output_texture = context().get_output_texture();
|
||||
const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
|
||||
GPU_texture_image_bind(output_texture, image_unit);
|
||||
|
||||
compute_dispatch_threads_at_least(shader, size);
|
||||
|
||||
first_image.unbind_as_texture();
|
||||
second_image.unbind_as_texture();
|
||||
GPU_texture_image_unbind(output_texture);
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
/* The operation domain have the same dimensions of the output without any transformations. */
|
||||
Domain compute_domain() override
|
||||
{
|
||||
return Domain(context().get_output_size());
|
||||
}
|
||||
|
||||
GPUShader *get_split_viewer_shader()
|
||||
{
|
||||
if (get_split_axis() == CMP_NODE_SPLIT_VIEWER_HORIZONTAL) {
|
||||
return shader_manager().get("compositor_split_viewer_horizontal");
|
||||
}
|
||||
|
||||
return shader_manager().get("compositor_split_viewer_vertical");
|
||||
}
|
||||
|
||||
CMPNodeSplitViewerAxis get_split_axis()
|
||||
{
|
||||
return (CMPNodeSplitViewerAxis)bnode().custom2;
|
||||
}
|
||||
|
||||
float get_split_ratio()
|
||||
{
|
||||
return bnode().custom1 / 100.0f;
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, DNode node)
|
||||
{
|
||||
return new ViewerOperation(context, node);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_composite_split_viewer_cc
|
||||
|
||||
void register_node_type_cmp_splitviewer()
|
||||
|
@ -57,6 +127,7 @@ void register_node_type_cmp_splitviewer()
|
|||
ntype.flag |= NODE_PREVIEW;
|
||||
node_type_init(&ntype, file_ns::node_composit_init_splitviewer);
|
||||
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
|
||||
ntype.no_muting = true;
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* \ingroup cmpnodes
|
||||
*/
|
||||
|
||||
#include "BLI_math_vec_types.hh"
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_image.h"
|
||||
|
||||
|
@ -13,6 +15,13 @@
|
|||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_state.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
/* **************** VIEWER ******************** */
|
||||
|
@ -55,6 +64,125 @@ static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C),
|
|||
}
|
||||
}
|
||||
|
||||
using namespace blender::realtime_compositor;
|
||||
|
||||
class ViewerOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
const Result &image = get_input("Image");
|
||||
const Result &alpha = get_input("Alpha");
|
||||
|
||||
if (image.is_single_value() && alpha.is_single_value()) {
|
||||
execute_clear();
|
||||
}
|
||||
else if (ignore_alpha()) {
|
||||
execute_ignore_alpha();
|
||||
}
|
||||
else if (!node().input_by_identifier("Alpha")->is_logically_linked()) {
|
||||
execute_copy();
|
||||
}
|
||||
else {
|
||||
execute_set_alpha();
|
||||
}
|
||||
}
|
||||
|
||||
/* Executes when all inputs are single values, in which case, the output texture can just be
|
||||
* cleared to the appropriate color. */
|
||||
void execute_clear()
|
||||
{
|
||||
const Result &image = get_input("Image");
|
||||
const Result &alpha = get_input("Alpha");
|
||||
|
||||
float4 color = image.get_color_value();
|
||||
if (ignore_alpha()) {
|
||||
color.w = 1.0f;
|
||||
}
|
||||
else if (node().input_by_identifier("Alpha")->is_logically_linked()) {
|
||||
color.w = alpha.get_float_value();
|
||||
}
|
||||
|
||||
GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color);
|
||||
}
|
||||
|
||||
/* Executes when the alpha channel of the image is ignored. */
|
||||
void execute_ignore_alpha()
|
||||
{
|
||||
GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
const Result &image = get_input("Image");
|
||||
image.bind_as_texture(shader, "input_tx");
|
||||
|
||||
GPUTexture *output_texture = context().get_output_texture();
|
||||
const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
|
||||
GPU_texture_image_bind(output_texture, image_unit);
|
||||
|
||||
compute_dispatch_threads_at_least(shader, compute_domain().size);
|
||||
|
||||
image.unbind_as_texture();
|
||||
GPU_texture_image_unbind(output_texture);
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
/* Executes when the image texture is written with no adjustments and can thus be copied directly
|
||||
* to the output texture. */
|
||||
void execute_copy()
|
||||
{
|
||||
const Result &image = get_input("Image");
|
||||
|
||||
/* Make sure any prior writes to the texture are reflected before copying it. */
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
|
||||
|
||||
GPU_texture_copy(context().get_output_texture(), image.texture());
|
||||
}
|
||||
|
||||
/* Executes when the alpha channel of the image is set as the value of the input alpha. */
|
||||
void execute_set_alpha()
|
||||
{
|
||||
GPUShader *shader = shader_manager().get("compositor_set_alpha");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
const Result &image = get_input("Image");
|
||||
image.bind_as_texture(shader, "image_tx");
|
||||
|
||||
const Result &alpha = get_input("Alpha");
|
||||
alpha.bind_as_texture(shader, "alpha_tx");
|
||||
|
||||
GPUTexture *output_texture = context().get_output_texture();
|
||||
const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
|
||||
GPU_texture_image_bind(output_texture, image_unit);
|
||||
|
||||
compute_dispatch_threads_at_least(shader, compute_domain().size);
|
||||
|
||||
image.unbind_as_texture();
|
||||
alpha.unbind_as_texture();
|
||||
GPU_texture_image_unbind(output_texture);
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
/* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the
|
||||
* alpha channel of the image is retained, but only if the alpha input is not linked. If the
|
||||
* alpha input is linked, it the value of that input will be used as the alpha of the image. */
|
||||
bool ignore_alpha()
|
||||
{
|
||||
return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA;
|
||||
}
|
||||
|
||||
/* The operation domain have the same dimensions of the output without any transformations. */
|
||||
Domain compute_domain() override
|
||||
{
|
||||
return Domain(context().get_output_size());
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, DNode node)
|
||||
{
|
||||
return new ViewerOperation(context, node);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_composite_viewer_cc
|
||||
|
||||
void register_node_type_cmp_viewer()
|
||||
|
@ -70,6 +198,7 @@ void register_node_type_cmp_viewer()
|
|||
ntype.flag |= NODE_PREVIEW;
|
||||
node_type_init(&ntype, file_ns::node_composit_init_viewer);
|
||||
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
|
||||
ntype.no_muting = true;
|
||||
|
||||
|
|
Loading…
Reference in New Issue