Compositor: Support viewport in Cryptomatte picker

This patch supports the Cryptomatte picker in the 3D viewport. Instead
of picking a color from the viewport GPU texture, we instead sample the
scene directly to get the object or material under the cursor, then hash
their names to get the Cryptomatte hash value. We do this because the
viewport texture have limited precision, so it can't store the
Cryptomatte hash values.

Additionally, we adjust the Cryptomatte session code to extract the
Cryptomatte manifest from the scene directly, as opposed to the
RenderResult. This is done to make it work even when no RenderResult
exist, as is the case for the viewport compositor, which is needed
especially after #123378.

Pull Request: https://projects.blender.org/blender/blender/pulls/123815
This commit is contained in:
Omar Emara 2024-07-04 13:42:23 +02:00 committed by Omar Emara
parent b724756873
commit 4cd1245396
4 changed files with 115 additions and 31 deletions

@ -25,7 +25,11 @@ struct Scene;
struct CryptomatteSession *BKE_cryptomatte_init(void);
struct CryptomatteSession *BKE_cryptomatte_init_from_render_result(
const struct RenderResult *render_result);
struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *scene);
/* Initializes a cryptomatte session from the view layers of the given scene. If build_meta_data is
* true, the object and material IDs in the view layer will be hashed and added to the Cryptomatte
* layers, allowing hash-name lookups. */
struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *scene,
bool build_meta_data);
struct CryptomatteSession *BKE_cryptomatte_init_from_view_layer(
const struct ViewLayer *view_layer);
void BKE_cryptomatte_free(struct CryptomatteSession *session);

@ -9,7 +9,9 @@
#include "BKE_cryptomatte.h"
#include "BKE_cryptomatte.hh"
#include "BKE_image.h"
#include "BKE_layer.hh"
#include "BKE_main.hh"
#include "BKE_material.h"
#include "DNA_layer_types.h"
#include "DNA_material_types.h"
@ -43,8 +45,8 @@ struct CryptomatteSession {
CryptomatteSession(const Main *bmain);
CryptomatteSession(StampData *stamp_data);
CryptomatteSession(const ViewLayer *view_layer);
CryptomatteSession(const Scene *scene);
void init(const ViewLayer *view_layer);
CryptomatteSession(const Scene *scene, bool build_meta_data = false);
void init(const ViewLayer *view_layer, bool build_meta_data = false);
blender::bke::cryptomatte::CryptomatteLayer &add_layer(std::string layer_name);
std::optional<std::string> operator[](float encoded_hash) const;
@ -93,14 +95,19 @@ CryptomatteSession::CryptomatteSession(const ViewLayer *view_layer)
init(view_layer);
}
CryptomatteSession::CryptomatteSession(const Scene *scene)
CryptomatteSession::CryptomatteSession(const Scene *scene, bool build_meta_data)
{
if (build_meta_data) {
BKE_scene_view_layers_synced_ensure(scene);
}
LISTBASE_FOREACH (const ViewLayer *, view_layer, &scene->view_layers) {
init(view_layer);
init(view_layer, build_meta_data);
}
}
void CryptomatteSession::init(const ViewLayer *view_layer)
void CryptomatteSession::init(const ViewLayer *view_layer, bool build_meta_data)
{
eViewLayerCryptomatteFlags cryptoflags = static_cast<eViewLayerCryptomatteFlags>(
view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ALL);
@ -108,14 +115,37 @@ void CryptomatteSession::init(const ViewLayer *view_layer)
cryptoflags = static_cast<eViewLayerCryptomatteFlags>(VIEW_LAYER_CRYPTOMATTE_ALL);
}
ListBase *object_bases = BKE_view_layer_object_bases_get(const_cast<ViewLayer *>(view_layer));
if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_OBJECT) {
add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_OBJECT);
blender::bke::cryptomatte::CryptomatteLayer &objects = add_layer(
blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_OBJECT);
if (build_meta_data) {
LISTBASE_FOREACH (Base *, base, object_bases) {
objects.add_ID(base->object->id);
}
}
}
if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_ASSET) {
add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_ASSET);
}
if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_MATERIAL) {
add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_MATERIAL);
blender::bke::cryptomatte::CryptomatteLayer &materials = add_layer(
blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_MATERIAL);
if (build_meta_data) {
LISTBASE_FOREACH (Base *, base, object_bases) {
for (int i = 0; i < base->object->totcol; i++) {
Material *material = BKE_object_material_get(base->object, i + 1);
if (material) {
materials.add_ID(material->id);
}
}
}
}
}
}
@ -144,15 +174,21 @@ CryptomatteSession *BKE_cryptomatte_init()
return session;
}
CryptomatteSession *BKE_cryptomatte_init_from_main(const Main *bmain)
{
CryptomatteSession *session = new CryptomatteSession(bmain);
return session;
}
CryptomatteSession *BKE_cryptomatte_init_from_render_result(const RenderResult *render_result)
{
CryptomatteSession *session = new CryptomatteSession(render_result->stamp_data);
return session;
}
CryptomatteSession *BKE_cryptomatte_init_from_scene(const Scene *scene)
CryptomatteSession *BKE_cryptomatte_init_from_scene(const Scene *scene, bool build_meta_data)
{
CryptomatteSession *session = new CryptomatteSession(scene);
CryptomatteSession *session = new CryptomatteSession(scene, build_meta_data);
return session;
}

@ -13,16 +13,20 @@
#include "MEM_guardedalloc.h"
#include "DNA_material_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "BLI_string_ref.hh"
#include "BKE_context.hh"
#include "BKE_cryptomatte.h"
#include "BKE_image.h"
#include "BKE_material.h"
#include "BKE_report.hh"
#include "BKE_screen.hh"
@ -174,6 +178,40 @@ static void eyedropper_exit(bContext *C, wmOperator *op)
/* *** eyedropper_color_ helper functions *** */
static bool eyedropper_cryptomatte_sample_view3d_fl(bContext *C,
const char *type_name,
const int mval[2],
float r_col[3])
{
int material_slot = 0;
Object *object = ED_view3d_give_material_slot_under_cursor(C, mval, &material_slot);
if (!object) {
return false;
}
const ID *id = nullptr;
if (blender::StringRef(type_name).endswith(RE_PASSNAME_CRYPTOMATTE_OBJECT)) {
id = &object->id;
}
else if (blender::StringRef(type_name).endswith(RE_PASSNAME_CRYPTOMATTE_MATERIAL)) {
Material *material = BKE_object_material_get(object, material_slot);
if (!material) {
return false;
}
id = &material->id;
}
if (!id) {
return false;
}
const char *name = &id->name[2];
const int name_length = BLI_strnlen(name, MAX_NAME - 2);
uint32_t cryptomatte_hash = BKE_cryptomatte_hash(name, name_length);
r_col[0] = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
return true;
}
static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_layer,
const char *prefix,
const float fpos[2],
@ -214,6 +252,7 @@ static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_lay
return false;
}
static bool eyedropper_cryptomatte_sample_render_fl(const bNode *node,
const char *prefix,
const float fpos[2],
@ -297,7 +336,7 @@ static bool eyedropper_cryptomatte_sample_fl(bContext *C,
ED_region_tag_redraw(CTX_wm_region(C));
}
if (!area || !ELEM(area->spacetype, SPACE_IMAGE, SPACE_NODE, SPACE_CLIP)) {
if (!area || !ELEM(area->spacetype, SPACE_IMAGE, SPACE_NODE, SPACE_CLIP, SPACE_VIEW3D)) {
return false;
}
@ -334,7 +373,9 @@ static bool eyedropper_cryptomatte_sample_fl(bContext *C,
}
}
if (fpos[0] < 0.0f || fpos[1] < 0.0f || fpos[0] >= 1.0f || fpos[1] >= 1.0f) {
if (area->spacetype != SPACE_VIEW3D &&
(fpos[0] < 0.0f || fpos[1] < 0.0f || fpos[0] >= 1.0f || fpos[1] >= 1.0f))
{
return false;
}
@ -352,6 +393,23 @@ static bool eyedropper_cryptomatte_sample_fl(bContext *C,
ntreeCompositCryptomatteLayerPrefix(scene, node, prefix, sizeof(prefix) - 1);
prefix[MAX_NAME] = '\0';
if (area->spacetype == SPACE_VIEW3D) {
wmWindow *win_prev = CTX_wm_window(C);
ScrArea *area_prev = CTX_wm_area(C);
ARegion *region_prev = CTX_wm_region(C);
CTX_wm_window_set(C, win);
CTX_wm_area_set(C, area);
CTX_wm_region_set(C, region);
const bool success = eyedropper_cryptomatte_sample_view3d_fl(C, prefix, mval, r_col);
CTX_wm_window_set(C, win_prev);
CTX_wm_area_set(C, area_prev);
CTX_wm_region_set(C, region_prev);
return success;
}
if (node->custom1 == CMP_NODE_CRYPTOMATTE_SOURCE_RENDER) {
return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col);
}

@ -54,7 +54,7 @@
* \{ */
static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_render(
const bNode &node, const bool use_meta_data)
const bNode &node, const bool build_meta_data)
{
blender::bke::cryptomatte::CryptomatteSessionPtr session;
@ -64,22 +64,8 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no
}
BLI_assert(GS(scene->id.name) == ID_SCE);
if (use_meta_data) {
Render *render = RE_GetSceneRender(scene);
RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr;
if (render_result) {
session = blender::bke::cryptomatte::CryptomatteSessionPtr(
BKE_cryptomatte_init_from_render_result(render_result));
}
if (render) {
RE_ReleaseResult(render);
}
}
if (session == nullptr) {
session = blender::bke::cryptomatte::CryptomatteSessionPtr(
BKE_cryptomatte_init_from_scene(scene));
}
session = blender::bke::cryptomatte::CryptomatteSessionPtr(
BKE_cryptomatte_init_from_scene(scene, build_meta_data));
return session;
}
@ -107,7 +93,7 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no
}
static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node(
const Scene &scene, const bNode &node, const bool use_meta_data)
const Scene &scene, const bNode &node, const bool build_meta_data)
{
blender::bke::cryptomatte::CryptomatteSessionPtr session;
if (node.type != CMP_NODE_CRYPTOMATTE) {
@ -116,7 +102,7 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no
switch (node.custom1) {
case CMP_NODE_CRYPTOMATTE_SOURCE_RENDER: {
return cryptomatte_init_from_node_render(node, use_meta_data);
return cryptomatte_init_from_node_render(node, build_meta_data);
}
case CMP_NODE_CRYPTOMATTE_SOURCE_IMAGE: {