Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Stockner <lukasstockner97>2019-12-04 21:57:28 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2019-12-10 22:44:46 +0300
commite760972221e68d3c81f2ee3687cc71836dde8ae9 (patch)
treeb1a2efbb17c05a429e4509d336a1eb14c73cfb8c /intern/cycles/blender/addon
parent35b5888b157d05d378df3acc899d28856a9eb9a4 (diff)
Cycles: support for custom shader AOVs
Custom render passes are added in the Shader AOVs panel in the view layer settings, with a name and data type. In shader nodes, an AOV Output node is then used to output either a value or color to the pass. Arbitrary names can be used for these passes, as long as they don't conflict with built-in passes that are enabled. The AOV Output node can be used in both material and world shader nodes. Implemented by Lukas, with tweaks by Brecht. Differential Revision: https://developer.blender.org/D4837
Diffstat (limited to 'intern/cycles/blender/addon')
-rw-r--r--intern/cycles/blender/addon/engine.py124
-rw-r--r--intern/cycles/blender/addon/operators.py32
-rw-r--r--intern/cycles/blender/addon/properties.py38
-rw-r--r--intern/cycles/blender/addon/ui.py38
4 files changed, 185 insertions, 47 deletions
diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py
index 013d86a560b..ee7ac7737c0 100644
--- a/intern/cycles/blender/addon/engine.py
+++ b/intern/cycles/blender/addon/engine.py
@@ -223,65 +223,95 @@ def system_info():
import _cycles
return _cycles.system_info()
-
-def register_passes(engine, scene, srl):
- engine.register_pass(scene, srl, "Combined", 4, "RGBA", 'COLOR')
-
- if srl.use_pass_z: engine.register_pass(scene, srl, "Depth", 1, "Z", 'VALUE')
- if srl.use_pass_mist: engine.register_pass(scene, srl, "Mist", 1, "Z", 'VALUE')
- if srl.use_pass_normal: engine.register_pass(scene, srl, "Normal", 3, "XYZ", 'VECTOR')
- if srl.use_pass_vector: engine.register_pass(scene, srl, "Vector", 4, "XYZW", 'VECTOR')
- if srl.use_pass_uv: engine.register_pass(scene, srl, "UV", 3, "UVA", 'VECTOR')
- if srl.use_pass_object_index: engine.register_pass(scene, srl, "IndexOB", 1, "X", 'VALUE')
- if srl.use_pass_material_index: engine.register_pass(scene, srl, "IndexMA", 1, "X", 'VALUE')
- if srl.use_pass_shadow: engine.register_pass(scene, srl, "Shadow", 3, "RGB", 'COLOR')
- if srl.use_pass_ambient_occlusion: engine.register_pass(scene, srl, "AO", 3, "RGB", 'COLOR')
- if srl.use_pass_diffuse_direct: engine.register_pass(scene, srl, "DiffDir", 3, "RGB", 'COLOR')
- if srl.use_pass_diffuse_indirect: engine.register_pass(scene, srl, "DiffInd", 3, "RGB", 'COLOR')
- if srl.use_pass_diffuse_color: engine.register_pass(scene, srl, "DiffCol", 3, "RGB", 'COLOR')
- if srl.use_pass_glossy_direct: engine.register_pass(scene, srl, "GlossDir", 3, "RGB", 'COLOR')
- if srl.use_pass_glossy_indirect: engine.register_pass(scene, srl, "GlossInd", 3, "RGB", 'COLOR')
- if srl.use_pass_glossy_color: engine.register_pass(scene, srl, "GlossCol", 3, "RGB", 'COLOR')
- if srl.use_pass_transmission_direct: engine.register_pass(scene, srl, "TransDir", 3, "RGB", 'COLOR')
- if srl.use_pass_transmission_indirect: engine.register_pass(scene, srl, "TransInd", 3, "RGB", 'COLOR')
- if srl.use_pass_transmission_color: engine.register_pass(scene, srl, "TransCol", 3, "RGB", 'COLOR')
- if srl.use_pass_subsurface_direct: engine.register_pass(scene, srl, "SubsurfaceDir", 3, "RGB", 'COLOR')
- if srl.use_pass_subsurface_indirect: engine.register_pass(scene, srl, "SubsurfaceInd", 3, "RGB", 'COLOR')
- if srl.use_pass_subsurface_color: engine.register_pass(scene, srl, "SubsurfaceCol", 3, "RGB", 'COLOR')
- if srl.use_pass_emit: engine.register_pass(scene, srl, "Emit", 3, "RGB", 'COLOR')
- if srl.use_pass_environment: engine.register_pass(scene, srl, "Env", 3, "RGB", 'COLOR')
-
+def list_render_passes(srl):
+ # Builtin Blender passes.
+ yield ("Combined", "RGBA", 'COLOR')
+
+ if srl.use_pass_z: yield ("Depth", "Z", 'VALUE')
+ if srl.use_pass_mist: yield ("Mist", "Z", 'VALUE')
+ if srl.use_pass_normal: yield ("Normal", "XYZ", 'VECTOR')
+ if srl.use_pass_vector: yield ("Vector", "XYZW", 'VECTOR')
+ if srl.use_pass_uv: yield ("UV", "UVA", 'VECTOR')
+ if srl.use_pass_object_index: yield ("IndexOB", "X", 'VALUE')
+ if srl.use_pass_material_index: yield ("IndexMA", "X", 'VALUE')
+ if srl.use_pass_shadow: yield ("Shadow", "RGB", 'COLOR')
+ if srl.use_pass_ambient_occlusion: yield ("AO", "RGB", 'COLOR')
+ if srl.use_pass_diffuse_direct: yield ("DiffDir", "RGB", 'COLOR')
+ if srl.use_pass_diffuse_indirect: yield ("DiffInd", "RGB", 'COLOR')
+ if srl.use_pass_diffuse_color: yield ("DiffCol", "RGB", 'COLOR')
+ if srl.use_pass_glossy_direct: yield ("GlossDir", "RGB", 'COLOR')
+ if srl.use_pass_glossy_indirect: yield ("GlossInd", "RGB", 'COLOR')
+ if srl.use_pass_glossy_color: yield ("GlossCol", "RGB", 'COLOR')
+ if srl.use_pass_transmission_direct: yield ("TransDir", "RGB", 'COLOR')
+ if srl.use_pass_transmission_indirect: yield ("TransInd", "RGB", 'COLOR')
+ if srl.use_pass_transmission_color: yield ("TransCol", "RGB", 'COLOR')
+ if srl.use_pass_subsurface_direct: yield ("SubsurfaceDir", "RGB", 'COLOR')
+ if srl.use_pass_subsurface_indirect: yield ("SubsurfaceInd", "RGB", 'COLOR')
+ if srl.use_pass_subsurface_color: yield ("SubsurfaceCol", "RGB", 'COLOR')
+ if srl.use_pass_emit: yield ("Emit", "RGB", 'COLOR')
+ if srl.use_pass_environment: yield ("Env", "RGB", 'COLOR')
+
+ # Cycles specific passes.
crl = srl.cycles
- if crl.pass_debug_render_time: engine.register_pass(scene, srl, "Debug Render Time", 1, "X", 'VALUE')
- if crl.pass_debug_bvh_traversed_nodes: engine.register_pass(scene, srl, "Debug BVH Traversed Nodes", 1, "X", 'VALUE')
- if crl.pass_debug_bvh_traversed_instances: engine.register_pass(scene, srl, "Debug BVH Traversed Instances", 1, "X", 'VALUE')
- if crl.pass_debug_bvh_intersections: engine.register_pass(scene, srl, "Debug BVH Intersections", 1, "X", 'VALUE')
- if crl.pass_debug_ray_bounces: engine.register_pass(scene, srl, "Debug Ray Bounces", 1, "X", 'VALUE')
- if crl.use_pass_volume_direct: engine.register_pass(scene, srl, "VolumeDir", 3, "RGB", 'COLOR')
- if crl.use_pass_volume_indirect: engine.register_pass(scene, srl, "VolumeInd", 3, "RGB", 'COLOR')
-
+ if crl.pass_debug_render_time: yield ("Debug Render Time", "X", 'VALUE')
+ if crl.pass_debug_bvh_traversed_nodes: yield ("Debug BVH Traversed Nodes", "X", 'VALUE')
+ if crl.pass_debug_bvh_traversed_instances: yield ("Debug BVH Traversed Instances", "X", 'VALUE')
+ if crl.pass_debug_bvh_intersections: yield ("Debug BVH Intersections", "X", 'VALUE')
+ if crl.pass_debug_ray_bounces: yield ("Debug Ray Bounces", "X", 'VALUE')
+ if crl.use_pass_volume_direct: yield ("VolumeDir", "RGB", 'COLOR')
+ if crl.use_pass_volume_indirect: yield ("VolumeInd", "RGB", 'COLOR')
+
+ # Cryptomatte passes.
if crl.use_pass_crypto_object:
for i in range(0, crl.pass_crypto_depth, 2):
- engine.register_pass(scene, srl, "CryptoObject" + '{:02d}'.format(i//2), 4, "RGBA", 'COLOR')
+ yield ("CryptoObject" + '{:02d}'.format(i//2), "RGBA", 'COLOR')
if crl.use_pass_crypto_material:
for i in range(0, crl.pass_crypto_depth, 2):
- engine.register_pass(scene, srl, "CryptoMaterial" + '{:02d}'.format(i//2), 4, "RGBA", 'COLOR')
+ yield ("CryptoMaterial" + '{:02d}'.format(i//2), "RGBA", 'COLOR')
if srl.cycles.use_pass_crypto_asset:
for i in range(0, srl.cycles.pass_crypto_depth, 2):
- engine.register_pass(scene, srl, "CryptoAsset" + '{:02d}'.format(i//2), 4, "RGBA", 'COLOR')
+ yield ("CryptoAsset" + '{:02d}'.format(i//2), "RGBA", 'COLOR')
+ # Denoising passes.
if crl.use_denoising or crl.denoising_store_passes:
- engine.register_pass(scene, srl, "Noisy Image", 4, "RGBA", 'COLOR')
+ yield ("Noisy Image", "RGBA", 'COLOR')
if crl.denoising_store_passes:
- engine.register_pass(scene, srl, "Denoising Normal", 3, "XYZ", 'VECTOR')
- engine.register_pass(scene, srl, "Denoising Albedo", 3, "RGB", 'COLOR')
- engine.register_pass(scene, srl, "Denoising Depth", 1, "Z", 'VALUE')
- engine.register_pass(scene, srl, "Denoising Shadowing", 1, "X", 'VALUE')
- engine.register_pass(scene, srl, "Denoising Variance", 3, "RGB", 'COLOR')
- engine.register_pass(scene, srl, "Denoising Intensity", 1, "X", 'VALUE')
+ yield ("Denoising Normal", "XYZ", 'VECTOR')
+ yield ("Denoising Albedo", "RGB", 'COLOR')
+ yield ("Denoising Depth", "Z", 'VALUE')
+ yield ("Denoising Shadowing", "X", 'VALUE')
+ yield ("Denoising Variance", "RGB", 'COLOR')
+ yield ("Denoising Intensity", "X", 'VALUE')
clean_options = ("denoising_diffuse_direct", "denoising_diffuse_indirect",
"denoising_glossy_direct", "denoising_glossy_indirect",
"denoising_transmission_direct", "denoising_transmission_indirect",
"denoising_subsurface_direct", "denoising_subsurface_indirect")
if any(getattr(crl, option) for option in clean_options):
- engine.register_pass(scene, srl, "Denoising Clean", 3, "RGB", 'COLOR')
+ yield ("Denoising Clean", "RGB", 'COLOR')
+
+ # Custom AOV passes.
+ for aov in crl.aovs:
+ if aov.type == 'VALUE':
+ yield (aov.name, "X", 'VALUE')
+ else:
+ yield (aov.name, "RGBA", 'COLOR')
+
+def register_passes(engine, scene, view_layer):
+ # Detect duplicate render pass names, first one wins.
+ listed = set()
+ for name, channelids, channeltype in list_render_passes(view_layer):
+ if name not in listed:
+ engine.register_pass(scene, view_layer, name, len(channelids), channelids, channeltype)
+ listed.add(name)
+
+def detect_conflicting_passes(view_layer):
+ # Detect conflicting render pass names for UI.
+ counter = {}
+ for name, _, _ in list_render_passes(view_layer):
+ counter[name] = counter.get(name, 0) + 1
+
+ for aov in view_layer.cycles.aovs:
+ if counter[aov.name] > 1:
+ aov.conflict = "Conflicts with another render pass with the same name"
+ else:
+ aov.conflict = ""
diff --git a/intern/cycles/blender/addon/operators.py b/intern/cycles/blender/addon/operators.py
index e75d3ab7549..80bb663330b 100644
--- a/intern/cycles/blender/addon/operators.py
+++ b/intern/cycles/blender/addon/operators.py
@@ -44,6 +44,36 @@ class CYCLES_OT_use_shading_nodes(Operator):
return {'FINISHED'}
+class CYCLES_OT_add_aov(bpy.types.Operator):
+ """Add an AOV pass"""
+ bl_idname="cycles.add_aov"
+ bl_label="Add AOV"
+
+ def execute(self, context):
+ view_layer = context.view_layer
+ cycles_view_layer = view_layer.cycles
+
+ cycles_view_layer.aovs.add()
+
+ view_layer.update_render_passes()
+ return {'FINISHED'}
+
+
+class CYCLES_OT_remove_aov(bpy.types.Operator):
+ """Remove an AOV pass"""
+ bl_idname="cycles.remove_aov"
+ bl_label="Remove AOV"
+
+ def execute(self, context):
+ view_layer = context.view_layer
+ cycles_view_layer = view_layer.cycles
+
+ cycles_view_layer.aovs.remove(cycles_view_layer.active_aov)
+
+ view_layer.update_render_passes()
+ return {'FINISHED'}
+
+
class CYCLES_OT_denoise_animation(Operator):
"Denoise rendered animation sequence using current scene and view " \
"layer settings. Requires denoising data passes and output to " \
@@ -167,6 +197,8 @@ class CYCLES_OT_merge_images(Operator):
classes = (
CYCLES_OT_use_shading_nodes,
+ CYCLES_OT_add_aov,
+ CYCLES_OT_remove_aov,
CYCLES_OT_denoise_animation,
CYCLES_OT_merge_images
)
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 26e1a6a223a..e09f15b46e8 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -19,6 +19,7 @@
import bpy
from bpy.props import (
BoolProperty,
+ CollectionProperty,
EnumProperty,
FloatProperty,
IntProperty,
@@ -31,6 +32,7 @@ from math import pi
# enums
import _cycles
+from . import engine
enum_devices = (
('CPU', "CPU", "Use CPU for rendering"),
@@ -190,6 +192,10 @@ enum_view3d_shading_render_pass= (
('MIST', "Mist", "Show the Mist render pass", 32),
)
+enum_aov_types = (
+ ('VALUE', "Value", "Write a Value pass", 0),
+ ('COLOR', "Color", "Write a Color pass", 1),
+)
class CyclesRenderSettings(bpy.types.PropertyGroup):
@@ -1218,8 +1224,29 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup):
def update_render_passes(self, context):
view_layer = context.view_layer
view_layer.update_render_passes()
+ engine.detect_conflicting_passes(view_layer)
+class CyclesAOVPass(bpy.types.PropertyGroup):
+ name: StringProperty(
+ name="Name",
+ description="Name of the pass, to use in the AOV Output shader node",
+ update=update_render_passes,
+ default="AOV"
+ )
+ type: EnumProperty(
+ name="Type",
+ description="Pass data type",
+ update=update_render_passes,
+ items=enum_aov_types,
+ default='COLOR'
+ )
+ conflict: StringProperty(
+ name="Conflict",
+ description="If there is a conflict with another render passes, message explaining why",
+ default=""
+ )
+
class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
pass_debug_bvh_traversed_nodes: BoolProperty(
@@ -1378,6 +1405,15 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
update=update_render_passes,
)
+ aovs: CollectionProperty(
+ type=CyclesAOVPass,
+ description="Custom render passes that can be output by shader nodes",
+ )
+ active_aov: IntProperty(
+ default=0,
+ min=0
+ )
+
@classmethod
def register(cls):
bpy.types.ViewLayer.cycles = PointerProperty(
@@ -1552,6 +1588,7 @@ def register():
bpy.utils.register_class(CyclesCurveRenderSettings)
bpy.utils.register_class(CyclesDeviceSettings)
bpy.utils.register_class(CyclesPreferences)
+ bpy.utils.register_class(CyclesAOVPass)
bpy.utils.register_class(CyclesRenderLayerSettings)
bpy.utils.register_class(CyclesView3DShadingSettings)
@@ -1573,5 +1610,6 @@ def unregister():
bpy.utils.unregister_class(CyclesCurveRenderSettings)
bpy.utils.unregister_class(CyclesDeviceSettings)
bpy.utils.unregister_class(CyclesPreferences)
+ bpy.utils.unregister_class(CyclesAOVPass)
bpy.utils.unregister_class(CyclesRenderLayerSettings)
bpy.utils.unregister_class(CyclesView3DShadingSettings)
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 4ff154d4bcd..011c83d44b8 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -918,6 +918,42 @@ class CYCLES_RENDER_PT_passes_debug(CyclesButtonsPanel, Panel):
layout.prop(cycles_view_layer, "pass_debug_ray_bounces")
+class CYCLES_RENDER_UL_aov(bpy.types.UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ row = layout.row()
+ split = row.split(factor=0.65)
+ icon = 'ERROR' if item.conflict else 'NONE'
+ split.row().prop(item, "name", text="", icon=icon, emboss=False)
+ split.row().prop(item, "type", text="", emboss=False)
+
+
+class CYCLES_RENDER_PT_passes_aov(CyclesButtonsPanel, Panel):
+ bl_label = "Shader AOV"
+ bl_context = "view_layer"
+ bl_parent_id = "CYCLES_RENDER_PT_passes"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ cycles_view_layer = context.view_layer.cycles
+
+ row = layout.row()
+ col = row.column()
+ col.template_list("CYCLES_RENDER_UL_aov", "aovs", cycles_view_layer, "aovs", cycles_view_layer, "active_aov", rows=2)
+
+ col = row.column()
+ sub = col.column(align=True)
+ sub.operator("cycles.add_aov", icon='ADD', text="")
+ sub.operator("cycles.remove_aov", icon='REMOVE', text="")
+
+ if cycles_view_layer.active_aov < len(cycles_view_layer.aovs):
+ active_aov = cycles_view_layer.aovs[cycles_view_layer.active_aov]
+ if active_aov.conflict:
+ layout.label(text=active_aov.conflict, icon='ERROR')
+
+
class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel):
bl_label = "Denoising"
bl_context = "view_layer"
@@ -2233,6 +2269,8 @@ classes = (
CYCLES_RENDER_PT_passes_light,
CYCLES_RENDER_PT_passes_crypto,
CYCLES_RENDER_PT_passes_debug,
+ CYCLES_RENDER_UL_aov,
+ CYCLES_RENDER_PT_passes_aov,
CYCLES_RENDER_PT_filter,
CYCLES_RENDER_PT_override,
CYCLES_RENDER_PT_denoising,