Fix #121480: Cryptomatte shows some objects as black
The issue originates to the change in default view transform from Filmic to AgX, which does slightly different clipping, and clips color to black if there is any negative values. This change implements an idea of skipping view transform for viewer node when it is connected to the Pick output of the cryptomatte node. It actually goes a bit deeper than this and any operation can tag its result as a non-color data, and the viewer node will respect that. It is achieved by passing some extra meta-data along the evaluation pipeline. For the CPU compositor it is done via MetaData, and for the GPU compositor it is done as part of Result. Connecting any other node in-between of viewer and Cryptomatte's Pick will treat the result as color values, and apply color management. Connecting Pick to the Composite output will also consider it as color, since there is no concept of non-color-managed render result. An alternative approaches were tested, including: - Doing negative value clamping at the viewer node. It does not work for legacy cryptomatte node, as it needs to have access to original non-modified Pick result. - Change the order of components, and store ID in another channel. Using one of other of Green or Blue channels might work for some view transforms, but it does not work for AgX. Using Alpha channel seemingly works better, but it is has different issues caused by the fact that display transform de-associates alpha, leading to over-exposed regions which are hard to see in the file from the report. And might lead to the similar issues as the initial report with other objects or view transforms. - Use positive values in the Pick channel. It does make things visible, but they are all white due to the nature of how AgX works, making it not so useful as a result. Pull Request: https://projects.blender.org/blender/blender/pulls/122177
This commit is contained in:
committed by
Sergey Sharybin
parent
ae7edf26ed
commit
1b18e07232
@@ -36,6 +36,9 @@ class MetaData {
|
||||
const blender::StringRef value);
|
||||
|
||||
public:
|
||||
/* The pixels in the result represents data, which is not to be color-managed. */
|
||||
bool is_data = false;
|
||||
|
||||
void add(const blender::StringRef key, const blender::StringRef value);
|
||||
/**
|
||||
* Replace the hash neutral cryptomatte keys with hashed versions.
|
||||
@@ -45,8 +48,10 @@ class MetaData {
|
||||
*/
|
||||
void replace_hash_neutral_cryptomatte_keys(const blender::StringRef layer_name);
|
||||
void add_to_render_result(RenderResult *render_result) const;
|
||||
|
||||
/* Invokes the given callback on each entry of the meta data. */
|
||||
void for_each_entry(FunctionRef<void(const std::string &, const std::string &)> callback) const;
|
||||
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MetaData")
|
||||
#endif
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "COM_MultilayerImageOperation.h"
|
||||
#include "COM_RenderLayersProg.h"
|
||||
#include "COM_SetAlphaMultiplyOperation.h"
|
||||
#include "COM_SetAlphaReplaceOperation.h"
|
||||
#include "COM_SetColorOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
@@ -52,7 +51,7 @@ void CryptomatteBaseNode::convert_to_operations(NodeConverter &converter,
|
||||
converter.map_output_socket(output_image_socket, apply_mask_operation->get_output_socket(0));
|
||||
|
||||
NodeOutput *output_pick_socket = this->get_output_socket(2);
|
||||
SetAlphaReplaceOperation *extract_pick_operation = new SetAlphaReplaceOperation();
|
||||
CryptomattePickOperation *extract_pick_operation = new CryptomattePickOperation();
|
||||
converter.add_operation(extract_pick_operation);
|
||||
converter.add_input_value(extract_pick_operation->get_input_socket(1), 1.0f);
|
||||
converter.add_link(cryptomatte_operation->get_output_socket(0),
|
||||
|
||||
@@ -60,4 +60,29 @@ void CryptomatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
}
|
||||
}
|
||||
|
||||
CryptomattePickOperation::CryptomattePickOperation()
|
||||
{
|
||||
this->add_input_socket(DataType::Color);
|
||||
this->add_input_socket(DataType::Value);
|
||||
this->add_output_socket(DataType::Color);
|
||||
}
|
||||
|
||||
void CryptomattePickOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const float *color = it.in(0);
|
||||
copy_v3_v3(it.out, color);
|
||||
it.out[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<MetaData> CryptomattePickOperation::get_meta_data()
|
||||
{
|
||||
std::unique_ptr<MetaData> meta_data = std::make_unique<MetaData>();
|
||||
meta_data->is_data = true;
|
||||
return meta_data;
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
||||
@@ -26,4 +26,15 @@ class CryptomatteOperation : public MultiThreadedOperation {
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
class CryptomattePickOperation : public MultiThreadedOperation {
|
||||
public:
|
||||
CryptomattePickOperation();
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
|
||||
std::unique_ptr<MetaData> get_meta_data() override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
||||
@@ -161,6 +161,25 @@ void ViewerOperation::update_memory_buffer_partial(MemoryBuffer * /*output*/,
|
||||
update_image(&area);
|
||||
}
|
||||
|
||||
void ViewerOperation::update_memory_buffer_finished(MemoryBuffer * /*output*/,
|
||||
const rcti & /*area*/,
|
||||
Span<MemoryBuffer *> /*inputs*/)
|
||||
{
|
||||
const std::unique_ptr<MetaData> meta_data =
|
||||
this->get_input_socket(0)->get_reader()->get_meta_data();
|
||||
|
||||
if (meta_data && meta_data->is_data) {
|
||||
image_->flag &= ~IMA_VIEW_AS_RENDER;
|
||||
IMB_colormanagement_assign_float_colorspace(
|
||||
ibuf_, IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DATA));
|
||||
}
|
||||
else {
|
||||
image_->flag |= IMA_VIEW_AS_RENDER;
|
||||
IMB_colormanagement_assign_float_colorspace(
|
||||
ibuf_, IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR));
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerOperation::clear_display_buffer()
|
||||
{
|
||||
BLI_assert(is_active_viewer_output());
|
||||
|
||||
@@ -82,6 +82,10 @@ class ViewerOperation : public MultiThreadedOperation {
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
|
||||
void update_memory_buffer_finished(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
|
||||
void clear_display_buffer();
|
||||
|
||||
private:
|
||||
|
||||
@@ -80,7 +80,7 @@ class Context {
|
||||
/* Get the texture where the result of the compositor viewer should be written, given the domain
|
||||
* of the result to be viewed. This should be called by viewer output nodes to get their target
|
||||
* texture. */
|
||||
virtual GPUTexture *get_viewer_output_texture(Domain domain) = 0;
|
||||
virtual GPUTexture *get_viewer_output_texture(Domain domain, bool is_data) = 0;
|
||||
|
||||
/* Get the texture where the given render pass is stored. This should be called by the Render
|
||||
* Layer node to populate its outputs. */
|
||||
|
||||
@@ -129,6 +129,9 @@ class Result {
|
||||
bool is_external_ = false;
|
||||
|
||||
public:
|
||||
/* The pixels in the result represents data, which is not to be color-managed. */
|
||||
bool is_data = false;
|
||||
|
||||
/* Construct a result of the given type and precision with the given texture pool that will be
|
||||
* used to allocate and release the result's texture. */
|
||||
Result(ResultType type, TexturePool &texture_pool, ResultPrecision precision);
|
||||
|
||||
@@ -127,7 +127,8 @@ class Context : public realtime_compositor::Context {
|
||||
return DRW_viewport_texture_list_get()->color;
|
||||
}
|
||||
|
||||
GPUTexture *get_viewer_output_texture(realtime_compositor::Domain /* domain */) override
|
||||
GPUTexture *get_viewer_output_texture(realtime_compositor::Domain /* domain */,
|
||||
bool /*is_data*/) override
|
||||
{
|
||||
return DRW_viewport_texture_list_get()->color;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_image.h"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "ED_node_c.hh"
|
||||
|
||||
#include "draw_color_management.hh"
|
||||
|
||||
@@ -74,8 +79,16 @@ static eDRWColorManagementType drw_color_management_type_for_space_image(const S
|
||||
return eDRWColorManagementType::ViewTransform;
|
||||
}
|
||||
|
||||
static eDRWColorManagementType drw_color_management_type_for_space_node(const SpaceNode &snode)
|
||||
static eDRWColorManagementType drw_color_management_type_for_space_node(Main &bmain,
|
||||
const SpaceNode &snode)
|
||||
{
|
||||
if ((snode.flag & SNODE_BACKDRAW) && ED_node_is_compositor(&snode)) {
|
||||
Image *image = BKE_image_ensure_viewer(&bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
|
||||
if ((image->flag & IMA_VIEW_AS_RENDER) == 0) {
|
||||
return eDRWColorManagementType::ViewTransform;
|
||||
}
|
||||
}
|
||||
|
||||
const eSpaceNode_Flag display_channels_mode = static_cast<eSpaceNode_Flag>(snode.flag);
|
||||
const bool display_color_channel = (display_channels_mode & SNODE_SHOW_ALPHA) == 0;
|
||||
if (display_color_channel) {
|
||||
@@ -84,7 +97,8 @@ static eDRWColorManagementType drw_color_management_type_for_space_node(const Sp
|
||||
return eDRWColorManagementType::ViewTransform;
|
||||
}
|
||||
|
||||
static eDRWColorManagementType drw_color_management_type_get(const Scene &scene,
|
||||
static eDRWColorManagementType drw_color_management_type_get(Main *bmain,
|
||||
const Scene &scene,
|
||||
const View3D *v3d,
|
||||
const SpaceLink *space_data)
|
||||
{
|
||||
@@ -101,7 +115,7 @@ static eDRWColorManagementType drw_color_management_type_get(const Scene &scene,
|
||||
case SPACE_NODE: {
|
||||
const SpaceNode *snode = static_cast<const SpaceNode *>(
|
||||
static_cast<const void *>(space_data));
|
||||
return drw_color_management_type_for_space_node(*snode);
|
||||
return drw_color_management_type_for_space_node(*bmain, *snode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,9 +158,11 @@ static void viewport_settings_apply(GPUViewport &viewport,
|
||||
static void viewport_color_management_set(GPUViewport &viewport)
|
||||
{
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const Depsgraph *depsgraph = draw_ctx->depsgraph;
|
||||
Main *bmain = DEG_get_bmain(depsgraph);
|
||||
|
||||
const eDRWColorManagementType color_management_type = drw_color_management_type_get(
|
||||
*draw_ctx->scene, draw_ctx->v3d, draw_ctx->space_data);
|
||||
bmain, *draw_ctx->scene, draw_ctx->v3d, draw_ctx->space_data);
|
||||
viewport_settings_apply(viewport, *draw_ctx->scene, color_management_type);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ float ED_node_grid_size();
|
||||
/* `node_edit.cc` */
|
||||
|
||||
void ED_node_set_tree_type(SpaceNode *snode, blender::bke::bNodeTreeType *typeinfo);
|
||||
bool ED_node_is_compositor(SpaceNode *snode);
|
||||
bool ED_node_is_compositor(const SpaceNode *snode);
|
||||
bool ED_node_is_shader(SpaceNode *snode);
|
||||
bool ED_node_is_texture(SpaceNode *snode);
|
||||
bool ED_node_is_geometry(SpaceNode *snode);
|
||||
|
||||
@@ -515,7 +515,7 @@ void ED_node_set_tree_type(SpaceNode *snode, blender::bke::bNodeTreeType *typein
|
||||
}
|
||||
}
|
||||
|
||||
bool ED_node_is_compositor(SpaceNode *snode)
|
||||
bool ED_node_is_compositor(const SpaceNode *snode)
|
||||
{
|
||||
return STREQ(snode->tree_idname, ntreeType_Composite->idname);
|
||||
}
|
||||
|
||||
@@ -325,6 +325,8 @@ class BaseCryptoMatteOperation : public NodeOperation {
|
||||
* which is a 32-bit float. See the shader for more information. */
|
||||
output_pick.set_precision(ResultPrecision::Full);
|
||||
|
||||
output_pick.is_data = true;
|
||||
|
||||
const Domain domain = compute_domain();
|
||||
output_pick.allocate_texture(domain);
|
||||
output_pick.bind_as_image(shader, "output_img");
|
||||
|
||||
@@ -94,7 +94,8 @@ class ViewerOperation : public NodeOperation {
|
||||
}
|
||||
|
||||
const Domain domain = compute_domain();
|
||||
GPU_texture_clear(context().get_viewer_output_texture(domain), GPU_DATA_FLOAT, color);
|
||||
GPU_texture_clear(
|
||||
context().get_viewer_output_texture(domain, image.is_data), GPU_DATA_FLOAT, color);
|
||||
}
|
||||
|
||||
/* Executes when the alpha channel of the image is ignored. */
|
||||
@@ -126,7 +127,7 @@ class ViewerOperation : public NodeOperation {
|
||||
const Result &image = get_input("Image");
|
||||
image.bind_as_texture(shader, "input_tx");
|
||||
|
||||
GPUTexture *output_texture = context().get_viewer_output_texture(domain);
|
||||
GPUTexture *output_texture = context().get_viewer_output_texture(domain, image.is_data);
|
||||
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
|
||||
GPU_texture_image_bind(output_texture, image_unit);
|
||||
|
||||
@@ -166,7 +167,7 @@ class ViewerOperation : public NodeOperation {
|
||||
const Result &image = get_input("Image");
|
||||
image.bind_as_texture(shader, "input_tx");
|
||||
|
||||
GPUTexture *output_texture = context().get_viewer_output_texture(domain);
|
||||
GPUTexture *output_texture = context().get_viewer_output_texture(domain, image.is_data);
|
||||
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
|
||||
GPU_texture_image_bind(output_texture, image_unit);
|
||||
|
||||
@@ -209,7 +210,7 @@ class ViewerOperation : public NodeOperation {
|
||||
const Result &alpha = get_input("Alpha");
|
||||
alpha.bind_as_texture(shader, "alpha_tx");
|
||||
|
||||
GPUTexture *output_texture = context().get_viewer_output_texture(domain);
|
||||
GPUTexture *output_texture = context().get_viewer_output_texture(domain, image.is_data);
|
||||
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
|
||||
GPU_texture_image_bind(output_texture, image_unit);
|
||||
|
||||
|
||||
@@ -239,7 +239,8 @@ class Context : public realtime_compositor::Context {
|
||||
return output_texture_;
|
||||
}
|
||||
|
||||
GPUTexture *get_viewer_output_texture(realtime_compositor::Domain domain) override
|
||||
GPUTexture *get_viewer_output_texture(realtime_compositor::Domain domain,
|
||||
const bool is_data) override
|
||||
{
|
||||
/* Re-create texture if the viewer size changes. */
|
||||
const int2 size = domain.size;
|
||||
@@ -272,6 +273,13 @@ class Context : public realtime_compositor::Context {
|
||||
image->runtime.backdrop_offset[0] = translation.x;
|
||||
image->runtime.backdrop_offset[1] = translation.y;
|
||||
|
||||
if (is_data) {
|
||||
image->flag &= ~IMA_VIEW_AS_RENDER;
|
||||
}
|
||||
else {
|
||||
image->flag |= IMA_VIEW_AS_RENDER;
|
||||
}
|
||||
|
||||
return viewer_output_texture_;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user