diff options
Diffstat (limited to 'source/blender/draw')
16 files changed, 1030 insertions, 56 deletions
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 2de6ee1f57d..a6cc9fddd69 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -80,6 +80,7 @@ set(SRC engines/image/image_engine.c engines/image/image_shader.c engines/eevee/eevee_bloom.c + engines/eevee/eevee_cryptomatte.c engines/eevee/eevee_data.c engines/eevee/eevee_depth_of_field.c engines/eevee/eevee_effects.c @@ -250,6 +251,7 @@ data_to_c_simple(engines/eevee/shaders/bsdf_sampling_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/raytrace_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/renderpass_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/renderpass_postprocess_frag.glsl SRC) +data_to_c_simple(engines/eevee/shaders/cryptomatte_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/ltc_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/ssr_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/surface_frag.glsl SRC) diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c new file mode 100644 index 00000000000..44ff86b3333 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -0,0 +1,654 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup EEVEE + * + * This file implements Cryptomatte for EEVEE. Cryptomatte is used to extract mattes using + * information already available at render time. See + * https://raw.githubusercontent.com/Psyop/Cryptomatte/master/specification/IDmattes_poster.pdf + * for reference to the cryptomatte specification. + * + * The challenge with cryptomatte in EEVEE is the merging and sorting of the samples. + * User can enable up to 3 cryptomatte layers (Object, Material and Asset). + * + * Process + * + * - Cryptomatte sample: Rendering of a cryptomatte sample is stored in a GPUBuffer. The buffer + * holds a single float per pixel per number of active cryptomatte layers. The float is the + * cryptomatte hash of each layer. After drawing the cryptomatte sample the intermediate result is + * downloaded to a CPU buffer (`cryptomatte_download_buffer`). + * + * Accurate mode + * + * There are two accuracy modes. The difference between the two is the number of render samples + * they take into account to create the render passes. When accurate mode is off the number of + * levels is used as the number of cryptomatte samples to take. When accuracy mode is on the number + * of render samples is used. + * + */ + +#include "DRW_engine.h" +#include "DRW_render.h" + +#include "BKE_cryptomatte.h" + +#include "GPU_batch.h" + +#include "RE_pipeline.h" + +#include "BLI_alloca.h" +#include "BLI_math_bits.h" +#include "BLI_rect.h" + +#include "DNA_hair_types.h" +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_particle_types.h" + +#include "eevee_private.h" + +/* -------------------------------------------------------------------- */ +/** \name Data Management cryptomatte accum buffer + * \{ */ + +BLI_INLINE eViewLayerCryptomatteFlags eevee_cryptomatte_active_layers(const ViewLayer *view_layer) +{ + const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag & + VIEW_LAYER_CRYPTOMATTE_ALL; + return cryptomatte_layers; +} + +/* The number of cryptomatte layers that are enabled */ +BLI_INLINE int eevee_cryptomatte_layers_count(const ViewLayer *view_layer) +{ + const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers( + view_layer); + return count_bits_i(cryptomatte_layers); +} + +/* The number of render result passes are needed to store a single cryptomatte layer. Per + * renderpass 2 cryptomatte samples can be stored. */ +BLI_INLINE int eevee_cryptomatte_passes_per_layer(const ViewLayer *view_layer) +{ + const int num_cryptomatte_levels = view_layer->cryptomatte_levels; + const int num_cryptomatte_passes = (num_cryptomatte_levels + 1) / 2; + return num_cryptomatte_passes; +} + +BLI_INLINE int eevee_cryptomatte_layer_stride(const ViewLayer *view_layer) +{ + return view_layer->cryptomatte_levels; +} + +BLI_INLINE int eevee_cryptomatte_layer_offset(const ViewLayer *view_layer, const int layer) +{ + return view_layer->cryptomatte_levels * layer; +} + +BLI_INLINE int eevee_cryptomatte_pixel_stride(const ViewLayer *view_layer) +{ + return eevee_cryptomatte_layer_stride(view_layer) * eevee_cryptomatte_layers_count(view_layer); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Init Renderpasses + * \{ */ + +void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata) +{ + EEVEE_StorageList *stl = vedata->stl; + EEVEE_PrivateData *g_data = stl->g_data; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + + /* Cryptomatte is only rendered for final image renders */ + if (!DRW_state_is_image_render()) { + return; + } + if (eevee_cryptomatte_active_layers(view_layer) != 0) { + g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE; + g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag & + VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0; + } +} + +void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata), + EEVEE_Data *vedata, + int UNUSED(tot_samples)) +{ + EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_TextureList *txl = vedata->txl; + EEVEE_PrivateData *g_data = vedata->stl->g_data; + + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + const DRWContextState *draw_ctx = DRW_context_state_get(); + const ViewLayer *view_layer = draw_ctx->view_layer; + + const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer); + eGPUTextureFormat format = (num_cryptomatte_layers == 1) ? + GPU_R32F : + (num_cryptomatte_layers == 2) ? GPU_RG32F : GPU_RGBA32F; + const float *viewport_size = DRW_viewport_size_get(); + const int buffer_size = viewport_size[0] * viewport_size[1]; + + if (g_data->cryptomatte_accum_buffer == NULL) { + g_data->cryptomatte_accum_buffer = MEM_calloc_arrayN( + sizeof(EEVEE_CryptomatteSample), + buffer_size * eevee_cryptomatte_pixel_stride(view_layer), + __func__); + /* Download buffer should store a float per active cryptomatte layer. */ + g_data->cryptomatte_download_buffer = MEM_malloc_arrayN( + sizeof(float), buffer_size * num_cryptomatte_layers, __func__); + } + + DRW_texture_ensure_fullscreen_2d(&txl->cryptomatte, format, 0); + GPU_framebuffer_ensure_config(&fbl->cryptomatte_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(txl->cryptomatte), + }); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Populate Cache + * \{ */ + +void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) +{ + EEVEE_PassList *psl = vedata->psl; + if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) { + DRW_PASS_CREATE(psl->cryptomatte_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); + } +} + +static DRWShadingGroup *eevee_cryptomatte_shading_group_create(EEVEE_Data *vedata, + EEVEE_ViewLayerData *UNUSED(sldata), + Object *ob, + Material *material, + bool is_hair) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const ViewLayer *view_layer = draw_ctx->view_layer; + const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers( + view_layer); + float cryptohash[4] = {0.0f}; + + EEVEE_PassList *psl = vedata->psl; + int layer_offset = 0; + if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) { + uint32_t cryptomatte_hash = BKE_cryptomatte_object_hash(ob); + float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash); + cryptohash[layer_offset] = cryptomatte_color_value; + layer_offset++; + } + if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) { + uint32_t cryptomatte_hash = BKE_cryptomatte_material_hash(material); + float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash); + cryptohash[layer_offset] = cryptomatte_color_value; + layer_offset++; + } + if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) { + uint32_t cryptomatte_hash = BKE_cryptomatte_asset_hash(ob); + float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash); + cryptohash[layer_offset] = cryptomatte_color_value; + layer_offset++; + } + + DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_cryptomatte_sh_get(is_hair), + psl->cryptomatte_ps); + DRW_shgroup_uniform_vec4_copy(grp, "cryptohash", cryptohash); + + return grp; +} + +static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob, + ParticleSystem *psys, + ModifierData *md, + Material *material) +{ + DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create( + vedata, sldata, ob, material, true); + DRW_shgroup_hair_create_sub(ob, psys, md, grp); +} + +void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob) +{ + BLI_assert(ob->type == OB_HAIR); + Hair *hair = ob->data; + Material *material = hair->mat ? hair->mat[HAIR_MATERIAL_NR - 1] : NULL; + eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material); +} + +void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + + if (ob->type == OB_MESH) { + if (ob != draw_ctx->object_edit) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type != eModifierType_ParticleSystem) { + continue; + } + ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; + if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { + continue; + } + ParticleSettings *part = psys->part; + const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; + if (draw_as != PART_DRAW_PATH) { + continue; + } + Mesh *mesh = ob->data; + Material *material = part->omat - 1 < mesh->totcol ? NULL : mesh->mat[part->omat - 1]; + eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material); + } + } + } +} + +void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const ViewLayer *view_layer = draw_ctx->view_layer; + const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers( + view_layer); + + if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) { + const int materials_len = DRW_cache_object_material_count_get(ob); + struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); + memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); + struct GPUBatch **geoms = DRW_cache_object_surface_material_get( + ob, gpumat_array, materials_len); + if (geoms) { + for (int i = 0; i < materials_len; i++) { + struct GPUBatch *geom = geoms[i]; + if (geom == NULL) { + continue; + } + Material *material = BKE_object_material_get(ob, i + 1); + DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create( + vedata, sldata, ob, material, false); + DRW_shgroup_call(grp, geom, ob); + } + } + } + else { + GPUBatch *geom = DRW_cache_object_surface_get(ob); + if (geom) { + DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create( + vedata, sldata, ob, false, NULL); + DRW_shgroup_call(grp, geom, ob); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Accumulate Samples + * \{ */ + +/* Downloads cryptomatte sample buffer from the GPU and integrate the samples with the accumulated + * cryptomatte samples. */ +static void eevee_cryptomatte_download_buffer(EEVEE_Data *vedata, GPUFrameBuffer *framebuffer) +{ + EEVEE_StorageList *stl = vedata->stl; + EEVEE_PrivateData *g_data = stl->g_data; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const ViewLayer *view_layer = draw_ctx->view_layer; + const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer); + const int num_levels = view_layer->cryptomatte_levels; + const float *viewport_size = DRW_viewport_size_get(); + const int buffer_size = viewport_size[0] * viewport_size[1]; + + EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer; + float *download_buffer = g_data->cryptomatte_download_buffer; + + BLI_assert(accum_buffer); + BLI_assert(download_buffer); + + GPU_framebuffer_read_color(framebuffer, + 0, + 0, + viewport_size[0], + viewport_size[1], + num_cryptomatte_layers, + 0, + GPU_DATA_FLOAT, + download_buffer); + + /* Integrate download buffer into the accum buffer. + * The download buffer contains upto 3 floats per pixel (one float per cryptomatte layer. + * + * NOTE: here we deviate from the cryptomatte standard. During integration the standard always + * sort the samples by its weight to make sure that samples with the lowest weight + * are discarded first. In our case the weight of each sample is always 1 as we don't have + * subsamples and apply the coverage during the post processing. When there is no room for new + * samples the new samples has a weight of 1 and will always be discarded. */ + int download_pixel_index = 0; + int accum_pixel_index = 0; + int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer); + for (int pixel_index = 0; pixel_index < buffer_size; pixel_index++) { + for (int layer = 0; layer < num_cryptomatte_layers; layer++) { + const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer); + float download_hash = download_buffer[download_pixel_index++]; + for (int level = 0; level < num_levels; level++) { + EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level]; + if (sample->hash == download_hash) { + sample->weight += 1.0f; + break; + } + /* We test against weight as hash 0.0f is used for samples hitting the world background. */ + if (sample->weight == 0.0f) { + sample->hash = download_hash; + sample->weight = 1.0f; + break; + } + } + } + accum_pixel_index += accum_pixel_stride; + } +} + +void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) +{ + EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_StorageList *stl = vedata->stl; + EEVEE_PrivateData *g_data = stl->g_data; + EEVEE_EffectsInfo *effects = stl->effects; + EEVEE_PassList *psl = vedata->psl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const ViewLayer *view_layer = draw_ctx->view_layer; + const int cryptomatte_levels = view_layer->cryptomatte_levels; + const int current_sample = effects->taa_current_sample; + + /* In accurate mode all render samples are evaluated. In inaccurate mode this is limited to the + * number of cryptomatte levels. This will reduce the overhead of downloading the GPU buffer and + * integrating it into the accum buffer. */ + if (g_data->cryptomatte_accurate_mode || current_sample < cryptomatte_levels) { + static float clear_color[4] = {0.0}; + GPU_framebuffer_bind(fbl->cryptomatte_fb); + GPU_framebuffer_clear_color(fbl->cryptomatte_fb, clear_color); + DRW_draw_pass(psl->cryptomatte_ps); + + eevee_cryptomatte_download_buffer(vedata, fbl->cryptomatte_fb); + + /* Restore */ + GPU_framebuffer_bind(fbl->main_fb); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Update Render Passes + * \{ */ + +/* Register the render passes needed for cryptomatte + * normally this is done in `EEVEE_render_update_passes`, but it has been placed here to keep + * related code side-by-side for clarity. */ +void EEVEE_cryptomatte_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) +{ + char cryptomatte_pass_name[MAX_NAME]; + const short num_passes = eevee_cryptomatte_passes_per_layer(view_layer); + if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) { + for (short pass = 0; pass < num_passes; pass++) { + BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoObject%02d", pass); + RE_engine_register_pass( + engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA); + } + } + if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) { + for (short pass = 0; pass < num_passes; pass++) { + BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoMaterial%02d", pass); + RE_engine_register_pass( + engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA); + } + } + if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) { + for (short pass = 0; pass < num_passes; pass++) { + BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoAsset%02d", pass); + RE_engine_register_pass( + engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Construct Render Result + * \{ */ + +/* Compare function for cryptomatte samples. Samples with the highest weight will be at the + * beginning of the list. */ +static int eevee_cryptomatte_sample_cmp_reverse(const void *a_, const void *b_) +{ + const EEVEE_CryptomatteSample *a = a_; + const EEVEE_CryptomatteSample *b = b_; + if (a->weight < b->weight) { + return 1; + } + if (a->weight > b->weight) { + return -1; + } + + return 0; +} + +/* Post process the weights. The accumulated weights buffer adds one to each weight per sample. + * During post processing ensure that the total of weights per sample is between 0 and 1. */ +static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata) +{ + EEVEE_StorageList *stl = vedata->stl; + EEVEE_PrivateData *g_data = stl->g_data; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const ViewLayer *view_layer = draw_ctx->view_layer; + const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer); + const int num_levels = view_layer->cryptomatte_levels; + const float *viewport_size = DRW_viewport_size_get(); + const int buffer_size = viewport_size[0] * viewport_size[1]; + + EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer; + BLI_assert(accum_buffer); + int accum_pixel_index = 0; + int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer); + + for (int pixel_index = 0; pixel_index < buffer_size; + pixel_index++, accum_pixel_index += accum_pixel_stride) { + for (int layer = 0; layer < num_cryptomatte_layers; layer++) { + const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer); + /* Calculate the total weight of the sample. */ + float total_weight = 0.0f; + for (int level = 0; level < num_levels; level++) { + EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level]; + total_weight += sample->weight; + } + BLI_assert(total_weight > 0.0f); + + float total_weight_inv = 1.0f / total_weight; + for (int level = 0; level < num_levels; level++) { + EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level]; + /* Remove background samples. These samples were used to determine the correct weight + * but won't be part of the final result. */ + if (sample->hash == 0.0f) { + sample->weight = 0.0f; + } + sample->weight *= total_weight_inv; + } + + /* Sort accum buffer by coverage of each sample. */ + qsort(&accum_buffer[accum_pixel_index + layer_offset], + num_levels, + sizeof(EEVEE_CryptomatteSample), + eevee_cryptomatte_sample_cmp_reverse); + } + } +} + +/* Extract cryptomatte layer from the cryptomatte_accum_buffer to render passes. */ +static void eevee_cryptomatte_extract_render_passes( + RenderLayer *rl, + const char *viewname, + const char *render_pass_name_format, + EEVEE_CryptomatteSample *accum_buffer, + /* number of render passes per cryptomatte layer. */ + const int num_cryptomatte_passes, + const int num_cryptomatte_levels, + const int accum_pixel_stride, + const int layer_stride, + const int layer_index, + const int rect_width, + const int rect_height, + const int rect_offset_x, + const int rect_offset_y, + const int viewport_width) +{ + char cryptomatte_pass_name[MAX_NAME]; + /* A pass can store 2 levels. Technically the last pass can have a single level if the number of + * levels is an odd number. This parameter counts the number of levels it has processed. */ + int levels_done = 0; + for (int pass = 0; pass < num_cryptomatte_passes; pass++) { + /* Each pass holds 2 cryptomatte samples. */ + const int pass_offset = pass * 2; + BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, render_pass_name_format, pass); + RenderPass *rp_object = RE_pass_find_by_name(rl, cryptomatte_pass_name, viewname); + for (int y = 0; y < rect_height; y++) { + for (int x = 0; x < rect_width; x++) { + const int accum_buffer_offset = (rect_offset_x + x + + (rect_offset_y + y) * viewport_width) * + accum_pixel_stride + + layer_index * layer_stride + pass_offset; + const int render_pass_offset = (y * rect_width + x) * 4; + rp_object->rect[render_pass_offset] = accum_buffer[accum_buffer_offset].hash; + rp_object->rect[render_pass_offset + 1] = accum_buffer[accum_buffer_offset].weight; + if (levels_done + 1 < num_cryptomatte_levels) { + rp_object->rect[render_pass_offset + 2] = accum_buffer[accum_buffer_offset + 1].hash; + rp_object->rect[render_pass_offset + 3] = accum_buffer[accum_buffer_offset + 1].weight; + } + else { + rp_object->rect[render_pass_offset + 2] = 0.0f; + rp_object->rect[render_pass_offset + 3] = 0.0f; + } + } + } + levels_done++; + } +} + +void EEVEE_cryptomatte_render_result(RenderLayer *rl, + const char *viewname, + const rcti *rect, + EEVEE_Data *vedata, + EEVEE_ViewLayerData *UNUSED(sldata)) +{ + EEVEE_PrivateData *g_data = vedata->stl->g_data; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const ViewLayer *view_layer = draw_ctx->view_layer; + const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag & + VIEW_LAYER_CRYPTOMATTE_ALL; + + eevee_cryptomatte_postprocess_weights(vedata); + + const int rect_width = BLI_rcti_size_x(rect); + const int rect_height = BLI_rcti_size_y(rect); + const int rect_offset_x = vedata->stl->g_data->overscan_pixels + rect->xmin; + const int rect_offset_y = vedata->stl->g_data->overscan_pixels + rect->ymin; + const float *viewport_size = DRW_viewport_size_get(); + const int viewport_width = viewport_size[0]; + EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer; + BLI_assert(accum_buffer); + const int num_cryptomatte_levels = view_layer->cryptomatte_levels; + const int num_cryptomatte_passes = eevee_cryptomatte_passes_per_layer(view_layer); + const int layer_stride = eevee_cryptomatte_layer_stride(view_layer); + const int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer); + + int layer_index = 0; + if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) { + eevee_cryptomatte_extract_render_passes(rl, + viewname, + "CryptoObject%02d", + accum_buffer, + num_cryptomatte_passes, + num_cryptomatte_levels, + accum_pixel_stride, + layer_stride, + layer_index, + rect_width, + rect_height, + rect_offset_x, + rect_offset_y, + viewport_width); + layer_index++; + } + if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) { + eevee_cryptomatte_extract_render_passes(rl, + viewname, + "CryptoMaterial%02d", + accum_buffer, + num_cryptomatte_passes, + num_cryptomatte_levels, + accum_pixel_stride, + layer_stride, + layer_index, + rect_width, + rect_height, + rect_offset_x, + rect_offset_y, + viewport_width); + layer_index++; + } + if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) { + eevee_cryptomatte_extract_render_passes(rl, + viewname, + "CryptoAsset%02d", + accum_buffer, + num_cryptomatte_passes, + num_cryptomatte_levels, + accum_pixel_stride, + layer_stride, + layer_index, + rect_width, + rect_height, + rect_offset_x, + rect_offset_y, + viewport_width); + layer_index++; + } +} + +/** \} */ + +void EEVEE_cryptomatte_free(EEVEE_Data *vedata) +{ + EEVEE_PrivateData *g_data = vedata->stl->g_data; + MEM_SAFE_FREE(g_data->cryptomatte_accum_buffer); + MEM_SAFE_FREE(g_data->cryptomatte_download_buffer); +} diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index 5c4ee015c86..47068d0b843 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -240,6 +240,9 @@ void EEVEE_view_layer_data_free(void *storage) DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_light); DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.emit); DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.environment); + for (int aov_index = 0; aov_index < MAX_AOVS; aov_index++) { + DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.aovs[aov_index]); + } if (sldata->material_cache) { BLI_memblock_destroy(sldata->material_cache, NULL); diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 7d1f40ba5d8..f233b0fda96 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -570,6 +570,8 @@ static void eevee_render_to_image(void *vedata, EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur); if (RE_engine_test_break(engine)) { + /* Cryptomatte buffers are freed during render_read_result */ + EEVEE_cryptomatte_free(vedata); return; } diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 58f182ecf8d..c7a8f7729eb 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -244,31 +244,31 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, /* Create RenderPass UBO */ if (sldata->renderpass_ubo.combined == NULL) { EEVEE_RenderPassData data; - data = (EEVEE_RenderPassData){true, true, true, true, true, false, false}; + data = (EEVEE_RenderPassData){true, true, true, true, true, false, false, false, 0}; sldata->renderpass_ubo.combined = GPU_uniformbuf_create_ex( sizeof(data), &data, "renderpass_ubo.combined"); - data = (EEVEE_RenderPassData){true, false, false, false, false, true, false}; + data = (EEVEE_RenderPassData){true, false, false, false, false, true, false, false, 0}; sldata->renderpass_ubo.diff_color = GPU_uniformbuf_create_ex( sizeof(data), &data, "renderpass_ubo.diff_color"); - data = (EEVEE_RenderPassData){true, true, false, false, false, false, false}; + data = (EEVEE_RenderPassData){true, true, false, false, false, false, false, false, 0}; sldata->renderpass_ubo.diff_light = GPU_uniformbuf_create_ex( sizeof(data), &data, "renderpass_ubo.diff_light"); - data = (EEVEE_RenderPassData){false, false, true, false, false, false, false}; + data = (EEVEE_RenderPassData){false, false, true, false, false, false, false, false, 0}; sldata->renderpass_ubo.spec_color = GPU_uniformbuf_create_ex( sizeof(data), &data, "renderpass_ubo.spec_color"); - data = (EEVEE_RenderPassData){false, false, true, true, false, false, false}; + data = (EEVEE_RenderPassData){false, false, true, true, false, false, false, false, 0}; sldata->renderpass_ubo.spec_light = GPU_uniformbuf_create_ex( sizeof(data), &data, "renderpass_ubo.spec_light"); - data = (EEVEE_RenderPassData){false, false, false, false, true, false, false}; + data = (EEVEE_RenderPassData){false, false, false, false, true, false, false, false, 0}; sldata->renderpass_ubo.emit = GPU_uniformbuf_create_ex( sizeof(data), &data, "renderpass_ubo.emit"); - data = (EEVEE_RenderPassData){true, true, true, true, true, false, true}; + data = (EEVEE_RenderPassData){true, true, true, true, true, false, true, false, 0}; sldata->renderpass_ubo.environment = GPU_uniformbuf_create_ex( sizeof(data), &data, "renderpass_ubo.environment"); } @@ -276,6 +276,51 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, /* Used combined pass by default. */ g_data->renderpass_ubo = sldata->renderpass_ubo.combined; + { + g_data->num_aovs_used = 0; + if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_AOV) != 0) { + EEVEE_RenderPassData data = {true, true, true, true, true, false, false, true, 0}; + if (stl->g_data->aov_hash == EEVEE_AOV_HASH_ALL) { + ViewLayer *view_layer = draw_ctx->view_layer; + int aov_index = 0; + LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { + if ((aov->flag & AOV_CONFLICT) != 0) { + continue; + } + if (aov_index == MAX_AOVS) { + break; + } + data.renderPassAOVActive = EEVEE_renderpasses_aov_hash(aov); + if (sldata->renderpass_ubo.aovs[aov_index]) { + GPU_uniformbuf_update(sldata->renderpass_ubo.aovs[aov_index], &data); + } + else { + sldata->renderpass_ubo.aovs[aov_index] = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.aovs"); + } + aov_index++; + } + g_data->num_aovs_used = aov_index; + } + else { + /* Rendering a single AOV in the 3d viewport */ + data.renderPassAOVActive = stl->g_data->aov_hash; + if (sldata->renderpass_ubo.aovs[0]) { + GPU_uniformbuf_update(sldata->renderpass_ubo.aovs[0], &data); + } + else { + sldata->renderpass_ubo.aovs[0] = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.aovs"); + } + g_data->num_aovs_used = 1; + } + } + /* Free AOV UBO's that are not in use. */ + for (int aov_index = g_data->num_aovs_used; aov_index < MAX_AOVS; aov_index++) { + DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.aovs[aov_index]); + } + } + /* HACK: EEVEE_material_get can create a new context. This can only be * done when there is no active framebuffer. We do this here otherwise * `EEVEE_renderpasses_output_init` will fail. It cannot be done in @@ -949,6 +994,11 @@ void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) { material_renderpass_init(fbl, &txl->spec_color_accum, texture_format, do_clear); } + if (pd->render_passes & EEVEE_RENDER_PASS_AOV) { + for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) { + material_renderpass_init(fbl, &txl->aov_surface_accum[aov_index], texture_format, do_clear); + } + } if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) { material_renderpass_init(fbl, &txl->spec_light_accum, texture_format, do_clear); @@ -960,6 +1010,7 @@ void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl, DRWPass *renderpass, + DRWPass *renderpass2, EEVEE_PrivateData *pd, GPUTexture *output_tx, struct GPUUniformBuf *renderpass_option_ubo) @@ -969,6 +1020,9 @@ static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl, pd->renderpass_ubo = renderpass_option_ubo; DRW_draw_pass(renderpass); + if (renderpass2) { + DRW_draw_pass(renderpass2); + } GPU_framebuffer_texture_detach(fbl->material_accum_fb, output_tx); } @@ -983,38 +1037,69 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v if (fbl->material_accum_fb != NULL) { DRWPass *material_accum_ps = psl->material_accum_ps; + DRWPass *background_accum_ps = psl->background_accum_ps; if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) { material_renderpass_accumulate( - fbl, psl->background_accum_ps, pd, txl->env_accum, sldata->renderpass_ubo.environment); + fbl, background_accum_ps, NULL, pd, txl->env_accum, sldata->renderpass_ubo.environment); } if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) { material_renderpass_accumulate( - fbl, material_accum_ps, pd, txl->emit_accum, sldata->renderpass_ubo.emit); + fbl, material_accum_ps, NULL, pd, txl->emit_accum, sldata->renderpass_ubo.emit); } if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) { - material_renderpass_accumulate( - fbl, material_accum_ps, pd, txl->diff_color_accum, sldata->renderpass_ubo.diff_color); + material_renderpass_accumulate(fbl, + material_accum_ps, + NULL, + pd, + txl->diff_color_accum, + sldata->renderpass_ubo.diff_color); } if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) { - material_renderpass_accumulate( - fbl, material_accum_ps, pd, txl->diff_light_accum, sldata->renderpass_ubo.diff_light); + material_renderpass_accumulate(fbl, + material_accum_ps, + NULL, + pd, + txl->diff_light_accum, + sldata->renderpass_ubo.diff_light); if (effects->enabled_effects & EFFECT_SSS) { EEVEE_subsurface_output_accumulate(sldata, vedata); } } if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) { - material_renderpass_accumulate( - fbl, material_accum_ps, pd, txl->spec_color_accum, sldata->renderpass_ubo.spec_color); + material_renderpass_accumulate(fbl, + material_accum_ps, + NULL, + pd, + txl->spec_color_accum, + sldata->renderpass_ubo.spec_color); } if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) { - material_renderpass_accumulate( - fbl, material_accum_ps, pd, txl->spec_light_accum, sldata->renderpass_ubo.spec_light); + material_renderpass_accumulate(fbl, + material_accum_ps, + NULL, + pd, + txl->spec_light_accum, + sldata->renderpass_ubo.spec_light); if (effects->enabled_effects & EFFECT_SSR) { EEVEE_reflection_output_accumulate(sldata, vedata); } } + if (pd->render_passes & EEVEE_RENDER_PASS_AOV) { + for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) { + material_renderpass_accumulate(fbl, + material_accum_ps, + background_accum_ps, + pd, + txl->aov_surface_accum[aov_index], + sldata->renderpass_ubo.aovs[aov_index]); + } + } + /* Free unused aov textures. */ + for (int aov_index = pd->num_aovs_used; aov_index < MAX_AOVS; aov_index++) { + DRW_TEXTURE_FREE_SAFE(txl->aov_surface_accum[aov_index]); + } /* Restore default. */ pd->renderpass_ubo = sldata->renderpass_ubo.combined; diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index f5cef8f3c25..a6a480ca967 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -53,6 +53,7 @@ extern struct DrawEngineType draw_engine_eevee_type; #define MAX_SHADOW_CASCADE 8 #define MAX_SHADOW_CUBE (MAX_SHADOW - MAX_CASCADE_NUM * MAX_SHADOW_CASCADE) #define MAX_BLOOM_STEP 16 +#define MAX_AOVS 64 // #define DEBUG_SHADOW_DISTRIBUTION @@ -163,7 +164,10 @@ BLI_INLINE bool eevee_hdri_preview_overlay_enabled(const View3D *v3d) #define EEVEE_RENDERPASSES_MATERIAL \ (EEVEE_RENDER_PASS_EMIT | EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_DIFFUSE_LIGHT | \ EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_SPECULAR_LIGHT | \ - EEVEE_RENDER_PASS_ENVIRONMENT) + EEVEE_RENDER_PASS_ENVIRONMENT | EEVEE_RENDER_PASS_AOV) +#define EEVEE_AOV_HASH_ALL -1 +#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1 +#define MAX_CRYPTOMATTE_LAYERS 3 /* Material shader variations */ enum { @@ -293,6 +297,7 @@ typedef struct EEVEE_PassList { /* Renderpass Accumulation. */ struct DRWPass *material_accum_ps; struct DRWPass *background_accum_ps; + struct DRWPass *cryptomatte_ps; struct DRWPass *depth_ps; struct DRWPass *depth_cull_ps; @@ -325,6 +330,7 @@ typedef struct EEVEE_FramebufferList { struct GPUFrameBuffer *bloom_down_fb[MAX_BLOOM_STEP]; struct GPUFrameBuffer *bloom_accum_fb[MAX_BLOOM_STEP - 1]; struct GPUFrameBuffer *bloom_pass_accum_fb; + struct GPUFrameBuffer *cryptomatte_fb; struct GPUFrameBuffer *shadow_accum_fb; struct GPUFrameBuffer *ssr_accum_fb; struct GPUFrameBuffer *sss_blur_fb; @@ -376,10 +382,12 @@ typedef struct EEVEE_TextureList { struct GPUTexture *diff_light_accum; struct GPUTexture *spec_color_accum; struct GPUTexture *spec_light_accum; + struct GPUTexture *aov_surface_accum[MAX_AOVS]; struct GPUTexture *emit_accum; struct GPUTexture *bloom_accum; struct GPUTexture *ssr_accum; struct GPUTexture *shadow_accum; + struct GPUTexture *cryptomatte; struct GPUTexture *refract_color; struct GPUTexture *taa_history; @@ -430,7 +438,9 @@ typedef struct EEVEE_RenderPassData { int renderPassEmit; int renderPassSSSColor; int renderPassEnvironment; - int _pad[1]; + int renderPassAOV; + int renderPassAOVActive; + int _pad[3]; } EEVEE_RenderPassData; /* ************ LIGHT UBO ************* */ @@ -860,6 +870,7 @@ typedef struct EEVEE_ViewLayerData { struct GPUUniformBuf *spec_color; struct GPUUniformBuf *spec_light; struct GPUUniformBuf *emit; + struct GPUUniformBuf *aovs[MAX_AOVS]; } renderpass_ubo; /* Common Uniform Buffer */ @@ -904,6 +915,11 @@ typedef struct EEVEE_WorldEngineData { DrawData dd; } EEVEE_WorldEngineData; +typedef struct EEVEE_CryptomatteSample { + float hash; + float weight; +} EEVEE_CryptomatteSample; + /* *********************************** */ typedef struct EEVEE_Data { @@ -959,6 +975,12 @@ typedef struct EEVEE_PrivateData { /* Renderpasses */ /* Bitmask containing the active render_passes */ eViewLayerEEVEEPassType render_passes; + int aov_hash; + int num_aovs_used; + bool cryptomatte_accurate_mode; + EEVEE_CryptomatteSample *cryptomatte_accum_buffer; + float *cryptomatte_download_buffer; + /* Uniform references that are referenced inside the `renderpass_pass`. They are updated * to reuse the drawing pass and the shading group. */ int renderpass_type; @@ -1111,6 +1133,7 @@ struct GPUShader *EEVEE_shaders_effect_ambient_occlusion_layer_sh_get(void); struct GPUShader *EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(void); struct GPUShader *EEVEE_shaders_effect_screen_raytrace_sh_get(EEVEE_SSRShaderOptions options); struct GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void); +struct GPUShader *EEVEE_shaders_cryptomatte_sh_get(bool is_hair); struct GPUShader *EEVEE_shaders_shadow_sh_get(void); struct GPUShader *EEVEE_shaders_shadow_accum_sh_get(void); struct GPUShader *EEVEE_shaders_subsurface_first_pass_sh_get(void); @@ -1215,6 +1238,30 @@ void EEVEE_bloom_draw(EEVEE_Data *vedata); void EEVEE_bloom_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples); void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); +/* eevee_cryptomatte.c */ +void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata); +void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *sldata, + EEVEE_Data *vedata, + int tot_samples); +void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); +void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob); +void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob); +void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob); +void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); +void EEVEE_cryptomatte_update_passes(struct RenderEngine *engine, + struct Scene *scene, + struct ViewLayer *view_layer); +void EEVEE_cryptomatte_render_result(struct RenderLayer *rl, + const char *viewname, + const rcti *rect, + EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata); +void EEVEE_cryptomatte_free(EEVEE_Data *vedata); + /* eevee_occlusion.c */ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, @@ -1284,10 +1331,12 @@ void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata, bool post_effect); void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, - eViewLayerEEVEEPassType renderpass_type); + eViewLayerEEVEEPassType renderpass_type, + int aov_index); void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_renderpasses_draw_debug(EEVEE_Data *vedata); bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata); +int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov); /* eevee_temporal_sampling.c */ void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata); diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index 504e4e1d336..2b2ff2e5c90 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -191,6 +191,7 @@ void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) EEVEE_subsurface_cache_init(sldata, vedata); EEVEE_temporal_sampling_cache_init(sldata, vedata); EEVEE_volumes_cache_init(sldata, vedata); + EEVEE_cryptomatte_cache_init(sldata, vedata); } /* Used by light cache. in this case engine is NULL. */ @@ -200,9 +201,15 @@ void EEVEE_render_cache(void *vedata, struct Depsgraph *depsgraph) { EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); + EEVEE_Data *data = vedata; + EEVEE_StorageList *stl = data->stl; + EEVEE_PrivateData *g_data = stl->g_data; EEVEE_LightProbesInfo *pinfo = sldata->probes; bool cast_shadow = false; + const bool do_cryptomatte = (engine != NULL) && + ((g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0); + eevee_id_update(vedata, &ob->id); if (pinfo->vis_data.collection) { @@ -227,14 +234,23 @@ void EEVEE_render_cache(void *vedata, const int ob_visibility = DRW_object_visibility_in_active_context(ob); if (ob_visibility & OB_VISIBLE_PARTICLES) { EEVEE_particle_hair_cache_populate(vedata, sldata, ob, &cast_shadow); + if (do_cryptomatte) { + EEVEE_cryptomatte_particle_hair_cache_populate(data, sldata, ob); + } } if (ob_visibility & OB_VISIBLE_SELF) { if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow); + if (do_cryptomatte) { + EEVEE_cryptomatte_cache_populate(data, sldata, ob); + } } else if (ob->type == OB_HAIR) { EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow); + if (do_cryptomatte) { + EEVEE_cryptomatte_object_hair_cache_populate(data, sldata, ob); + } } else if (ob->type == OB_VOLUME) { Scene *scene = DEG_get_evaluated_scene(depsgraph); @@ -301,7 +317,7 @@ static void eevee_render_result_normal(RenderLayer *rl, } if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_NORMAL) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_NORMAL); + EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_NORMAL, 0); eevee_render_color_result( rl, viewname, rect, RE_PASSNAME_NORMAL, 3, vedata->fbl->renderpass_fb, vedata); } @@ -321,7 +337,7 @@ static void eevee_render_result_z(RenderLayer *rl, } if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_Z) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_Z); + EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_Z, 0); eevee_render_color_result( rl, viewname, rect, RE_PASSNAME_Z, 1, vedata->fbl->renderpass_fb, vedata); } @@ -334,7 +350,7 @@ static void eevee_render_result_mist(RenderLayer *rl, EEVEE_ViewLayerData *sldata) { if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_MIST) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_MIST); + EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_MIST, 0); eevee_render_color_result( rl, viewname, rect, RE_PASSNAME_MIST, 1, vedata->fbl->renderpass_fb, vedata); } @@ -347,7 +363,7 @@ static void eevee_render_result_shadow(RenderLayer *rl, EEVEE_ViewLayerData *sldata) { if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_SHADOW) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_SHADOW); + EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_SHADOW, 0); eevee_render_color_result( rl, viewname, rect, RE_PASSNAME_SHADOW, 3, vedata->fbl->renderpass_fb, vedata); } @@ -360,7 +376,7 @@ static void eevee_render_result_occlusion(RenderLayer *rl, EEVEE_ViewLayerData *sldata) { if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AO); + EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AO, 0); eevee_render_color_result( rl, viewname, rect, RE_PASSNAME_AO, 3, vedata->fbl->renderpass_fb, vedata); } @@ -378,7 +394,7 @@ static void eevee_render_result_bloom(RenderLayer *rl, } if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_BLOOM); + EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_BLOOM, 0); eevee_render_color_result( rl, viewname, rect, RE_PASSNAME_BLOOM, 3, vedata->fbl->renderpass_fb, vedata); } @@ -386,7 +402,7 @@ static void eevee_render_result_bloom(RenderLayer *rl, #define EEVEE_RENDER_RESULT_MATERIAL_PASS(pass_name, eevee_pass_type) \ if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_##eevee_pass_type) != 0) { \ - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_##eevee_pass_type); \ + EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_##eevee_pass_type, 0); \ eevee_render_color_result( \ rl, viewname, rect, RE_PASSNAME_##pass_name, 3, vedata->fbl->renderpass_fb, vedata); \ } @@ -462,8 +478,49 @@ static void eevee_render_result_volume_transmittance(RenderLayer *rl, EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_TRANSMITTANCE, VOLUME_TRANSMITTANCE) } +static void eevee_render_result_aovs(RenderLayer *rl, + const char *viewname, + const rcti *rect, + EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata) +{ + if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AOV) != 0) { + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + int aov_index = 0; + LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { + if ((aov->flag & AOV_CONFLICT) != 0) { + continue; + } + EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AOV, aov_index); + switch (aov->type) { + case AOV_TYPE_COLOR: + eevee_render_color_result( + rl, viewname, rect, aov->name, 4, vedata->fbl->renderpass_fb, vedata); + break; + case AOV_TYPE_VALUE: + eevee_render_color_result( + rl, viewname, rect, aov->name, 1, vedata->fbl->renderpass_fb, vedata); + } + aov_index++; + } + } +} + #undef EEVEE_RENDER_RESULT_MATERIAL_PASS +static void eevee_render_result_cryptomatte(RenderLayer *rl, + const char *viewname, + const rcti *rect, + EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata) +{ + if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) { + EEVEE_cryptomatte_render_result(rl, viewname, rect, vedata, sldata); + } + EEVEE_cryptomatte_free(vedata); +} + static void eevee_render_draw_background(EEVEE_Data *vedata) { EEVEE_FramebufferList *fbl = vedata->fbl; @@ -641,6 +698,8 @@ void EEVEE_render_read_result(EEVEE_Data *vedata, eevee_render_result_bloom(rl, viewname, rect, vedata, sldata); eevee_render_result_volume_scatter(rl, viewname, rect, vedata, sldata); eevee_render_result_volume_transmittance(rl, viewname, rect, vedata, sldata); + eevee_render_result_aovs(rl, viewname, rect, vedata, sldata); + eevee_render_result_cryptomatte(rl, viewname, rect, vedata, sldata); } void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) @@ -675,6 +734,23 @@ void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *v CHECK_PASS_EEVEE(VOLUME_TRANSMITTANCE, SOCK_RGBA, 3, "RGB"); CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "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_cryptomatte_update_passes(engine, scene, view_layer); + #undef CHECK_PASS_LEGACY #undef CHECK_PASS_EEVEE } diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index be73225b348..e7a03c678a8 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -27,6 +27,7 @@ #include "BKE_global.h" /* for G.debug_value */ +#include "BLI_hash.h" #include "BLI_string_utils.h" #include "DEG_depsgraph_query.h" @@ -36,12 +37,13 @@ typedef enum eRenderPassPostProcessType { PASS_POST_UNDEFINED = 0, PASS_POST_ACCUMULATED_COLOR = 1, - PASS_POST_ACCUMULATED_LIGHT = 2, - PASS_POST_ACCUMULATED_VALUE = 3, - PASS_POST_DEPTH = 4, - PASS_POST_AO = 5, - PASS_POST_NORMAL = 6, - PASS_POST_TWO_LIGHT_BUFFERS = 7, + PASS_POST_ACCUMULATED_COLOR_ALPHA = 2, + PASS_POST_ACCUMULATED_LIGHT = 3, + PASS_POST_ACCUMULATED_VALUE = 4, + PASS_POST_DEPTH = 5, + PASS_POST_AO = 6, + PASS_POST_NORMAL = 7, + PASS_POST_TWO_LIGHT_BUFFERS = 8, } eRenderPassPostProcessType; /* bitmask containing all renderpasses that need post-processing */ @@ -70,6 +72,15 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata) return (g_data->render_passes & ~EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) == 0; } +/* Calculate the hash for an AOV. The least significant bit is used to store the AOV + * type the rest of the bits are used for the name hash. */ +int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov) +{ + int hash = BLI_hash_string(aov->name); + SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK); + return hash; +} + void EEVEE_renderpasses_init(EEVEE_Data *vedata) { const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -81,10 +92,24 @@ void EEVEE_renderpasses_init(EEVEE_Data *vedata) if (v3d) { const Scene *scene = draw_ctx->scene; eViewLayerEEVEEPassType render_pass = v3d->shading.render_pass; + g_data->aov_hash = 0; + if (render_pass == EEVEE_RENDER_PASS_BLOOM && ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) { render_pass = EEVEE_RENDER_PASS_COMBINED; } + if (render_pass == EEVEE_RENDER_PASS_AOV) { + ViewLayerAOV *aov = BLI_findstring( + &view_layer->aovs, v3d->shading.aov_name, offsetof(ViewLayerAOV, name)); + if (aov != NULL) { + g_data->aov_hash = EEVEE_renderpasses_aov_hash(aov); + } + else { + /* AOV not found in view layer. */ + render_pass = EEVEE_RENDER_PASS_COMBINED; + } + } + g_data->render_passes = render_pass; } else { @@ -110,11 +135,16 @@ void EEVEE_renderpasses_init(EEVEE_Data *vedata) ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT) #undef ENABLE_FROM_LEGACY + if (DRW_state_is_image_render() && !BLI_listbase_is_empty(&view_layer->aovs)) { + enabled_render_passes |= EEVEE_RENDER_PASS_AOV; + g_data->aov_hash = EEVEE_AOV_HASH_ALL; + } + g_data->render_passes = (enabled_render_passes & EEVEE_RENDERPASSES_ALL) | EEVEE_RENDER_PASS_COMBINED; } - EEVEE_material_renderpasses_init(vedata); + EEVEE_cryptomatte_renderpasses_init(vedata); } void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata, @@ -174,6 +204,11 @@ void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata, DRW_TEXTURE_FREE_SAFE(txl->renderpass); GPU_FRAMEBUFFER_FREE_SAFE(fbl->renderpass_fb); } + + /* Cryptomatte doesn't use the GPU shader for post processing */ + if ((g_data->render_passes & (EEVEE_RENDER_PASS_CRYPTOMATTE)) != 0) { + EEVEE_cryptomatte_output_init(sldata, vedata, tot_samples); + } } void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) @@ -216,7 +251,8 @@ void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ve * After invoking this function the active frame-buffer is set to `vedata->fbl->renderpass_fb`. */ void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, - eViewLayerEEVEEPassType renderpass_type) + eViewLayerEEVEEPassType renderpass_type, + int aov_index) { EEVEE_PassList *psl = vedata->psl; EEVEE_TextureList *txl = vedata->txl; @@ -311,6 +347,11 @@ void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata), } break; } + case EEVEE_RENDER_PASS_AOV: { + g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR_ALPHA; + g_data->renderpass_input = txl->aov_surface_accum[aov_index]; + break; + } case EEVEE_RENDER_PASS_BLOOM: { g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR; g_data->renderpass_input = txl->bloom_accum; @@ -350,6 +391,9 @@ void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata, (EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE | EEVEE_RENDER_PASS_VOLUME_SCATTER)) != 0) { EEVEE_volumes_output_accumulate(sldata, vedata); } + if ((render_pass & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) { + EEVEE_cryptomatte_output_accumulate(sldata, vedata); + } } else { if ((render_pass & EEVEE_RENDER_PASS_BLOOM) != 0 && @@ -392,7 +436,7 @@ void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) } if (is_valid) { - EEVEE_renderpasses_postprocess(sldata, vedata, render_pass); + EEVEE_renderpasses_postprocess(sldata, vedata, render_pass, 0); GPU_framebuffer_bind(dfbl->default_fb); DRW_transform_none(txl->renderpass); } diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index f46a98ed845..7a277c18f01 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -121,6 +121,7 @@ static struct { /* Render Passes */ struct GPUShader *postprocess_sh; + struct GPUShader *cryptomatte_sh[2]; /* Screen Space Reflection */ struct GPUShader *ssr_sh[SSR_MAX_SHADER]; @@ -186,6 +187,7 @@ extern char datatoc_btdf_lut_frag_glsl[]; extern char datatoc_closure_lib_glsl[]; extern char datatoc_common_uniforms_lib_glsl[]; extern char datatoc_common_utiltex_lib_glsl[]; +extern char datatoc_cryptomatte_frag_glsl[]; extern char datatoc_cubemap_lib_glsl[]; extern char datatoc_default_frag_glsl[]; extern char datatoc_lookdev_world_frag_glsl[]; @@ -695,6 +697,34 @@ GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Cryptomatte + * \{ */ + +GPUShader *EEVEE_shaders_cryptomatte_sh_get(bool is_hair) +{ + const int index = is_hair ? 1 : 0; + if (e_data.cryptomatte_sh[index] == NULL) { + DynStr *ds = BLI_dynstr_new(); + BLI_dynstr_append(ds, SHADER_DEFINES); + + if (is_hair) { + BLI_dynstr_append(ds, "#define HAIR_SHADER\n"); + } + else { + BLI_dynstr_append(ds, "#define MESH_SHADER\n"); + } + char *defines = BLI_dynstr_get_cstring(ds); + e_data.cryptomatte_sh[index] = DRW_shader_create_with_shaderlib( + datatoc_surface_vert_glsl, NULL, datatoc_cryptomatte_frag_glsl, e_data.lib, defines); + BLI_dynstr_free(ds); + MEM_freeN(defines); + } + return e_data.cryptomatte_sh[index]; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Screen Raytrace * \{ */ @@ -1428,6 +1458,8 @@ void EEVEE_shaders_free(void) DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh); DRW_SHADER_FREE_SAFE(e_data.taa_resolve_sh); DRW_SHADER_FREE_SAFE(e_data.taa_resolve_reproject_sh); + DRW_SHADER_FREE_SAFE(e_data.cryptomatte_sh[0]); + DRW_SHADER_FREE_SAFE(e_data.cryptomatte_sh[1]); for (int i = 0; i < 2; i++) { DRW_SHADER_FREE_SAFE(e_data.bloom_blit_sh[i]); DRW_SHADER_FREE_SAFE(e_data.bloom_downsample_sh[i]); diff --git a/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl b/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl new file mode 100644 index 00000000000..1e499dbf991 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl @@ -0,0 +1,7 @@ +uniform vec4 cryptohash; +out vec4 fragColor; + +void main() +{ + fragColor = cryptohash; +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl index 36cf3cecf40..3e0a5e76d00 100644 --- a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl @@ -1,3 +1,4 @@ +#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1 /* ---------------------------------------------------------------------- */ /** \name Resources @@ -12,6 +13,8 @@ layout(std140) uniform renderpass_block bool renderPassEmit; bool renderPassSSSColor; bool renderPassEnvironment; + bool renderPassAOV; + int renderPassAOVActive; }; /** \} */ @@ -40,4 +43,14 @@ vec3 render_pass_emission_mask(vec3 emission_light) return renderPassEmit ? emission_light : vec3(0.0); } +bool render_pass_aov_is_color() +{ + return (renderPassAOVActive & EEVEE_AOV_HASH_COLOR_TYPE_MASK) != 0; +} + +int render_pass_aov_hash() +{ + return renderPassAOVActive & ~EEVEE_AOV_HASH_COLOR_TYPE_MASK; +} + /** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl index 89a411bc7cb..eb6ca4b9de8 100644 --- a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl @@ -4,12 +4,13 @@ #define PASS_POST_UNDEFINED 0 #define PASS_POST_ACCUMULATED_COLOR 1 -#define PASS_POST_ACCUMULATED_LIGHT 2 -#define PASS_POST_ACCUMULATED_VALUE 3 -#define PASS_POST_DEPTH 4 -#define PASS_POST_AO 5 -#define PASS_POST_NORMAL 6 -#define PASS_POST_TWO_LIGHT_BUFFERS 7 +#define PASS_POST_ACCUMULATED_COLOR_ALPHA 2 +#define PASS_POST_ACCUMULATED_LIGHT 3 +#define PASS_POST_ACCUMULATED_VALUE 4 +#define PASS_POST_DEPTH 5 +#define PASS_POST_AO 6 +#define PASS_POST_NORMAL 7 +#define PASS_POST_TWO_LIGHT_BUFFERS 8 uniform int postProcessType; uniform int currentSample; @@ -55,7 +56,7 @@ vec3 safe_divide_even_color(vec3 a, vec3 b) void main() { - vec3 color; + vec4 color = vec4(0.0, 0.0, 0.0, 1.0); ivec2 texel = ivec2(gl_FragCoord.xy); if (postProcessType == PASS_POST_DEPTH) { @@ -66,11 +67,11 @@ void main() else { depth = -get_view_z_from_depth(depth); } - color = vec3(depth); + color.rgb = vec3(depth); } else if (postProcessType == PASS_POST_AO) { float ao_accum = texelFetch(inputBuffer, texel, 0).r; - color = vec3(min(1.0, ao_accum / currentSample)); + color.rgb = vec3(min(1.0, ao_accum / currentSample)); } else if (postProcessType == PASS_POST_NORMAL) { float depth = texelFetch(depthBuffer, texel, 0).r; @@ -80,35 +81,39 @@ void main() if (depth != 1.0 && any(notEqual(encoded_normal, vec2(0.0)))) { vec3 decoded_normal = normal_decode(texelFetch(inputBuffer, texel, 0).rg, vec3(0.0)); vec3 world_normal = mat3(ViewMatrixInverse) * decoded_normal; - color = world_normal; + color.rgb = world_normal; } else { - color = vec3(0.0); + color.rgb = vec3(0.0); } } else if (postProcessType == PASS_POST_ACCUMULATED_VALUE) { float accumulated_value = texelFetch(inputBuffer, texel, 0).r; - color = vec3(accumulated_value / currentSample); + color.rgb = vec3(accumulated_value / currentSample); } else if (postProcessType == PASS_POST_ACCUMULATED_COLOR) { vec3 accumulated_color = texelFetch(inputBuffer, texel, 0).rgb; + color.rgb = (accumulated_color / currentSample); + } + else if (postProcessType == PASS_POST_ACCUMULATED_COLOR_ALPHA) { + vec4 accumulated_color = texelFetch(inputBuffer, texel, 0); color = (accumulated_color / currentSample); } else if (postProcessType == PASS_POST_ACCUMULATED_LIGHT) { vec3 accumulated_light = texelFetch(inputBuffer, texel, 0).rgb; vec3 accumulated_color = texelFetch(inputColorBuffer, texel, 0).rgb; - color = safe_divide_even_color(accumulated_light, accumulated_color); + color.rgb = safe_divide_even_color(accumulated_light, accumulated_color); } else if (postProcessType == PASS_POST_TWO_LIGHT_BUFFERS) { vec3 accumulated_light = texelFetch(inputBuffer, texel, 0).rgb + texelFetch(inputSecondLightBuffer, texel, 0).rgb; vec3 accumulated_color = texelFetch(inputColorBuffer, texel, 0).rgb; - color = safe_divide_even_color(accumulated_light, accumulated_color); + color.rgb = safe_divide_even_color(accumulated_light, accumulated_color); } else { /* Output error color: Unknown how to post process this pass. */ - color = vec3(1.0, 0.0, 1.0); + color.rgb = vec3(1.0, 0.0, 1.0); } - fragColor = vec4(color, 1.0); + fragColor = color; } diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 368530fde05..519b015a6ad 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -252,7 +252,7 @@ void GPENCIL_cache_init(void *ved) pd->do_fast_drawing = false; pd->obact = draw_ctx->obact; - if (pd->obact && pd->obact->type == OB_GPENCIL) { + if (pd->obact && pd->obact->type == OB_GPENCIL && !(pd->draw_depth_only)) { /* Check if active object has a temp stroke data. */ bGPdata *gpd = (bGPdata *)pd->obact->data; if (gpd->runtime.sbuffer_used > 0) { @@ -498,7 +498,7 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl, bool hide_onion = gpl && gpf && gpf->runtime.onion_id != 0 && ((gp_style->flag & GP_MATERIAL_HIDE_ONIONSKIN) != 0); - if (hide_material || (!show_stroke && !show_fill) || only_lines || hide_onion) { + if (hide_material || (!show_stroke && !show_fill) || (only_lines && hide_onion) || hide_onion) { return; } diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c index d01aaaed8b0..cb65fbd6ae7 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c +++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c @@ -95,7 +95,7 @@ static DRWShadingGroup *gpencil_vfx_pass_create(const char *name, static void gpencil_vfx_blur(BlurShaderFxData *fx, Object *ob, gpIterVfxData *iter) { - if (fx->radius[0] == 0.0f && fx->radius[1] == 0.0f) { + if ((fx->samples == 0.0f) || (fx->radius[0] == 0.0f && fx->radius[1] == 0.0f)) { return; } diff --git a/source/blender/draw/engines/image/image_private.h b/source/blender/draw/engines/image/image_private.h index ad7ff78cb41..d5821cc5d70 100644 --- a/source/blender/draw/engines/image/image_private.h +++ b/source/blender/draw/engines/image/image_private.h @@ -43,7 +43,7 @@ typedef struct IMAGE_PrivateData { void *lock; struct ImBuf *ibuf; struct Image *image; - struct DRWView* view; + struct DRWView *view; struct GPUTexture *texture; bool owns_texture; diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index 39d8e45cc19..d365f76aabe 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -332,6 +332,8 @@ TEST_F(DrawTest, eevee_glsl_shaders_static) EXPECT_NE(EEVEE_shaders_probe_grid_fill_sh_get(), nullptr); EXPECT_NE(EEVEE_shaders_probe_planar_downsample_sh_get(), nullptr); EXPECT_NE(EEVEE_shaders_renderpasses_post_process_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_cryptomatte_sh_get(false), nullptr); + EXPECT_NE(EEVEE_shaders_cryptomatte_sh_get(true), nullptr); EXPECT_NE(EEVEE_shaders_shadow_sh_get(), nullptr); EXPECT_NE(EEVEE_shaders_shadow_accum_sh_get(), nullptr); EXPECT_NE(EEVEE_shaders_subsurface_first_pass_sh_get(), nullptr); |