Cycles: support for custom shader AOVs
authorLukas Stockner <lukasstockner97>
Wed, 4 Dec 2019 18:57:28 +0000 (19:57 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Tue, 10 Dec 2019 19:44:46 +0000 (20:44 +0100)
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

46 files changed:
intern/cycles/blender/addon/engine.py
intern/cycles/blender/addon/operators.py
intern/cycles/blender/addon/properties.py
intern/cycles/blender/addon/ui.py
intern/cycles/blender/blender_session.cpp
intern/cycles/blender/blender_shader.cpp
intern/cycles/blender/blender_sync.cpp
intern/cycles/kernel/CMakeLists.txt
intern/cycles/kernel/kernel_bake.h
intern/cycles/kernel/kernel_emission.h
intern/cycles/kernel/kernel_passes.h
intern/cycles/kernel/kernel_path.h
intern/cycles/kernel/kernel_path_branched.h
intern/cycles/kernel/kernel_shader.h
intern/cycles/kernel/kernel_shadow.h
intern/cycles/kernel/kernel_subsurface.h
intern/cycles/kernel/kernel_types.h
intern/cycles/kernel/kernel_write_passes.h [new file with mode: 0644]
intern/cycles/kernel/osl/osl_services.cpp
intern/cycles/kernel/split/kernel_indirect_background.h
intern/cycles/kernel/split/kernel_shader_eval.h
intern/cycles/kernel/svm/svm.h
intern/cycles/kernel/svm/svm_aov.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_types.h
intern/cycles/render/buffers.cpp
intern/cycles/render/buffers.h
intern/cycles/render/film.cpp
intern/cycles/render/film.h
intern/cycles/render/graph.cpp
intern/cycles/render/graph.h
intern/cycles/render/nodes.cpp
intern/cycles/render/nodes.h
intern/cycles/render/shader.cpp
intern/cycles/render/svm.cpp
intern/cycles/render/svm.h
release/scripts/startup/nodeitems_builtins.py
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/node.c
source/blender/editors/space_node/drawnode.c
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/nodes/CMakeLists.txt
source/blender/nodes/NOD_shader.h
source/blender/nodes/NOD_static_types.h
source/blender/nodes/shader/nodes/node_shader_output_aov.c [new file with mode: 0644]

index 013d86a560b89db72bec64bf4ce8ce746c1e02cb..ee7ac7737c08fd1b53397027e0bc39740bb59df7 100644 (file)
@@ -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 = ""
index e75d3ab7549e906aaf8ed773cd918799a4077828..80bb663330b9adc4fcffe54e788940952c2adc1a 100644 (file)
@@ -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
 )
index 26e1a6a223a5f4ac4f33810efbca027336863ef2..e09f15b46e82112f59ff3605e6631a16db63564c 100644 (file)
@@ -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)
index 4ff154d4bcd22bbb30fcc0ee1db0eb49d345eff5..011c83d44b8708b94cca67276fd2ad89a0d6ad06 100644 (file)
@@ -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,
index 1f0816a6edb1080e24ad7135985acb2e330dc23e..53f2fdb91b94001b3ba83017f1b4b6958504bb6a 100644 (file)
@@ -793,18 +793,13 @@ void BlenderSession::do_write_update_render_result(BL::RenderLayer &b_rlay,
 
     for (b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
       BL::RenderPass b_pass(*b_iter);
-
-      /* find matching pass type */
-      PassType pass_type = BlenderSync::get_pass_type(b_pass);
       int components = b_pass.channels();
 
-      bool read = false;
-      if (pass_type != PASS_NONE) {
-        /* copy pixels */
-        read = buffers->get_pass_rect(
-            pass_type, exposure, sample, components, &pixels[0], b_pass.name());
-      }
-      else {
+      /* Copy pixels from regular render passes. */
+      bool read = buffers->get_pass_rect(b_pass.name(), exposure, sample, components, &pixels[0]);
+
+      /* If denoising pass, */
+      if (!read) {
         int denoising_offset = BlenderSync::get_denoising_pass(b_pass);
         if (denoising_offset >= 0) {
           read = buffers->get_denoising_pass_rect(
@@ -822,7 +817,7 @@ void BlenderSession::do_write_update_render_result(BL::RenderLayer &b_rlay,
   else {
     /* copy combined pass */
     BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str()));
-    if (buffers->get_pass_rect(PASS_COMBINED, exposure, sample, 4, &pixels[0], "Combined"))
+    if (buffers->get_pass_rect("Combined", exposure, sample, 4, &pixels[0]))
       b_combined_pass.rect(&pixels[0]);
   }
 }
index 412e54ea29d0a2166c0921192f653f1e9ac8961e..c3564eac940eef440f0623f88839cee7c94aea88 100644 (file)
@@ -921,6 +921,12 @@ static ShaderNode *add_node(Scene *scene,
     disp->attribute = "";
     node = disp;
   }
+  else if (b_node.is_a(&RNA_ShaderNodeOutputAOV)) {
+    BL::ShaderNodeOutputAOV b_aov_node(b_node);
+    OutputAOVNode *aov = new OutputAOVNode();
+    aov->name = b_aov_node.name();
+    node = aov;
+  }
 
   if (node) {
     node->name = b_node.name();
index f04455ff75e355d598270e101e72842007169a3d..bb52c740bfb4cf02387bb9d167e710856dcaf25f 100644 (file)
@@ -531,7 +531,7 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLa
     if (pass_type == PASS_MOTION && scene->integrator->motion_blur)
       continue;
     if (pass_type != PASS_NONE)
-      Pass::add(pass_type, passes);
+      Pass::add(pass_type, passes, b_pass.name().c_str());
   }
 
   PointerRNA crp = RNA_pointer_get(&b_view_layer.ptr, "cycles");
@@ -570,32 +570,32 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLa
 #ifdef __KERNEL_DEBUG__
   if (get_boolean(crp, "pass_debug_bvh_traversed_nodes")) {
     b_engine.add_pass("Debug BVH Traversed Nodes", 1, "X", b_view_layer.name().c_str());
-    Pass::add(PASS_BVH_TRAVERSED_NODES, passes);
+    Pass::add(PASS_BVH_TRAVERSED_NODES, passes, "Debug BVH Traversed Nodes");
   }
   if (get_boolean(crp, "pass_debug_bvh_traversed_instances")) {
     b_engine.add_pass("Debug BVH Traversed Instances", 1, "X", b_view_layer.name().c_str());
-    Pass::add(PASS_BVH_TRAVERSED_INSTANCES, passes);
+    Pass::add(PASS_BVH_TRAVERSED_INSTANCES, passes, "Debug BVH Traversed Instances");
   }
   if (get_boolean(crp, "pass_debug_bvh_intersections")) {
     b_engine.add_pass("Debug BVH Intersections", 1, "X", b_view_layer.name().c_str());
-    Pass::add(PASS_BVH_INTERSECTIONS, passes);
+    Pass::add(PASS_BVH_INTERSECTIONS, passes, "Debug BVH Intersections");
   }
   if (get_boolean(crp, "pass_debug_ray_bounces")) {
     b_engine.add_pass("Debug Ray Bounces", 1, "X", b_view_layer.name().c_str());
-    Pass::add(PASS_RAY_BOUNCES, passes);
+    Pass::add(PASS_RAY_BOUNCES, passes, "Debug Ray Bounces");
   }
 #endif
   if (get_boolean(crp, "pass_debug_render_time")) {
     b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str());
-    Pass::add(PASS_RENDER_TIME, passes);
+    Pass::add(PASS_RENDER_TIME, passes, "Debug Render Time");
   }
   if (get_boolean(crp, "use_pass_volume_direct")) {
     b_engine.add_pass("VolumeDir", 3, "RGB", b_view_layer.name().c_str());
-    Pass::add(PASS_VOLUME_DIRECT, passes);
+    Pass::add(PASS_VOLUME_DIRECT, passes, "VolumeDir");
   }
   if (get_boolean(crp, "use_pass_volume_indirect")) {
     b_engine.add_pass("VolumeInd", 3, "RGB", b_view_layer.name().c_str());
-    Pass::add(PASS_VOLUME_INDIRECT, passes);
+    Pass::add(PASS_VOLUME_INDIRECT, passes, "VolumeInd");
   }
 
   /* Cryptomatte stores two ID/weight pairs per RGBA layer.
@@ -635,6 +635,21 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLa
                                                         CRYPT_ACCURATE);
   }
 
+  RNA_BEGIN (&crp, b_aov, "aovs") {
+    bool is_color = (get_enum(b_aov, "type") == 1);
+    string name = get_string(b_aov, "name");
+
+    if (is_color) {
+      b_engine.add_pass(name.c_str(), 4, "RGBA", b_view_layer.name().c_str());
+      Pass::add(PASS_AOV_COLOR, passes, name.c_str());
+    }
+    else {
+      b_engine.add_pass(name.c_str(), 1, "X", b_view_layer.name().c_str());
+      Pass::add(PASS_AOV_VALUE, passes, name.c_str());
+    }
+  }
+  RNA_END;
+
   return passes;
 }
 
index 4077a1ad516269659bbb1b89102458bce5f40298..99172f30b8b1ae1170403b20464c54d14cd6b02d 100644 (file)
@@ -129,6 +129,7 @@ set(SRC_HEADERS
   kernel_types.h
   kernel_volume.h
   kernel_work_stealing.h
+  kernel_write_passes.h
 )
 
 set(SRC_KERNELS_CPU_HEADERS
@@ -182,6 +183,7 @@ set(SRC_CLOSURE_HEADERS
 set(SRC_SVM_HEADERS
   svm/svm.h
   svm/svm_ao.h
+  svm/svm_aov.h
   svm/svm_attribute.h
   svm/svm_bevel.h
   svm/svm_blackbody.h
index cd1ca5ea7ecb8177d7223fa7099ca7d5f693dd53..8e5a279e6cd8bebcf3f3b3112012a57490a85ce0 100644 (file)
@@ -45,7 +45,7 @@ ccl_device_inline void compute_light_pass(
   path_state_init(kg, &emission_sd, &state, rng_hash, sample, NULL);
 
   /* evaluate surface shader */
-  shader_eval_surface(kg, sd, &state, state.flag);
+  shader_eval_surface(kg, sd, &state, NULL, state.flag);
 
   /* TODO, disable more closures we don't need besides transparent */
   shader_bsdf_disable_transparency(kg, sd);
@@ -209,12 +209,12 @@ ccl_device float3 kernel_bake_evaluate_direct_indirect(KernelGlobals *kg,
     }
     else {
       /* surface color of the pass only */
-      shader_eval_surface(kg, sd, state, 0);
+      shader_eval_surface(kg, sd, state, NULL, 0);
       return kernel_bake_shader_bsdf(kg, sd, type);
     }
   }
   else {
-    shader_eval_surface(kg, sd, state, 0);
+    shader_eval_surface(kg, sd, state, NULL, 0);
     color = kernel_bake_shader_bsdf(kg, sd, type);
   }
 
@@ -332,7 +332,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg,
     case SHADER_EVAL_EMISSION: {
       if (type != SHADER_EVAL_NORMAL || (sd.flag & SD_HAS_BUMP)) {
         int path_flag = (type == SHADER_EVAL_EMISSION) ? PATH_RAY_EMISSION : 0;
-        shader_eval_surface(kg, &sd, &state, path_flag);
+        shader_eval_surface(kg, &sd, &state, NULL, path_flag);
       }
 
       if (type == SHADER_EVAL_NORMAL) {
@@ -445,7 +445,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg,
 
       /* evaluate */
       int path_flag = 0; /* we can't know which type of BSDF this is for */
-      shader_eval_surface(kg, &sd, &state, path_flag | PATH_RAY_EMISSION);
+      shader_eval_surface(kg, &sd, &state, NULL, path_flag | PATH_RAY_EMISSION);
       out = shader_background_eval(&sd);
       break;
     }
@@ -524,7 +524,7 @@ ccl_device void kernel_background_evaluate(KernelGlobals *kg,
 
   /* evaluate */
   int path_flag = 0; /* we can't know which type of BSDF this is for */
-  shader_eval_surface(kg, &sd, &state, path_flag | PATH_RAY_EMISSION);
+  shader_eval_surface(kg, &sd, &state, NULL, path_flag | PATH_RAY_EMISSION);
   float3 color = shader_background_eval(&sd);
 
   /* write output */
index 459280cf43340dc40efaef4e39ee868e8ca614fe..e70958c2b068ed520227ee0f2227c1ec5e0c81fe 100644 (file)
@@ -73,7 +73,7 @@ ccl_device_noinline_cpu float3 direct_emissive_eval(KernelGlobals *kg,
     /* No proper path flag, we're evaluating this for all closures. that's
      * weak but we'd have to do multiple evaluations otherwise. */
     path_state_modify_bounce(state, true);
-    shader_eval_surface(kg, emission_sd, state, PATH_RAY_EMISSION);
+    shader_eval_surface(kg, emission_sd, state, NULL, PATH_RAY_EMISSION);
     path_state_modify_bounce(state, false);
 
     /* Evaluate closures. */
@@ -294,6 +294,7 @@ ccl_device_noinline_cpu bool indirect_lamp_emission(KernelGlobals *kg,
 ccl_device_noinline_cpu float3 indirect_background(KernelGlobals *kg,
                                                    ShaderData *emission_sd,
                                                    ccl_addr_space PathState *state,
+                                                   ccl_global float *buffer,
                                                    ccl_addr_space Ray *ray)
 {
 #ifdef __BACKGROUND__
@@ -322,7 +323,7 @@ ccl_device_noinline_cpu float3 indirect_background(KernelGlobals *kg,
 #  endif
 
     path_state_modify_bounce(state, true);
-    shader_eval_surface(kg, emission_sd, state, state->flag | PATH_RAY_EMISSION);
+    shader_eval_surface(kg, emission_sd, state, buffer, state->flag | PATH_RAY_EMISSION);
     path_state_modify_bounce(state, false);
 
     L = shader_background_eval(emission_sd);
index 3e423e425739ea83149b3a78640dec5e1c427ff3..828add9dc1379d2f11345f9f54acbe201faf46e1 100644 (file)
  * limitations under the License.
  */
 
-#if defined(__SPLIT_KERNEL__) || defined(__KERNEL_CUDA__)
-#  define __ATOMIC_PASS_WRITE__
-#endif
-
 #include "kernel/kernel_id_passes.h"
 
 CCL_NAMESPACE_BEGIN
 
-ccl_device_inline void kernel_write_pass_float(ccl_global float *buffer, float value)
-{
-  ccl_global float *buf = buffer;
-#ifdef __ATOMIC_PASS_WRITE__
-  atomic_add_and_fetch_float(buf, value);
-#else
-  *buf += value;
-#endif
-}
-
-ccl_device_inline void kernel_write_pass_float3(ccl_global float *buffer, float3 value)
-{
-#ifdef __ATOMIC_PASS_WRITE__
-  ccl_global float *buf_x = buffer + 0;
-  ccl_global float *buf_y = buffer + 1;
-  ccl_global float *buf_z = buffer + 2;
-
-  atomic_add_and_fetch_float(buf_x, value.x);
-  atomic_add_and_fetch_float(buf_y, value.y);
-  atomic_add_and_fetch_float(buf_z, value.z);
-#else
-  ccl_global float3 *buf = (ccl_global float3 *)buffer;
-  *buf += value;
-#endif
-}
-
-ccl_device_inline void kernel_write_pass_float4(ccl_global float *buffer, float4 value)
-{
-#ifdef __ATOMIC_PASS_WRITE__
-  ccl_global float *buf_x = buffer + 0;
-  ccl_global float *buf_y = buffer + 1;
-  ccl_global float *buf_z = buffer + 2;
-  ccl_global float *buf_w = buffer + 3;
-
-  atomic_add_and_fetch_float(buf_x, value.x);
-  atomic_add_and_fetch_float(buf_y, value.y);
-  atomic_add_and_fetch_float(buf_z, value.z);
-  atomic_add_and_fetch_float(buf_w, value.w);
-#else
-  ccl_global float4 *buf = (ccl_global float4 *)buffer;
-  *buf += value;
-#endif
-}
-
 #ifdef __DENOISING_FEATURES__
-ccl_device_inline void kernel_write_pass_float_variance(ccl_global float *buffer, float value)
-{
-  kernel_write_pass_float(buffer, value);
-
-  /* The online one-pass variance update that's used for the mega-kernel can't easily be
-   * implemented with atomics,
-   * so for the split kernel the E[x^2] - 1/N * (E[x])^2 fallback is used. */
-  kernel_write_pass_float(buffer + 1, value * value);
-}
-
-#  ifdef __ATOMIC_PASS_WRITE__
-#    define kernel_write_pass_float3_unaligned kernel_write_pass_float3
-#  else
-ccl_device_inline void kernel_write_pass_float3_unaligned(ccl_global float *buffer, float3 value)
-{
-  buffer[0] += value.x;
-  buffer[1] += value.y;
-  buffer[2] += value.z;
-}
-#  endif
-
-ccl_device_inline void kernel_write_pass_float3_variance(ccl_global float *buffer, float3 value)
-{
-  kernel_write_pass_float3_unaligned(buffer, value);
-  kernel_write_pass_float3_unaligned(buffer + 3, value * value);
-}
 
 ccl_device_inline void kernel_write_denoising_shadow(KernelGlobals *kg,
                                                      ccl_global float *buffer,
index 55abe39c465153e717abd7f91dc46ebec61ee27e..693efad8c50102e43d7ea65b5286cf23ba7b94ef 100644 (file)
@@ -27,6 +27,7 @@
 #include "kernel/geom/geom.h"
 #include "kernel/bvh/bvh.h"
 
+#include "kernel/kernel_write_passes.h"
 #include "kernel/kernel_accumulate.h"
 #include "kernel/kernel_shader.h"
 #include "kernel/kernel_light.h"
@@ -116,6 +117,7 @@ ccl_device_forceinline void kernel_path_background(KernelGlobals *kg,
                                                    ccl_addr_space Ray *ray,
                                                    float3 throughput,
                                                    ShaderData *sd,
+                                                   ccl_global float *buffer,
                                                    PathRadiance *L)
 {
   /* eval background shader if nothing hit */
@@ -136,7 +138,7 @@ ccl_device_forceinline void kernel_path_background(KernelGlobals *kg,
 
 #ifdef __BACKGROUND__
   /* sample background shader */
-  float3 L_background = indirect_background(kg, sd, state, ray);
+  float3 L_background = indirect_background(kg, sd, state, buffer, ray);
   path_radiance_accum_background(L, state, throughput, L_background);
 #endif /* __BACKGROUND__ */
 }
@@ -267,7 +269,7 @@ ccl_device_forceinline bool kernel_path_shader_apply(KernelGlobals *kg,
 
       float3 bg = make_float3(0.0f, 0.0f, 0.0f);
       if (!kernel_data.background.transparent) {
-        bg = indirect_background(kg, emission_sd, state, ray);
+        bg = indirect_background(kg, emission_sd, state, NULL, ray);
       }
       path_radiance_accum_shadowcatcher(L, throughput, bg);
     }
@@ -418,7 +420,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
 
       /* Shade background. */
       if (!hit) {
-        kernel_path_background(kg, state, ray, throughput, sd, L);
+        kernel_path_background(kg, state, ray, throughput, sd, NULL, L);
         break;
       }
       else if (path_state_ao_bounce(kg, state)) {
@@ -434,7 +436,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
 #    endif
 
         /* Evaluate shader. */
-        shader_eval_surface(kg, sd, state, state->flag);
+        shader_eval_surface(kg, sd, state, NULL, state->flag);
         shader_prepare_closures(sd, state);
 
         /* Apply shadow catcher, holdout, emission. */
@@ -556,7 +558,7 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg,
 
       /* Shade background. */
       if (!hit) {
-        kernel_path_background(kg, state, ray, throughput, &sd, L);
+        kernel_path_background(kg, state, ray, throughput, &sd, buffer, L);
         break;
       }
       else if (path_state_ao_bounce(kg, state)) {
@@ -572,7 +574,7 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg,
 #  endif
 
         /* Evaluate shader. */
-        shader_eval_surface(kg, &sd, state, state->flag);
+        shader_eval_surface(kg, &sd, state, buffer, state->flag);
         shader_prepare_closures(&sd, state);
 
         /* Apply shadow catcher, holdout, emission. */
index ea6b23e7eb47ed4ae7b491643891ee5cb1911121..39309379f0cb59f50339c06dbdf3de668aa28e93 100644 (file)
@@ -405,7 +405,7 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
 
     /* Shade background. */
     if (!hit) {
-      kernel_path_background(kg, &state, &ray, throughput, &sd, L);
+      kernel_path_background(kg, &state, &ray, throughput, &sd, buffer, L);
       break;
     }
 
@@ -417,7 +417,7 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
     if (!(sd.flag & SD_HAS_ONLY_VOLUME)) {
 #    endif
 
-      shader_eval_surface(kg, &sd, &state, state.flag);
+      shader_eval_surface(kg, &sd, &state, buffer, state.flag);
       shader_merge_closures(&sd);
 
       /* Apply shadow catcher, holdout, emission. */
index 7ccb99cad2a1ceca574a5c25c577460f464acc5f..d03faff4242611d38a65d85fc1ee5c6104972ecc 100644 (file)
@@ -1076,6 +1076,7 @@ ccl_device float3 shader_holdout_eval(KernelGlobals *kg, ShaderData *sd)
 ccl_device void shader_eval_surface(KernelGlobals *kg,
                                     ShaderData *sd,
                                     ccl_addr_space PathState *state,
+                                    ccl_global float *buffer,
                                     int path_flag)
 {
   PROFILING_INIT(kg, PROFILING_SHADER_EVAL);
@@ -1107,7 +1108,7 @@ ccl_device void shader_eval_surface(KernelGlobals *kg,
 #endif
   {
 #ifdef __SVM__
-    svm_eval_nodes(kg, sd, state, SHADER_TYPE_SURFACE, path_flag);
+    svm_eval_nodes(kg, sd, state, buffer, SHADER_TYPE_SURFACE, path_flag);
 #else
     if (sd->object == OBJECT_NONE) {
       sd->closure_emission_background = make_float3(0.8f, 0.8f, 0.8f);
@@ -1319,7 +1320,7 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg,
     else
 #    endif
     {
-      svm_eval_nodes(kg, sd, state, SHADER_TYPE_VOLUME, path_flag);
+      svm_eval_nodes(kg, sd, state, NULL, SHADER_TYPE_VOLUME, path_flag);
     }
 #  endif
 
@@ -1348,7 +1349,7 @@ ccl_device void shader_eval_displacement(KernelGlobals *kg,
   else
 #  endif
   {
-    svm_eval_nodes(kg, sd, state, SHADER_TYPE_DISPLACEMENT, 0);
+    svm_eval_nodes(kg, sd, state, NULL, SHADER_TYPE_DISPLACEMENT, 0);
   }
 #endif
 }
index 61fcc61264a4a90a1e4655b51918269f9031505e..b3ae29932da6585645c5bf3f7aa0f4b1925e817e 100644 (file)
@@ -71,7 +71,7 @@ ccl_device_forceinline bool shadow_handle_transparent_isect(KernelGlobals *kg,
   /* Attenuation from transparent surface. */
   if (!(shadow_sd->flag & SD_HAS_ONLY_VOLUME)) {
     path_state_modify_bounce(state, true);
-    shader_eval_surface(kg, shadow_sd, state, PATH_RAY_SHADOW);
+    shader_eval_surface(kg, shadow_sd, state, NULL, PATH_RAY_SHADOW);
     path_state_modify_bounce(state, false);
     *throughput *= shader_bsdf_transparency(kg, shadow_sd);
   }
index dbe2c12ce8100a9b167fdd00672e3b8ea220c96a..23e30db1b08932acd1cd51f5c721bec29d7ce968 100644 (file)
@@ -138,7 +138,7 @@ ccl_device void subsurface_color_bump_blur(
 
   if (bump || texture_blur > 0.0f) {
     /* average color and normal at incoming point */
-    shader_eval_surface(kg, sd, state, state->flag);
+    shader_eval_surface(kg, sd, state, NULL, state->flag);
     float3 in_color = shader_bssrdf_sum(sd, (bump) ? N : NULL, NULL);
 
     /* we simply divide out the average color and multiply with the average
index 7306c32d7c825e609d534bf0b9f4fdf0e85336c6..c35e345763aa4ce6ea3420953488b86a12646a66 100644 (file)
@@ -222,6 +222,8 @@ typedef enum ShaderEvalType {
   SHADER_EVAL_TRANSMISSION_COLOR,
   SHADER_EVAL_SUBSURFACE_COLOR,
   SHADER_EVAL_EMISSION,
+  SHADER_EVAL_AOV_COLOR,
+  SHADER_EVAL_AOV_VALUE,
 
   /* light passes */
   SHADER_EVAL_AO,
@@ -371,6 +373,8 @@ typedef enum PassType {
 #endif
   PASS_RENDER_TIME,
   PASS_CRYPTOMATTE,
+  PASS_AOV_COLOR,
+  PASS_AOV_VALUE,
   PASS_CATEGORY_MAIN_END = 31,
 
   PASS_MIST = 32,
@@ -1244,6 +1248,11 @@ typedef struct KernelFilm {
   int pass_denoising_clean;
   int denoising_flags;
 
+  int pass_aov_color;
+  int pass_aov_value;
+  int pad1;
+  int pad2;
+
   /* XYZ to rendering color space transform. float4 instead of float3 to
    * ensure consistent padding/alignment across devices. */
   float4 xyz_to_r;
diff --git a/intern/cycles/kernel/kernel_write_passes.h b/intern/cycles/kernel/kernel_write_passes.h
new file mode 100644 (file)
index 0000000..410218d
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(__SPLIT_KERNEL__) || defined(__KERNEL_CUDA__)
+#  define __ATOMIC_PASS_WRITE__
+#endif
+
+CCL_NAMESPACE_BEGIN
+
+ccl_device_inline void kernel_write_pass_float(ccl_global float *buffer, float value)
+{
+  ccl_global float *buf = buffer;
+#ifdef __ATOMIC_PASS_WRITE__
+  atomic_add_and_fetch_float(buf, value);
+#else
+  *buf += value;
+#endif
+}
+
+ccl_device_inline void kernel_write_pass_float3(ccl_global float *buffer, float3 value)
+{
+#ifdef __ATOMIC_PASS_WRITE__
+  ccl_global float *buf_x = buffer + 0;
+  ccl_global float *buf_y = buffer + 1;
+  ccl_global float *buf_z = buffer + 2;
+
+  atomic_add_and_fetch_float(buf_x, value.x);
+  atomic_add_and_fetch_float(buf_y, value.y);
+  atomic_add_and_fetch_float(buf_z, value.z);
+#else
+  ccl_global float3 *buf = (ccl_global float3 *)buffer;
+  *buf += value;
+#endif
+}
+
+ccl_device_inline void kernel_write_pass_float4(ccl_global float *buffer, float4 value)
+{
+#ifdef __ATOMIC_PASS_WRITE__
+  ccl_global float *buf_x = buffer + 0;
+  ccl_global float *buf_y = buffer + 1;
+  ccl_global float *buf_z = buffer + 2;
+  ccl_global float *buf_w = buffer + 3;
+
+  atomic_add_and_fetch_float(buf_x, value.x);
+  atomic_add_and_fetch_float(buf_y, value.y);
+  atomic_add_and_fetch_float(buf_z, value.z);
+  atomic_add_and_fetch_float(buf_w, value.w);
+#else
+  ccl_global float4 *buf = (ccl_global float4 *)buffer;
+  *buf += value;
+#endif
+}
+
+#ifdef __DENOISING_FEATURES__
+ccl_device_inline void kernel_write_pass_float_variance(ccl_global float *buffer, float value)
+{
+  kernel_write_pass_float(buffer, value);
+
+  /* The online one-pass variance update that's used for the megakernel can't easily be implemented
+   * with atomics, so for the split kernel the E[x^2] - 1/N * (E[x])^2 fallback is used. */
+  kernel_write_pass_float(buffer + 1, value * value);
+}
+
+#  ifdef __ATOMIC_PASS_WRITE__
+#    define kernel_write_pass_float3_unaligned kernel_write_pass_float3
+#  else
+ccl_device_inline void kernel_write_pass_float3_unaligned(ccl_global float *buffer, float3 value)
+{
+  buffer[0] += value.x;
+  buffer[1] += value.y;
+  buffer[2] += value.z;
+}
+#  endif
+
+ccl_device_inline void kernel_write_pass_float3_variance(ccl_global float *buffer, float3 value)
+{
+  kernel_write_pass_float3_unaligned(buffer, value);
+  kernel_write_pass_float3_unaligned(buffer + 3, value * value);
+}
+#endif /* __DENOISING_FEATURES__ */
+
+CCL_NAMESPACE_END
index 1b161fbc8ee705b4a01db98768e814f3bd65b360..767bd7702aec64ee9fd71a1e5bfb4789f90bf3b4 100644 (file)
@@ -44,6 +44,7 @@
 #include "kernel/kernel_globals.h"
 #include "kernel/kernel_color.h"
 #include "kernel/kernel_random.h"
+#include "kernel/kernel_write_passes.h"
 #include "kernel/kernel_projection.h"
 #include "kernel/kernel_differential.h"
 #include "kernel/kernel_montecarlo.h"
index b1c65f61e2c9057ecba33df94745d8dfd8612933..6d500650cc093b3375d84be92f6f84c1548b70bd 100644 (file)
@@ -58,8 +58,10 @@ ccl_device void kernel_indirect_background(KernelGlobals *kg)
     ccl_global Ray *ray = &kernel_split_state.ray[ray_index];
     float3 throughput = kernel_split_state.throughput[ray_index];
     ShaderData *sd = kernel_split_sd(sd, ray_index);
+    uint buffer_offset = kernel_split_state.buffer_offset[ray_index];
+    ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset;
 
-    kernel_path_background(kg, state, ray, throughput, sd, L);
+    kernel_path_background(kg, state, ray, throughput, sd, buffer, L);
     kernel_split_path_end(kg, ray_index);
   }
 }
index 8e39c9797e58f23e171df5fd2cb055bfb657cdd6..c760a2b20493bd1f0c712388064e2962547e2303 100644 (file)
@@ -50,8 +50,10 @@ ccl_device void kernel_shader_eval(KernelGlobals *kg)
   ccl_global char *ray_state = kernel_split_state.ray_state;
   if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) {
     ccl_global PathState *state = &kernel_split_state.path_state[ray_index];
+    uint buffer_offset = kernel_split_state.buffer_offset[ray_index];
+    ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset;
 
-    shader_eval_surface(kg, kernel_split_sd(sd, ray_index), state, state->flag);
+    shader_eval_surface(kg, kernel_split_sd(sd, ray_index), state, buffer, state->flag);
 #ifdef __BRANCHED_PATH__
     if (kernel_data.integrator.branched) {
       shader_merge_closures(kernel_split_sd(sd, ray_index));
index 18f086d6726c96ec8df30021d380d5aec8ac7677..c4d7164a4d83413fc19580a30042df5530a88927 100644 (file)
@@ -164,6 +164,7 @@ CCL_NAMESPACE_END
 #include "kernel/svm/svm_math_util.h"
 #include "kernel/svm/svm_mapping_util.h"
 
+#include "kernel/svm/svm_aov.h"
 #include "kernel/svm/svm_attribute.h"
 #include "kernel/svm/svm_gradient.h"
 #include "kernel/svm/svm_blackbody.h"
@@ -218,6 +219,7 @@ CCL_NAMESPACE_BEGIN
 ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg,
                                         ShaderData *sd,
                                         ccl_addr_space PathState *state,
+                                        ccl_global float *buffer,
                                         ShaderType type,
                                         int path_flag)
 {
@@ -467,6 +469,17 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg,
       case NODE_IES:
         svm_node_ies(kg, sd, stack, node, &offset);
         break;
+      case NODE_AOV_START:
+        if (!svm_node_aov_check(state, buffer)) {
+          return;
+        }
+        break;
+      case NODE_AOV_COLOR:
+        svm_node_aov_color(kg, sd, stack, node, buffer);
+        break;
+      case NODE_AOV_VALUE:
+        svm_node_aov_value(kg, sd, stack, node, buffer);
+        break;
 #  endif /* __EXTRA_NODES__ */
 #endif   /* NODES_GROUP(NODE_GROUP_LEVEL_2) */
 
diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/svm_aov.h
new file mode 100644 (file)
index 0000000..899e466
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+CCL_NAMESPACE_BEGIN
+
+ccl_device_inline bool svm_node_aov_check(ccl_addr_space PathState *state,
+                                          ccl_global float *buffer)
+{
+  int path_flag = state->flag;
+
+  bool is_primary = (path_flag & PATH_RAY_CAMERA) && (!(path_flag & PATH_RAY_SINGLE_PASS_DONE));
+
+  return ((buffer != NULL) && is_primary);
+}
+
+ccl_device void svm_node_aov_color(
+    KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, ccl_global float *buffer)
+{
+  float3 val = stack_load_float3(stack, node.y);
+
+  if (buffer) {
+    kernel_write_pass_float4(buffer + kernel_data.film.pass_aov_color + 4 * node.z,
+                             make_float4(val.x, val.y, val.z, 1.0f));
+  }
+}
+
+ccl_device void svm_node_aov_value(
+    KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, ccl_global float *buffer)
+{
+  float val = stack_load_float(stack, node.y);
+
+  if (buffer) {
+    kernel_write_pass_float(buffer + kernel_data.film.pass_aov_value + node.z, val);
+  }
+}
+CCL_NAMESPACE_END
index 040e7c6a0f88128488ef3dbe0d3919a359ff53bb..8dbb147e76a5648ebdadbeffa1afc9183efa16ee 100644 (file)
@@ -150,6 +150,9 @@ typedef enum ShaderNodeType {
   NODE_VERTEX_COLOR,
   NODE_VERTEX_COLOR_BUMP_DX,
   NODE_VERTEX_COLOR_BUMP_DY,
+  NODE_AOV_START,
+  NODE_AOV_VALUE,
+  NODE_AOV_COLOR,
 } ShaderNodeType;
 
 typedef enum NodeAttributeType {
index 49e15d9eaf16c4bb55b1d5341ba3a3286dc5824d..fe8606e1939ebb0010874e22c7b54aba377f838b 100644 (file)
@@ -234,7 +234,7 @@ bool RenderBuffers::get_denoising_pass_rect(
 }
 
 bool RenderBuffers::get_pass_rect(
-    PassType type, float exposure, int sample, int components, float *pixels, const string &name)
+    const string &name, float exposure, int sample, int components, float *pixels)
 {
   if (buffer.data() == NULL) {
     return false;
@@ -245,18 +245,14 @@ bool RenderBuffers::get_pass_rect(
   for (size_t j = 0; j < params.passes.size(); j++) {
     Pass &pass = params.passes[j];
 
-    if (pass.type != type) {
+    /* Pass is identified by both type and name, multiple of the same type
+     * may exist with a different name. */
+    if (pass.name != name) {
       pass_offset += pass.components;
       continue;
     }
 
-    /* Tell Cryptomatte passes apart by their name. */
-    if (pass.type == PASS_CRYPTOMATTE) {
-      if (pass.name != name) {
-        pass_offset += pass.components;
-        continue;
-      }
-    }
+    PassType type = pass.type;
 
     float *in = buffer.data() + pass_offset;
     int pass_stride = params.get_passes_size();
index 74bb656c5a476300f05596a9654853b99737a642..1042b42810f5d3f720616be87214c7b60b2d4fa7 100644 (file)
@@ -88,12 +88,8 @@ class RenderBuffers {
   void zero();
 
   bool copy_from_device();
-  bool get_pass_rect(PassType type,
-                     float exposure,
-                     int sample,
-                     int components,
-                     float *pixels,
-                     const string &name);
+  bool get_pass_rect(
+      const string &name, float exposure, int sample, int components, float *pixels);
   bool get_denoising_pass_rect(
       int offset, float exposure, int sample, int components, float *pixels);
 };
index 7f5bec2a66ef7a0ef3f9f26888ee7bdc91e5e7ad..379b0e6c214dbb112f07ca0f06969f8ebdac16bb 100644 (file)
@@ -163,6 +163,12 @@ void Pass::add(PassType type, vector<Pass> &passes, const char *name)
     case PASS_CRYPTOMATTE:
       pass.components = 4;
       break;
+    case PASS_AOV_COLOR:
+      pass.components = 4;
+      break;
+    case PASS_AOV_VALUE:
+      pass.components = 1;
+      break;
     default:
       assert(false);
       break;
@@ -327,7 +333,7 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
   kfilm->pass_stride = 0;
   kfilm->use_light_pass = use_light_visibility || use_sample_clamp;
 
-  bool have_cryptomatte = false;
+  bool have_cryptomatte = false, have_aov_color = false, have_aov_value = false;
 
   for (size_t i = 0; i < passes.size(); i++) {
     Pass &pass = passes[i];
@@ -464,6 +470,18 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
                                       kfilm->pass_stride;
         have_cryptomatte = true;
         break;
+      case PASS_AOV_COLOR:
+        if (!have_aov_color) {
+          kfilm->pass_aov_color = kfilm->pass_stride;
+          have_aov_color = true;
+        }
+        break;
+      case PASS_AOV_VALUE:
+        if (!have_aov_value) {
+          kfilm->pass_aov_value = kfilm->pass_stride;
+          have_aov_value = true;
+        }
+        break;
       default:
         assert(false);
         break;
@@ -569,4 +587,27 @@ void Film::tag_update(Scene * /*scene*/)
   need_update = true;
 }
 
+int Film::get_aov_offset(string name, bool &is_color)
+{
+  int num_color = 0, num_value = 0;
+  foreach (const Pass &pass, passes) {
+    if (pass.type == PASS_AOV_COLOR) {
+      num_color++;
+    }
+    else if (pass.type == PASS_AOV_VALUE) {
+      num_value++;
+    }
+    else {
+      continue;
+    }
+
+    if (pass.name == name) {
+      is_color = (pass.type == PASS_AOV_COLOR);
+      return (is_color ? num_color : num_value) - 1;
+    }
+  }
+
+  return -1;
+}
+
 CCL_NAMESPACE_END
index 2c4e07d60aefea95742d6edaa4c06439aa92365e..48673af01143356929d33d7c099aede55228e479 100644 (file)
@@ -93,6 +93,8 @@ class Film : public Node {
   bool modified(const Film &film);
   void tag_passes_update(Scene *scene, const vector<Pass> &passes_, bool update_passes = true);
   void tag_update(Scene *scene);
+
+  int get_aov_offset(string name, bool &is_color);
 };
 
 CCL_NAMESPACE_END
index 501d1e33a9b40463caa70e067f5806bef0098ba3..0e520c700a76ceca415d07495e161436dd729c03 100644 (file)
@@ -55,6 +55,25 @@ bool check_node_inputs_traversed(const ShaderNode *node, const ShaderNodeSet &do
 
 } /* namespace */
 
+/* Sockets */
+
+void ShaderInput::disconnect()
+{
+  if (link) {
+    link->links.erase(remove(link->links.begin(), link->links.end(), this), link->links.end());
+  }
+  link = NULL;
+}
+
+void ShaderOutput::disconnect()
+{
+  foreach (ShaderInput *sock, links) {
+    sock->link = NULL;
+  }
+
+  links.clear();
+}
+
 /* Node */
 
 ShaderNode::ShaderNode(const NodeType *type) : Node(type)
@@ -285,11 +304,7 @@ void ShaderGraph::disconnect(ShaderOutput *from)
   assert(!finalized);
   simplified = false;
 
-  foreach (ShaderInput *sock, from->links) {
-    sock->link = NULL;
-  }
-
-  from->links.clear();
+  from->disconnect();
 }
 
 void ShaderGraph::disconnect(ShaderInput *to)
@@ -298,10 +313,7 @@ void ShaderGraph::disconnect(ShaderInput *to)
   assert(to->link);
   simplified = false;
 
-  ShaderOutput *from = to->link;
-
-  to->link = NULL;
-  from->links.erase(remove(from->links.begin(), from->links.end(), to), from->links.end());
+  to->disconnect();
 }
 
 void ShaderGraph::relink(ShaderInput *from, ShaderInput *to)
@@ -782,6 +794,11 @@ void ShaderGraph::clean(Scene *scene)
 
   /* break cycles */
   break_cycles(output(), visited, on_stack);
+  foreach (ShaderNode *node, nodes) {
+    if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) {
+      break_cycles(node, visited, on_stack);
+    }
+  }
 
   /* disconnect unused nodes */
   foreach (ShaderNode *node, nodes) {
index cade04de374a6c6d94954459e43d160dcdb607f1..0ea7935f7142c0f178d1a63645dd5c3a98029393 100644 (file)
@@ -67,6 +67,7 @@ enum ShaderNodeSpecialType {
   SHADER_SPECIAL_TYPE_COMBINE_CLOSURE,
   SHADER_SPECIAL_TYPE_OUTPUT,
   SHADER_SPECIAL_TYPE_BUMP,
+  SHADER_SPECIAL_TYPE_OUTPUT_AOV,
 };
 
 /* Input
@@ -104,6 +105,8 @@ class ShaderInput {
     ((Node *)parent)->set(socket_type, f);
   }
 
+  void disconnect();
+
   const SocketType &socket_type;
   ShaderNode *parent;
   ShaderOutput *link;
@@ -130,6 +133,8 @@ class ShaderOutput {
     return socket_type.type;
   }
 
+  void disconnect();
+
   const SocketType &socket_type;
   ShaderNode *parent;
   vector<ShaderInput *> links;
index 26f16d5ee80077516367e5a3cecc521513970aa4..5e12d79bc6be2d0dfeed80526063d3b321c84afd 100644 (file)
@@ -5709,6 +5709,58 @@ void ClampNode::compile(OSLCompiler &compiler)
   compiler.add(this, "node_clamp");
 }
 
+/* AOV Output */
+
+NODE_DEFINE(OutputAOVNode)
+{
+  NodeType *type = NodeType::add("aov_output", create, NodeType::SHADER);
+
+  SOCKET_IN_COLOR(color, "Color", make_float3(0.0f, 0.0f, 0.0f));
+  SOCKET_IN_FLOAT(value, "Value", 0.0f);
+
+  SOCKET_STRING(name, "AOV Name", ustring(""));
+
+  return type;
+}
+
+OutputAOVNode::OutputAOVNode() : ShaderNode(node_type)
+{
+  special_type = SHADER_SPECIAL_TYPE_OUTPUT_AOV;
+  slot = -1;
+}
+
+void OutputAOVNode::simplify_settings(Scene *scene)
+{
+  slot = scene->film->get_aov_offset(name.string(), is_color);
+  if (slot == -1) {
+    slot = scene->film->get_aov_offset(name.string(), is_color);
+  }
+
+  if (slot == -1 || is_color) {
+    input("Value")->disconnect();
+  }
+  if (slot == -1 || !is_color) {
+    input("Color")->disconnect();
+  }
+}
+
+void OutputAOVNode::compile(SVMCompiler &compiler)
+{
+  assert(slot >= 0);
+
+  if (is_color) {
+    compiler.add_node(NODE_AOV_COLOR, compiler.stack_assign(input("Color")), slot);
+  }
+  else {
+    compiler.add_node(NODE_AOV_VALUE, compiler.stack_assign(input("Value")), slot);
+  }
+}
+
+void OutputAOVNode::compile(OSLCompiler & /*compiler*/)
+{
+  /* TODO */
+}
+
 /* Math */
 
 NODE_DEFINE(MathNode)
index ae762f3db1c56ba13c7fafe2ad09b697d5ea905f..54124cd2175746bf56676da28a1452b7a6467a36 100644 (file)
@@ -189,6 +189,26 @@ class OutputNode : public ShaderNode {
   }
 };
 
+class OutputAOVNode : public ShaderNode {
+ public:
+  SHADER_NODE_CLASS(OutputAOVNode)
+  virtual void simplify_settings(Scene *scene);
+
+  float value;
+  float3 color;
+
+  ustring name;
+
+  /* Don't allow output node de-duplication. */
+  virtual bool equals(const ShaderNode & /*other*/)
+  {
+    return false;
+  }
+
+  int slot;
+  bool is_color;
+};
+
 class GradientTextureNode : public TextureNode {
  public:
   SHADER_NODE_CLASS(GradientTextureNode)
index 00d9cd5e6721e22076bc9c61ca7045f9296fa3f2..3b73fa4139f994458db0c1025d6fb7c406daa434 100644 (file)
@@ -225,6 +225,13 @@ Shader::~Shader()
 
 bool Shader::is_constant_emission(float3 *emission)
 {
+  /* If the shader has AOVs, they need to be evaluated, so we can't skip the shader. */
+  foreach (ShaderNode *node, graph->nodes) {
+    if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) {
+      return false;
+    }
+  }
+
   ShaderInput *surf = graph->output()->input("Surface");
 
   if (surf->link == NULL) {
index f69c89b1fd46bf0e237ac33be016e26b7b4084ff..8466742ae38a7ee0f7d35bbb97de3e9cf9d4989c 100644 (file)
@@ -554,6 +554,24 @@ void SVMCompiler::generated_shared_closure_nodes(ShaderNode *root_node,
   }
 }
 
+void SVMCompiler::generate_aov_node(ShaderNode *node, CompilerState *state)
+{
+  /* execute dependencies for node */
+  foreach (ShaderInput *in, node->inputs) {
+    if (in->link != NULL) {
+      ShaderNodeSet dependencies;
+      find_dependencies(dependencies, state->nodes_done, in);
+      generate_svm_nodes(dependencies, state);
+    }
+  }
+
+  /* compile node itself */
+  generate_node(node, state->nodes_done);
+
+  state->nodes_done.insert(node);
+  state->nodes_done_flag[node->id] = true;
+}
+
 void SVMCompiler::generate_multi_closure(ShaderNode *root_node,
                                          ShaderNode *node,
                                          CompilerState *state)
@@ -703,21 +721,21 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
   current_graph = graph;
 
   /* get input in output node */
-  ShaderNode *node = graph->output();
+  ShaderNode *output = graph->output();
   ShaderInput *clin = NULL;
 
   switch (type) {
     case SHADER_TYPE_SURFACE:
-      clin = node->input("Surface");
+      clin = output->input("Surface");
       break;
     case SHADER_TYPE_VOLUME:
-      clin = node->input("Volume");
+      clin = output->input("Volume");
       break;
     case SHADER_TYPE_DISPLACEMENT:
-      clin = node->input("Displacement");
+      clin = output->input("Displacement");
       break;
     case SHADER_TYPE_BUMP:
-      clin = node->input("Normal");
+      clin = output->input("Normal");
       break;
     default:
       assert(0);
@@ -728,10 +746,10 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
   memset((void *)&active_stack, 0, sizeof(active_stack));
   current_svm_nodes.clear();
 
-  foreach (ShaderNode *node_iter, graph->nodes) {
-    foreach (ShaderInput *input, node_iter->inputs)
+  foreach (ShaderNode *node, graph->nodes) {
+    foreach (ShaderInput *input, node->inputs)
       input->stack_offset = SVM_STACK_INVALID;
-    foreach (ShaderOutput *output, node_iter->outputs)
+    foreach (ShaderOutput *output, node->outputs)
       output->stack_offset = SVM_STACK_INVALID;
   }
 
@@ -745,6 +763,7 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
   }
 
   if (shader->used) {
+    CompilerState state(graph);
     if (clin->link) {
       bool generate = false;
 
@@ -769,13 +788,36 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
       }
 
       if (generate) {
-        CompilerState state(graph);
         generate_multi_closure(clin->link->parent, clin->link->parent, &state);
       }
     }
 
     /* compile output node */
-    node->compile(*this);
+    output->compile(*this);
+
+    if (type == SHADER_TYPE_SURFACE) {
+      vector<OutputAOVNode *> aov_outputs;
+      foreach (ShaderNode *node, graph->nodes) {
+        if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) {
+          OutputAOVNode *aov_node = static_cast<OutputAOVNode *>(node);
+          if (aov_node->slot >= 0) {
+            aov_outputs.push_back(aov_node);
+          }
+        }
+      }
+      if (aov_outputs.size() > 0) {
+        /* AOV passes are only written if the object is directly visible, so
+         * there is no point in evaluating all the nodes generated only for the
+         * AOV outputs if that's not the case. Therefore, we insert
+         * NODE_AOV_START into the shader before the AOV-only nodes are
+         * generated which tells the kernel that it can stop evaluation
+         * early if AOVs will not be written. */
+        add_node(NODE_AOV_START, 0, 0, 0);
+        foreach (OutputAOVNode *node, aov_outputs) {
+          generate_aov_node(node, &state);
+        }
+      }
+    }
   }
 
   /* add node to restore state after bump shader has finished */
index 7eac3e19bb3200ecacfd8c4c807d07b318ccf77d..d1534567beaaea9c41a0feaa507c28462f163ad8 100644 (file)
@@ -204,6 +204,7 @@ class SVMCompiler {
                          ShaderInput *input,
                          ShaderNode *skip_node = NULL);
   void generate_node(ShaderNode *node, ShaderNodeSet &done);
+  void generate_aov_node(ShaderNode *node, CompilerState *state);
   void generate_closure_node(ShaderNode *node, CompilerState *state);
   void generated_shared_closure_nodes(ShaderNode *root_node,
                                       ShaderNode *node,
index f0f1baba642efc34ea6c4f808f08e01dc0138e9f..d9a374503b38fecb6e5ad2d3a462b1f9d9599604 100644 (file)
@@ -157,6 +157,11 @@ def object_cycles_shader_nodes_poll(context):
             cycles_shader_nodes_poll(context))
 
 
+def cycles_aov_node_poll(context):
+    return (object_cycles_shader_nodes_poll(context) or
+            world_shader_nodes_poll(context))
+
+
 def object_eevee_shader_nodes_poll(context):
     return (object_shader_nodes_poll(context) and
             eevee_shader_nodes_poll(context))
@@ -197,6 +202,7 @@ shader_node_categories = [
     ShaderNodeCategory("SH_NEW_OUTPUT", "Output", items=[
         NodeItem("ShaderNodeOutputMaterial", poll=object_eevee_cycles_shader_nodes_poll),
         NodeItem("ShaderNodeOutputLight", poll=object_cycles_shader_nodes_poll),
+        NodeItem("ShaderNodeOutputAOV", poll=cycles_aov_node_poll),
         NodeItem("ShaderNodeOutputWorld", poll=world_shader_nodes_poll),
         NodeItem("ShaderNodeOutputLineStyle", poll=line_style_shader_nodes_poll),
         NodeItem("NodeGroupOutput", poll=group_input_output_item_poll),
index 9501d3f8ec9808dd376594e0ef373757dfe6beb4..11f151af44d9242ef44a0edee84b010beeb4dd09 100644 (file)
@@ -990,6 +990,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree,
 #define SH_NODE_TEX_WHITE_NOISE 704
 #define SH_NODE_VOLUME_INFO 705
 #define SH_NODE_VERTEX_COLOR 706
+#define SH_NODE_OUTPUT_AOV 707
 
 /* custom defines options for Material node */
 #define SH_NODE_MAT_DIFF 1
index 578d81b157c0a179eb82a82ea9dffa22bcc8fb53..be28ca264596eb4a649a8c00c4285e658696add3 100644 (file)
@@ -4007,6 +4007,7 @@ static void registerShaderNodes(void)
   register_node_type_sh_output_material();
   register_node_type_sh_output_world();
   register_node_type_sh_output_linestyle();
+  register_node_type_sh_output_aov();
 
   register_node_type_sh_tex_image();
   register_node_type_sh_tex_environment();
index a02eeaa150240c8b20425feee4d363c538cb99d3..35c9082b54cf7c3ceb403da06d6e5f806fc89ed3 100644 (file)
@@ -1154,6 +1154,11 @@ static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C),
   uiItemR(layout, ptr, "noise_dimensions", 0, "", ICON_NONE);
 }
 
+static void node_shader_buts_output_aov(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+  uiItemR(layout, ptr, "name", 0, NULL, ICON_NONE);
+}
+
 /* only once called */
 static void node_shader_set_butfunc(bNodeType *ntype)
 {
@@ -1310,6 +1315,9 @@ static void node_shader_set_butfunc(bNodeType *ntype)
     case SH_NODE_TEX_WHITE_NOISE:
       ntype->draw_buttons = node_shader_buts_white_noise;
       break;
+    case SH_NODE_OUTPUT_AOV:
+      ntype->draw_buttons = node_shader_buts_output_aov;
+      break;
   }
 }
 
index e3ea3a5b3f8d8d5d1628539c675e2ea7addcd6a7..d94a05ccb816d7412e6ab122ee9ad002f4051fd1 100644 (file)
@@ -1010,6 +1010,10 @@ typedef struct NodeShaderTexIES {
   char filepath[1024];
 } NodeShaderTexIES;
 
+typedef struct NodeShaderOutputAOV {
+  char name[64];
+} NodeShaderOutputAOV;
+
 typedef struct NodeSunBeams {
   float source[2];
 
index 7fe88ec94b7cd3c10b208dee730d8999ed2474b4..94f2ec25ded1adb73664db244b1ad79c523fbf49 100644 (file)
@@ -574,6 +574,7 @@ extern StructRNA RNA_ShaderNodeMath;
 extern StructRNA RNA_ShaderNodeMixRGB;
 extern StructRNA RNA_ShaderNodeNormal;
 extern StructRNA RNA_ShaderNodeOutput;
+extern StructRNA RNA_ShaderNodeOutputAOV;
 extern StructRNA RNA_ShaderNodeRGB;
 extern StructRNA RNA_ShaderNodeRGBCurve;
 extern StructRNA RNA_ShaderNodeRGBToBW;
index 0a47830b1e3f17859e0b77fad91327c1f40c6293..17e34cd60adb11d0276d368c95bfb2c5e3d9f0a0 100644 (file)
@@ -5187,6 +5187,19 @@ static void def_sh_tex_ies(StructRNA *srna)
   RNA_def_struct_sdna_from(srna, "bNode", NULL);
 }
 
+static void def_sh_output_aov(StructRNA *srna)
+{
+  PropertyRNA *prop;
+
+  RNA_def_struct_sdna_from(srna, "NodeShaderOutputAOV", "storage");
+
+  prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+  RNA_def_property_ui_text(prop, "Name", "Name of the AOV that this output writes to");
+  RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+  RNA_def_struct_sdna_from(srna, "bNode", NULL);
+}
+
 static void def_sh_script(StructRNA *srna)
 {
   PropertyRNA *prop;
index f618f964b22fd2b183c90a4d0d907ea7e93faef7..fb2cb11d269d4bc1d44655233ae749a63177e34a 100644 (file)
@@ -177,6 +177,7 @@ set(SRC
   shader/nodes/node_shader_output_linestyle.c
   shader/nodes/node_shader_output_material.c
   shader/nodes/node_shader_output_world.c
+  shader/nodes/node_shader_output_aov.c
   shader/nodes/node_shader_particle_info.c
   shader/nodes/node_shader_rgb.c
   shader/nodes/node_shader_script.c
index a004090d03d402dd5b4082c67dfa494cb281d489..69d779c9b7fd5b55e834cd1c2ad6d7c29a52b9c3 100644 (file)
@@ -120,6 +120,7 @@ void register_node_type_sh_output_material(void);
 void register_node_type_sh_output_eevee_material(void);
 void register_node_type_sh_output_world(void);
 void register_node_type_sh_output_linestyle(void);
+void register_node_type_sh_output_aov(void);
 
 void register_node_type_sh_tex_image(void);
 void register_node_type_sh_tex_environment(void);
index a8163391c1385d120bcb70bcb8ca8d58e3eb7c42..2b141e1a6391edfc5ff3262143591803efde0c6d 100644 (file)
@@ -130,6 +130,7 @@ DefNode(ShaderNode,     SH_NODE_DISPLACEMENT,       def_sh_displacement,    "DIS
 DefNode(ShaderNode,     SH_NODE_VECTOR_DISPLACEMENT,def_sh_vector_displacement,"VECTOR_DISPLACEMENT",VectorDisplacement,"Vector Displacement",""  )
 DefNode(ShaderNode,     SH_NODE_TEX_IES,            def_sh_tex_ies,         "TEX_IES",            TexIES,           "IES Texture",       ""       )
 DefNode(ShaderNode,     SH_NODE_TEX_WHITE_NOISE,    def_sh_tex_white_noise, "TEX_WHITE_NOISE",    TexWhiteNoise,    "White Noise",       ""       )
+DefNode(ShaderNode,     SH_NODE_OUTPUT_AOV,         def_sh_output_aov,      "OUTPUT_AOV",         OutputAOV,        "AOV Output",        ""       )
 
 DefNode(CompositorNode, CMP_NODE_VIEWER,         def_cmp_viewer,         "VIEWER",         Viewer,           "Viewer",            ""              )
 DefNode(CompositorNode, CMP_NODE_RGB,            0,                      "RGB",            RGB,              "RGB",               ""              )
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.c b/source/blender/nodes/shader/nodes/node_shader_output_aov.c
new file mode 100644 (file)
index 0000000..a3ecb06
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "../node_shader_util.h"
+
+/* **************** OUTPUT ******************** */
+
+static bNodeSocketTemplate sh_node_output_aov_in[] = {
+    {SOCK_RGBA, 1, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
+    {SOCK_FLOAT, 1, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
+    {-1, 0, ""},
+};
+
+static void node_shader_init_output_aov(bNodeTree *UNUSED(ntree), bNode *node)
+{
+  NodeShaderOutputAOV *aov = MEM_callocN(sizeof(NodeShaderOutputAOV), "NodeShaderOutputAOV");
+  node->storage = aov;
+}
+
+/* node type definition */
+void register_node_type_sh_output_aov(void)
+{
+  static bNodeType ntype;
+
+  sh_node_type_base(&ntype, SH_NODE_OUTPUT_AOV, "AOV Output", NODE_CLASS_OUTPUT, 0);
+  node_type_socket_templates(&ntype, sh_node_output_aov_in, NULL);
+  node_type_init(&ntype, node_shader_init_output_aov);
+  node_type_storage(
+      &ntype, "NodeShaderOutputAOV", node_free_standard_storage, node_copy_standard_storage);
+
+  /* Do not allow muting output node. */
+  node_type_internal_links(&ntype, NULL);
+
+  nodeRegisterType(&ntype);
+}