Compositor: Add Image Coordinates node
This patch adds a new Image Coordinates node for the compositor. The Texture Coordinates and Pixel Coordinates outputs were removed from the Image Info node and were added to the new node instead. Additionally, a new Normalized Coordinates output was added. The Pixel Coordinates output now no longer includes half pixel offsets. Pull Request: https://projects.blender.org/blender/blender/pulls/138935
This commit is contained in:
@@ -27,6 +27,7 @@ class NODE_MT_category_compositor_input(Menu):
|
||||
node_add_menu.add_node_type(layout, "CompositorNodeMovieClip")
|
||||
node_add_menu.add_node_type(layout, "CompositorNodeTexture")
|
||||
node_add_menu.add_node_type(layout, "CompositorNodeImageInfo")
|
||||
node_add_menu.add_node_type(layout, "CompositorNodeImageCoordinates")
|
||||
|
||||
if is_group:
|
||||
layout.separator()
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 77
|
||||
#define BLENDER_FILE_SUBVERSION 78
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
||||
@@ -3976,6 +3976,80 @@ static void do_version_color_balance_node_options_to_inputs_animation(bNodeTree
|
||||
});
|
||||
}
|
||||
|
||||
/* The Coordinates outputs were moved into their own Texture Coordinate node. If used, add a
|
||||
* Texture Coordinates node and use it instead. */
|
||||
static void do_version_replace_image_info_node_coordinates(bNodeTree *node_tree)
|
||||
{
|
||||
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
|
||||
if (!STREQ(node->idname, "CompositorNodeImageInfo")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bNodeLink *input_link = nullptr;
|
||||
bNodeLink *output_texture_link = nullptr;
|
||||
bNodeLink *output_pixel_link = nullptr;
|
||||
LISTBASE_FOREACH (bNodeLink *, link, &node_tree->links) {
|
||||
if (link->tonode == node) {
|
||||
input_link = link;
|
||||
}
|
||||
|
||||
if (link->fromnode == node &&
|
||||
blender::StringRef(link->fromsock->identifier) == "Texture Coordinates")
|
||||
{
|
||||
output_texture_link = link;
|
||||
}
|
||||
|
||||
if (link->fromnode == node &&
|
||||
blender::StringRef(link->fromsock->identifier) == "Pixel Coordinates")
|
||||
{
|
||||
output_pixel_link = link;
|
||||
}
|
||||
}
|
||||
|
||||
if (!output_texture_link && !output_pixel_link) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bNode *image_coordinates_node = blender::bke::node_add_node(
|
||||
nullptr, *node_tree, "CompositorNodeImageCoordinates");
|
||||
image_coordinates_node->parent = node->parent;
|
||||
image_coordinates_node->location[0] = node->location[0];
|
||||
image_coordinates_node->location[1] = node->location[1] - node->height - 10.0f;
|
||||
|
||||
if (input_link) {
|
||||
bNodeSocket *image_input = blender::bke::node_find_socket(
|
||||
*image_coordinates_node, SOCK_IN, "Image");
|
||||
version_node_add_link(*node_tree,
|
||||
*input_link->fromnode,
|
||||
*input_link->fromsock,
|
||||
*image_coordinates_node,
|
||||
*image_input);
|
||||
}
|
||||
|
||||
if (output_texture_link) {
|
||||
bNodeSocket *uniform_output = blender::bke::node_find_socket(
|
||||
*image_coordinates_node, SOCK_OUT, "Uniform");
|
||||
version_node_add_link(*node_tree,
|
||||
*image_coordinates_node,
|
||||
*uniform_output,
|
||||
*output_texture_link->tonode,
|
||||
*output_texture_link->tosock);
|
||||
blender::bke::node_remove_link(node_tree, *output_texture_link);
|
||||
}
|
||||
|
||||
if (output_pixel_link) {
|
||||
bNodeSocket *pixel_output = blender::bke::node_find_socket(
|
||||
*image_coordinates_node, SOCK_OUT, "Pixel");
|
||||
version_node_add_link(*node_tree,
|
||||
*image_coordinates_node,
|
||||
*pixel_output,
|
||||
*output_pixel_link->tonode,
|
||||
*output_pixel_link->tosock);
|
||||
blender::bke::node_remove_link(node_tree, *output_pixel_link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_versions_after_linking_450(FileData * /*fd*/, Main *bmain)
|
||||
{
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 8)) {
|
||||
@@ -5737,6 +5811,15 @@ void blo_do_versions_450(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
|
||||
FOREACH_NODETREE_END;
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 78)) {
|
||||
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
|
||||
if (node_tree->type == NTREE_COMPOSIT) {
|
||||
do_version_replace_image_info_node_coordinates(node_tree);
|
||||
}
|
||||
}
|
||||
FOREACH_NODETREE_END;
|
||||
}
|
||||
|
||||
/* Always run this versioning (keep at the bottom of the function). Meshes are written with the
|
||||
* legacy format which always needs to be converted to the new format on file load. To be moved
|
||||
* to a subversion check in 5.0. */
|
||||
|
||||
@@ -106,14 +106,13 @@ set(SRC
|
||||
cached_resources/intern/deriche_gaussian_coefficients.cc
|
||||
cached_resources/intern/distortion_grid.cc
|
||||
cached_resources/intern/fog_glow_kernel.cc
|
||||
cached_resources/intern/image_coordinates.cc
|
||||
cached_resources/intern/keying_screen.cc
|
||||
cached_resources/intern/morphological_distance_feather_weights.cc
|
||||
cached_resources/intern/ocio_color_space_conversion_shader.cc
|
||||
cached_resources/intern/pixel_coordinates.cc
|
||||
cached_resources/intern/smaa_precomputed_textures.cc
|
||||
cached_resources/intern/symmetric_blur_weights.cc
|
||||
cached_resources/intern/symmetric_separable_blur_weights.cc
|
||||
cached_resources/intern/texture_coordinates.cc
|
||||
cached_resources/intern/van_vliet_gaussian_coefficients.cc
|
||||
|
||||
cached_resources/COM_bokeh_kernel.hh
|
||||
@@ -125,14 +124,13 @@ set(SRC
|
||||
cached_resources/COM_deriche_gaussian_coefficients.hh
|
||||
cached_resources/COM_distortion_grid.hh
|
||||
cached_resources/COM_fog_glow_kernel.hh
|
||||
cached_resources/COM_image_coordinates.hh
|
||||
cached_resources/COM_keying_screen.hh
|
||||
cached_resources/COM_morphological_distance_feather_weights.hh
|
||||
cached_resources/COM_ocio_color_space_conversion_shader.hh
|
||||
cached_resources/COM_pixel_coordinates.hh
|
||||
cached_resources/COM_smaa_precomputed_textures.hh
|
||||
cached_resources/COM_symmetric_blur_weights.hh
|
||||
cached_resources/COM_symmetric_separable_blur_weights.hh
|
||||
cached_resources/COM_texture_coordinates.hh
|
||||
cached_resources/COM_van_vliet_gaussian_coefficients.hh
|
||||
|
||||
derived_resources/intern/denoised_auxiliary_pass.cc
|
||||
@@ -205,6 +203,9 @@ set(GLSL_SRC
|
||||
shaders/compositor_glare_write_highlights_output.glsl
|
||||
shaders/compositor_horizontal_lens_distortion.glsl
|
||||
shaders/compositor_id_mask.glsl
|
||||
shaders/compositor_image_coordinates_normalized.glsl
|
||||
shaders/compositor_image_coordinates_pixel.glsl
|
||||
shaders/compositor_image_coordinates_uniform.glsl
|
||||
shaders/compositor_image_crop.glsl
|
||||
shaders/compositor_inpaint_compute_boundary.glsl
|
||||
shaders/compositor_inpaint_compute_region.glsl
|
||||
@@ -231,7 +232,6 @@ set(GLSL_SRC
|
||||
shaders/compositor_movie_distortion.glsl
|
||||
shaders/compositor_normalize.glsl
|
||||
shaders/compositor_parallel_reduction.glsl
|
||||
shaders/compositor_pixel_coordinates.glsl
|
||||
shaders/compositor_pixelate.glsl
|
||||
shaders/compositor_plane_deform_anisotropic.glsl
|
||||
shaders/compositor_plane_deform_mask.glsl
|
||||
@@ -256,7 +256,6 @@ set(GLSL_SRC
|
||||
shaders/compositor_symmetric_blur_variable_size.glsl
|
||||
shaders/compositor_symmetric_separable_blur.glsl
|
||||
shaders/compositor_symmetric_separable_blur_variable_size.glsl
|
||||
shaders/compositor_texture_coordinates.glsl
|
||||
shaders/compositor_tone_map_photoreceptor.glsl
|
||||
shaders/compositor_tone_map_simple.glsl
|
||||
shaders/compositor_translate_wrapped.glsl
|
||||
|
||||
@@ -12,14 +12,13 @@
|
||||
#include "COM_deriche_gaussian_coefficients.hh"
|
||||
#include "COM_distortion_grid.hh"
|
||||
#include "COM_fog_glow_kernel.hh"
|
||||
#include "COM_image_coordinates.hh"
|
||||
#include "COM_keying_screen.hh"
|
||||
#include "COM_morphological_distance_feather_weights.hh"
|
||||
#include "COM_ocio_color_space_conversion_shader.hh"
|
||||
#include "COM_pixel_coordinates.hh"
|
||||
#include "COM_smaa_precomputed_textures.hh"
|
||||
#include "COM_symmetric_blur_weights.hh"
|
||||
#include "COM_symmetric_separable_blur_weights.hh"
|
||||
#include "COM_texture_coordinates.hh"
|
||||
#include "COM_van_vliet_gaussian_coefficients.hh"
|
||||
|
||||
namespace blender::compositor {
|
||||
@@ -65,8 +64,7 @@ class StaticCacheManager {
|
||||
DericheGaussianCoefficientsContainer deriche_gaussian_coefficients;
|
||||
VanVlietGaussianCoefficientsContainer van_vliet_gaussian_coefficients;
|
||||
FogGlowKernelContainer fog_glow_kernels;
|
||||
TextureCoordinatesContainer texture_coordinates;
|
||||
PixelCoordinatesContainer pixel_coordinates;
|
||||
ImageCoordinatesContainer image_coordinates;
|
||||
|
||||
private:
|
||||
/* The cache manager should skip the next reset. See the skip_next_reset() method for more
|
||||
|
||||
@@ -17,55 +17,61 @@ namespace blender::compositor {
|
||||
|
||||
class Context;
|
||||
|
||||
enum class CoordinatesType : uint8_t {
|
||||
Uniform,
|
||||
Normalized,
|
||||
Pixel,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Pixel Coordinates Key.
|
||||
* Image Coordinates Key.
|
||||
*/
|
||||
class PixelCoordinatesKey {
|
||||
class ImageCoordinatesKey {
|
||||
public:
|
||||
int2 size;
|
||||
CoordinatesType type;
|
||||
|
||||
PixelCoordinatesKey(const int2 &size);
|
||||
ImageCoordinatesKey(const int2 &size, const CoordinatesType type);
|
||||
|
||||
uint64_t hash() const;
|
||||
};
|
||||
|
||||
bool operator==(const PixelCoordinatesKey &a, const PixelCoordinatesKey &b);
|
||||
bool operator==(const ImageCoordinatesKey &a, const ImageCoordinatesKey &b);
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Pixel Coordinates.
|
||||
* Image Coordinates.
|
||||
*
|
||||
* A cached resource that computes and caches a result containing the pixel coordinates of an image
|
||||
* with the given size. The coordinates represent the center of pixels, so they include half pixel
|
||||
* offsets. */
|
||||
class PixelCoordinates : public CachedResource {
|
||||
* A cached resource that computes and caches a result containing the coordinates of the pixels of
|
||||
* an image with the given size. */
|
||||
class ImageCoordinates : public CachedResource {
|
||||
public:
|
||||
Result result;
|
||||
|
||||
PixelCoordinates(Context &context, const int2 &size);
|
||||
ImageCoordinates(Context &context, const int2 &size, const CoordinatesType type);
|
||||
|
||||
~PixelCoordinates();
|
||||
~ImageCoordinates();
|
||||
|
||||
private:
|
||||
void compute_gpu(Context &context);
|
||||
void compute_gpu(Context &context, const CoordinatesType type);
|
||||
|
||||
void compute_cpu();
|
||||
void compute_cpu(const CoordinatesType type);
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Pixel Coordinates Container.
|
||||
* Image Coordinates Container.
|
||||
*/
|
||||
class PixelCoordinatesContainer : CachedResourceContainer {
|
||||
class ImageCoordinatesContainer : CachedResourceContainer {
|
||||
private:
|
||||
Map<PixelCoordinatesKey, std::unique_ptr<PixelCoordinates>> map_;
|
||||
Map<ImageCoordinatesKey, std::unique_ptr<ImageCoordinates>> map_;
|
||||
|
||||
public:
|
||||
void reset() override;
|
||||
|
||||
/* Check if there is an available PixelCoordinates cached resource with the given parameters in
|
||||
/* Check if there is an available ImageCoordinates cached resource with the given parameters in
|
||||
* the container, if one exists, return it, otherwise, return a newly created one and add it to
|
||||
* the container. In both cases, tag the cached resource as needed to keep it cached for the next
|
||||
* evaluation. */
|
||||
Result &get(Context &context, const int2 &size);
|
||||
Result &get(Context &context, const int2 &size, const CoordinatesType type);
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
@@ -1,72 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "COM_cached_resource.hh"
|
||||
#include "COM_result.hh"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class Context;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Texture Coordinates Key.
|
||||
*/
|
||||
class TextureCoordinatesKey {
|
||||
public:
|
||||
int2 size;
|
||||
|
||||
TextureCoordinatesKey(const int2 &size);
|
||||
|
||||
uint64_t hash() const;
|
||||
};
|
||||
|
||||
bool operator==(const TextureCoordinatesKey &a, const TextureCoordinatesKey &b);
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Texture Coordinates.
|
||||
*
|
||||
* A cached resource that computes and caches a result containing the texture coordinates of an
|
||||
* image with the given size. The texture coordinates are the zero centered pixel coordinates
|
||||
* normalized along the greater dimension. Pixel coordinates represent the center of pixels, so
|
||||
* they include half pixel offsets. */
|
||||
class TextureCoordinates : public CachedResource {
|
||||
public:
|
||||
Result result;
|
||||
|
||||
TextureCoordinates(Context &context, const int2 &size);
|
||||
|
||||
~TextureCoordinates();
|
||||
|
||||
private:
|
||||
void compute_gpu(Context &context);
|
||||
|
||||
void compute_cpu();
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Texture Coordinates Container.
|
||||
*/
|
||||
class TextureCoordinatesContainer : CachedResourceContainer {
|
||||
private:
|
||||
Map<TextureCoordinatesKey, std::unique_ptr<TextureCoordinates>> map_;
|
||||
|
||||
public:
|
||||
void reset() override;
|
||||
|
||||
/* Check if there is an available TextureCoordinates cached resource with the given parameters in
|
||||
* the container, if one exists, return it, otherwise, return a newly created one and add it to
|
||||
* the container. In both cases, tag the cached resource as needed to keep it cached for the next
|
||||
* evaluation. */
|
||||
Result &get(Context &context, const int2 &size);
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
@@ -0,0 +1,149 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_image_coordinates.hh"
|
||||
#include "COM_result.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Image Coordinates Key.
|
||||
*/
|
||||
|
||||
ImageCoordinatesKey::ImageCoordinatesKey(const int2 &size, const CoordinatesType type)
|
||||
: size(size), type(type)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t ImageCoordinatesKey::hash() const
|
||||
{
|
||||
return get_default_hash(this->size, this->type);
|
||||
}
|
||||
|
||||
bool operator==(const ImageCoordinatesKey &a, const ImageCoordinatesKey &b)
|
||||
{
|
||||
return a.size == b.size && a.type == b.type;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Image Coordinates.
|
||||
*/
|
||||
|
||||
ImageCoordinates::ImageCoordinates(Context &context, const int2 &size, const CoordinatesType type)
|
||||
: result(context.create_result(ResultType::Float3))
|
||||
{
|
||||
this->result.allocate_texture(Domain(size), false);
|
||||
|
||||
if (context.use_gpu()) {
|
||||
this->compute_gpu(context, type);
|
||||
}
|
||||
else {
|
||||
this->compute_cpu(type);
|
||||
}
|
||||
}
|
||||
|
||||
ImageCoordinates::~ImageCoordinates()
|
||||
{
|
||||
this->result.release();
|
||||
}
|
||||
|
||||
static const char *get_shader_name(const CoordinatesType type)
|
||||
{
|
||||
switch (type) {
|
||||
case CoordinatesType::Uniform:
|
||||
return "compositor_image_coordinates_uniform";
|
||||
case CoordinatesType::Normalized:
|
||||
return "compositor_image_coordinates_normalized";
|
||||
case CoordinatesType::Pixel:
|
||||
return "compositor_image_coordinates_pixel";
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return "";
|
||||
}
|
||||
|
||||
void ImageCoordinates::compute_gpu(Context &context, const CoordinatesType type)
|
||||
{
|
||||
GPUShader *shader = context.get_shader(get_shader_name(type));
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
this->result.bind_as_image(shader, "output_img");
|
||||
|
||||
compute_dispatch_threads_at_least(shader, this->result.domain().size);
|
||||
|
||||
this->result.unbind_as_image();
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
void ImageCoordinates::compute_cpu(const CoordinatesType type)
|
||||
{
|
||||
switch (type) {
|
||||
case CoordinatesType::Uniform: {
|
||||
const int2 size = this->result.domain().size;
|
||||
const int max_size = math::max(size.x, size.y);
|
||||
parallel_for(size, [&](const int2 texel) {
|
||||
float2 centered_coordinates = (float2(texel) + 0.5f) - float2(size) / 2.0f;
|
||||
float2 normalized_coordinates = (centered_coordinates / max_size) * 2.0f;
|
||||
this->result.store_pixel(texel, float3(normalized_coordinates, 0.0f));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case CoordinatesType::Normalized: {
|
||||
const int2 size = this->result.domain().size;
|
||||
parallel_for(size, [&](const int2 texel) {
|
||||
float2 normalized_coordinates = (float2(texel) + 0.5f) / float2(size);
|
||||
this->result.store_pixel(texel, float3(normalized_coordinates, 0.0f));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case CoordinatesType::Pixel: {
|
||||
parallel_for(this->result.domain().size, [&](const int2 texel) {
|
||||
this->result.store_pixel(texel, float3(float2(texel), 0.0f));
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Image Coordinates Container.
|
||||
*/
|
||||
|
||||
void ImageCoordinatesContainer::reset()
|
||||
{
|
||||
/* First, delete all resources that are no longer needed. */
|
||||
map_.remove_if([](auto item) { return !item.value->needed; });
|
||||
|
||||
/* Second, reset the needed status of the remaining resources to false to ready them to track
|
||||
* their needed status for the next evaluation. */
|
||||
for (auto &value : map_.values()) {
|
||||
value->needed = false;
|
||||
}
|
||||
}
|
||||
|
||||
Result &ImageCoordinatesContainer::get(Context &context,
|
||||
const int2 &size,
|
||||
const CoordinatesType type)
|
||||
{
|
||||
const ImageCoordinatesKey key(size, type);
|
||||
|
||||
auto &pixel_coordinates = *map_.lookup_or_add_cb(
|
||||
key, [&]() { return std::make_unique<ImageCoordinates>(context, size, type); });
|
||||
|
||||
pixel_coordinates.needed = true;
|
||||
return pixel_coordinates.result;
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
@@ -1,105 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_pixel_coordinates.hh"
|
||||
#include "COM_result.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Pixel Coordinates Key.
|
||||
*/
|
||||
|
||||
PixelCoordinatesKey::PixelCoordinatesKey(const int2 &size) : size(size) {}
|
||||
|
||||
uint64_t PixelCoordinatesKey::hash() const
|
||||
{
|
||||
return get_default_hash(size);
|
||||
}
|
||||
|
||||
bool operator==(const PixelCoordinatesKey &a, const PixelCoordinatesKey &b)
|
||||
{
|
||||
return a.size == b.size;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Pixel Coordinates.
|
||||
*/
|
||||
|
||||
PixelCoordinates::PixelCoordinates(Context &context, const int2 &size)
|
||||
: result(context.create_result(ResultType::Float3))
|
||||
{
|
||||
this->result.allocate_texture(Domain(size), false);
|
||||
|
||||
if (context.use_gpu()) {
|
||||
this->compute_gpu(context);
|
||||
}
|
||||
else {
|
||||
this->compute_cpu();
|
||||
}
|
||||
}
|
||||
|
||||
PixelCoordinates::~PixelCoordinates()
|
||||
{
|
||||
this->result.release();
|
||||
}
|
||||
|
||||
void PixelCoordinates::compute_gpu(Context &context)
|
||||
{
|
||||
GPUShader *shader = context.get_shader("compositor_pixel_coordinates");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
this->result.bind_as_image(shader, "output_img");
|
||||
|
||||
compute_dispatch_threads_at_least(shader, this->result.domain().size);
|
||||
|
||||
this->result.unbind_as_image();
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
void PixelCoordinates::compute_cpu()
|
||||
{
|
||||
parallel_for(this->result.domain().size, [&](const int2 texel) {
|
||||
this->result.store_pixel(texel, float3(float2(texel) + float2(0.5f), 0.0f));
|
||||
});
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Pixel Coordinates Container.
|
||||
*/
|
||||
|
||||
void PixelCoordinatesContainer::reset()
|
||||
{
|
||||
/* First, delete all resources that are no longer needed. */
|
||||
map_.remove_if([](auto item) { return !item.value->needed; });
|
||||
|
||||
/* Second, reset the needed status of the remaining resources to false to ready them to track
|
||||
* their needed status for the next evaluation. */
|
||||
for (auto &value : map_.values()) {
|
||||
value->needed = false;
|
||||
}
|
||||
}
|
||||
|
||||
Result &PixelCoordinatesContainer::get(Context &context, const int2 &size)
|
||||
{
|
||||
const PixelCoordinatesKey key(size);
|
||||
|
||||
auto &pixel_coordinates = *map_.lookup_or_add_cb(
|
||||
key, [&]() { return std::make_unique<PixelCoordinates>(context, size); });
|
||||
|
||||
pixel_coordinates.needed = true;
|
||||
return pixel_coordinates.result;
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
@@ -1,112 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_result.hh"
|
||||
#include "COM_texture_coordinates.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Texture Coordinates Key.
|
||||
*/
|
||||
|
||||
TextureCoordinatesKey::TextureCoordinatesKey(const int2 &size) : size(size) {}
|
||||
|
||||
uint64_t TextureCoordinatesKey::hash() const
|
||||
{
|
||||
return get_default_hash(size);
|
||||
}
|
||||
|
||||
bool operator==(const TextureCoordinatesKey &a, const TextureCoordinatesKey &b)
|
||||
{
|
||||
return a.size == b.size;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Texture Coordinates.
|
||||
*/
|
||||
|
||||
TextureCoordinates::TextureCoordinates(Context &context, const int2 &size)
|
||||
: result(context.create_result(ResultType::Float3))
|
||||
{
|
||||
this->result.allocate_texture(Domain(size), false);
|
||||
|
||||
if (context.use_gpu()) {
|
||||
this->compute_gpu(context);
|
||||
}
|
||||
else {
|
||||
this->compute_cpu();
|
||||
}
|
||||
}
|
||||
|
||||
TextureCoordinates::~TextureCoordinates()
|
||||
{
|
||||
this->result.release();
|
||||
}
|
||||
|
||||
void TextureCoordinates::compute_gpu(Context &context)
|
||||
{
|
||||
GPUShader *shader = context.get_shader("compositor_texture_coordinates");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
this->result.bind_as_image(shader, "output_img");
|
||||
|
||||
compute_dispatch_threads_at_least(shader, this->result.domain().size);
|
||||
|
||||
this->result.unbind_as_image();
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
void TextureCoordinates::compute_cpu()
|
||||
{
|
||||
const int2 size = this->result.domain().size;
|
||||
parallel_for(size, [&](const int2 texel) {
|
||||
float2 centered_coordinates = (float2(texel) + 0.5f) - float2(size) / 2.0f;
|
||||
|
||||
int max_size = math::max(size.x, size.y);
|
||||
float2 normalized_coordinates = (centered_coordinates / max_size) * 2.0f;
|
||||
|
||||
this->result.store_pixel(texel, float3(normalized_coordinates, 0.0f));
|
||||
});
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Texture Coordinates Container.
|
||||
*/
|
||||
|
||||
void TextureCoordinatesContainer::reset()
|
||||
{
|
||||
/* First, delete all resources that are no longer needed. */
|
||||
map_.remove_if([](auto item) { return !item.value->needed; });
|
||||
|
||||
/* Second, reset the needed status of the remaining resources to false to ready them to track
|
||||
* their needed status for the next evaluation. */
|
||||
for (auto &value : map_.values()) {
|
||||
value->needed = false;
|
||||
}
|
||||
}
|
||||
|
||||
Result &TextureCoordinatesContainer::get(Context &context, const int2 &size)
|
||||
{
|
||||
const TextureCoordinatesKey key(size);
|
||||
|
||||
auto &texture_coordinates = *map_.lookup_or_add_cb(
|
||||
key, [&]() { return std::make_unique<TextureCoordinates>(context, size); });
|
||||
|
||||
texture_coordinates.needed = true;
|
||||
return texture_coordinates.result;
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
@@ -44,8 +44,8 @@ void ImplicitInputOperation::execute()
|
||||
break;
|
||||
case ImplicitInput::TextureCoordinates:
|
||||
const int2 size = this->context().get_compositing_region_size();
|
||||
result.wrap_external(
|
||||
this->context().cache_manager().texture_coordinates.get(this->context(), size));
|
||||
result.wrap_external(this->context().cache_manager().image_coordinates.get(
|
||||
this->context(), size, CoordinatesType::Uniform));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,7 @@ void StaticCacheManager::reset()
|
||||
deriche_gaussian_coefficients.reset();
|
||||
van_vliet_gaussian_coefficients.reset();
|
||||
fog_glow_kernels.reset();
|
||||
texture_coordinates.reset();
|
||||
pixel_coordinates.reset();
|
||||
image_coordinates.reset();
|
||||
}
|
||||
|
||||
void StaticCacheManager::skip_next_reset()
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
void main()
|
||||
{
|
||||
const int2 texel = int2(gl_GlobalInvocationID.xy);
|
||||
|
||||
const int2 size = imageSize(output_img);
|
||||
const float2 normalized_coordinates = (float2(texel) + 0.5f) / float2(size);
|
||||
|
||||
imageStore(output_img, texel, float4(normalized_coordinates, float2(0.0f)));
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
void main()
|
||||
{
|
||||
const int2 texel = int2(gl_GlobalInvocationID.xy);
|
||||
|
||||
imageStore(output_img, texel, float4(float2(texel), float2(0.0f)));
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
void main()
|
||||
{
|
||||
const int2 texel = int2(gl_GlobalInvocationID.xy);
|
||||
|
||||
const int2 size = imageSize(output_img);
|
||||
const float2 centered_coordinates = (float2(texel) + 0.5f) - float2(size) / 2.0f;
|
||||
|
||||
const int max_size = max(size.x, size.y);
|
||||
const float2 normalized_coordinates = (centered_coordinates / max_size) * 2.0f;
|
||||
|
||||
imageStore(output_img, texel, float4(normalized_coordinates, float2(0.0f)));
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
void main()
|
||||
{
|
||||
int2 texel = int2(gl_GlobalInvocationID.xy);
|
||||
|
||||
imageStore(output_img, texel, float4(float2(texel) + float2(0.5f), float2(0.0f)));
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
void main()
|
||||
{
|
||||
int2 texel = int2(gl_GlobalInvocationID.xy);
|
||||
|
||||
int2 size = imageSize(output_img);
|
||||
float2 centered_coordinates = (float2(texel) + 0.5f) - float2(size) / 2.0f;
|
||||
|
||||
int max_size = max(size.x, size.y);
|
||||
float2 normalized_coordinates = (centered_coordinates / max_size) * 2.0f;
|
||||
|
||||
imageStore(output_img, texel, float4(normalized_coordinates, float2(0.0f)));
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_image_coordinates_uniform)
|
||||
LOCAL_GROUP_SIZE(16, 16)
|
||||
IMAGE(0, GPU_RGBA16F, write, image2D, output_img)
|
||||
COMPUTE_SOURCE("compositor_image_coordinates_uniform.glsl")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_image_coordinates_normalized)
|
||||
LOCAL_GROUP_SIZE(16, 16)
|
||||
IMAGE(0, GPU_RGBA16F, write, image2D, output_img)
|
||||
COMPUTE_SOURCE("compositor_image_coordinates_normalized.glsl")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_image_coordinates_pixel)
|
||||
LOCAL_GROUP_SIZE(16, 16)
|
||||
IMAGE(0, GPU_RGBA16F, write, image2D, output_img)
|
||||
COMPUTE_SOURCE("compositor_image_coordinates_pixel.glsl")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
@@ -1,12 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_pixel_coordinates)
|
||||
LOCAL_GROUP_SIZE(16, 16)
|
||||
IMAGE(0, GPU_RGBA16F, write, image2D, output_img)
|
||||
COMPUTE_SOURCE("compositor_pixel_coordinates.glsl")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
@@ -1,12 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_texture_coordinates)
|
||||
LOCAL_GROUP_SIZE(16, 16)
|
||||
IMAGE(0, GPU_RGBA16F, write, image2D, output_img)
|
||||
COMPUTE_SOURCE("compositor_texture_coordinates.glsl")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
@@ -73,6 +73,7 @@
|
||||
#include "compositor_gamma_correct_info.hh"
|
||||
#include "compositor_glare_info.hh"
|
||||
#include "compositor_id_mask_info.hh"
|
||||
#include "compositor_image_coordinates_info.hh"
|
||||
#include "compositor_image_crop_info.hh"
|
||||
#include "compositor_inpaint_info.hh"
|
||||
#include "compositor_jump_flooding_info.hh"
|
||||
@@ -90,7 +91,6 @@
|
||||
#include "compositor_movie_distortion_info.hh"
|
||||
#include "compositor_normalize_info.hh"
|
||||
#include "compositor_parallel_reduction_info.hh"
|
||||
#include "compositor_pixel_coordinates_info.hh"
|
||||
#include "compositor_pixelate_info.hh"
|
||||
#include "compositor_plane_deform_info.hh"
|
||||
#include "compositor_premultiply_alpha_info.hh"
|
||||
@@ -105,7 +105,6 @@
|
||||
#include "compositor_symmetric_blur_variable_size_info.hh"
|
||||
#include "compositor_symmetric_separable_blur_info.hh"
|
||||
#include "compositor_symmetric_separable_blur_variable_size_info.hh"
|
||||
#include "compositor_texture_coordinates_info.hh"
|
||||
#include "compositor_tone_map_photoreceptor_info.hh"
|
||||
#include "compositor_tone_map_simple_info.hh"
|
||||
#include "compositor_translate_wrapped_info.hh"
|
||||
|
||||
@@ -13838,6 +13838,7 @@ static void rna_def_nodes(BlenderRNA *brna)
|
||||
define("CompositorNode", "CompositorNodeHueSat");
|
||||
define("CompositorNode", "CompositorNodeIDMask", def_cmp_id_mask);
|
||||
define("CompositorNode", "CompositorNodeImage", def_cmp_image);
|
||||
define("CompositorNode", "CompositorNodeImageCoordinates");
|
||||
define("CompositorNode", "CompositorNodeImageInfo");
|
||||
define("CompositorNode", "CompositorNodeInpaint", def_cmp_inpaint);
|
||||
define("CompositorNode", "CompositorNodeInvert", def_cmp_invert);
|
||||
|
||||
@@ -63,6 +63,7 @@ set(SRC
|
||||
nodes/node_composite_huecorrect.cc
|
||||
nodes/node_composite_id_mask.cc
|
||||
nodes/node_composite_image.cc
|
||||
nodes/node_composite_image_coordinates.cc
|
||||
nodes/node_composite_image_info.cc
|
||||
nodes/node_composite_inpaint.cc
|
||||
nodes/node_composite_invert.cc
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
namespace blender::nodes::node_composite_image_coordinates_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>("Image").hide_value().compositor_realization_mode(
|
||||
CompositorInputRealizationMode::None);
|
||||
|
||||
b.add_output<decl::Vector>("Uniform").description(
|
||||
"Zero centered coordinates normalizes along the larger dimension for uniform scaling");
|
||||
b.add_output<decl::Vector>("Normalized")
|
||||
.description("Normalized coordinates with half pixel offsets");
|
||||
b.add_output<decl::Vector>("Pixel").description("Integer pixel coordinates");
|
||||
}
|
||||
|
||||
using namespace blender::compositor;
|
||||
|
||||
class ImageCoordinatesOperation : public NodeOperation {
|
||||
public:
|
||||
ImageCoordinatesOperation(Context &context, DNode node) : NodeOperation(context, node)
|
||||
{
|
||||
InputDescriptor &image_descriptor = this->get_input_descriptor("Image");
|
||||
image_descriptor.skip_type_conversion = true;
|
||||
}
|
||||
|
||||
void execute() override
|
||||
{
|
||||
const Result &input = this->get_input("Image");
|
||||
Result &uniform_coordinates_result = this->get_result("Uniform");
|
||||
Result &normalized_coordinates_result = this->get_result("Normalized");
|
||||
Result &pixel_coordinates_result = this->get_result("Pixel");
|
||||
if (input.is_single_value()) {
|
||||
uniform_coordinates_result.allocate_invalid();
|
||||
normalized_coordinates_result.allocate_invalid();
|
||||
pixel_coordinates_result.allocate_invalid();
|
||||
return;
|
||||
}
|
||||
|
||||
const Domain domain = input.domain();
|
||||
|
||||
if (uniform_coordinates_result.should_compute()) {
|
||||
const Result &uniform_coordinates = this->context().cache_manager().image_coordinates.get(
|
||||
this->context(), domain.size, CoordinatesType::Uniform);
|
||||
uniform_coordinates_result.wrap_external(uniform_coordinates);
|
||||
uniform_coordinates_result.transform(domain.transformation);
|
||||
}
|
||||
|
||||
if (normalized_coordinates_result.should_compute()) {
|
||||
const Result &normalized_coordinates = this->context().cache_manager().image_coordinates.get(
|
||||
this->context(), domain.size, CoordinatesType::Normalized);
|
||||
normalized_coordinates_result.wrap_external(normalized_coordinates);
|
||||
normalized_coordinates_result.transform(domain.transformation);
|
||||
}
|
||||
|
||||
if (pixel_coordinates_result.should_compute()) {
|
||||
const Result &pixel_coordinates = this->context().cache_manager().image_coordinates.get(
|
||||
this->context(), domain.size, CoordinatesType::Pixel);
|
||||
pixel_coordinates_result.wrap_external(pixel_coordinates);
|
||||
pixel_coordinates_result.transform(domain.transformation);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, DNode node)
|
||||
{
|
||||
return new ImageCoordinatesOperation(context, node);
|
||||
}
|
||||
|
||||
static void register_node()
|
||||
{
|
||||
static blender::bke::bNodeType ntype;
|
||||
|
||||
cmp_node_type_base(&ntype, "CompositorNodeImageCoordinates");
|
||||
ntype.ui_name = "Image Coordinates";
|
||||
ntype.ui_description = "Returns the coordinates of the pixels of an image";
|
||||
ntype.nclass = NODE_CLASS_INPUT;
|
||||
ntype.declare = node_declare;
|
||||
ntype.get_compositor_operation = get_compositor_operation;
|
||||
|
||||
blender::bke::node_register_type(ntype);
|
||||
}
|
||||
NOD_REGISTER_NODE(register_node)
|
||||
|
||||
} // namespace blender::nodes::node_composite_image_coordinates_cc
|
||||
@@ -6,9 +6,6 @@
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
@@ -17,11 +14,9 @@ namespace blender::nodes::node_composite_image_info_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>("Image").compositor_domain_priority(0).compositor_realization_mode(
|
||||
b.add_input<decl::Color>("Image").compositor_realization_mode(
|
||||
CompositorInputRealizationMode::None);
|
||||
|
||||
b.add_output<decl::Vector>("Texture Coordinates");
|
||||
b.add_output<decl::Vector>("Pixel Coordinates");
|
||||
b.add_output<decl::Vector>("Resolution");
|
||||
b.add_output<decl::Vector>("Location");
|
||||
b.add_output<decl::Float>("Rotation");
|
||||
@@ -48,22 +43,6 @@ class ImageInfoOperation : public NodeOperation {
|
||||
|
||||
const Domain domain = input.domain();
|
||||
|
||||
Result &texture_coordinates_result = this->get_result("Texture Coordinates");
|
||||
if (texture_coordinates_result.should_compute()) {
|
||||
const Result &texture_coordinates = this->context().cache_manager().texture_coordinates.get(
|
||||
this->context(), domain.size);
|
||||
texture_coordinates_result.wrap_external(texture_coordinates);
|
||||
texture_coordinates_result.transform(domain.transformation);
|
||||
}
|
||||
|
||||
Result &pixel_coordinates_result = this->get_result("Pixel Coordinates");
|
||||
if (pixel_coordinates_result.should_compute()) {
|
||||
const Result &pixel_coordinates = this->context().cache_manager().pixel_coordinates.get(
|
||||
this->context(), domain.size);
|
||||
pixel_coordinates_result.wrap_external(pixel_coordinates);
|
||||
pixel_coordinates_result.transform(domain.transformation);
|
||||
}
|
||||
|
||||
Result &resolution_result = this->get_result("Resolution");
|
||||
if (resolution_result.should_compute()) {
|
||||
resolution_result.allocate_single_value();
|
||||
@@ -95,16 +74,6 @@ class ImageInfoOperation : public NodeOperation {
|
||||
|
||||
void execute_invalid()
|
||||
{
|
||||
Result &texture_coordinates_result = this->get_result("Texture Coordinates");
|
||||
if (texture_coordinates_result.should_compute()) {
|
||||
texture_coordinates_result.allocate_invalid();
|
||||
}
|
||||
|
||||
Result &pixel_coordinates_result = this->get_result("Pixel Coordinates");
|
||||
if (pixel_coordinates_result.should_compute()) {
|
||||
pixel_coordinates_result.allocate_invalid();
|
||||
}
|
||||
|
||||
Result &resolution_result = this->get_result("Resolution");
|
||||
if (resolution_result.should_compute()) {
|
||||
resolution_result.allocate_invalid();
|
||||
|
||||
Reference in New Issue
Block a user