diff options
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/BKE_cryptomatte.h | 42 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/cryptomatte.c | 87 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/layer.c | 2 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_290.c | 10 | ||||
-rw-r--r-- | source/blender/draw/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_cryptomatte.c | 654 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_engine.c | 2 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_private.h | 38 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_render.c | 30 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_renderpasses.c | 9 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_shaders.c | 32 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl | 7 | ||||
-rw-r--r-- | source/blender/draw/tests/shaders_test.cc | 2 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_layer_types.h | 20 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_scene.c | 40 |
16 files changed, 975 insertions, 3 deletions
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 */ |