Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeroen Bakker <jbakker>2022-09-13 12:07:30 +0300
committerJeroen Bakker <jeroen@blender.org>2022-09-13 12:07:38 +0300
commit8068b89a681c467f3d449b9ddc2ebec5b817c2fc (patch)
tree2bed18be11a6b13ccd9092ea133993a19509a726
parentbb3a021427f2132f1db62a76eeca2ca4be1601da (diff)
EEVEE-Next: Cryptomatte render passes.
This change adds cryptomatte render passes to EEVEE-Next. Due to the upcoming viewport compositor we also improved cryptomatte so it will be real-time. This also allows viewing the cryptomatte passes in the viewport directly. {F13482749} A surface shader would store any active cryptomatte layer to a texture. Object hash is stored as R, Asset hash as G and Material hash as B. Hashes are only calculated when the cryptomatte layer is active to reduce any unneeded work. During film accumulation the hashes are separated and stored in a texture array that matches the cryptomatte standard. For the real-time use case sorting is skipped. For final rendering the samples are sorted and normalized. NOTE: Eventually we should also do sample normalization in the viewport in order to extract the correct mask when using the viewport compositor. Reviewed By: fclem Maniphest Tasks: T99390 Differential Revision: https://developer.blender.org/D15753
-rw-r--r--release/scripts/startup/bl_ui/properties_view_layer.py2
-rw-r--r--source/blender/blenkernel/BKE_cryptomatte.h2
-rw-r--r--source/blender/blenkernel/BKE_cryptomatte.hh26
-rw-r--r--source/blender/blenkernel/intern/cryptomatte.cc79
-rw-r--r--source/blender/draw/CMakeLists.txt3
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc130
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh69
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_defines.hh3
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_engine.cc66
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_film.cc156
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_film.hh107
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_instance.cc98
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_instance.hh6
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_pipeline.cc4
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc15
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh2
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.cc2
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.hh1
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader_shared.hh17
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sync.cc12
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl70
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl77
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl8
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl54
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl3
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl1
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh13
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh14
-rw-r--r--source/blender/gpu/intern/gpu_codegen.cc31
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.h1
-rw-r--r--source/blender/makesdna/DNA_layer_types.h12
-rw-r--r--source/blender/makesdna/DNA_scene_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_space.c10
33 files changed, 933 insertions, 165 deletions
diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py
index e087b431ad8..c6d1ee2a065 100644
--- a/release/scripts/startup/bl_ui/properties_view_layer.py
+++ b/release/scripts/startup/bl_ui/properties_view_layer.py
@@ -209,7 +209,7 @@ class ViewLayerCryptomattePanel(ViewLayerButtonsPanel, Panel):
class VIEWLAYER_PT_layer_passes_cryptomatte(ViewLayerCryptomattePanel, Panel):
bl_parent_id = "VIEWLAYER_PT_layer_passes"
- COMPAT_ENGINES = {'BLENDER_EEVEE'}
+ COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT'}
class VIEWLAYER_MT_lightgroup_sync(Menu):
diff --git a/source/blender/blenkernel/BKE_cryptomatte.h b/source/blender/blenkernel/BKE_cryptomatte.h
index 56049ecf405..b2024f09278 100644
--- a/source/blender/blenkernel/BKE_cryptomatte.h
+++ b/source/blender/blenkernel/BKE_cryptomatte.h
@@ -25,6 +25,8 @@ 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);
+struct CryptomatteSession *BKE_cryptomatte_init_from_view_layer(
+ const struct ViewLayer *view_layer);
void BKE_cryptomatte_free(struct CryptomatteSession *session);
void BKE_cryptomatte_add_layer(struct CryptomatteSession *session, const char *layer_name);
diff --git a/source/blender/blenkernel/BKE_cryptomatte.hh b/source/blender/blenkernel/BKE_cryptomatte.hh
index cd3f8dc9f58..dd08f7b5c4f 100644
--- a/source/blender/blenkernel/BKE_cryptomatte.hh
+++ b/source/blender/blenkernel/BKE_cryptomatte.hh
@@ -12,6 +12,7 @@
#include "BKE_cryptomatte.h"
+#include "BLI_hash_mm3.h"
#include "BLI_map.hh"
#include "BLI_string_ref.hh"
@@ -54,10 +55,14 @@ struct CryptomatteHash {
uint32_t hash;
CryptomatteHash(uint32_t hash);
- CryptomatteHash(const char *name, int name_len);
- static CryptomatteHash from_hex_encoded(blender::StringRef hex_encoded);
+ CryptomatteHash(const char *name, int name_len)
+ {
+ hash = BLI_hash_mm3((const unsigned char *)name, name_len, 0);
+ }
+ static CryptomatteHash from_hex_encoded(blender::StringRef hex_encoded);
std::string hex_encoded() const;
+
/**
* Convert a cryptomatte hash to a float.
*
@@ -70,7 +75,20 @@ struct CryptomatteHash {
*
* Note that this conversion assumes to be running on a L-endian system.
*/
- float float_encoded() const;
+ float float_encoded() const
+ {
+ uint32_t mantissa = hash & ((1 << 23) - 1);
+ uint32_t exponent = (hash >> 23) & ((1 << 8) - 1);
+ exponent = MAX2(exponent, (uint32_t)1);
+ exponent = MIN2(exponent, (uint32_t)254);
+ exponent = exponent << 23;
+ uint32_t sign = (hash >> 31);
+ sign = sign << 31;
+ uint32_t float_bits = sign | exponent | mantissa;
+ float f;
+ memcpy(&f, &float_bits, sizeof(uint32_t));
+ return f;
+ }
};
struct CryptomatteLayer {
@@ -107,6 +125,8 @@ struct CryptomatteStampDataCallbackData {
const blender::Vector<std::string> &BKE_cryptomatte_layer_names_get(
const CryptomatteSession &session);
+CryptomatteLayer *BKE_cryptomatte_layer_get(CryptomatteSession &session,
+ const StringRef layer_name);
struct CryptomatteSessionDeleter {
void operator()(CryptomatteSession *session)
diff --git a/source/blender/blenkernel/intern/cryptomatte.cc b/source/blender/blenkernel/intern/cryptomatte.cc
index 102bda0f2b6..72204f6624e 100644
--- a/source/blender/blenkernel/intern/cryptomatte.cc
+++ b/source/blender/blenkernel/intern/cryptomatte.cc
@@ -41,7 +41,9 @@ struct CryptomatteSession {
CryptomatteSession() = default;
CryptomatteSession(const Main *bmain);
CryptomatteSession(StampData *stamp_data);
+ CryptomatteSession(const ViewLayer *view_layer);
CryptomatteSession(const Scene *scene);
+ void init(const ViewLayer *view_layer);
blender::bke::cryptomatte::CryptomatteLayer &add_layer(std::string layer_name);
std::optional<std::string> operator[](float encoded_hash) const;
@@ -54,13 +56,15 @@ struct CryptomatteSession {
CryptomatteSession::CryptomatteSession(const Main *bmain)
{
if (!BLI_listbase_is_empty(&bmain->objects)) {
- blender::bke::cryptomatte::CryptomatteLayer &objects = add_layer("CryptoObject");
+ blender::bke::cryptomatte::CryptomatteLayer &objects = add_layer(
+ RE_PASSNAME_CRYPTOMATTE_OBJECT);
LISTBASE_FOREACH (ID *, id, &bmain->objects) {
objects.add_ID(*id);
}
}
if (!BLI_listbase_is_empty(&bmain->materials)) {
- blender::bke::cryptomatte::CryptomatteLayer &materials = add_layer("CryptoMaterial");
+ blender::bke::cryptomatte::CryptomatteLayer &materials = add_layer(
+ RE_PASSNAME_CRYPTOMATTE_MATERIAL);
LISTBASE_FOREACH (ID *, id, &bmain->materials) {
materials.add_ID(*id);
}
@@ -83,24 +87,34 @@ CryptomatteSession::CryptomatteSession(StampData *stamp_data)
false);
}
+CryptomatteSession::CryptomatteSession(const ViewLayer *view_layer)
+{
+ init(view_layer);
+}
+
CryptomatteSession::CryptomatteSession(const Scene *scene)
{
- LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
- eViewLayerCryptomatteFlags cryptoflags = static_cast<eViewLayerCryptomatteFlags>(
- view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ALL);
- if (cryptoflags == 0) {
- cryptoflags = static_cast<eViewLayerCryptomatteFlags>(VIEW_LAYER_CRYPTOMATTE_ALL);
- }
+ LISTBASE_FOREACH (const ViewLayer *, view_layer, &scene->view_layers) {
+ init(view_layer);
+ }
+}
- if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_OBJECT) {
- add_layer(blender::StringRefNull(view_layer->name) + ".CryptoObject");
- }
- if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_ASSET) {
- add_layer(blender::StringRefNull(view_layer->name) + ".CryptoAsset");
- }
- if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_MATERIAL) {
- add_layer(blender::StringRefNull(view_layer->name) + ".CryptoMaterial");
- }
+void CryptomatteSession::init(const ViewLayer *view_layer)
+{
+ eViewLayerCryptomatteFlags cryptoflags = static_cast<eViewLayerCryptomatteFlags>(
+ view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ALL);
+ if (cryptoflags == 0) {
+ cryptoflags = static_cast<eViewLayerCryptomatteFlags>(VIEW_LAYER_CRYPTOMATTE_ALL);
+ }
+
+ if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_OBJECT) {
+ add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_OBJECT);
+ }
+ 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);
}
}
@@ -142,6 +156,12 @@ struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *s
return session;
}
+struct CryptomatteSession *BKE_cryptomatte_init_from_view_layer(const struct ViewLayer *view_layer)
+{
+ CryptomatteSession *session = new CryptomatteSession(view_layer);
+ return session;
+}
+
void BKE_cryptomatte_add_layer(struct CryptomatteSession *session, const char *layer_name)
{
session->add_layer(layer_name);
@@ -485,11 +505,6 @@ CryptomatteHash::CryptomatteHash(uint32_t hash) : hash(hash)
{
}
-CryptomatteHash::CryptomatteHash(const char *name, const int name_len)
-{
- hash = BLI_hash_mm3((const unsigned char *)name, name_len, 0);
-}
-
CryptomatteHash CryptomatteHash::from_hex_encoded(blender::StringRef hex_encoded)
{
CryptomatteHash result(0);
@@ -504,21 +519,6 @@ std::string CryptomatteHash::hex_encoded() const
return encoded.str();
}
-float CryptomatteHash::float_encoded() const
-{
- uint32_t mantissa = hash & ((1 << 23) - 1);
- uint32_t exponent = (hash >> 23) & ((1 << 8) - 1);
- exponent = MAX2(exponent, (uint32_t)1);
- exponent = MIN2(exponent, (uint32_t)254);
- exponent = exponent << 23;
- uint32_t sign = (hash >> 31);
- sign = sign << 31;
- uint32_t float_bits = sign | exponent | mantissa;
- float f;
- memcpy(&f, &float_bits, sizeof(uint32_t));
- return f;
-}
-
std::unique_ptr<CryptomatteLayer> CryptomatteLayer::read_from_manifest(
blender::StringRefNull manifest)
{
@@ -625,4 +625,9 @@ const blender::Vector<std::string> &BKE_cryptomatte_layer_names_get(
return session.layer_names;
}
+CryptomatteLayer *BKE_cryptomatte_layer_get(CryptomatteSession &session, StringRef layer_name)
+{
+ return session.layers.lookup_ptr(layer_name);
+}
+
} // namespace blender::bke::cryptomatte
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index ac7f1c5ff68..32103692421 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -135,6 +135,7 @@ set(SRC
engines/eevee/eevee_temporal_sampling.c
engines/eevee/eevee_volumes.c
engines/eevee_next/eevee_camera.cc
+ engines/eevee_next/eevee_cryptomatte.cc
engines/eevee_next/eevee_depth_of_field.cc
engines/eevee_next/eevee_engine.cc
engines/eevee_next/eevee_film.cc
@@ -395,6 +396,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_attributes_lib.glsl
engines/eevee_next/shaders/eevee_camera_lib.glsl
engines/eevee_next/shaders/eevee_colorspace_lib.glsl
+ engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl
engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl
engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl
engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl
@@ -411,6 +413,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl
engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl
engines/eevee_next/shaders/eevee_film_comp.glsl
+ engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl
engines/eevee_next/shaders/eevee_film_frag.glsl
engines/eevee_next/shaders/eevee_film_lib.glsl
engines/eevee_next/shaders/eevee_geom_curves_vert.glsl
diff --git a/source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc
new file mode 100644
index 00000000000..340a587b1c1
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc
@@ -0,0 +1,130 @@
+#include "BKE_cryptomatte.hh"
+
+#include "GPU_material.h"
+
+#include "eevee_cryptomatte.hh"
+#include "eevee_instance.hh"
+#include "eevee_renderbuffers.hh"
+
+namespace blender::eevee {
+
+void Cryptomatte::begin_sync()
+{
+ const eViewLayerEEVEEPassType enabled_passes = static_cast<eViewLayerEEVEEPassType>(
+ inst_.film.enabled_passes_get() &
+ (EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET |
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET));
+
+ session_.reset();
+ object_layer_ = nullptr;
+ asset_layer_ = nullptr;
+ material_layer_ = nullptr;
+
+ if (enabled_passes && !inst_.is_viewport()) {
+ session_.reset(BKE_cryptomatte_init_from_view_layer(inst_.view_layer));
+
+ for (const std::string &layer_name :
+ bke::cryptomatte::BKE_cryptomatte_layer_names_get(*session_)) {
+ StringRef layer_name_ref = layer_name;
+ bke::cryptomatte::CryptomatteLayer *layer = bke::cryptomatte::BKE_cryptomatte_layer_get(
+ *session_, layer_name);
+ if (layer_name_ref.endswith(RE_PASSNAME_CRYPTOMATTE_OBJECT)) {
+ object_layer_ = layer;
+ }
+ else if (layer_name_ref.endswith(RE_PASSNAME_CRYPTOMATTE_ASSET)) {
+ asset_layer_ = layer;
+ }
+ else if (layer_name_ref.endswith(RE_PASSNAME_CRYPTOMATTE_MATERIAL)) {
+ material_layer_ = layer;
+ }
+ }
+ }
+
+ if (!(enabled_passes &
+ (EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET))) {
+ cryptomatte_object_buf.resize(16);
+ }
+}
+
+void Cryptomatte::sync_object(Object *ob, ResourceHandle res_handle)
+{
+ const eViewLayerEEVEEPassType enabled_passes = inst_.film.enabled_passes_get();
+ if (!(enabled_passes &
+ (EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET))) {
+ return;
+ }
+
+ uint32_t resource_id = res_handle.resource_index();
+ float2 object_hashes(0.0f, 0.0f);
+
+ if (enabled_passes & EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT) {
+ object_hashes[0] = register_id(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT, ob->id);
+ }
+
+ if (enabled_passes & EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET) {
+ Object *asset = ob;
+ while (asset->parent) {
+ asset = asset->parent;
+ }
+ object_hashes[1] = register_id(EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET, asset->id);
+ }
+ cryptomatte_object_buf.get_or_resize(resource_id) = object_hashes;
+}
+
+void Cryptomatte::sync_material(const ::Material *material)
+{
+ /* Material crypto hashes are generated during shader codegen stage. We only need to register
+ * them to store inside the metadata. */
+ if (material_layer_ && material) {
+ material_layer_->add_ID(material->id);
+ }
+}
+
+void Cryptomatte::end_sync()
+{
+ cryptomatte_object_buf.push_update();
+
+ object_layer_ = nullptr;
+ asset_layer_ = nullptr;
+ material_layer_ = nullptr;
+}
+
+float Cryptomatte::register_id(const eViewLayerEEVEEPassType layer, const ID &id) const
+{
+ BLI_assert(ELEM(layer,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL));
+
+ uint32_t cryptomatte_hash = 0;
+ if (session_) {
+ if (layer == EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT) {
+ BLI_assert(object_layer_);
+ cryptomatte_hash = object_layer_->add_ID(id);
+ }
+ else if (layer == EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET) {
+ BLI_assert(asset_layer_);
+ cryptomatte_hash = asset_layer_->add_ID(id);
+ }
+ else if (layer == EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL) {
+ BLI_assert(material_layer_);
+ cryptomatte_hash = material_layer_->add_ID(id);
+ }
+ }
+ else {
+ const char *name = &id.name[2];
+ const int name_len = BLI_strnlen(name, MAX_NAME - 2);
+ cryptomatte_hash = BKE_cryptomatte_hash(name, name_len);
+ }
+
+ return BKE_cryptomatte_hash_to_float(cryptomatte_hash);
+}
+
+void Cryptomatte::store_metadata(RenderResult *render_result)
+{
+ if (session_) {
+ BKE_cryptomatte_store_metadata(&*session_, render_result, inst_.view_layer);
+ }
+}
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh
new file mode 100644
index 00000000000..37f5edf4c6d
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Cryptomatte.
+ *
+ * During rasterization, cryptomatte hashes are stored into a single array texture.
+ * The film pass then resamples this texture using pixel filter weighting.
+ * Each cryptomatte layer can hold N samples. These are stored in sequential layers
+ * of the array texture. The samples are sorted and merged only for final rendering.
+ */
+
+#pragma once
+
+#include "eevee_shader_shared.hh"
+
+#include "BKE_cryptomatte.hh"
+
+extern "C" {
+struct Material;
+struct CryptomatteSession;
+}
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Cryptomatte
+ * \{ */
+
+class Cryptomatte {
+ private:
+ class Instance &inst_;
+
+ bke::cryptomatte::CryptomatteSessionPtr session_;
+
+ /* Cached pointer to the cryptomatte layer instances. */
+ bke::cryptomatte::CryptomatteLayer *object_layer_ = nullptr;
+ bke::cryptomatte::CryptomatteLayer *asset_layer_ = nullptr;
+ bke::cryptomatte::CryptomatteLayer *material_layer_ = nullptr;
+
+ /** Contains per object hashes (object and asset hash). Indexed by resource ID. */
+ CryptomatteObjectBuf cryptomatte_object_buf;
+
+ public:
+ Cryptomatte(Instance &inst) : inst_(inst){};
+
+ void begin_sync();
+ void sync_object(Object *ob, ResourceHandle res_handle);
+ void sync_material(const ::Material *material);
+ void end_sync();
+
+ template<typename T> void bind_resources(draw::detail::PassBase<T> *pass)
+ {
+ pass->bind_ssbo(CRYPTOMATTE_BUF_SLOT, &cryptomatte_object_buf);
+ }
+
+ /* Register ID to use inside cryptomatte layer and returns associated hash as float. */
+ float register_id(const eViewLayerEEVEEPassType layer, const ID &id) const;
+ void store_metadata(RenderResult *render_result);
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh
index 2f338e707c0..248dfae6df9 100644
--- a/source/blender/draw/engines/eevee_next/eevee_defines.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh
@@ -82,6 +82,7 @@
#define RBUFS_EMISSION_SLOT 4
#define RBUFS_AOV_COLOR_SLOT 5
#define RBUFS_AOV_VALUE_SLOT 6
+#define RBUFS_CRYPTOMATTE_SLOT 7
/* Uniform Buffers. */
/* Only during prepass. */
@@ -96,6 +97,8 @@
#define LIGHT_TILE_BUF_SLOT 3
#define RBUFS_AOV_BUF_SLOT 5
#define SAMPLING_BUF_SLOT 6
+#define CRYPTOMATTE_BUF_SLOT 7
+
/* Only during pre-pass. */
#define VELOCITY_OBJ_PREV_BUF_SLOT 0
#define VELOCITY_OBJ_NEXT_BUF_SLOT 1
diff --git a/source/blender/draw/engines/eevee_next/eevee_engine.cc b/source/blender/draw/engines/eevee_next/eevee_engine.cc
index 2e476b7d891..5ef198838c9 100644
--- a/source/blender/draw/engines/eevee_next/eevee_engine.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_engine.cc
@@ -140,7 +140,7 @@ static void eevee_instance_free(void *instance)
delete reinterpret_cast<eevee::Instance *>(instance);
}
-static void eevee_render_to_image(void *UNUSED(vedata),
+static void eevee_render_to_image(void *vedata,
struct RenderEngine *engine,
struct RenderLayer *layer,
const struct rcti *UNUSED(rect))
@@ -164,59 +164,31 @@ static void eevee_render_to_image(void *UNUSED(vedata),
instance->init(size, &rect, engine, depsgraph, nullptr, camera_original_ob, layer);
instance->render_frame(layer, viewname);
- delete instance;
+ EEVEE_Data *ved = static_cast<EEVEE_Data *>(vedata);
+ if (ved->instance) {
+ delete ved->instance;
+ }
+ ved->instance = instance;
}
-static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
+static void eevee_store_metadata(void *vedata, struct RenderResult *render_result)
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
+ EEVEE_Data *ved = static_cast<EEVEE_Data *>(vedata);
+ eevee::Instance *instance = ved->instance;
+ instance->store_metadata(render_result);
+ delete instance;
+ ved->instance = nullptr;
+}
- RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
-
-#define CHECK_PASS_LEGACY(name, type, channels, chanid) \
- if (view_layer->passflag & (SCE_PASS_##name)) { \
- RE_engine_register_pass( \
- engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
- } \
- ((void)0)
-#define CHECK_PASS_EEVEE(name, type, channels, chanid) \
- if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \
- RE_engine_register_pass( \
- engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
- } \
- ((void)0)
-
- CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
- CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
- CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
- CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
- /* TODO: CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
- * CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
- * When available they should be converted from Value textures to RGB. */
-
- LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
- if ((aov->flag & AOV_CONFLICT) != 0) {
- continue;
- }
- switch (aov->type) {
- case AOV_TYPE_COLOR:
- RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
- break;
- case AOV_TYPE_VALUE:
- RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
- break;
- default:
- break;
- }
+static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
}
+ eevee::Instance::update_passes(engine, scene, view_layer);
}
static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data);
@@ -238,7 +210,7 @@ DrawEngineType draw_engine_eevee_next_type = {
nullptr,
nullptr,
&eevee_render_to_image,
- nullptr,
+ &eevee_store_metadata,
};
RenderEngineType DRW_engine_viewport_eevee_next_type = {
diff --git a/source/blender/draw/engines/eevee_next/eevee_film.cc b/source/blender/draw/engines/eevee_next/eevee_film.cc
index 4679889e59a..b89746d99e2 100644
--- a/source/blender/draw/engines/eevee_next/eevee_film.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_film.cc
@@ -162,6 +162,45 @@ inline bool operator!=(const FilmData &a, const FilmData &b)
/** \name Film
* \{ */
+static eViewLayerEEVEEPassType enabled_passes(const ViewLayer *view_layer)
+{
+ eViewLayerEEVEEPassType result = eViewLayerEEVEEPassType(view_layer->eevee.render_passes);
+
+#define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \
+ SET_FLAG_FROM_TEST(result, \
+ (view_layer->passflag & SCE_PASS_##name_legacy) != 0, \
+ EEVEE_RENDER_PASS_##name_eevee);
+
+ ENABLE_FROM_LEGACY(COMBINED, COMBINED)
+ ENABLE_FROM_LEGACY(Z, Z)
+ ENABLE_FROM_LEGACY(MIST, MIST)
+ ENABLE_FROM_LEGACY(NORMAL, NORMAL)
+ ENABLE_FROM_LEGACY(SHADOW, SHADOW)
+ ENABLE_FROM_LEGACY(AO, AO)
+ ENABLE_FROM_LEGACY(EMIT, EMIT)
+ ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
+ ENABLE_FROM_LEGACY(DIFFUSE_COLOR, DIFFUSE_COLOR)
+ ENABLE_FROM_LEGACY(GLOSSY_COLOR, SPECULAR_COLOR)
+ ENABLE_FROM_LEGACY(DIFFUSE_DIRECT, DIFFUSE_LIGHT)
+ ENABLE_FROM_LEGACY(GLOSSY_DIRECT, SPECULAR_LIGHT)
+ ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
+ ENABLE_FROM_LEGACY(VECTOR, VECTOR)
+
+#undef ENABLE_FROM_LEGACY
+
+ SET_FLAG_FROM_TEST(result,
+ view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_OBJECT,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT);
+ SET_FLAG_FROM_TEST(result,
+ view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ASSET,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET);
+ SET_FLAG_FROM_TEST(result,
+ view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_MATERIAL,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL);
+
+ return result;
+}
+
void Film::init(const int2 &extent, const rcti *output_rect)
{
Sampling &sampling = inst_.sampling;
@@ -186,29 +225,7 @@ void Film::init(const int2 &extent, const rcti *output_rect)
}
else {
/* Render Case. */
- render_passes = eViewLayerEEVEEPassType(inst_.view_layer->eevee.render_passes);
-
-#define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \
- SET_FLAG_FROM_TEST(render_passes, \
- (inst_.view_layer->passflag & SCE_PASS_##name_legacy) != 0, \
- EEVEE_RENDER_PASS_##name_eevee);
-
- ENABLE_FROM_LEGACY(COMBINED, COMBINED)
- ENABLE_FROM_LEGACY(Z, Z)
- ENABLE_FROM_LEGACY(MIST, MIST)
- ENABLE_FROM_LEGACY(NORMAL, NORMAL)
- ENABLE_FROM_LEGACY(SHADOW, SHADOW)
- ENABLE_FROM_LEGACY(AO, AO)
- ENABLE_FROM_LEGACY(EMIT, EMIT)
- ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
- ENABLE_FROM_LEGACY(DIFFUSE_COLOR, DIFFUSE_COLOR)
- ENABLE_FROM_LEGACY(GLOSSY_COLOR, SPECULAR_COLOR)
- ENABLE_FROM_LEGACY(DIFFUSE_DIRECT, DIFFUSE_LIGHT)
- ENABLE_FROM_LEGACY(GLOSSY_DIRECT, SPECULAR_LIGHT)
- ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
- ENABLE_FROM_LEGACY(VECTOR, VECTOR)
-
-#undef ENABLE_FROM_LEGACY
+ render_passes = enabled_passes(inst_.view_layer);
}
/* Filter obsolete passes. */
@@ -241,6 +258,7 @@ void Film::init(const int2 &extent, const rcti *output_rect)
/* TODO(fclem): parameter hidden in experimental.
* We need to figure out LOD bias first in order to preserve texture crispiness. */
data.scaling_factor = 1;
+ data.cryptomatte_samples_len = inst_.view_layer->cryptomatte_levels;
data.background_opacity = (scene.r.alphamode == R_ALPHAPREMUL) ? 0.0f : 1.0f;
if (inst_.is_viewport() && false /* TODO(fclem): StudioLight */) {
@@ -273,7 +291,8 @@ void Film::init(const int2 &extent, const rcti *output_rect)
/* Set pass offsets. */
data_.display_id = aovs_info.display_id;
- data_.display_is_value = aovs_info.display_is_value;
+ data_.display_storage_type = aovs_info.display_is_value ? PASS_STORAGE_VALUE :
+ PASS_STORAGE_COLOR;
/* Combined is in a separate buffer. */
data_.combined_id = (enabled_passes_ & EEVEE_RENDER_PASS_COMBINED) ? 0 : -1;
@@ -284,13 +303,13 @@ void Film::init(const int2 &extent, const rcti *output_rect)
data_.value_len = 0;
auto pass_index_get = [&](eViewLayerEEVEEPassType pass_type) {
- bool is_value = pass_is_value(pass_type);
+ ePassStorageType storage_type = pass_storage_type(pass_type);
int index = (enabled_passes_ & pass_type) ?
- (is_value ? data_.value_len : data_.color_len)++ :
+ (storage_type == PASS_STORAGE_VALUE ? data_.value_len : data_.color_len)++ :
-1;
if (inst_.is_viewport() && inst_.v3d->shading.render_pass == pass_type) {
data_.display_id = index;
- data_.display_is_value = is_value;
+ data_.display_storage_type = storage_type;
}
return index;
};
@@ -316,6 +335,24 @@ void Film::init(const int2 &extent, const rcti *output_rect)
data_.color_len += data_.aov_color_len;
data_.value_len += data_.aov_value_len;
+
+ int cryptomatte_id = 0;
+ auto cryptomatte_index_get = [&](eViewLayerEEVEEPassType pass_type) {
+ int index = -1;
+ if (enabled_passes_ & pass_type) {
+ index = cryptomatte_id;
+ cryptomatte_id += data_.cryptomatte_samples_len / 2;
+
+ if (inst_.is_viewport() && inst_.v3d->shading.render_pass == pass_type) {
+ data_.display_id = index;
+ data_.display_storage_type = PASS_STORAGE_CRYPTOMATTE;
+ }
+ }
+ return index;
+ };
+ data_.cryptomatte_object_id = cryptomatte_index_get(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT);
+ data_.cryptomatte_asset_id = cryptomatte_index_get(EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET);
+ data_.cryptomatte_material_id = cryptomatte_index_get(EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL);
}
{
/* TODO(@fclem): Over-scans. */
@@ -327,6 +364,7 @@ void Film::init(const int2 &extent, const rcti *output_rect)
eGPUTextureFormat float_format = GPU_R16F;
eGPUTextureFormat weight_format = GPU_R32F;
eGPUTextureFormat depth_format = GPU_R32F;
+ eGPUTextureFormat cryptomatte_format = GPU_RGBA32F;
int reset = 0;
reset += depth_tx_.ensure_2d(depth_format, data_.extent);
@@ -341,6 +379,12 @@ void Film::init(const int2 &extent, const rcti *output_rect)
reset += value_accum_tx_.ensure_2d_array(float_format,
(data_.value_len > 0) ? data_.extent : int2(1),
(data_.value_len > 0) ? data_.value_len : 1);
+ /* Divided by two as two cryptomatte samples fit in pixel (RG, BA). */
+ int cryptomatte_array_len = cryptomatte_layer_len_get() * data_.cryptomatte_samples_len / 2;
+ reset += cryptomatte_tx_.ensure_2d_array(cryptomatte_format,
+ (cryptomatte_array_len > 0) ? data_.extent : int2(1),
+ (cryptomatte_array_len > 0) ? cryptomatte_array_len :
+ 1);
if (reset > 0) {
sampling.reset();
@@ -353,6 +397,7 @@ void Film::init(const int2 &extent, const rcti *output_rect)
combined_tx_.current().clear(float4(0.0f));
weight_tx_.current().clear(float4(0.0f));
depth_tx_.clear(float4(0.0f));
+ cryptomatte_tx_.clear(float4(0.0f));
}
}
@@ -398,6 +443,7 @@ void Film::sync()
accumulate_ps_.bind_texture("ambient_occlusion_tx", &rbuffers.ambient_occlusion_tx);
accumulate_ps_.bind_texture("aov_color_tx", &rbuffers.aov_color_tx);
accumulate_ps_.bind_texture("aov_value_tx", &rbuffers.aov_value_tx);
+ accumulate_ps_.bind_texture("cryptomatte_tx", &rbuffers.cryptomatte_tx);
/* NOTE(@fclem): 16 is the max number of sampled texture in many implementations.
* If we need more, we need to pack more of the similar passes in the same textures as arrays or
* use image binding instead. */
@@ -408,6 +454,7 @@ void Film::sync()
accumulate_ps_.bind_image("depth_img", &depth_tx_);
accumulate_ps_.bind_image("color_accum_img", &color_accum_tx_);
accumulate_ps_.bind_image("value_accum_img", &value_accum_tx_);
+ accumulate_ps_.bind_image("cryptomatte_img", &cryptomatte_tx_);
/* Sync with rendering passes. */
accumulate_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS);
if (use_compute) {
@@ -416,6 +463,22 @@ void Film::sync()
else {
accumulate_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
+
+ const int cryptomatte_layer_count = cryptomatte_layer_len_get();
+ const bool is_cryptomatte_pass_enabled = cryptomatte_layer_count > 0;
+ const bool do_cryptomatte_sorting = inst_.is_viewport() == false;
+ cryptomatte_post_ps_.init();
+ if (is_cryptomatte_pass_enabled && do_cryptomatte_sorting) {
+ cryptomatte_post_ps_.state_set(DRW_STATE_NO_DRAW);
+ cryptomatte_post_ps_.shader_set(inst_.shaders.static_shader_get(FILM_CRYPTOMATTE_POST));
+ cryptomatte_post_ps_.bind_image("cryptomatte_img", &cryptomatte_tx_);
+ cryptomatte_post_ps_.bind_image("weight_img", &weight_tx_.current());
+ cryptomatte_post_ps_.push_constant("cryptomatte_layer_len", cryptomatte_layer_count);
+ cryptomatte_post_ps_.push_constant("cryptomatte_samples_per_layer",
+ inst_.view_layer->cryptomatte_levels);
+ int2 dispatch_size = math::divide_ceil(int2(cryptomatte_tx_.size()), int2(FILM_GROUP_SIZE));
+ cryptomatte_post_ps_.dispatch(int3(UNPACK2(dispatch_size), 1));
+ }
}
void Film::end_sync()
@@ -463,6 +526,29 @@ eViewLayerEEVEEPassType Film::enabled_passes_get() const
return enabled_passes_;
}
+int Film::cryptomatte_layer_len_get() const
+{
+ int result = 0;
+ result += data_.cryptomatte_object_id == -1 ? 0 : 1;
+ result += data_.cryptomatte_asset_id == -1 ? 0 : 1;
+ result += data_.cryptomatte_material_id == -1 ? 0 : 1;
+ return result;
+}
+
+int Film::cryptomatte_layer_max_get() const
+{
+ if (data_.cryptomatte_material_id != -1) {
+ return 3;
+ }
+ if (data_.cryptomatte_asset_id != -1) {
+ return 2;
+ }
+ if (data_.cryptomatte_object_id != -1) {
+ return 1;
+ }
+ return 0;
+}
+
void Film::update_sample_table()
{
data_.subpixel_offset = pixel_jitter_get();
@@ -599,20 +685,28 @@ void Film::display()
/* IMPORTANT: Do not swap! No accumulation has happened. */
}
-float *Film::read_pass(eViewLayerEEVEEPassType pass_type)
+void Film::cryptomatte_sort()
{
+ DRW_manager_get()->submit(cryptomatte_post_ps_);
+}
+
+float *Film::read_pass(eViewLayerEEVEEPassType pass_type, int layer_offset)
+{
+ ePassStorageType storage_type = pass_storage_type(pass_type);
+ const bool is_value = storage_type == PASS_STORAGE_VALUE;
+ const bool is_cryptomatte = storage_type == PASS_STORAGE_CRYPTOMATTE;
- bool is_value = pass_is_value(pass_type);
Texture &accum_tx = (pass_type == EEVEE_RENDER_PASS_COMBINED) ?
combined_tx_.current() :
(pass_type == EEVEE_RENDER_PASS_Z) ?
depth_tx_ :
- (is_value ? value_accum_tx_ : color_accum_tx_);
+ (is_cryptomatte ? cryptomatte_tx_ :
+ (is_value ? value_accum_tx_ : color_accum_tx_));
accum_tx.ensure_layer_views();
int index = pass_id_get(pass_type);
- GPUTexture *pass_tx = accum_tx.layer_view(index);
+ GPUTexture *pass_tx = accum_tx.layer_view(index + layer_offset);
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
diff --git a/source/blender/draw/engines/eevee_next/eevee_film.hh b/source/blender/draw/engines/eevee_next/eevee_film.hh
index 796fcb24808..5478c20aff2 100644
--- a/source/blender/draw/engines/eevee_next/eevee_film.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_film.hh
@@ -43,11 +43,16 @@ class Film {
/** Incoming combined buffer with post FX applied (motion blur + depth of field). */
GPUTexture *combined_final_tx_ = nullptr;
- /** Main accumulation textures containing every render-pass except depth and combined. */
+ /**
+ * Main accumulation textures containing every render-pass except depth, cryptomatte and
+ * combined.
+ */
Texture color_accum_tx_;
Texture value_accum_tx_;
/** Depth accumulation texture. Separated because using a different format. */
Texture depth_tx_;
+ /** Cryptomatte texture. Separated because it requires full floats. */
+ Texture cryptomatte_tx_;
/** Combined "Color" buffer. Double buffered to allow re-projection. */
SwapChain<Texture, 2> combined_tx_;
/** Weight buffers. Double buffered to allow updating it during accumulation. */
@@ -56,6 +61,7 @@ class Film {
bool force_disable_reprojection_ = false;
PassSimple accumulate_ps_ = {"Film.Accumulate"};
+ PassSimple cryptomatte_post_ps_ = {"Film.Cryptomatte.Post"};
FilmDataBuf data_;
@@ -73,10 +79,13 @@ class Film {
/** Accumulate the newly rendered sample contained in #RenderBuffers and blit to display. */
void accumulate(const DRWView *view, GPUTexture *combined_final_tx);
+ /** Sort and normalize cryptomatte samples. */
+ void cryptomatte_sort();
+
/** Blit to display. No rendered sample needed. */
void display();
- float *read_pass(eViewLayerEEVEEPassType pass_type);
+ float *read_pass(eViewLayerEEVEEPassType pass_type, int layer_offset);
float *read_aov(ViewLayerAOV *aov);
/** Returns shading views internal resolution. */
@@ -93,17 +102,23 @@ class Film {
}
eViewLayerEEVEEPassType enabled_passes_get() const;
+ int cryptomatte_layer_max_get() const;
+ int cryptomatte_layer_len_get() const;
- static bool pass_is_value(eViewLayerEEVEEPassType pass_type)
+ static ePassStorageType pass_storage_type(eViewLayerEEVEEPassType pass_type)
{
switch (pass_type) {
case EEVEE_RENDER_PASS_Z:
case EEVEE_RENDER_PASS_MIST:
case EEVEE_RENDER_PASS_SHADOW:
case EEVEE_RENDER_PASS_AO:
- return true;
+ return PASS_STORAGE_VALUE;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT:
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET:
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL:
+ return PASS_STORAGE_CRYPTOMATTE;
default:
- return false;
+ return PASS_STORAGE_COLOR;
}
}
@@ -154,8 +169,12 @@ class Film {
return data_.shadow_id;
case EEVEE_RENDER_PASS_AO:
return data_.ambient_occlusion_id;
- case EEVEE_RENDER_PASS_CRYPTOMATTE:
- return -1; /* TODO */
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT:
+ return data_.cryptomatte_object_id;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET:
+ return data_.cryptomatte_asset_id;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL:
+ return data_.cryptomatte_material_id;
case EEVEE_RENDER_PASS_VECTOR:
return data_.vector_id;
default:
@@ -163,44 +182,80 @@ class Film {
}
}
- static const char *pass_to_render_pass_name(eViewLayerEEVEEPassType pass_type)
+ static const Vector<std::string> pass_to_render_pass_names(eViewLayerEEVEEPassType pass_type,
+ const ViewLayer *view_layer)
{
+ Vector<std::string> result;
+
+ auto build_cryptomatte_passes = [&](const char *pass_name) {
+ const int num_cryptomatte_passes = (view_layer->cryptomatte_levels + 1) / 2;
+ for (int pass = 0; pass < num_cryptomatte_passes; pass++) {
+ std::stringstream ss;
+ ss.fill('0');
+ ss << pass_name;
+ ss.width(2);
+ ss << pass;
+ result.append(ss.str());
+ }
+ };
+
switch (pass_type) {
case EEVEE_RENDER_PASS_COMBINED:
- return RE_PASSNAME_COMBINED;
+ result.append(RE_PASSNAME_COMBINED);
+ break;
case EEVEE_RENDER_PASS_Z:
- return RE_PASSNAME_Z;
+ result.append(RE_PASSNAME_Z);
+ break;
case EEVEE_RENDER_PASS_MIST:
- return RE_PASSNAME_MIST;
+ result.append(RE_PASSNAME_MIST);
+ break;
case EEVEE_RENDER_PASS_NORMAL:
- return RE_PASSNAME_NORMAL;
+ result.append(RE_PASSNAME_NORMAL);
+ break;
case EEVEE_RENDER_PASS_DIFFUSE_LIGHT:
- return RE_PASSNAME_DIFFUSE_DIRECT;
+ result.append(RE_PASSNAME_DIFFUSE_DIRECT);
+ break;
case EEVEE_RENDER_PASS_DIFFUSE_COLOR:
- return RE_PASSNAME_DIFFUSE_COLOR;
+ result.append(RE_PASSNAME_DIFFUSE_COLOR);
+ break;
case EEVEE_RENDER_PASS_SPECULAR_LIGHT:
- return RE_PASSNAME_GLOSSY_DIRECT;
+ result.append(RE_PASSNAME_GLOSSY_DIRECT);
+ break;
case EEVEE_RENDER_PASS_SPECULAR_COLOR:
- return RE_PASSNAME_GLOSSY_COLOR;
+ result.append(RE_PASSNAME_GLOSSY_COLOR);
+ break;
case EEVEE_RENDER_PASS_VOLUME_LIGHT:
- return RE_PASSNAME_VOLUME_LIGHT;
+ result.append(RE_PASSNAME_VOLUME_LIGHT);
+ break;
case EEVEE_RENDER_PASS_EMIT:
- return RE_PASSNAME_EMIT;
+ result.append(RE_PASSNAME_EMIT);
+ break;
case EEVEE_RENDER_PASS_ENVIRONMENT:
- return RE_PASSNAME_ENVIRONMENT;
+ result.append(RE_PASSNAME_ENVIRONMENT);
+ break;
case EEVEE_RENDER_PASS_SHADOW:
- return RE_PASSNAME_SHADOW;
+ result.append(RE_PASSNAME_SHADOW);
+ break;
case EEVEE_RENDER_PASS_AO:
- return RE_PASSNAME_AO;
- case EEVEE_RENDER_PASS_CRYPTOMATTE:
- BLI_assert_msg(0, "Cryptomatte is not implemented yet.");
- return ""; /* TODO */
+ result.append(RE_PASSNAME_AO);
+ break;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT:
+ build_cryptomatte_passes(RE_PASSNAME_CRYPTOMATTE_OBJECT);
+ break;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET:
+ build_cryptomatte_passes(RE_PASSNAME_CRYPTOMATTE_ASSET);
+ break;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL:
+ build_cryptomatte_passes(RE_PASSNAME_CRYPTOMATTE_MATERIAL);
+ break;
case EEVEE_RENDER_PASS_VECTOR:
- return RE_PASSNAME_VECTOR;
+ result.append(RE_PASSNAME_VECTOR);
+ break;
default:
BLI_assert(0);
- return "";
+ break;
}
+ return result;
}
private:
diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc
index 6150f32f150..9cba3749d52 100644
--- a/source/blender/draw/engines/eevee_next/eevee_instance.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc
@@ -102,6 +102,7 @@ void Instance::begin_sync()
materials.begin_sync();
velocity.begin_sync(); /* NOTE: Also syncs camera. */
lights.begin_sync();
+ cryptomatte.begin_sync();
gpencil_engine_enabled = false;
@@ -182,6 +183,7 @@ void Instance::end_sync()
lights.end_sync();
sampling.end_sync();
film.end_sync();
+ cryptomatte.end_sync();
}
void Instance::render_sync()
@@ -236,10 +238,15 @@ void Instance::render_read_result(RenderLayer *render_layer, const char *view_na
continue;
}
- const char *pass_name = Film::pass_to_render_pass_name(pass_type);
- RenderPass *rp = RE_pass_find_by_name(render_layer, pass_name, view_name);
- if (rp) {
- float *result = film.read_pass(pass_type);
+ Vector<std::string> pass_names = Film::pass_to_render_pass_names(pass_type, view_layer);
+ for (int64_t pass_offset : IndexRange(pass_names.size())) {
+ RenderPass *rp = RE_pass_find_by_name(
+ render_layer, pass_names[pass_offset].c_str(), view_name);
+ if (!rp) {
+ continue;
+ }
+ float *result = film.read_pass(pass_type, pass_offset);
+
if (result) {
BLI_mutex_lock(&render->update_render_passes_mutex);
/* WORKAROUND: We use texture read to avoid using a framebuffer to get the render result.
@@ -255,10 +262,13 @@ void Instance::render_read_result(RenderLayer *render_layer, const char *view_na
/* The vector pass is initialized to weird values. Set it to neutral value if not rendered. */
if ((pass_bits & EEVEE_RENDER_PASS_VECTOR) == 0) {
- const char *vector_pass_name = Film::pass_to_render_pass_name(EEVEE_RENDER_PASS_VECTOR);
- RenderPass *vector_rp = RE_pass_find_by_name(render_layer, vector_pass_name, view_name);
- if (vector_rp) {
- memset(vector_rp->rect, 0, sizeof(float) * 4 * vector_rp->rectx * vector_rp->recty);
+ for (std::string vector_pass_name :
+ Film::pass_to_render_pass_names(EEVEE_RENDER_PASS_VECTOR, view_layer)) {
+ RenderPass *vector_rp = RE_pass_find_by_name(
+ render_layer, vector_pass_name.c_str(), view_name);
+ if (vector_rp) {
+ memset(vector_rp->rect, 0, sizeof(float) * 4 * vector_rp->rectx * vector_rp->recty);
+ }
}
}
}
@@ -290,6 +300,8 @@ void Instance::render_frame(RenderLayer *render_layer, const char *view_name)
#endif
}
+ this->film.cryptomatte_sort();
+
this->render_read_result(render_layer, view_name);
}
@@ -313,6 +325,76 @@ void Instance::draw_viewport(DefaultFramebufferList *dfbl)
}
}
+void Instance::store_metadata(RenderResult *render_result)
+{
+ cryptomatte.store_metadata(render_result);
+}
+
+void Instance::update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
+{
+ RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
+
+#define CHECK_PASS_LEGACY(name, type, channels, chanid) \
+ if (view_layer->passflag & (SCE_PASS_##name)) { \
+ RE_engine_register_pass( \
+ engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
+ } \
+ ((void)0)
+#define CHECK_PASS_EEVEE(name, type, channels, chanid) \
+ if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \
+ RE_engine_register_pass( \
+ engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
+ } \
+ ((void)0)
+
+ CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
+ CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
+ CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
+ CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
+ /* TODO: CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
+ * CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
+ * When available they should be converted from Value textures to RGB. */
+
+ LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
+ if ((aov->flag & AOV_CONFLICT) != 0) {
+ continue;
+ }
+ switch (aov->type) {
+ case AOV_TYPE_COLOR:
+ RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
+ break;
+ case AOV_TYPE_VALUE:
+ RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* NOTE: Name channels lowercase rgba so that compression rules check in OpenEXR DWA code uses
+ * loseless compression. Reportedly this naming is the only one which works good from the
+ * interoperability point of view. Using xyzw naming is not portable. */
+ auto register_cryptomatte_passes = [&](eViewLayerCryptomatteFlags cryptomatte_layer,
+ eViewLayerEEVEEPassType eevee_pass) {
+ if (view_layer->cryptomatte_flag & cryptomatte_layer) {
+ for (std::string pass_name : Film::pass_to_render_pass_names(eevee_pass, view_layer)) {
+ RE_engine_register_pass(
+ engine, scene, view_layer, pass_name.c_str(), 4, "rgba", SOCK_RGBA);
+ }
+ }
+ };
+ register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_OBJECT, EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT);
+ register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_ASSET, EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET);
+ register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_MATERIAL,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL);
+}
+
/** \} */
} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh
index 4ab20d540bf..c8eecbd812d 100644
--- a/source/blender/draw/engines/eevee_next/eevee_instance.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh
@@ -16,6 +16,7 @@
#include "DRW_render.h"
#include "eevee_camera.hh"
+#include "eevee_cryptomatte.hh"
#include "eevee_depth_of_field.hh"
#include "eevee_film.hh"
#include "eevee_hizbuffer.hh"
@@ -49,6 +50,7 @@ class Instance {
VelocityModule velocity;
MotionBlurModule motion_blur;
DepthOfField depth_of_field;
+ Cryptomatte cryptomatte;
HiZBuffer hiz_buffer;
Sampling sampling;
Camera camera;
@@ -91,6 +93,7 @@ class Instance {
velocity(*this),
motion_blur(*this),
depth_of_field(*this),
+ cryptomatte(*this),
hiz_buffer(*this),
sampling(*this),
camera(*this),
@@ -117,9 +120,12 @@ class Instance {
void render_sync();
void render_frame(RenderLayer *render_layer, const char *view_name);
+ void store_metadata(RenderResult *render_result);
void draw_viewport(DefaultFramebufferList *dfbl);
+ static void update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer);
+
bool is_viewport() const
{
return render == nullptr;
diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc
index 16bdfb04d14..33978518ffc 100644
--- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc
@@ -44,6 +44,7 @@ void WorldPipeline::sync(GPUMaterial *gpumat)
world_ps_.bind_image("rp_diffuse_color_img", &rbufs.diffuse_color_tx);
world_ps_.bind_image("rp_specular_color_img", &rbufs.specular_color_tx);
world_ps_.bind_image("rp_emission_img", &rbufs.emission_tx);
+ world_ps_.bind_image("rp_cryptomatte_img", &rbufs.cryptomatte_tx);
world_ps_.draw(DRW_cache_fullscreen_quad_get(), handle);
/* To allow opaque pass rendering over it. */
@@ -110,6 +111,8 @@ void ForwardPipeline::sync()
/* AOVs. */
opaque_ps_.bind_image(RBUFS_AOV_COLOR_SLOT, &inst_.render_buffers.aov_color_tx);
opaque_ps_.bind_image(RBUFS_AOV_VALUE_SLOT, &inst_.render_buffers.aov_value_tx);
+ /* Cryptomatte. */
+ opaque_ps_.bind_image(RBUFS_CRYPTOMATTE_SLOT, &inst_.render_buffers.cryptomatte_tx);
/* Storage Buf. */
opaque_ps_.bind_ssbo(RBUFS_AOV_BUF_SLOT, &inst_.film.aovs_info);
/* Textures. */
@@ -117,6 +120,7 @@ void ForwardPipeline::sync()
inst_.lights.bind_resources(&opaque_ps_);
inst_.sampling.bind_resources(&opaque_ps_);
+ inst_.cryptomatte.bind_resources(&opaque_ps_);
}
opaque_single_sided_ps_ = &opaque_ps_.sub("SingleSided");
diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc
index c18c913d797..8e36e1d071c 100644
--- a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc
@@ -72,6 +72,20 @@ void RenderBuffers::acquire(int2 extent)
color_format, (aovs.color_len > 0) ? extent : int2(1), max_ii(1, aovs.color_len));
aov_value_tx.ensure_2d_array(
float_format, (aovs.value_len > 0) ? extent : int2(1), max_ii(1, aovs.value_len));
+
+ eGPUTextureFormat cryptomatte_format = GPU_R32F;
+ const int cryptomatte_layer_len = inst_.film.cryptomatte_layer_max_get();
+ if (cryptomatte_layer_len == 2) {
+ cryptomatte_format = GPU_RG32F;
+ }
+ else if (cryptomatte_layer_len == 3) {
+ cryptomatte_format = GPU_RGBA32F;
+ }
+ cryptomatte_tx.acquire(
+ pass_extent(static_cast<eViewLayerEEVEEPassType>(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT |
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET |
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL)),
+ cryptomatte_format);
}
void RenderBuffers::release()
@@ -88,6 +102,7 @@ void RenderBuffers::release()
environment_tx.release();
shadow_tx.release();
ambient_occlusion_tx.release();
+ cryptomatte_tx.release();
}
} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh
index 0b761d618cc..ae5d7fbae5c 100644
--- a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh
@@ -35,7 +35,7 @@ class RenderBuffers {
TextureFromPool environment_tx;
TextureFromPool shadow_tx;
TextureFromPool ambient_occlusion_tx;
- // TextureFromPool cryptomatte_tx; /* TODO */
+ TextureFromPool cryptomatte_tx;
/* TODO(fclem): Use texture from pool once they support texture array. */
Texture light_tx;
Texture aov_color_tx;
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc
index 7ff343d14a8..64b1d4891a9 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc
@@ -84,6 +84,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_film_frag";
case FILM_COMP:
return "eevee_film_comp";
+ case FILM_CRYPTOMATTE_POST:
+ return "eevee_film_cryptomatte_post";
case HIZ_DEBUG:
return "eevee_hiz_debug";
case HIZ_UPDATE:
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh
index 9ef42c84373..88538557c07 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh
@@ -28,6 +28,7 @@ namespace blender::eevee {
enum eShaderType {
FILM_FRAG = 0,
FILM_COMP,
+ FILM_CRYPTOMATTE_POST,
DOF_BOKEH_LUT,
DOF_DOWNSAMPLE,
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
index bcdb42c0093..8e96445d6b9 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
@@ -199,6 +199,12 @@ enum eFilmWeightLayerIndex : uint32_t {
FILM_WEIGHT_LAYER_DISTANCE = 1u,
};
+enum ePassStorageType : uint32_t {
+ PASS_STORAGE_COLOR = 0u,
+ PASS_STORAGE_VALUE = 1u,
+ PASS_STORAGE_CRYPTOMATTE = 2u,
+};
+
struct FilmSample {
int2 texel;
float weight;
@@ -255,13 +261,19 @@ struct FilmData {
int combined_id;
/** Id of the render-pass to be displayed. -1 for combined. */
int display_id;
- /** True if the render-pass to be displayed is from the value accum buffer. */
- bool1 display_is_value;
+ /** Storage type of the render-pass to be displayed. */
+ ePassStorageType display_storage_type;
/** True if we bypass the accumulation and directly output the accumulation buffer. */
bool1 display_only;
/** Start of AOVs and number of aov. */
int aov_color_id, aov_color_len;
int aov_value_id, aov_value_len;
+ /** Start of cryptomatte per layer (-1 if pass is not enabled). */
+ int cryptomatte_object_id;
+ int cryptomatte_asset_id;
+ int cryptomatte_material_id;
+ /** Max number of samples stored per layer (is even number). */
+ int cryptomatte_samples_len;
/** Settings to render mist pass */
float mist_scale, mist_bias, mist_exponent;
/** Scene exposure used for better noise reduction. */
@@ -750,6 +762,7 @@ using SamplingDataBuf = draw::StorageBuffer<SamplingData>;
using VelocityGeometryBuf = draw::StorageArrayBuffer<float4, 16, true>;
using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>;
using VelocityObjectBuf = draw::StorageArrayBuffer<float4x4, 16>;
+using CryptomatteObjectBuf = draw::StorageArrayBuffer<float2, 16>;
} // namespace blender::eevee
#endif
diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc
index 5f8b87c24b9..09ea7c9ec3d 100644
--- a/source/blender/draw/engines/eevee_next/eevee_sync.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc
@@ -120,10 +120,14 @@ void SyncModule::sync_mesh(Object *ob,
is_shadow_caster = is_shadow_caster || material->shadow.sub_pass != nullptr;
is_alpha_blend = is_alpha_blend || material->is_alpha_blend_transparent;
+
+ GPUMaterial *gpu_material = material_array.gpu_materials[i];
+ ::Material *mat = GPU_material_get_material(gpu_material);
+ inst_.cryptomatte.sync_material(mat);
}
inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials);
-
+ inst_.cryptomatte.sync_object(ob, res_handle);
// shadows.sync_object(ob, ob_handle, is_shadow_caster, is_alpha_blend);
}
@@ -320,6 +324,12 @@ void SyncModule::sync_curves(Object *ob,
shgroup_curves_call(material.prepass, ob, part_sys, modifier_data);
shgroup_curves_call(material.shadow, ob, part_sys, modifier_data);
+ inst_.cryptomatte.sync_object(ob, res_handle);
+ GPUMaterial *gpu_material =
+ inst_.materials.material_array_get(ob, has_motion).gpu_materials[mat_nr - 1];
+ ::Material *mat = GPU_material_get_material(gpu_material);
+ inst_.cryptomatte.sync_material(mat);
+
/* TODO(fclem) Hair velocity. */
// shading_passes.velocity.gpencil_add(ob, ob_handle);
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl
new file mode 100644
index 00000000000..e874a6b56ea
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl
@@ -0,0 +1,70 @@
+/** Storing/merging and sorting cryptomatte samples. */
+
+bool cryptomatte_can_merge_sample(vec2 dst, vec2 src)
+{
+ if (dst == vec2(0.0, 0.0)) {
+ return true;
+ }
+ if (dst.x == src.x) {
+ return true;
+ }
+ return false;
+}
+
+vec2 cryptomatte_merge_sample(vec2 dst, vec2 src)
+{
+ return vec2(src.x, dst.y + src.y);
+}
+
+vec4 cryptomatte_false_color(float hash)
+{
+ uint m3hash = floatBitsToUint(hash);
+ return vec4(hash,
+ float(m3hash << 8) / float(0xFFFFFFFFu),
+ float(m3hash << 16) / float(0xFFFFFFFFu),
+ 1.0);
+}
+
+void cryptomatte_clear_samples(FilmSample dst)
+{
+ int layer_len = imageSize(cryptomatte_img).z;
+ for (int i = 0; i < layer_len; i++) {
+ imageStore(cryptomatte_img, ivec3(dst.texel, i), vec4(0.0));
+ }
+}
+
+void cryptomatte_store_film_sample(FilmSample dst,
+ int cryptomatte_layer_id,
+ vec2 crypto_sample,
+ out vec4 out_color)
+{
+ if (crypto_sample.y == 0.0) {
+ return;
+ }
+ for (int i = 0; i < film_buf.cryptomatte_samples_len / 2; i++) {
+ ivec3 img_co = ivec3(dst.texel, cryptomatte_layer_id + i);
+ vec4 sample_pair = imageLoad(cryptomatte_img, img_co);
+ if (cryptomatte_can_merge_sample(sample_pair.xy, crypto_sample)) {
+ sample_pair.xy = cryptomatte_merge_sample(sample_pair.xy, crypto_sample);
+ /* In viewport only one layer is active. */
+ /* TODO(jbakker): we are displaying the first sample, but we should display the highest
+ * weighted one. */
+ if (cryptomatte_layer_id + i == 0) {
+ out_color = cryptomatte_false_color(sample_pair.x);
+ }
+ }
+ else if (cryptomatte_can_merge_sample(sample_pair.zw, crypto_sample)) {
+ sample_pair.zw = cryptomatte_merge_sample(sample_pair.zw, crypto_sample);
+ }
+ else if (i == film_buf.cryptomatte_samples_len / 2 - 1) {
+ /* TODO(jbakker): New hash detected, but there is no space left to store it. Currently we
+ * will ignore this sample, but ideally we could replace a sample with a lowest weight. */
+ continue;
+ }
+ else {
+ continue;
+ }
+ imageStore(cryptomatte_img, img_co, sample_pair);
+ break;
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl
new file mode 100644
index 00000000000..120edd9c35e
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl
@@ -0,0 +1,77 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+#define CRYPTOMATTE_LEVELS_MAX 16
+
+void cryptomatte_load_samples(ivec2 texel, int layer, out vec2 samples[CRYPTOMATTE_LEVELS_MAX])
+{
+ int pass_len = divide_ceil(cryptomatte_samples_per_layer, 2);
+ int layer_id = layer * pass_len;
+
+ /* Read all samples from the cryptomatte layer. */
+ for (int p = 0; p < pass_len; p++) {
+ vec4 pass_sample = imageLoad(cryptomatte_img, ivec3(texel, p + layer_id));
+ samples[p * 2] = pass_sample.xy;
+ samples[p * 2 + 1] = pass_sample.zw;
+ }
+ for (int i = pass_len * 2; i < CRYPTOMATTE_LEVELS_MAX; i++) {
+ samples[i] = vec2(0.0);
+ }
+}
+
+void cryptomatte_sort_samples(inout vec2 samples[CRYPTOMATTE_LEVELS_MAX])
+{
+ /* Sort samples. Lame implementation, can be replaced with a more efficient algorithm. */
+ for (int i = 0; i < cryptomatte_samples_per_layer - 1 && samples[i].y != 0.0; i++) {
+ int highest_index = i;
+ float highest_weight = samples[i].y;
+ for (int j = i + 1; j < cryptomatte_samples_per_layer && samples[j].y != 0.0; j++) {
+ if (samples[j].y > highest_weight) {
+ highest_index = j;
+ highest_weight = samples[j].y;
+ }
+ };
+
+ if (highest_index != i) {
+ vec2 tmp = samples[i];
+ samples[i] = samples[highest_index];
+ samples[highest_index] = tmp;
+ }
+ }
+}
+void cryptomatte_normalize_weight(float total_weight, inout vec2 samples[CRYPTOMATTE_LEVELS_MAX])
+{
+ for (int i = 0; i < CRYPTOMATTE_LEVELS_MAX; i++) {
+ samples[i].y /= total_weight;
+ }
+}
+
+void cryptomatte_store_samples(ivec2 texel, int layer, in vec2 samples[CRYPTOMATTE_LEVELS_MAX])
+{
+ int pass_len = divide_ceil(cryptomatte_samples_per_layer, 2);
+ int layer_id = layer * pass_len;
+
+ /* Store samples back to the cryptomatte layer. */
+ for (int p = 0; p < pass_len; p++) {
+ vec4 pass_sample;
+ pass_sample.xy = samples[p * 2];
+ pass_sample.zw = samples[p * 2 + 1];
+ imageStore(cryptomatte_img, ivec3(texel, p + layer_id), pass_sample);
+ }
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ for (int layer = 0; layer < cryptomatte_layer_len; layer++) {
+ vec2 samples[CRYPTOMATTE_LEVELS_MAX];
+ cryptomatte_load_samples(texel, layer, samples);
+ cryptomatte_sort_samples(samples);
+ /* Repeat texture coordinates as the weight can be optimized to a small portion of the film. */
+ float weight = imageLoad(
+ weight_img,
+ ivec3(texel % imageSize(weight_img).xy, FILM_WEIGHT_LAYER_ACCUMULATION))
+ .x;
+ cryptomatte_normalize_weight(weight, samples);
+ cryptomatte_store_samples(texel, layer, samples);
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl
index 26040234fd0..e2aaf9128a5 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl
@@ -13,13 +13,17 @@ void main()
if (film_buf.display_id == -1) {
out_color = texelFetch(in_combined_tx, texel_film, 0);
}
- else if (film_buf.display_is_value) {
+ else if (film_buf.display_storage_type == PASS_STORAGE_VALUE) {
out_color.rgb = imageLoad(value_accum_img, ivec3(texel_film, film_buf.display_id)).rrr;
out_color.a = 1.0;
}
- else {
+ else if (film_buf.display_storage_type == PASS_STORAGE_COLOR) {
out_color = imageLoad(color_accum_img, ivec3(texel_film, film_buf.display_id));
}
+ else /* PASS_STORAGE_CRYPTOMATTE */ {
+ out_color = cryptomatte_false_color(
+ imageLoad(cryptomatte_img, ivec3(texel_film, film_buf.display_id)).r);
+ }
}
else {
film_process_data(texel_film, out_color, out_depth);
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl
index 087efa9100d..21b9a83abb9 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl
@@ -8,6 +8,7 @@
#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_cryptomatte_lib.glsl)
/* Return scene linear Z depth from the camera or radial depth for panoramic cameras. */
float film_depth_convert_to_scene(float depth)
@@ -158,6 +159,45 @@ void film_sample_accum_combined(FilmSample samp, inout vec4 accum, inout float w
weight_accum += weight;
}
+void film_sample_cryptomatte_accum(FilmSample samp,
+ int layer,
+ sampler2D tex,
+ inout vec2 crypto_samples[4])
+{
+ float hash = texelFetch(tex, samp.texel, 0)[layer];
+ /* Find existing entry. */
+ for (int i = 0; i < 4; i++) {
+ if (crypto_samples[i].x == hash) {
+ crypto_samples[i].y += samp.weight;
+ return;
+ }
+ }
+ /* Overwrite entry with less weight. */
+ for (int i = 0; i < 4; i++) {
+ if (crypto_samples[i].y < samp.weight) {
+ crypto_samples[i] = vec2(hash, samp.weight);
+ return;
+ }
+ }
+}
+
+void film_cryptomatte_layer_accum_and_store(
+ FilmSample dst, ivec2 texel_film, int pass_id, int layer_component, inout vec4 out_color)
+{
+ if (pass_id == -1) {
+ return;
+ }
+ /* x = hash, y = accumed weight. Only keep track of 4 highest weighted samples. */
+ vec2 crypto_samples[4] = vec2[4](vec2(0.0), vec2(0.0), vec2(0.0), vec2(0.0));
+ for (int i = 0; i < film_buf.samples_len; i++) {
+ FilmSample src = film_sample_get(i, texel_film);
+ film_sample_cryptomatte_accum(src, layer_component, cryptomatte_tx, crypto_samples);
+ }
+ for (int i = 0; i < 4; i++) {
+ cryptomatte_store_film_sample(dst, pass_id, crypto_samples[i], out_color);
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -698,4 +738,18 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
}
film_store_value(dst, film_buf.aov_value_id + aov, aov_accum, out_color);
}
+
+ if (film_buf.cryptomatte_samples_len != 0) {
+ /* Cryptomatte passes cannot be cleared by a weighted store like other passes. */
+ if (!film_buf.use_history || film_buf.use_reprojection) {
+ cryptomatte_clear_samples(dst);
+ }
+
+ film_cryptomatte_layer_accum_and_store(
+ dst, texel_film, film_buf.cryptomatte_object_id, 0, out_color);
+ film_cryptomatte_layer_accum_and_store(
+ dst, texel_film, film_buf.cryptomatte_asset_id, 1, out_color);
+ film_cryptomatte_layer_accum_and_store(
+ dst, texel_film, film_buf.cryptomatte_material_id, 2, out_color);
+ }
}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
index 39758c0dfc1..ab29067763d 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
@@ -107,6 +107,9 @@ void main()
imageStore(rp_diffuse_color_img, out_texel, vec4(g_diffuse_data.color, 1.0));
imageStore(rp_specular_color_img, out_texel, vec4(specular_color, 1.0));
imageStore(rp_emission_img, out_texel, vec4(g_emission, 1.0));
+ imageStore(rp_cryptomatte_img,
+ out_texel,
+ vec4(cryptomatte_object_buf[resource_id], node_tree.crypto_hash, 0.0));
#endif
out_radiance.rgb *= 1.0 - g_holdout;
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
index 1ef1c1f84b8..442c2579c84 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
@@ -33,6 +33,7 @@ void main()
imageStore(rp_diffuse_color_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
imageStore(rp_specular_color_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
imageStore(rp_emission_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
+ imageStore(rp_cryptomatte_img, out_texel, vec4(0.0));
out_background.rgb = safe_color(g_emission) * (1.0 - g_holdout);
out_background.a = saturate(avg(g_transmittance)) * g_holdout;
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
index db82a3265d7..4541f14d96c 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
@@ -21,7 +21,7 @@ GPU_SHADER_CREATE_INFO(eevee_film)
.sampler(13, ImageType::FLOAT_2D_ARRAY, "aov_value_tx")
/* Color History for TAA needs to be sampler to leverage bilinear sampling. */
.sampler(14, ImageType::FLOAT_2D, "in_combined_tx")
- // .sampler(15, ImageType::FLOAT_2D, "cryptomatte_tx") /* TODO */
+ .sampler(15, ImageType::FLOAT_2D, "cryptomatte_tx")
.image(0, GPU_R32F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "in_weight_img")
.image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img")
/* Color History for TAA needs to be sampler to leverage bilinear sampling. */
@@ -30,6 +30,7 @@ GPU_SHADER_CREATE_INFO(eevee_film)
.image(4, GPU_R32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "depth_img")
.image(5, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "color_accum_img")
.image(6, GPU_R16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "value_accum_img")
+ .image(7, GPU_RGBA32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "cryptomatte_img")
.additional_info("eevee_shared")
.additional_info("eevee_velocity_camera")
.additional_info("draw_view");
@@ -45,3 +46,13 @@ GPU_SHADER_CREATE_INFO(eevee_film_comp)
.local_group_size(FILM_GROUP_SIZE, FILM_GROUP_SIZE)
.compute_source("eevee_film_comp.glsl")
.additional_info("eevee_film");
+
+GPU_SHADER_CREATE_INFO(eevee_film_cryptomatte_post)
+ .do_static_compilation(true)
+ .image(0, GPU_RGBA32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "cryptomatte_img")
+ .image(1, GPU_R32F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "weight_img")
+ .push_constant(Type::INT, "cryptomatte_layer_len")
+ .push_constant(Type::INT, "cryptomatte_samples_per_layer")
+ .local_group_size(FILM_GROUP_SIZE, FILM_GROUP_SIZE)
+ .compute_source("eevee_film_cryptomatte_post_comp.glsl")
+ .additional_info("eevee_shared");
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
index 9abdd1f8adf..78d52d4b90e 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
@@ -92,6 +92,10 @@ GPU_SHADER_CREATE_INFO(eevee_render_pass_out)
.image_out(RBUFS_SPEC_COLOR_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img")
.image_out(RBUFS_EMISSION_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img");
+GPU_SHADER_CREATE_INFO(eevee_cryptomatte_out)
+ .storage_buf(7, Qualifier::READ, "vec2", "cryptomatte_object_buf[]", Frequency::PASS)
+ .image_out(7, Qualifier::WRITE, GPU_RGBA32F, "rp_cryptomatte_img");
+
GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
.vertex_out(eevee_surf_iface)
/* NOTE: This removes the possibility of using gl_FragDepth. */
@@ -121,7 +125,10 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward)
.fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0)
.fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1)
.fragment_source("eevee_surf_forward_frag.glsl")
- .additional_info("eevee_light_data", "eevee_utility_texture", "eevee_sampling_data"
+ .additional_info("eevee_cryptomatte_out",
+ "eevee_light_data",
+ "eevee_utility_texture",
+ "eevee_sampling_data"
// "eevee_lightprobe_data",
// "eevee_shadow_data"
/* Optionally added depending on the material. */
@@ -141,7 +148,10 @@ GPU_SHADER_CREATE_INFO(eevee_surf_world)
.push_constant(Type::FLOAT, "world_opacity_fade")
.fragment_out(0, Type::VEC4, "out_background")
.fragment_source("eevee_surf_world_frag.glsl")
- .additional_info("eevee_aov_out", "eevee_render_pass_out", "eevee_utility_texture");
+ .additional_info("eevee_aov_out",
+ "eevee_cryptomatte_out",
+ "eevee_render_pass_out",
+ "eevee_utility_texture");
#undef image_out
#undef image_array_out
diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc
index 0102b8db5b2..75e148e0a8f 100644
--- a/source/blender/gpu/intern/gpu_codegen.cc
+++ b/source/blender/gpu/intern/gpu_codegen.cc
@@ -11,6 +11,7 @@
#include "DNA_customdata_types.h"
#include "DNA_image_types.h"
+#include "DNA_material_types.h"
#include "BLI_ghash.h"
#include "BLI_hash_mm2a.h"
@@ -20,6 +21,7 @@
#include "PIL_time.h"
+#include "BKE_cryptomatte.hh"
#include "BKE_material.h"
#include "GPU_capabilities.h"
@@ -238,6 +240,7 @@ class GPUCodegen {
uint32_t hash_ = 0;
BLI_HashMurmur2A hm2a_;
ListBase ubo_inputs_ = {nullptr, nullptr};
+ GPUInput *cryptomatte_input_ = nullptr;
public:
GPUCodegen(GPUMaterial *mat_, GPUNodeGraph *graph_) : mat(*mat_), graph(*graph_)
@@ -262,11 +265,13 @@ class GPUCodegen {
MEM_SAFE_FREE(output.displacement);
MEM_SAFE_FREE(output.composite);
MEM_SAFE_FREE(output.material_functions);
+ MEM_SAFE_FREE(cryptomatte_input_);
delete create_info;
BLI_freelistN(&ubo_inputs_);
};
void generate_graphs();
+ void generate_cryptomatte();
void generate_uniform_buffer();
void generate_attribs();
void generate_resources();
@@ -399,7 +404,12 @@ void GPUCodegen::generate_resources()
ss << "struct NodeTree {\n";
LISTBASE_FOREACH (LinkData *, link, &ubo_inputs_) {
GPUInput *input = (GPUInput *)(link->data);
- ss << input->type << " u" << input->id << ";\n";
+ if (input->source == GPU_SOURCE_CRYPTOMATTE) {
+ ss << input->type << " crypto_hash;\n";
+ }
+ else {
+ ss << input->type << " u" << input->id << ";\n";
+ }
}
ss << "};\n\n";
@@ -535,6 +545,24 @@ char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag)
return eval_c_str;
}
+void GPUCodegen::generate_cryptomatte()
+{
+ cryptomatte_input_ = static_cast<GPUInput *>(MEM_callocN(sizeof(GPUInput), __func__));
+ cryptomatte_input_->type = GPU_FLOAT;
+ cryptomatte_input_->source = GPU_SOURCE_CRYPTOMATTE;
+
+ float material_hash = 0.0f;
+ Material *material = GPU_material_get_material(&mat);
+ if (material) {
+ blender::bke::cryptomatte::CryptomatteHash hash(material->id.name,
+ BLI_strnlen(material->id.name, MAX_NAME - 2));
+ material_hash = hash.float_encoded();
+ }
+ cryptomatte_input_->vec[0] = material_hash;
+
+ BLI_addtail(&ubo_inputs_, BLI_genericNodeN(cryptomatte_input_));
+}
+
void GPUCodegen::generate_uniform_buffer()
{
/* Extract uniform inputs. */
@@ -615,6 +643,7 @@ GPUPass *GPU_generate_pass(GPUMaterial *material,
GPUCodegen codegen(material, graph);
codegen.generate_graphs();
+ codegen.generate_cryptomatte();
codegen.generate_uniform_buffer();
/* Cache lookup: Reuse shaders already compiled. */
diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h
index 08ff8bbef58..74afb721a1c 100644
--- a/source/blender/gpu/intern/gpu_node_graph.h
+++ b/source/blender/gpu/intern/gpu_node_graph.h
@@ -35,6 +35,7 @@ typedef enum eGPUDataSource {
GPU_SOURCE_TEX,
GPU_SOURCE_TEX_TILED_MAPPING,
GPU_SOURCE_FUNCTION_CALL,
+ GPU_SOURCE_CRYPTOMATTE,
} eGPUDataSource;
typedef enum {
diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h
index 2176df7f4ec..b9aadcaf183 100644
--- a/source/blender/makesdna/DNA_layer_types.h
+++ b/source/blender/makesdna/DNA_layer_types.h
@@ -34,10 +34,18 @@ typedef enum eViewLayerEEVEEPassType {
EEVEE_RENDER_PASS_AO = (1 << 13),
EEVEE_RENDER_PASS_BLOOM = (1 << 14),
EEVEE_RENDER_PASS_AOV = (1 << 15),
+ /*
+ * TODO(jbakker): Clean up confliting bits after EEVEE has been removed.
+ * EEVEE_RENDER_PASS_CRYPTOMATTE is for EEVEE, EEVEE_RENDER_PASS_CRYTPOMATTE_* are for
+ * EEVEE-Next.
+ */
EEVEE_RENDER_PASS_CRYPTOMATTE = (1 << 16),
- EEVEE_RENDER_PASS_VECTOR = (1 << 17),
+ EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT = (1 << 16),
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET = (1 << 17),
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL = (1 << 18),
+ EEVEE_RENDER_PASS_VECTOR = (1 << 19),
} eViewLayerEEVEEPassType;
-#define EEVEE_RENDER_PASS_MAX_BIT 18
+#define EEVEE_RENDER_PASS_MAX_BIT 20
/* #ViewLayerAOV.type */
typedef enum eViewLayerAOVType {
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 40345c31fef..f184460cba4 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -304,6 +304,10 @@ typedef enum eScenePassType {
#define RE_PASSNAME_BLOOM "BloomCol"
#define RE_PASSNAME_VOLUME_LIGHT "VolumeDir"
+#define RE_PASSNAME_CRYPTOMATTE_OBJECT "CryptoObject"
+#define RE_PASSNAME_CRYPTOMATTE_ASSET "CryptoAsset"
+#define RE_PASSNAME_CRYPTOMATTE_MATERIAL "CryptoMaterial"
+
/** View - MultiView. */
typedef struct SceneRenderView {
struct SceneRenderView *next, *prev;
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 51acb4da5c3..a5bae9bad8e 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -455,6 +455,9 @@ static const EnumPropertyItem rna_enum_view3dshading_render_pass_type_items[] =
RNA_ENUM_ITEM_HEADING(N_("Data"), NULL),
{EEVEE_RENDER_PASS_NORMAL, "NORMAL", 0, "Normal", ""},
{EEVEE_RENDER_PASS_MIST, "MIST", 0, "Mist", ""},
+ {EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT, "CryptoObject", 0, "CryptoObject", ""},
+ {EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET, "CryptoAsset", 0, "CryptoAsset", ""},
+ {EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL, "CryptoMaterial", 0, "CryptoMaterial", ""},
RNA_ENUM_ITEM_HEADING(N_("Shader AOV"), NULL),
{EEVEE_RENDER_PASS_AOV, "AOV", 0, "AOV", ""},
@@ -1423,6 +1426,7 @@ static const EnumPropertyItem *rna_3DViewShading_render_pass_itemf(bContext *C,
const bool bloom_enabled = scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED;
const bool aov_available = BKE_view_layer_has_valid_aov(view_layer);
+ const bool eevee_next_active = STREQ(scene->r.engine, "BLENDER_EEVEE_NEXT");
int totitem = 0;
EnumPropertyItem *result = NULL;
@@ -1443,6 +1447,12 @@ static const EnumPropertyItem *rna_3DViewShading_render_pass_itemf(bContext *C,
aov_template.value++;
}
}
+ else if (ELEM(item->value,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL) &&
+ !eevee_next_active) {
+ }
else if (!((!bloom_enabled &&
(item->value == EEVEE_RENDER_PASS_BLOOM || STREQ(item->name, "Effects"))) ||
(!aov_available && STREQ(item->name, "Shader AOV")))) {