EEVEE Cryptomatte

Cryptomatte is a standard to efficiently create mattes for compositing. The
renderer outputs the required render passes, which can then be used in the
compositor to create masks for specified objects. Unlike the Material and Object
Index passes, the objects to isolate are selected in compositing, and mattes
will be anti-aliased.

Cryptomatte was already available in Cycles this patch adds it to the EEVEE
render engine. Original specification can be found at
https://raw.githubusercontent.com/Psyop/Cryptomatte/master/specification/IDmattes_poster.pdf

**Accurate mode**

Following Cycles, there are two accuracy modes. The difference between the two
modes 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. When
accuracy mode is active, the number of render samples is used.

**Deviation from standard**

Cryptomatte specification is based on a path trace approach where samples and
coverage are calculated at the same time. In EEVEE a sample is an exact match on
top of a prepared depth buffer. Coverage is at that moment always 1. By sampling
multiple times the number of surface hits decides the actual surface coverage
for a matte per pixel.

**Implementation Overview**

When drawing to the cryptomatte GPU buffer the depth of the fragment is matched
to the active depth buffer. The hashes of each cryptomatte layer is written in
the GPU buffer. The exact layout depends on the active cryptomatte layers. The
GPU buffer is downloaded and integrated into an accumulation buffer (stored in
CPU RAM).

The accumulation buffer stores the hashes + weights for a number of levels,
layers per pixel. When a hash already exists the weight will be increased. When
the hash doesn't exists it will be added to the buffer.

After all the samples have been calculated the accumulation buffer is processed.
During this phase the total pixel weights of each layer is mapped to be in a
range between 0 and 1. The hashes are also sorted (highest weight first).

Blender Kernel now has a `BKE_cryptomatte` header that access to common
functions for cryptomatte. This will in the future be used by the API.

* Alpha blended materials aren't supported. Alpha blended materials support in
  render passes needs research how to implement it in a maintainable way for any
  render pass.

This is a list of tasks that needs to be done for the same release that this
patch lands on (Blender 2.92)

* T82571 Add render tests.
* T82572 Documentation.
* T82573 Store hashes + Object names in the render result header.
* T82574 Use threading to increase performance in accumulation and post
  processing.
* T82575 Merge the cycles and EEVEE settings as they are identical.
* T82576 Add RNA to extract the cryptomatte hashes to use in python scripts.

Reviewed By: Clément Foucault

Maniphest Tasks: T81058

Differential Revision: https://developer.blender.org/D9165
This commit is contained in:
Jeroen Bakker 2020-12-04 08:28:43 +01:00 committed by Jeroen Bakker
parent 2bae11d5c0
commit 76a0b322e4
Notes: blender-bot 2023-06-26 11:58:59 +02:00
Referenced by issue #81058, EEVEE: Cryptomatte
17 changed files with 1001 additions and 3 deletions

View File

@ -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,
)

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}
}
}
}
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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

View File

@ -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 &&

View File

@ -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[];
@ -694,6 +696,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]);

View File

@ -0,0 +1,7 @@
uniform vec4 cryptohash;
out vec4 fragColor;
void main()
{
fragColor = cryptohash;
}

View File

@ -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);

View File

@ -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;

View File

@ -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 */