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
This commit is contained in:
Jeroen Bakker 2022-09-13 11:07:30 +02:00 committed by Jeroen Bakker
parent bb3a021427
commit 8068b89a68
Notes: blender-bot 2023-02-14 10:43:47 +01:00
Referenced by issue #99390, Eevee-next: Cryptomatte render pass
33 changed files with 933 additions and 165 deletions

View File

@ -209,7 +209,7 @@ class ViewLayerCryptomattePanel(ViewLayerButtonsPanel, Panel):
class VIEWLAYER_PT_layer_passes_cryptomatte(ViewLayerCryptomattePanel, Panel):
bl_parent_id = "VIEWLAYER_PT_layer_passes"
COMPAT_ENGINES = {'BLENDER_EEVEE'}
COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT'}
class VIEWLAYER_MT_lightgroup_sync(Menu):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,7 +164,23 @@ 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);
EEVEE_Data *ved = static_cast<EEVEE_Data *>(vedata);
if (ved->instance) {
delete ved->instance;
}
ved->instance = instance;
}
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;
}
static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
@ -172,51 +188,7 @@ static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewL
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
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;
}
}
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 = {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,6 +28,7 @@ namespace blender::eevee {
enum eShaderType {
FILM_FRAG = 0,
FILM_COMP,
FILM_CRYPTOMATTE_POST,
DOF_BOKEH_LUT,
DOF_DOWNSAMPLE,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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