Cryptomatte is a standard to efficiently create mattes for compositing. The renderer outputs the required render passes, which can then be used in the compositor to create masks for specified objects. Unlike the Material and Object Index passes, the objects to isolate are selected in compositing, and mattes will be anti-aliased. Cryptomatte was already available in Cycles this patch adds it to the EEVEE render engine. Original specification can be found at https://raw.githubusercontent.com/Psyop/Cryptomatte/master/specification/IDmattes_poster.pdf **Accurate mode** Following Cycles, there are two accuracy modes. The difference between the two modes is the number of render samples they take into account to create the render passes. When accurate mode is off the number of levels is used. When accuracy mode is active, the number of render samples is used. **Deviation from standard** Cryptomatte specification is based on a path trace approach where samples and coverage are calculated at the same time. In EEVEE a sample is an exact match on top of a prepared depth buffer. Coverage is at that moment always 1. By sampling multiple times the number of surface hits decides the actual surface coverage for a matte per pixel. **Implementation Overview** When drawing to the cryptomatte GPU buffer the depth of the fragment is matched to the active depth buffer. The hashes of each cryptomatte layer is written in the GPU buffer. The exact layout depends on the active cryptomatte layers. The GPU buffer is downloaded and integrated into an accumulation buffer (stored in CPU RAM). The accumulation buffer stores the hashes + weights for a number of levels, layers per pixel. When a hash already exists the weight will be increased. When the hash doesn't exists it will be added to the buffer. After all the samples have been calculated the accumulation buffer is processed. During this phase the total pixel weights of each layer is mapped to be in a range between 0 and 1. The hashes are also sorted (highest weight first). Blender Kernel now has a `BKE_cryptomatte` header that access to common functions for cryptomatte. This will in the future be used by the API. * Alpha blended materials aren't supported. Alpha blended materials support in render passes needs research how to implement it in a maintainable way for any render pass. This is a list of tasks that needs to be done for the same release that this patch lands on (Blender 2.92) * T82571 Add render tests. * T82572 Documentation. * T82573 Store hashes + Object names in the render result header. * T82574 Use threading to increase performance in accumulation and post processing. * T82575 Merge the cycles and EEVEE settings as they are identical. * T82576 Add RNA to extract the cryptomatte hashes to use in python scripts. Reviewed By: Clément Foucault Maniphest Tasks: T81058 Differential Revision: https://developer.blender.org/D9165
214 lines
7.1 KiB
Python
214 lines
7.1 KiB
Python
# ##### BEGIN GPL LICENSE BLOCK #####
|
|
#
|
|
# 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.
|
|
#
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
# <pep8 compliant>
|
|
from bpy.types import Panel, UIList
|
|
|
|
|
|
class VIEWLAYER_UL_aov(UIList):
|
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
|
row = layout.row()
|
|
split = row.split(factor=0.65)
|
|
icon = 'NONE' if item.is_valid else 'ERROR'
|
|
split.row().prop(item, "name", text="", icon=icon, emboss=False)
|
|
split.row().prop(item, "type", text="", emboss=False)
|
|
|
|
|
|
class ViewLayerButtonsPanel:
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "view_layer"
|
|
# COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return (context.engine in cls.COMPAT_ENGINES)
|
|
|
|
|
|
class VIEWLAYER_PT_layer(ViewLayerButtonsPanel, Panel):
|
|
bl_label = "View Layer"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.use_property_split = True
|
|
|
|
scene = context.scene
|
|
rd = scene.render
|
|
layer = context.view_layer
|
|
|
|
col = layout.column()
|
|
col.prop(layer, "use", text="Use for Rendering")
|
|
col.prop(rd, "use_single_layer", text="Render Single Layer")
|
|
|
|
|
|
class VIEWLAYER_PT_layer_passes(ViewLayerButtonsPanel, Panel):
|
|
bl_label = "Passes"
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class VIEWLAYER_PT_eevee_layer_passes_data(ViewLayerButtonsPanel, Panel):
|
|
bl_label = "Data"
|
|
bl_parent_id = "VIEWLAYER_PT_layer_passes"
|
|
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
scene = context.scene
|
|
rd = scene.render
|
|
view_layer = context.view_layer
|
|
|
|
col = layout.column()
|
|
col.prop(view_layer, "use_pass_combined")
|
|
col.prop(view_layer, "use_pass_z")
|
|
col.prop(view_layer, "use_pass_mist")
|
|
col.prop(view_layer, "use_pass_normal")
|
|
|
|
|
|
class VIEWLAYER_PT_eevee_layer_passes_light(ViewLayerButtonsPanel, Panel):
|
|
bl_label = "Light"
|
|
bl_parent_id = "VIEWLAYER_PT_layer_passes"
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
view_layer = context.view_layer
|
|
view_layer_eevee = view_layer.eevee
|
|
scene = context.scene
|
|
scene_eevee = scene.eevee
|
|
|
|
col = layout.column(heading="Diffuse", align=True)
|
|
col.prop(view_layer, "use_pass_diffuse_direct", text="Light")
|
|
col.prop(view_layer, "use_pass_diffuse_color", text="Color")
|
|
|
|
col = layout.column(heading="Specular", align=True)
|
|
col.prop(view_layer, "use_pass_glossy_direct", text="Light")
|
|
col.prop(view_layer, "use_pass_glossy_color", text="Color")
|
|
|
|
col = layout.column(heading="Volume", align=True)
|
|
col.prop(view_layer_eevee, "use_pass_volume_transmittance", text="Transmittance")
|
|
col.prop(view_layer_eevee, "use_pass_volume_scatter", text="Scatter")
|
|
|
|
col = layout.column(heading="Other", align=True)
|
|
col.prop(view_layer, "use_pass_emit", text="Emission")
|
|
col.prop(view_layer, "use_pass_environment")
|
|
col.prop(view_layer, "use_pass_shadow")
|
|
col.prop(view_layer, "use_pass_ambient_occlusion", text="Ambient Occlusion")
|
|
|
|
|
|
class VIEWLAYER_PT_eevee_layer_passes_effects(ViewLayerButtonsPanel, Panel):
|
|
bl_label = "Effects"
|
|
bl_parent_id = "VIEWLAYER_PT_layer_passes"
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
view_layer = context.view_layer
|
|
view_layer_eevee = view_layer.eevee
|
|
scene = context.scene
|
|
scene_eevee = scene.eevee
|
|
|
|
col = layout.column()
|
|
col.prop(view_layer_eevee, "use_pass_bloom", text="Bloom")
|
|
col.active = scene_eevee.use_bloom
|
|
|
|
|
|
class VIEWLAYER_PT_layer_passes_aov(ViewLayerButtonsPanel, Panel):
|
|
bl_label = "Shader AOV"
|
|
bl_parent_id = "VIEWLAYER_PT_layer_passes"
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
view_layer = context.view_layer
|
|
|
|
row = layout.row()
|
|
col = row.column()
|
|
col.template_list("VIEWLAYER_UL_aov", "aovs", view_layer, "aovs", view_layer, "active_aov_index", rows=2)
|
|
|
|
col = row.column()
|
|
sub = col.column(align=True)
|
|
sub.operator("scene.view_layer_add_aov", icon='ADD', text="")
|
|
sub.operator("scene.view_layer_remove_aov", icon='REMOVE', text="")
|
|
|
|
aov = view_layer.active_aov
|
|
if aov and not aov.is_valid:
|
|
layout.label(text="Conflicts with another render pass with the same name", icon='ERROR')
|
|
|
|
|
|
class VIEWLAYER_PT_layer_passes_cryptomatte(ViewLayerButtonsPanel, Panel):
|
|
bl_label = "Cryptomatte"
|
|
bl_parent_id = "VIEWLAYER_PT_layer_passes"
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
view_layer = context.view_layer
|
|
|
|
col = layout.column()
|
|
col.prop(view_layer, "use_pass_cryptomatte_object", text="Object")
|
|
col.prop(view_layer, "use_pass_cryptomatte_material", text="Material")
|
|
col.prop(view_layer, "use_pass_cryptomatte_asset", text="Asset")
|
|
col = layout.column()
|
|
col.active = any((view_layer.use_pass_cryptomatte_object,
|
|
view_layer.use_pass_cryptomatte_material,
|
|
view_layer.use_pass_cryptomatte_asset))
|
|
col.prop(view_layer, "pass_cryptomatte_depth", text="Levels")
|
|
col.prop(view_layer, "use_pass_cryptomatte_accurate", text="Accurate Mode")
|
|
|
|
|
|
classes = (
|
|
VIEWLAYER_PT_layer,
|
|
VIEWLAYER_PT_layer_passes,
|
|
VIEWLAYER_PT_eevee_layer_passes_data,
|
|
VIEWLAYER_PT_eevee_layer_passes_light,
|
|
VIEWLAYER_PT_eevee_layer_passes_effects,
|
|
VIEWLAYER_PT_layer_passes_cryptomatte,
|
|
VIEWLAYER_PT_layer_passes_aov,
|
|
VIEWLAYER_UL_aov,
|
|
)
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
from bpy.utils import register_class
|
|
for cls in classes:
|
|
register_class(cls)
|