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:
-rw-r--r--release/scripts/startup/bl_ui/properties_view_layer.py26
-rw-r--r--source/blender/blenkernel/BKE_cryptomatte.h42
-rw-r--r--source/blender/blenkernel/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/intern/cryptomatte.c87
-rw-r--r--source/blender/blenkernel/intern/layer.c2
-rw-r--r--source/blender/blenloader/intern/versioning_290.c10
-rw-r--r--source/blender/draw/CMakeLists.txt2
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c654
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h38
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c30
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c9
-rw-r--r--source/blender/draw/engines/eevee/eevee_shaders.c32
-rw-r--r--source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl7
-rw-r--r--source/blender/draw/tests/shaders_test.cc2
-rw-r--r--source/blender/makesdna/DNA_layer_types.h20
-rw-r--r--source/blender/makesrna/intern/rna_scene.c40
17 files changed, 1001 insertions, 3 deletions
diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py
index 35cd7ae5ab9..b7aacd0c8be 100644
--- a/release/scripts/startup/bl_ui/properties_view_layer.py
+++ b/release/scripts/startup/bl_ui/properties_view_layer.py
@@ -171,12 +171,38 @@ class VIEWLAYER_PT_layer_passes_aov(ViewLayerButtonsPanel, Panel):
layout.label(text="Conflicts with another render pass with the same name", icon='ERROR')
+class VIEWLAYER_PT_layer_passes_cryptomatte(ViewLayerButtonsPanel, Panel):
+ bl_label = "Cryptomatte"
+ bl_parent_id = "VIEWLAYER_PT_layer_passes"
+ COMPAT_ENGINES = {'BLENDER_EEVEE'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ view_layer = context.view_layer
+
+ col = layout.column()
+ col.prop(view_layer, "use_pass_cryptomatte_object", text="Object")
+ col.prop(view_layer, "use_pass_cryptomatte_material", text="Material")
+ col.prop(view_layer, "use_pass_cryptomatte_asset", text="Asset")
+ col = layout.column()
+ col.active = any((view_layer.use_pass_cryptomatte_object,
+ view_layer.use_pass_cryptomatte_material,
+ view_layer.use_pass_cryptomatte_asset))
+ col.prop(view_layer, "pass_cryptomatte_depth", text="Levels")
+ col.prop(view_layer, "use_pass_cryptomatte_accurate", text="Accurate Mode")
+
+
classes = (
VIEWLAYER_PT_layer,
VIEWLAYER_PT_layer_passes,
VIEWLAYER_PT_eevee_layer_passes_data,
VIEWLAYER_PT_eevee_layer_passes_light,
VIEWLAYER_PT_eevee_layer_passes_effects,
+ VIEWLAYER_PT_layer_passes_cryptomatte,
VIEWLAYER_PT_layer_passes_aov,
VIEWLAYER_UL_aov,
)
diff --git a/source/blender/blenkernel/BKE_cryptomatte.h b/source/blender/blenkernel/BKE_cryptomatte.h
new file mode 100644
index 00000000000..9ad4770c754
--- /dev/null
+++ b/source/blender/blenkernel/BKE_cryptomatte.h
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#pragma once
+
+#include "BLI_sys_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Object;
+struct Material;
+
+uint32_t BKE_cryptomatte_object_hash(const struct Object *object);
+uint32_t BKE_cryptomatte_material_hash(const struct Material *material);
+uint32_t BKE_cryptomatte_asset_hash(const struct Object *object);
+float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index a328c600eac..c962f0a6a8c 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -101,6 +101,7 @@ set(SRC
intern/constraint.c
intern/context.c
intern/crazyspace.c
+ intern/cryptomatte.c
intern/curve.c
intern/curve_bevel.c
intern/curve_decimate.c
diff --git a/source/blender/blenkernel/intern/cryptomatte.c b/source/blender/blenkernel/intern/cryptomatte.c
new file mode 100644
index 00000000000..6570ffce920
--- /dev/null
+++ b/source/blender/blenkernel/intern/cryptomatte.c
@@ -0,0 +1,87 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BKE_cryptomatte.h"
+
+#include "DNA_material_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_compiler_attrs.h"
+#include "BLI_hash_mm3.h"
+#include "BLI_string.h"
+#include <string.h>
+
+static uint32_t cryptomatte_hash(const ID *id)
+{
+ const char *name = &id->name[2];
+ const int len = BLI_strnlen(name, MAX_NAME - 2);
+ uint32_t cryptohash_int = BLI_hash_mm3((const unsigned char *)name, len, 0);
+ return cryptohash_int;
+}
+
+uint32_t BKE_cryptomatte_object_hash(const Object *object)
+{
+ return cryptomatte_hash(&object->id);
+}
+
+uint32_t BKE_cryptomatte_material_hash(const Material *material)
+{
+ if (material == NULL) {
+ return 0.0f;
+ }
+ return cryptomatte_hash(&material->id);
+}
+
+uint32_t BKE_cryptomatte_asset_hash(const Object *object)
+{
+ const Object *asset_object = object;
+ while (asset_object->parent != NULL) {
+ asset_object = asset_object->parent;
+ }
+ return cryptomatte_hash(&asset_object->id);
+}
+
+/* Convert a cryptomatte hash to a float.
+ *
+ * Cryptomatte hashes are stored in float textures and images. The conversion is taken from the
+ * cryptomatte specification. See Floating point conversion section in
+ * https://github.com/Psyop/Cryptomatte/blob/master/specification/cryptomatte_specification.pdf.
+ *
+ * The conversion uses as many 32 bit floating point values as possible to minimize hash
+ * collisions. Unfortunately not all 32 bits can be as NaN and Inf can be problematic.
+ *
+ * Note that this conversion assumes to be running on a L-endian system. */
+float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash)
+{
+ uint32_t mantissa = cryptomatte_hash & ((1 << 23) - 1);
+ uint32_t exponent = (cryptomatte_hash >> 23) & ((1 << 8) - 1);
+ exponent = MAX2(exponent, (uint32_t)1);
+ exponent = MIN2(exponent, (uint32_t)254);
+ exponent = exponent << 23;
+ uint32_t sign = (cryptomatte_hash >> 31);
+ sign = sign << 31;
+ uint32_t float_bits = sign | exponent | mantissa;
+ float f;
+ memcpy(&f, &float_bits, sizeof(uint32_t));
+ return f;
+}
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index 6b346d7a337..8a699e31f37 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -177,6 +177,8 @@ static ViewLayer *view_layer_add(const char *name)
view_layer->layflag = 0x7FFF; /* solid ztra halo edge strand */
view_layer->passflag = SCE_PASS_COMBINED | SCE_PASS_Z;
view_layer->pass_alpha_threshold = 0.5f;
+ view_layer->cryptomatte_levels = 6;
+ view_layer->cryptomatte_flag = VIEW_LAYER_CRYPTOMATTE_ACCURATE;
BKE_freestyle_config_init(&view_layer->freestyle_config);
return view_layer;
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index cebeef1fc46..99e113f5c36 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -1230,5 +1230,15 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
LISTBASE_FOREACH (PointCloud *, pointcloud, &bmain->pointclouds) {
do_versions_point_attribute_names(&pointcloud->pdata);
}
+
+ /* Cryptomatte render pass */
+ if (!DNA_struct_elem_find(fd->filesdna, "ViewLayer", "short", "cryptomatte_levels")) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ view_layer->cryptomatte_levels = 6;
+ view_layer->cryptomatte_flag = VIEW_LAYER_CRYPTOMATTE_ACCURATE;
+ }
+ }
+ }
}
}
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 2de6ee1f57d..a6cc9fddd69 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -80,6 +80,7 @@ set(SRC
engines/image/image_engine.c
engines/image/image_shader.c
engines/eevee/eevee_bloom.c
+ engines/eevee/eevee_cryptomatte.c
engines/eevee/eevee_data.c
engines/eevee/eevee_depth_of_field.c
engines/eevee/eevee_effects.c
@@ -250,6 +251,7 @@ data_to_c_simple(engines/eevee/shaders/bsdf_sampling_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/raytrace_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/renderpass_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/renderpass_postprocess_frag.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/cryptomatte_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/ltc_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/ssr_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/surface_frag.glsl SRC)
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
new file mode 100644
index 00000000000..7bf7955eeb4
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
@@ -0,0 +1,654 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2020, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup EEVEE
+ *
+ * This file implements Cryptomatte for EEVEE. Cryptomatte is used to extract mattes using
+ * information already available at render time. See
+ * https://raw.githubusercontent.com/Psyop/Cryptomatte/master/specification/IDmattes_poster.pdf
+ * for reference to the cryptomatte specification.
+ *
+ * The challenge with cryptomatte in EEVEE is the merging and sorting of the samples.
+ * User can enable upto 3 cryptomatte layers (Object, Material and Asset).
+ *
+ * Process
+ *
+ * - Cryptomatte sample: Rendering of a cryptomatte sample is stored in a GPUBuffer. The buffer
+ * holds a single float per pixel per number of active cryptomatte layers. The float is the
+ * cryptomatte hash of each layer. After drawing the cryptomatte sample the intermediate result is
+ * downloaded to a CPU buffer (`cryptomatte_download_buffer`).
+ *
+ * Accurate mode
+ *
+ * There are two accuracy modes. The difference between the two is the number of render samples
+ * they take into account to create the render passes. When accurate mode is off the number of
+ * levels is used as the number of cryptomatte samples to take. When accuracy mode is on the number
+ * of render samples is used.
+ *
+ */
+
+#include "DRW_engine.h"
+#include "DRW_render.h"
+
+#include "BKE_cryptomatte.h"
+
+#include "GPU_batch.h"
+
+#include "RE_pipeline.h"
+
+#include "BLI_alloca.h"
+#include "BLI_math_bits.h"
+#include "BLI_rect.h"
+
+#include "DNA_hair_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_particle_types.h"
+
+#include "eevee_private.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Data Management cryptomatte accum buffer
+ * \{ */
+
+BLI_INLINE eViewLayerCryptomatteFlags eevee_cryptomatte_active_layers(const ViewLayer *view_layer)
+{
+ const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag &
+ VIEW_LAYER_CRYPTOMATTE_ALL;
+ return cryptomatte_layers;
+}
+
+/* The number of cryptomatte layers that are enabled */
+BLI_INLINE int eevee_cryptomatte_layers_count(const ViewLayer *view_layer)
+{
+ const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
+ view_layer);
+ return count_bits_i(cryptomatte_layers);
+}
+
+/* The number of render result passes are needed to store a single cryptomatte layer. Per
+ * renderpass 2 cryptomatte samples can be stored. */
+BLI_INLINE int eevee_cryptomatte_passes_per_layer(const ViewLayer *view_layer)
+{
+ const int num_cryptomatte_levels = view_layer->cryptomatte_levels;
+ const int num_cryptomatte_passes = (num_cryptomatte_levels + 1) / 2;
+ return num_cryptomatte_passes;
+}
+
+BLI_INLINE int eevee_cryptomatte_layer_stride(const ViewLayer *view_layer)
+{
+ return view_layer->cryptomatte_levels;
+}
+
+BLI_INLINE int eevee_cryptomatte_layer_offset(const ViewLayer *view_layer, const int layer)
+{
+ return view_layer->cryptomatte_levels * layer;
+}
+
+BLI_INLINE int eevee_cryptomatte_pixel_stride(const ViewLayer *view_layer)
+{
+ return eevee_cryptomatte_layer_stride(view_layer) * eevee_cryptomatte_layers_count(view_layer);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Init Renderpasses
+ * \{ */
+
+void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata)
+{
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_PrivateData *g_data = stl->g_data;
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ ViewLayer *view_layer = draw_ctx->view_layer;
+
+ /* Cryptomatte is only rendered for final image renders */
+ if (!DRW_state_is_image_render()) {
+ return;
+ }
+ if (eevee_cryptomatte_active_layers(view_layer) != 0) {
+ g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE;
+ g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag &
+ VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0;
+ }
+}
+
+void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
+ EEVEE_Data *vedata,
+ int UNUSED(tot_samples))
+{
+ EEVEE_FramebufferList *fbl = vedata->fbl;
+ EEVEE_TextureList *txl = vedata->txl;
+ EEVEE_PrivateData *g_data = vedata->stl->g_data;
+
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const ViewLayer *view_layer = draw_ctx->view_layer;
+
+ const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
+ eGPUDataFormat format = (num_cryptomatte_layers == 1) ?
+ GPU_R32F :
+ (num_cryptomatte_layers == 2) ? GPU_RG32F : GPU_RGBA32F;
+ const float *viewport_size = DRW_viewport_size_get();
+ const int buffer_size = viewport_size[0] * viewport_size[1];
+
+ if (g_data->cryptomatte_accum_buffer == NULL) {
+ g_data->cryptomatte_accum_buffer = MEM_calloc_arrayN(
+ sizeof(EEVEE_CryptomatteSample),
+ buffer_size * eevee_cryptomatte_pixel_stride(view_layer),
+ __func__);
+ /* Download buffer should store a float per active cryptomatte layer. */
+ g_data->cryptomatte_download_buffer = MEM_malloc_arrayN(
+ sizeof(float), buffer_size * num_cryptomatte_layers, __func__);
+ }
+
+ DRW_texture_ensure_fullscreen_2d(&txl->cryptomatte, format, 0);
+ GPU_framebuffer_ensure_config(&fbl->cryptomatte_fb,
+ {
+ GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+ GPU_ATTACHMENT_TEXTURE(txl->cryptomatte),
+ });
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Populate Cache
+ * \{ */
+
+void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
+{
+ EEVEE_PassList *psl = vedata->psl;
+ if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
+ DRW_PASS_CREATE(psl->cryptomatte_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
+ }
+}
+
+static DRWShadingGroup *eevee_cryptomatte_shading_group_create(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *UNUSED(sldata),
+ Object *ob,
+ Material *material,
+ bool is_hair)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const ViewLayer *view_layer = draw_ctx->view_layer;
+ const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
+ view_layer);
+ float cryptohash[4] = {0.0f};
+
+ EEVEE_PassList *psl = vedata->psl;
+ int layer_offset = 0;
+ if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
+ uint32_t cryptomatte_hash = BKE_cryptomatte_object_hash(ob);
+ float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
+ cryptohash[layer_offset] = cryptomatte_color_value;
+ layer_offset++;
+ }
+ if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
+ uint32_t cryptomatte_hash = BKE_cryptomatte_material_hash(material);
+ float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
+ cryptohash[layer_offset] = cryptomatte_color_value;
+ layer_offset++;
+ }
+ if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
+ uint32_t cryptomatte_hash = BKE_cryptomatte_asset_hash(ob);
+ float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
+ cryptohash[layer_offset] = cryptomatte_color_value;
+ layer_offset++;
+ }
+
+ DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_cryptomatte_sh_get(is_hair),
+ psl->cryptomatte_ps);
+ DRW_shgroup_uniform_vec4_copy(grp, "cryptohash", cryptohash);
+
+ return grp;
+}
+
+static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob,
+ ParticleSystem *psys,
+ ModifierData *md,
+ Material *material)
+{
+ DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
+ vedata, sldata, ob, material, true);
+ DRW_shgroup_hair_create_sub(ob, psys, md, grp);
+}
+
+void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob)
+{
+ BLI_assert(ob->type == OB_HAIR);
+ Hair *hair = ob->data;
+ Material *material = hair->mat ? hair->mat[HAIR_MATERIAL_NR - 1] : NULL;
+ eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material);
+}
+
+void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+
+ if (ob->type == OB_MESH) {
+ if (ob != draw_ctx->object_edit) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type != eModifierType_ParticleSystem) {
+ continue;
+ }
+ ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
+ if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
+ continue;
+ }
+ ParticleSettings *part = psys->part;
+ const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
+ if (draw_as != PART_DRAW_PATH) {
+ continue;
+ }
+ Mesh *mesh = ob->data;
+ Material *material = part->omat - 1 < mesh->totcol ? NULL : mesh->mat[part->omat - 1];
+ eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material);
+ }
+ }
+ }
+}
+
+void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const ViewLayer *view_layer = draw_ctx->view_layer;
+ const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
+ view_layer);
+
+ if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
+ const int materials_len = DRW_cache_object_material_count_get(ob);
+ struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
+ memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len);
+ struct GPUBatch **geoms = DRW_cache_object_surface_material_get(
+ ob, gpumat_array, materials_len);
+ if (geoms) {
+ for (int i = 0; i < materials_len; i++) {
+ struct GPUBatch *geom = geoms[i];
+ if (geom == NULL) {
+ continue;
+ }
+ Material *material = BKE_object_material_get(ob, i + 1);
+ DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
+ vedata, sldata, ob, material, false);
+ DRW_shgroup_call(grp, geom, ob);
+ }
+ }
+ }
+ else {
+ GPUBatch *geom = DRW_cache_object_surface_get(ob);
+ if (geom) {
+ DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
+ vedata, sldata, ob, false, NULL);
+ DRW_shgroup_call(grp, geom, ob);
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Accumulate Samples
+ * \{ */
+
+/* Downloads cryptomatte sample buffer from the GPU and integrate the samples with the accumulated
+ * cryptomatte samples. */
+static void eevee_cryptomatte_download_buffer(EEVEE_Data *vedata, GPUFrameBuffer *framebuffer)
+{
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_PrivateData *g_data = stl->g_data;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const ViewLayer *view_layer = draw_ctx->view_layer;
+ const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
+ const int num_levels = view_layer->cryptomatte_levels;
+ const float *viewport_size = DRW_viewport_size_get();
+ const int buffer_size = viewport_size[0] * viewport_size[1];
+
+ EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
+ float *download_buffer = g_data->cryptomatte_download_buffer;
+
+ BLI_assert(accum_buffer);
+ BLI_assert(download_buffer);
+
+ GPU_framebuffer_read_color(framebuffer,
+ 0,
+ 0,
+ viewport_size[0],
+ viewport_size[1],
+ num_cryptomatte_layers,
+ 0,
+ GPU_DATA_FLOAT,
+ download_buffer);
+
+ /* Integrate download buffer into the accum buffer.
+ * The download buffer contains upto 3 floats per pixel (one float per cryptomatte layer.
+ *
+ * NOTE: here we deviate from the cryptomatte standard. During integration the standard always
+ * sort the samples by its weight to make sure that samples with the lowest weight
+ * are discarded first. In our case the weight of each sample is always 1 as we don't have
+ * subsamples and apply the coverage during the post processing. When there is no room for new
+ * samples the new samples has a weight of 1 and will always be discarded. */
+ int download_pixel_index = 0;
+ int accum_pixel_index = 0;
+ int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
+ for (int pixel_index = 0; pixel_index < buffer_size; pixel_index++) {
+ for (int layer = 0; layer < num_cryptomatte_layers; layer++) {
+ const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer);
+ float download_hash = download_buffer[download_pixel_index++];
+ for (int level = 0; level < num_levels; level++) {
+ EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
+ if (sample->hash == download_hash) {
+ sample->weight += 1.0f;
+ break;
+ }
+ /* We test against weight as hash 0.0f is used for samples hitting the world background. */
+ if (sample->weight == 0.0f) {
+ sample->hash = download_hash;
+ sample->weight = 1.0f;
+ break;
+ }
+ }
+ }
+ accum_pixel_index += accum_pixel_stride;
+ }
+}
+
+void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
+{
+ EEVEE_FramebufferList *fbl = vedata->fbl;
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_PrivateData *g_data = stl->g_data;
+ EEVEE_EffectsInfo *effects = stl->effects;
+ EEVEE_PassList *psl = vedata->psl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const ViewLayer *view_layer = draw_ctx->view_layer;
+ const int cryptomatte_levels = view_layer->cryptomatte_levels;
+ const int current_sample = effects->taa_current_sample;
+
+ /* In accurate mode all render samples are evaluated. In inaccurate mode this is limited to the
+ * number of cryptomatte levels. This will reduce the overhead of downloading the GPU buffer and
+ * integrating it into the accum buffer. */
+ if (g_data->cryptomatte_accurate_mode || current_sample < cryptomatte_levels) {
+ static float clear_color[4] = {0.0};
+ GPU_framebuffer_bind(fbl->cryptomatte_fb);
+ GPU_framebuffer_clear_color(fbl->cryptomatte_fb, clear_color);
+ DRW_draw_pass(psl->cryptomatte_ps);
+
+ eevee_cryptomatte_download_buffer(vedata, fbl->cryptomatte_fb);
+
+ /* Restore */
+ GPU_framebuffer_bind(fbl->main_fb);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Update Render Passes
+ * \{ */
+
+/* Register the render passes needed for cryptomatte
+ * normally this is done in `EEVEE_render_update_passes`, but it has been placed here to keep
+ * related code side-by-side for clarity. */
+void EEVEE_cryptomatte_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
+{
+ char cryptomatte_pass_name[MAX_NAME];
+ const short num_passes = eevee_cryptomatte_passes_per_layer(view_layer);
+ if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
+ for (short pass = 0; pass < num_passes; pass++) {
+ BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoObject%02d", pass);
+ RE_engine_register_pass(
+ engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
+ }
+ }
+ if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
+ for (short pass = 0; pass < num_passes; pass++) {
+ BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoMaterial%02d", pass);
+ RE_engine_register_pass(
+ engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
+ }
+ }
+ if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
+ for (short pass = 0; pass < num_passes; pass++) {
+ BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoAsset%02d", pass);
+ RE_engine_register_pass(
+ engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Construct Render Result
+ * \{ */
+
+/* Compare function for cryptomatte samples. Samples with the highest weight will be at the
+ * beginning of the list. */
+static int eevee_cryptomatte_sample_cmp_reverse(const void *a_, const void *b_)
+{
+ const EEVEE_CryptomatteSample *a = a_;
+ const EEVEE_CryptomatteSample *b = b_;
+ if (a->weight < b->weight) {
+ return 1;
+ }
+ if (a->weight > b->weight) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Post process the weights. The accumulated weights buffer adds one to each weight per sample.
+ * During post processing ensure that the total of weights per sample is between 0 and 1. */
+static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata)
+{
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_PrivateData *g_data = stl->g_data;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const ViewLayer *view_layer = draw_ctx->view_layer;
+ const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
+ const int num_levels = view_layer->cryptomatte_levels;
+ const float *viewport_size = DRW_viewport_size_get();
+ const int buffer_size = viewport_size[0] * viewport_size[1];
+
+ EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
+ BLI_assert(accum_buffer);
+ int accum_pixel_index = 0;
+ int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
+
+ for (int pixel_index = 0; pixel_index < buffer_size;
+ pixel_index++, accum_pixel_index += accum_pixel_stride) {
+ for (int layer = 0; layer < num_cryptomatte_layers; layer++) {
+ const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer);
+ /* Calculate the total weight of the sample. */
+ float total_weight = 0.0f;
+ for (int level = 0; level < num_levels; level++) {
+ EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
+ total_weight += sample->weight;
+ }
+ BLI_assert(total_weight > 0.0f);
+
+ float total_weight_inv = 1.0f / total_weight;
+ for (int level = 0; level < num_levels; level++) {
+ EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
+ /* Remove background samples. These samples were used to determine the correct weight
+ * but won't be part of the final result. */
+ if (sample->hash == 0.0f) {
+ sample->weight = 0.0f;
+ }
+ sample->weight *= total_weight_inv;
+ }
+
+ /* Sort accum buffer by coverage of each sample. */
+ qsort(&accum_buffer[accum_pixel_index + layer_offset],
+ num_levels,
+ sizeof(EEVEE_CryptomatteSample),
+ eevee_cryptomatte_sample_cmp_reverse);
+ }
+ }
+}
+
+/* Extract cryptomatte layer from the cryptomatte_accum_buffer to render passes. */
+static void eevee_cryptomatte_extract_render_passes(
+ RenderLayer *rl,
+ const char *viewname,
+ const char *render_pass_name_format,
+ EEVEE_CryptomatteSample *accum_buffer,
+ /* number of render passes per cryptomatte layer. */
+ const int num_cryptomatte_passes,
+ const int num_cryptomatte_levels,
+ const int accum_pixel_stride,
+ const int layer_stride,
+ const int layer_index,
+ const int rect_width,
+ const int rect_height,
+ const int rect_offset_x,
+ const int rect_offset_y,
+ const int viewport_width)
+{
+ char cryptomatte_pass_name[MAX_NAME];
+ /* A pass can store 2 levels. Technically the last pass can have a single level if the number of
+ * levels is an odd number. This parameter counts the number of levels it has processed. */
+ int levels_done = 0;
+ for (int pass = 0; pass < num_cryptomatte_passes; pass++) {
+ /* Each pass holds 2 cryptomatte samples. */
+ const int pass_offset = pass * 2;
+ BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, render_pass_name_format, pass);
+ RenderPass *rp_object = RE_pass_find_by_name(rl, cryptomatte_pass_name, viewname);
+ for (int y = 0; y < rect_height; y++) {
+ for (int x = 0; x < rect_width; x++) {
+ const int accum_buffer_offset = (rect_offset_x + x +
+ (rect_offset_y + y) * viewport_width) *
+ accum_pixel_stride +
+ layer_index * layer_stride + pass_offset;
+ const int render_pass_offset = (y * rect_width + x) * 4;
+ rp_object->rect[render_pass_offset] = accum_buffer[accum_buffer_offset].hash;
+ rp_object->rect[render_pass_offset + 1] = accum_buffer[accum_buffer_offset].weight;
+ if (levels_done + 1 < num_cryptomatte_levels) {
+ rp_object->rect[render_pass_offset + 2] = accum_buffer[accum_buffer_offset + 1].hash;
+ rp_object->rect[render_pass_offset + 3] = accum_buffer[accum_buffer_offset + 1].weight;
+ }
+ else {
+ rp_object->rect[render_pass_offset + 2] = 0.0f;
+ rp_object->rect[render_pass_offset + 3] = 0.0f;
+ }
+ }
+ }
+ levels_done++;
+ }
+}
+
+void EEVEE_cryptomatte_render_result(RenderLayer *rl,
+ const char *viewname,
+ const rcti *rect,
+ EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *UNUSED(sldata))
+{
+ EEVEE_PrivateData *g_data = vedata->stl->g_data;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const ViewLayer *view_layer = draw_ctx->view_layer;
+ const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag &
+ VIEW_LAYER_CRYPTOMATTE_ALL;
+
+ eevee_cryptomatte_postprocess_weights(vedata);
+
+ const int rect_width = BLI_rcti_size_x(rect);
+ const int rect_height = BLI_rcti_size_y(rect);
+ const int rect_offset_x = vedata->stl->g_data->overscan_pixels + rect->xmin;
+ const int rect_offset_y = vedata->stl->g_data->overscan_pixels + rect->ymin;
+ const float *viewport_size = DRW_viewport_size_get();
+ const int viewport_width = viewport_size[0];
+ EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
+ BLI_assert(accum_buffer);
+ const int num_cryptomatte_levels = view_layer->cryptomatte_levels;
+ const int num_cryptomatte_passes = eevee_cryptomatte_passes_per_layer(view_layer);
+ const int layer_stride = eevee_cryptomatte_layer_stride(view_layer);
+ const int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
+
+ int layer_index = 0;
+ if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
+ eevee_cryptomatte_extract_render_passes(rl,
+ viewname,
+ "CryptoObject%02d",
+ accum_buffer,
+ num_cryptomatte_passes,
+ num_cryptomatte_levels,
+ accum_pixel_stride,
+ layer_stride,
+ layer_index,
+ rect_width,
+ rect_height,
+ rect_offset_x,
+ rect_offset_y,
+ viewport_width);
+ layer_index++;
+ }
+ if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
+ eevee_cryptomatte_extract_render_passes(rl,
+ viewname,
+ "CryptoMaterial%02d",
+ accum_buffer,
+ num_cryptomatte_passes,
+ num_cryptomatte_levels,
+ accum_pixel_stride,
+ layer_stride,
+ layer_index,
+ rect_width,
+ rect_height,
+ rect_offset_x,
+ rect_offset_y,
+ viewport_width);
+ layer_index++;
+ }
+ if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
+ eevee_cryptomatte_extract_render_passes(rl,
+ viewname,
+ "CryptoAsset%02d",
+ accum_buffer,
+ num_cryptomatte_passes,
+ num_cryptomatte_levels,
+ accum_pixel_stride,
+ layer_stride,
+ layer_index,
+ rect_width,
+ rect_height,
+ rect_offset_x,
+ rect_offset_y,
+ viewport_width);
+ layer_index++;
+ }
+}
+
+/** \} */
+
+void EEVEE_cryptomatte_free(EEVEE_Data *vedata)
+{
+ EEVEE_PrivateData *g_data = vedata->stl->g_data;
+ MEM_SAFE_FREE(g_data->cryptomatte_accum_buffer);
+ MEM_SAFE_FREE(g_data->cryptomatte_download_buffer);
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index 7d1f40ba5d8..f233b0fda96 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -570,6 +570,8 @@ static void eevee_render_to_image(void *vedata,
EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur);
if (RE_engine_test_break(engine)) {
+ /* Cryptomatte buffers are freed during render_read_result */
+ EEVEE_cryptomatte_free(vedata);
return;
}
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 1385721a569..a6a480ca967 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -167,6 +167,8 @@ BLI_INLINE bool eevee_hdri_preview_overlay_enabled(const View3D *v3d)
EEVEE_RENDER_PASS_ENVIRONMENT | EEVEE_RENDER_PASS_AOV)
#define EEVEE_AOV_HASH_ALL -1
#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1
+#define MAX_CRYPTOMATTE_LAYERS 3
+
/* Material shader variations */
enum {
VAR_MAT_MESH = (1 << 0),
@@ -295,6 +297,7 @@ typedef struct EEVEE_PassList {
/* Renderpass Accumulation. */
struct DRWPass *material_accum_ps;
struct DRWPass *background_accum_ps;
+ struct DRWPass *cryptomatte_ps;
struct DRWPass *depth_ps;
struct DRWPass *depth_cull_ps;
@@ -327,6 +330,7 @@ typedef struct EEVEE_FramebufferList {
struct GPUFrameBuffer *bloom_down_fb[MAX_BLOOM_STEP];
struct GPUFrameBuffer *bloom_accum_fb[MAX_BLOOM_STEP - 1];
struct GPUFrameBuffer *bloom_pass_accum_fb;
+ struct GPUFrameBuffer *cryptomatte_fb;
struct GPUFrameBuffer *shadow_accum_fb;
struct GPUFrameBuffer *ssr_accum_fb;
struct GPUFrameBuffer *sss_blur_fb;
@@ -383,6 +387,7 @@ typedef struct EEVEE_TextureList {
struct GPUTexture *bloom_accum;
struct GPUTexture *ssr_accum;
struct GPUTexture *shadow_accum;
+ struct GPUTexture *cryptomatte;
struct GPUTexture *refract_color;
struct GPUTexture *taa_history;
@@ -910,6 +915,11 @@ typedef struct EEVEE_WorldEngineData {
DrawData dd;
} EEVEE_WorldEngineData;
+typedef struct EEVEE_CryptomatteSample {
+ float hash;
+ float weight;
+} EEVEE_CryptomatteSample;
+
/* *********************************** */
typedef struct EEVEE_Data {
@@ -967,6 +977,9 @@ typedef struct EEVEE_PrivateData {
eViewLayerEEVEEPassType render_passes;
int aov_hash;
int num_aovs_used;
+ bool cryptomatte_accurate_mode;
+ EEVEE_CryptomatteSample *cryptomatte_accum_buffer;
+ float *cryptomatte_download_buffer;
/* Uniform references that are referenced inside the `renderpass_pass`. They are updated
* to reuse the drawing pass and the shading group. */
@@ -1120,6 +1133,7 @@ struct GPUShader *EEVEE_shaders_effect_ambient_occlusion_layer_sh_get(void);
struct GPUShader *EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(void);
struct GPUShader *EEVEE_shaders_effect_screen_raytrace_sh_get(EEVEE_SSRShaderOptions options);
struct GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void);
+struct GPUShader *EEVEE_shaders_cryptomatte_sh_get(bool is_hair);
struct GPUShader *EEVEE_shaders_shadow_sh_get(void);
struct GPUShader *EEVEE_shaders_shadow_accum_sh_get(void);
struct GPUShader *EEVEE_shaders_subsurface_first_pass_sh_get(void);
@@ -1224,6 +1238,30 @@ void EEVEE_bloom_draw(EEVEE_Data *vedata);
void EEVEE_bloom_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples);
void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
+/* eevee_cryptomatte.c */
+void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata);
+void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *sldata,
+ EEVEE_Data *vedata,
+ int tot_samples);
+void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
+void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob);
+void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob);
+void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob);
+void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
+void EEVEE_cryptomatte_update_passes(struct RenderEngine *engine,
+ struct Scene *scene,
+ struct ViewLayer *view_layer);
+void EEVEE_cryptomatte_render_result(struct RenderLayer *rl,
+ const char *viewname,
+ const rcti *rect,
+ EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata);
+void EEVEE_cryptomatte_free(EEVEE_Data *vedata);
+
/* eevee_occlusion.c */
int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata,
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index 32e6eac2402..2b2ff2e5c90 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -191,6 +191,7 @@ void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
EEVEE_subsurface_cache_init(sldata, vedata);
EEVEE_temporal_sampling_cache_init(sldata, vedata);
EEVEE_volumes_cache_init(sldata, vedata);
+ EEVEE_cryptomatte_cache_init(sldata, vedata);
}
/* Used by light cache. in this case engine is NULL. */
@@ -200,9 +201,15 @@ void EEVEE_render_cache(void *vedata,
struct Depsgraph *depsgraph)
{
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
+ EEVEE_Data *data = vedata;
+ EEVEE_StorageList *stl = data->stl;
+ EEVEE_PrivateData *g_data = stl->g_data;
EEVEE_LightProbesInfo *pinfo = sldata->probes;
bool cast_shadow = false;
+ const bool do_cryptomatte = (engine != NULL) &&
+ ((g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0);
+
eevee_id_update(vedata, &ob->id);
if (pinfo->vis_data.collection) {
@@ -227,14 +234,23 @@ void EEVEE_render_cache(void *vedata,
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
if (ob_visibility & OB_VISIBLE_PARTICLES) {
EEVEE_particle_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
+ if (do_cryptomatte) {
+ EEVEE_cryptomatte_particle_hair_cache_populate(data, sldata, ob);
+ }
}
if (ob_visibility & OB_VISIBLE_SELF) {
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) {
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
+ if (do_cryptomatte) {
+ EEVEE_cryptomatte_cache_populate(data, sldata, ob);
+ }
}
else if (ob->type == OB_HAIR) {
EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
+ if (do_cryptomatte) {
+ EEVEE_cryptomatte_object_hair_cache_populate(data, sldata, ob);
+ }
}
else if (ob->type == OB_VOLUME) {
Scene *scene = DEG_get_evaluated_scene(depsgraph);
@@ -493,6 +509,18 @@ static void eevee_render_result_aovs(RenderLayer *rl,
#undef EEVEE_RENDER_RESULT_MATERIAL_PASS
+static void eevee_render_result_cryptomatte(RenderLayer *rl,
+ const char *viewname,
+ const rcti *rect,
+ EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata)
+{
+ if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
+ EEVEE_cryptomatte_render_result(rl, viewname, rect, vedata, sldata);
+ }
+ EEVEE_cryptomatte_free(vedata);
+}
+
static void eevee_render_draw_background(EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
@@ -671,6 +699,7 @@ void EEVEE_render_read_result(EEVEE_Data *vedata,
eevee_render_result_volume_scatter(rl, viewname, rect, vedata, sldata);
eevee_render_result_volume_transmittance(rl, viewname, rect, vedata, sldata);
eevee_render_result_aovs(rl, viewname, rect, vedata, sldata);
+ eevee_render_result_cryptomatte(rl, viewname, rect, vedata, sldata);
}
void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
@@ -720,6 +749,7 @@ void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *v
break;
}
}
+ EEVEE_cryptomatte_update_passes(engine, scene, view_layer);
#undef CHECK_PASS_LEGACY
#undef CHECK_PASS_EEVEE
diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c
index 3f75f10b204..e7a03c678a8 100644
--- a/source/blender/draw/engines/eevee/eevee_renderpasses.c
+++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c
@@ -144,6 +144,7 @@ void EEVEE_renderpasses_init(EEVEE_Data *vedata)
EEVEE_RENDER_PASS_COMBINED;
}
EEVEE_material_renderpasses_init(vedata);
+ EEVEE_cryptomatte_renderpasses_init(vedata);
}
void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
@@ -203,6 +204,11 @@ void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
DRW_TEXTURE_FREE_SAFE(txl->renderpass);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->renderpass_fb);
}
+
+ /* Cryptomatte doesn't use the GPU shader for post processing */
+ if ((g_data->render_passes & (EEVEE_RENDER_PASS_CRYPTOMATTE)) != 0) {
+ EEVEE_cryptomatte_output_init(sldata, vedata, tot_samples);
+ }
}
void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
@@ -385,6 +391,9 @@ void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata,
(EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE | EEVEE_RENDER_PASS_VOLUME_SCATTER)) != 0) {
EEVEE_volumes_output_accumulate(sldata, vedata);
}
+ if ((render_pass & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
+ EEVEE_cryptomatte_output_accumulate(sldata, vedata);
+ }
}
else {
if ((render_pass & EEVEE_RENDER_PASS_BLOOM) != 0 &&
diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c
index f46a98ed845..7a277c18f01 100644
--- a/source/blender/draw/engines/eevee/eevee_shaders.c
+++ b/source/blender/draw/engines/eevee/eevee_shaders.c
@@ -121,6 +121,7 @@ static struct {
/* Render Passes */
struct GPUShader *postprocess_sh;
+ struct GPUShader *cryptomatte_sh[2];
/* Screen Space Reflection */
struct GPUShader *ssr_sh[SSR_MAX_SHADER];
@@ -186,6 +187,7 @@ extern char datatoc_btdf_lut_frag_glsl[];
extern char datatoc_closure_lib_glsl[];
extern char datatoc_common_uniforms_lib_glsl[];
extern char datatoc_common_utiltex_lib_glsl[];
+extern char datatoc_cryptomatte_frag_glsl[];
extern char datatoc_cubemap_lib_glsl[];
extern char datatoc_default_frag_glsl[];
extern char datatoc_lookdev_world_frag_glsl[];
@@ -695,6 +697,34 @@ GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Cryptomatte
+ * \{ */
+
+GPUShader *EEVEE_shaders_cryptomatte_sh_get(bool is_hair)
+{
+ const int index = is_hair ? 1 : 0;
+ if (e_data.cryptomatte_sh[index] == NULL) {
+ DynStr *ds = BLI_dynstr_new();
+ BLI_dynstr_append(ds, SHADER_DEFINES);
+
+ if (is_hair) {
+ BLI_dynstr_append(ds, "#define HAIR_SHADER\n");
+ }
+ else {
+ BLI_dynstr_append(ds, "#define MESH_SHADER\n");
+ }
+ char *defines = BLI_dynstr_get_cstring(ds);
+ e_data.cryptomatte_sh[index] = DRW_shader_create_with_shaderlib(
+ datatoc_surface_vert_glsl, NULL, datatoc_cryptomatte_frag_glsl, e_data.lib, defines);
+ BLI_dynstr_free(ds);
+ MEM_freeN(defines);
+ }
+ return e_data.cryptomatte_sh[index];
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Screen Raytrace
* \{ */
@@ -1428,6 +1458,8 @@ void EEVEE_shaders_free(void)
DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh);
DRW_SHADER_FREE_SAFE(e_data.taa_resolve_sh);
DRW_SHADER_FREE_SAFE(e_data.taa_resolve_reproject_sh);
+ DRW_SHADER_FREE_SAFE(e_data.cryptomatte_sh[0]);
+ DRW_SHADER_FREE_SAFE(e_data.cryptomatte_sh[1]);
for (int i = 0; i < 2; i++) {
DRW_SHADER_FREE_SAFE(e_data.bloom_blit_sh[i]);
DRW_SHADER_FREE_SAFE(e_data.bloom_downsample_sh[i]);
diff --git a/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl b/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl
new file mode 100644
index 00000000000..1e499dbf991
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl
@@ -0,0 +1,7 @@
+uniform vec4 cryptohash;
+out vec4 fragColor;
+
+void main()
+{
+ fragColor = cryptohash;
+} \ No newline at end of file
diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc
index 39d8e45cc19..6674e3eccc1 100644
--- a/source/blender/draw/tests/shaders_test.cc
+++ b/source/blender/draw/tests/shaders_test.cc
@@ -332,6 +332,8 @@ TEST_F(DrawTest, eevee_glsl_shaders_static)
EXPECT_NE(EEVEE_shaders_probe_grid_fill_sh_get(), nullptr);
EXPECT_NE(EEVEE_shaders_probe_planar_downsample_sh_get(), nullptr);
EXPECT_NE(EEVEE_shaders_renderpasses_post_process_sh_get(), nullptr);
+ EXPECT_NE(EEVEE_shaders_cryptomatte_sh_get(index, false), nullptr);
+ EXPECT_NE(EEVEE_shaders_cryptomatte_sh_get(index, true), nullptr);
EXPECT_NE(EEVEE_shaders_shadow_sh_get(), nullptr);
EXPECT_NE(EEVEE_shaders_shadow_accum_sh_get(), nullptr);
EXPECT_NE(EEVEE_shaders_subsurface_first_pass_sh_get(), nullptr);
diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h
index f2e3e0a3c9c..0da7c6b5937 100644
--- a/source/blender/makesdna/DNA_layer_types.h
+++ b/source/blender/makesdna/DNA_layer_types.h
@@ -48,20 +48,30 @@ typedef enum eViewLayerEEVEEPassType {
EEVEE_RENDER_PASS_AO = (1 << 13),
EEVEE_RENDER_PASS_BLOOM = (1 << 14),
EEVEE_RENDER_PASS_AOV = (1 << 15),
+ EEVEE_RENDER_PASS_CRYPTOMATTE = (1 << 16),
} eViewLayerEEVEEPassType;
-#define EEVEE_RENDER_PASS_MAX_BIT 16
+#define EEVEE_RENDER_PASS_MAX_BIT 17
-/* ViewLayerAOV.type */
+/* #ViewLayerAOV.type */
typedef enum eViewLayerAOVType {
AOV_TYPE_VALUE = 0,
AOV_TYPE_COLOR = 1,
} eViewLayerAOVType;
-/* ViewLayerAOV.type */
+/* #ViewLayerAOV.flag */
typedef enum eViewLayerAOVFlag {
AOV_CONFLICT = (1 << 0),
} eViewLayerAOVFlag;
+/* #ViewLayer.cryptomatte_flag */
+typedef enum eViewLayerCryptomatteFlags {
+ VIEW_LAYER_CRYPTOMATTE_OBJECT = (1<<0),
+ VIEW_LAYER_CRYPTOMATTE_MATERIAL = (1<<1),
+ VIEW_LAYER_CRYPTOMATTE_ASSET = (1<<2),
+ VIEW_LAYER_CRYPTOMATTE_ACCURATE = (1<<3),
+} eViewLayerCryptomatteFlags;
+#define VIEW_LAYER_CRYPTOMATTE_ALL (VIEW_LAYER_CRYPTOMATTE_OBJECT | VIEW_LAYER_CRYPTOMATTE_MATERIAL | VIEW_LAYER_CRYPTOMATTE_ASSET)
+
typedef struct Base {
struct Base *next, *prev;
@@ -150,6 +160,10 @@ typedef struct ViewLayer {
/** Pass_xor has to be after passflag. */
int passflag;
float pass_alpha_threshold;
+ short cryptomatte_flag;
+ short cryptomatte_levels;
+ char _pad1[4];
+
int samples;
struct Material *mat_override;
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 0f13ba29d3b..08b3d4f210e 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -4107,6 +4107,46 @@ void rna_def_view_layer_common(StructRNA *srna, const bool scene)
"rna_ViewLayer_active_aov_index_range");
RNA_def_property_ui_text(prop, "Active AOV Index", "Index of active aov");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "use_pass_cryptomatte_object", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "cryptomatte_flag", VIEW_LAYER_CRYPTOMATTE_OBJECT);
+ RNA_def_property_ui_text(
+ prop,
+ "Cryptomatte Object",
+ "Render cryptomatte object pass, for isolating objects in compositing");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
+
+ prop = RNA_def_property(srna, "use_pass_cryptomatte_material", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "cryptomatte_flag", VIEW_LAYER_CRYPTOMATTE_MATERIAL);
+ RNA_def_property_ui_text(
+ prop,
+ "Cryptomatte Material",
+ "Render cryptomatte material pass, for isolating materials in compositing");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
+
+ prop = RNA_def_property(srna, "use_pass_cryptomatte_asset", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "cryptomatte_flag", VIEW_LAYER_CRYPTOMATTE_ASSET);
+ RNA_def_property_ui_text(
+ prop,
+ "Cryptomatte Asset",
+ "Render cryptomatte asset pass, for isolating groups of objects with the same parent");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
+
+ prop = RNA_def_property(srna, "pass_cryptomatte_depth", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "cryptomatte_levels");
+ RNA_def_property_int_default(prop, 6);
+ RNA_def_property_range(prop, 2.0, 16.0);
+ RNA_def_property_ui_text(
+ prop, "Cryptomatte Levels", "Sets how many unique objects can be distinguished per pixel");
+ RNA_def_property_ui_range(prop, 2.0, 16.0, 2.0, 0.0);
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
+
+ prop = RNA_def_property(srna, "use_pass_cryptomatte_accurate", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "cryptomatte_flag", VIEW_LAYER_CRYPTOMATTE_ACCURATE);
+ RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_ui_text(
+ prop, "Cryptomatte Accurate", "Generate a more accurate cryptomatte pass");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
}
/* layer options */