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
This commit is contained in:
Lukas Stockner 2019-12-04 19:57:28 +01:00 committed by Brecht Van Lommel
parent 35b5888b15
commit e760972221
46 changed files with 722 additions and 195 deletions

@ -223,65 +223,95 @@ def system_info():
import _cycles
return _cycles.system_info()
def list_render_passes(srl):
# Builtin Blender passes.
yield ("Combined", "RGBA", 'COLOR')
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')
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 = ""

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

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

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

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

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

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

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

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

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

@ -14,85 +14,11 @@
* 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,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1010,6 +1010,10 @@ typedef struct NodeShaderTexIES {
char filepath[1024];
} NodeShaderTexIES;
typedef struct NodeShaderOutputAOV {
char name[64];
} NodeShaderOutputAOV;
typedef struct NodeSunBeams {
float source[2];

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

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

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

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

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

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