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