diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 9d7c71417f2..b8fb67deb52 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -86,6 +86,14 @@ enum_sampling_pattern = ( ('PROGRESSIVE_MULTI_JITTER', "Progressive Multi-Jitter", "Use Progressive Multi-Jitter random sampling pattern", 1), ) +enum_emission_sampling = ( + ('NONE', 'None', "Do not use this surface as a light for sampling", 0), + ('AUTO', 'Auto', "Automatically determine if the surface should be treated as a light for sampling, based on estimated emission intensity", 1), + ('FRONT', 'Front', "Treat only front side of the surface as a light, usually for closed meshes whose interior is not visible", 2), + ('BACK', 'Back', "Treat only back side of the surface as a light for sampling", 3), + ('FRONT_BACK', 'Front and Back', "Treat surface as a light for sampling, emitting from both the front and back side", 4), +) + enum_volume_sampling = ( ('DISTANCE', "Distance", @@ -1043,13 +1051,13 @@ class CyclesCameraSettings(bpy.types.PropertyGroup): class CyclesMaterialSettings(bpy.types.PropertyGroup): - sample_as_light: BoolProperty( - name="Multiple Importance Sample", - description="Use multiple importance sampling for this material, " - "disabling may reduce overall noise for large " - "objects that emit little light compared to other light sources", - default=True, + emission_sampling: EnumProperty( + name="Emission Sampling", + description="Sampling strategy for emissive surfaces", + items=enum_emission_sampling, + default="AUTO", ) + use_transparent_shadow: BoolProperty( name="Transparent Shadows", description="Use transparent shadows for this material if it contains a Transparent BSDF, " diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 60a7ee9708d..959b945461e 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1832,9 +1832,9 @@ class CYCLES_MATERIAL_PT_settings_surface(CyclesButtonsPanel, Panel): cmat = mat.cycles col = layout.column() - col.prop(cmat, "sample_as_light", text="Multiple Importance") - col.prop(cmat, "use_transparent_shadow") col.prop(cmat, "displacement_method", text="Displacement") + col.prop(cmat, "emission_sampling") + col.prop(cmat, "use_transparent_shadow") def draw(self, context): self.draw_shared(self, context.material) diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py index 12880496dfd..8ebb17e614a 100644 --- a/intern/cycles/blender/addon/version_update.py +++ b/intern/cycles/blender/addon/version_update.py @@ -99,7 +99,7 @@ def do_versions(self): library_versions.setdefault(library.version, []).append(library) # Do versioning per library, since they might have different versions. - max_need_versioning = (3, 0, 25) + max_need_versioning = (3, 5, 2) for version, libraries in library_versions.items(): if version > max_need_versioning: continue @@ -297,3 +297,8 @@ def do_versions(self): cmat = mat.cycles if not cmat.is_property_set("displacement_method"): cmat.displacement_method = 'DISPLACEMENT' + + if version <= (3, 5, 3): + cmat = mat.cycles + if not cmat.get("sample_as_light", True): + cmat.emission_sampling = 'NONE' diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index dbc49df7f22..f25e98469fb 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -61,6 +61,12 @@ static DisplacementMethod get_displacement_method(PointerRNA &ptr) ptr, "displacement_method", DISPLACE_NUM_METHODS, DISPLACE_BUMP); } +static EmissionSampling get_emission_sampling(PointerRNA &ptr) +{ + return (EmissionSampling)get_enum( + ptr, "emission_sampling", EMISSION_SAMPLING_NUM, EMISSION_SAMPLING_AUTO); +} + static int validate_enum_value(int value, int num_values, int default_value) { if (value >= num_values) { @@ -1559,7 +1565,7 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all) /* settings */ PointerRNA cmat = RNA_pointer_get(&b_mat.ptr, "cycles"); - shader->set_use_mis(get_boolean(cmat, "sample_as_light")); + shader->set_emission_sampling_method(get_emission_sampling(cmat)); shader->set_use_transparent_shadow(get_boolean(cmat, "use_transparent_shadow")); shader->set_heterogeneous_volume(!get_boolean(cmat, "homogeneous_volume")); shader->set_volume_sampling_method(get_volume_sampling(cmat)); diff --git a/intern/cycles/kernel/closure/bsdf_diffuse.h b/intern/cycles/kernel/closure/bsdf_diffuse.h index c9c26754651..827b762f4c7 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse.h @@ -69,7 +69,7 @@ ccl_device int bsdf_diffuse_sample(ccl_private const ShaderClosure *sc, ccl_device int bsdf_translucent_setup(ccl_private DiffuseBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_TRANSLUCENT_ID; - return SD_BSDF | SD_BSDF_HAS_EVAL; + return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION; } ccl_device Spectrum bsdf_translucent_eval(ccl_private const ShaderClosure *sc, diff --git a/intern/cycles/kernel/closure/bsdf_hair.h b/intern/cycles/kernel/closure/bsdf_hair.h index a8ba4044758..989714bd695 100644 --- a/intern/cycles/kernel/closure/bsdf_hair.h +++ b/intern/cycles/kernel/closure/bsdf_hair.h @@ -34,7 +34,7 @@ ccl_device int bsdf_hair_transmission_setup(ccl_private HairBsdf *bsdf) bsdf->type = CLOSURE_BSDF_HAIR_TRANSMISSION_ID; bsdf->roughness1 = clamp(bsdf->roughness1, 0.001f, 1.0f); bsdf->roughness2 = clamp(bsdf->roughness2, 0.001f, 1.0f); - return SD_BSDF | SD_BSDF_HAS_EVAL; + return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION; } ccl_device Spectrum bsdf_hair_reflection_eval(ccl_private const ShaderClosure *sc, diff --git a/intern/cycles/kernel/closure/bsdf_hair_principled.h b/intern/cycles/kernel/closure/bsdf_hair_principled.h index 857b3fbf3a6..5a6465c7af6 100644 --- a/intern/cycles/kernel/closure/bsdf_hair_principled.h +++ b/intern/cycles/kernel/closure/bsdf_hair_principled.h @@ -196,7 +196,7 @@ ccl_device int bsdf_principled_hair_setup(ccl_private ShaderData *sd, bsdf->extra->geom = make_float4(Y.x, Y.y, Y.z, h); - return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG; + return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG | SD_BSDF_HAS_TRANSMISSION; } #endif /* __HAIR__ */ diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index 4eb7cd5df22..39d0fb8f5f5 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -346,7 +346,7 @@ ccl_device int bsdf_microfacet_ggx_refraction_setup(ccl_private MicrofacetBsdf * bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; - return SD_BSDF | SD_BSDF_HAS_EVAL; + return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION; } ccl_device void bsdf_microfacet_ggx_blur(ccl_private ShaderClosure *sc, float roughness) @@ -776,7 +776,7 @@ ccl_device int bsdf_microfacet_beckmann_refraction_setup(ccl_private MicrofacetB bsdf->alpha_y = bsdf->alpha_x; bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID; - return SD_BSDF | SD_BSDF_HAS_EVAL; + return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION; } ccl_device void bsdf_microfacet_beckmann_blur(ccl_private ShaderClosure *sc, float roughness) diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h index 73cc0d292a1..73bbe80b2d4 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h @@ -559,7 +559,7 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_setup(ccl_private MicrofacetBsdf bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID; - return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG; + return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG | SD_BSDF_HAS_TRANSMISSION; } ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(ccl_private MicrofacetBsdf *bsdf, diff --git a/intern/cycles/kernel/integrator/path_state.h b/intern/cycles/kernel/integrator/path_state.h index 7197f0f2f3a..9d8ecdc47b4 100644 --- a/intern/cycles/kernel/integrator/path_state.h +++ b/intern/cycles/kernel/integrator/path_state.h @@ -91,7 +91,10 @@ ccl_device_inline void path_state_init_integrator(KernelGlobals kg, #endif } -ccl_device_inline void path_state_next(KernelGlobals kg, IntegratorState state, int label) +ccl_device_inline void path_state_next(KernelGlobals kg, + IntegratorState state, + const int label, + const int shader_flag) { uint32_t flag = INTEGRATOR_STATE(state, path, flag); @@ -120,12 +123,12 @@ ccl_device_inline void path_state_next(KernelGlobals kg, IntegratorState state, flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; } - flag &= ~(PATH_RAY_ALL_VISIBILITY | PATH_RAY_MIS_SKIP); + flag &= ~(PATH_RAY_ALL_VISIBILITY | PATH_RAY_MIS_SKIP | PATH_RAY_MIS_HAD_TRANSMISSION); #ifdef __VOLUME__ if (label & LABEL_VOLUME_SCATTER) { /* volume scatter */ - flag |= PATH_RAY_VOLUME_SCATTER; + flag |= PATH_RAY_VOLUME_SCATTER | PATH_RAY_MIS_HAD_TRANSMISSION; flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; if (!(flag & PATH_RAY_ANY_PASS)) { flag |= PATH_RAY_VOLUME_PASS; @@ -188,6 +191,11 @@ ccl_device_inline void path_state_next(KernelGlobals kg, IntegratorState state, flag |= PATH_RAY_GLOSSY | PATH_RAY_SINGULAR | PATH_RAY_MIS_SKIP; } + /* Flag for consistent MIS weights with light tree. */ + if (shader_flag & SD_BSDF_HAS_TRANSMISSION) { + flag |= PATH_RAY_MIS_HAD_TRANSMISSION; + } + /* Render pass categories. */ if (!(flag & PATH_RAY_ANY_PASS) && !(flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { flag |= PATH_RAY_SURFACE_PASS; diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index 05b42af0bfc..d70d88181be 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -112,11 +112,13 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg, Spectrum L = surface_shader_emission(sd); float mis_weight = 1.0f; + const bool has_mis = !(path_flag & PATH_RAY_MIS_SKIP) && + (sd->flag & ((sd->flag & SD_BACKFACING) ? SD_MIS_BACK : SD_MIS_FRONT)); + #ifdef __HAIR__ - if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS) && - (sd->type & PRIMITIVE_TRIANGLE)) + if (has_mis && (sd->type & PRIMITIVE_TRIANGLE)) #else - if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS)) + if (has_mis) #endif { mis_weight = light_sample_mis_weight_forward_surface(kg, state, path_flag, sd); @@ -447,7 +449,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( unguided_bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf)); } - path_state_next(kg, state, label); + path_state_next(kg, state, label, sd->flag); guiding_record_surface_bounce(kg, state, diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h index d84f41c1269..f557bc442d7 100644 --- a/intern/cycles/kernel/integrator/shade_volume.h +++ b/intern/cycles/kernel/integrator/shade_volume.h @@ -768,7 +768,7 @@ ccl_device_forceinline void integrate_volume_direct_light( sd->time, P, zero_float3(), - 0, + SD_BSDF_HAS_TRANSMISSION, bounce, path_flag, ls)) { @@ -984,7 +984,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter( INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf( unguided_phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf)); - path_state_next(kg, state, label); + path_state_next(kg, state, label, sd->flag); return true; } diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index 1a2210fd52a..90fd8f8e419 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -209,21 +209,26 @@ enum PathRayFlag : uint32_t { PATH_RAY_SHADOW_TRANSPARENT = (1U << 9U), PATH_RAY_SHADOW = (PATH_RAY_SHADOW_OPAQUE | PATH_RAY_SHADOW_TRANSPARENT), - /* Special flag to tag unaligned BVH nodes. - * Only set and used in BVH nodes to distinguish how to interpret bounding box information stored - * in the node (either it should be intersected as AABB or as OBBU). */ - PATH_RAY_NODE_UNALIGNED = (1U << 10U), - /* Subset of flags used for ray visibility for intersection. * * NOTE: SHADOW_CATCHER macros below assume there are no more than * 16 visibility bits. */ - PATH_RAY_ALL_VISIBILITY = ((1U << 11U) - 1U), + PATH_RAY_ALL_VISIBILITY = ((1U << 10U) - 1U), + + /* Special flag to tag unaligned BVH nodes. + * Only set and used in BVH nodes to distinguish how to interpret bounding box information stored + * in the node (either it should be intersected as AABB or as OBBU). + * So this can overlap with path flags. */ + PATH_RAY_NODE_UNALIGNED = (1U << 10U), /* -------------------------------------------------------------------- * Path flags. */ + /* Surface had transmission component at previous bounce. Used for light tree + * traversal and culling to be consistent with MIS pdf at the next bounce. */ + PATH_RAY_MIS_HAD_TRANSMISSION = (1U << 10U), + /* Don't apply multiple importance sampling weights to emission from * lamp or surface hits, because they were not direct light sampled. */ PATH_RAY_MIS_SKIP = (1U << 11U), @@ -462,6 +467,16 @@ typedef enum ShaderFlag { SHADER_EXCLUDE_ANY) } ShaderFlag; +enum EmissionSampling { + EMISSION_SAMPLING_NONE = 0, + EMISSION_SAMPLING_AUTO = 1, + EMISSION_SAMPLING_FRONT = 2, + EMISSION_SAMPLING_BACK = 3, + EMISSION_SAMPLING_FRONT_BACK = 4, + + EMISSION_SAMPLING_NUM +}; + /* Light Type */ typedef enum LightType { @@ -775,14 +790,16 @@ enum ShaderDataFlag { SD_TRANSPARENT = (1 << 9), /* BSDF requires LCG for evaluation. */ SD_BSDF_NEEDS_LCG = (1 << 10), + /* BSDF has a transmissive component. */ + SD_BSDF_HAS_TRANSMISSION = (1 << 11), SD_CLOSURE_FLAGS = (SD_EMISSION | SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSSRDF | SD_HOLDOUT | - SD_EXTINCTION | SD_SCATTER | SD_BSDF_NEEDS_LCG), + SD_EXTINCTION | SD_SCATTER | SD_BSDF_NEEDS_LCG | SD_BSDF_HAS_TRANSMISSION), /* Shader flags. */ - /* direct light sample */ - SD_USE_MIS = (1 << 16), + /* Use front side for direct light sampling. */ + SD_MIS_FRONT = (1 << 16), /* Has transparent shadow. */ SD_HAS_TRANSPARENT_SHADOW = (1 << 17), /* Has volume shader. */ @@ -811,12 +828,14 @@ enum ShaderDataFlag { SD_HAS_EMISSION = (1 << 29), /* Shader has raytracing */ SD_HAS_RAYTRACE = (1 << 30), + /* Use back side for direct light sampling. */ + SD_MIS_BACK = (1 << 31), - SD_SHADER_FLAGS = (SD_USE_MIS | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME | SD_HAS_ONLY_VOLUME | - SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP | SD_VOLUME_EQUIANGULAR | - SD_VOLUME_MIS | SD_VOLUME_CUBIC | SD_HAS_BUMP | SD_HAS_DISPLACEMENT | - SD_HAS_CONSTANT_EMISSION | SD_NEED_VOLUME_ATTRIBUTES | SD_HAS_EMISSION | - SD_HAS_RAYTRACE) + SD_SHADER_FLAGS = (SD_MIS_FRONT | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME | + SD_HAS_ONLY_VOLUME | SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP | + SD_VOLUME_EQUIANGULAR | SD_VOLUME_MIS | SD_VOLUME_CUBIC | SD_HAS_BUMP | + SD_HAS_DISPLACEMENT | SD_HAS_CONSTANT_EMISSION | SD_NEED_VOLUME_ATTRIBUTES | + SD_HAS_EMISSION | SD_HAS_RAYTRACE | SD_MIS_BACK) }; /* Object flags. */ diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index d1a3df851c1..8e831187477 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -271,7 +271,7 @@ void Geometry::tag_update(Scene *scene, bool rebuild) else { foreach (Node *node, used_shaders) { Shader *shader = static_cast(node); - if (shader->has_surface_emission) { + if (shader->emission_sampling != EMISSION_SAMPLING_NONE) { scene->light_manager->tag_update(scene, LightManager::EMISSIVE_MESH_MODIFIED); break; } diff --git a/intern/cycles/scene/light.cpp b/intern/cycles/scene/light.cpp index 3d5149cc647..108007a5dfb 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -162,7 +162,9 @@ bool Light::has_contribution(Scene *scene) if (light_type == LIGHT_BACKGROUND) { return true; } - return (shader) ? shader->has_surface_emission : scene->default_light->has_surface_emission; + + const Shader *effective_shader = (shader) ? shader : scene->default_light; + return !is_zero(effective_shader->emission_estimate); } /* Light Manager */ @@ -256,7 +258,7 @@ bool LightManager::object_usable_as_light(Object *object) */ foreach (Node *node, geom->get_used_shaders()) { Shader *shader = static_cast(node); - if (shader->get_use_mis() && shader->has_surface_emission) { + if (shader->emission_sampling != EMISSION_SAMPLING_NONE) { return true; } } @@ -295,7 +297,7 @@ void LightManager::device_update_distribution(Device *device, static_cast(mesh->get_used_shaders()[shader_index]) : scene->default_surface; - if (shader->get_use_mis() && shader->has_surface_emission) { + if (shader->emission_sampling != EMISSION_SAMPLING_NONE) { num_triangles++; } } @@ -359,7 +361,7 @@ void LightManager::device_update_distribution(Device *device, static_cast(mesh->get_used_shaders()[shader_index]) : scene->default_surface; - if (shader->get_use_mis() && shader->has_surface_emission) { + if (shader->emission_sampling != EMISSION_SAMPLING_NONE) { distribution[offset].totarea = totarea; distribution[offset].prim = i + mesh->prim_offset; distribution[offset].mesh_light.shader_flag = shader_flag; diff --git a/intern/cycles/scene/object.cpp b/intern/cycles/scene/object.cpp index 76ff670ed06..9ee196b7b45 100644 --- a/intern/cycles/scene/object.cpp +++ b/intern/cycles/scene/object.cpp @@ -231,7 +231,7 @@ void Object::tag_update(Scene *scene) foreach (Node *node, geometry->get_used_shaders()) { Shader *shader = static_cast(node); - if (shader->get_use_mis() && shader->has_surface_emission) + if (shader->emission_sampling != EMISSION_SAMPLING_NONE) scene->light_manager->tag_update(scene, LightManager::EMISSIVE_MESH_MODIFIED); } } diff --git a/intern/cycles/scene/osl.cpp b/intern/cycles/scene/osl.cpp index 39693562d34..9afd6577b10 100644 --- a/intern/cycles/scene/osl.cpp +++ b/intern/cycles/scene/osl.cpp @@ -137,7 +137,7 @@ void OSLShaderManager::device_update_specific(Device *device, compiler.compile(og, shader); }); - if (shader->get_use_mis() && shader->has_surface_emission) + if (shader->emission_sampling != EMISSION_SAMPLING_NONE) scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED); } @@ -819,8 +819,11 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath) if (current_type == SHADER_TYPE_SURFACE) { if (info) { - if (info->has_surface_emission) - current_shader->has_surface_emission = true; + if (info->has_surface_emission && node->special_type == SHADER_SPECIAL_TYPE_OSL) { + /* Will be used by Shader::estimate_emission. */ + OSLNode *oslnode = static_cast(node); + oslnode->has_emission = true; + } if (info->has_surface_transparent) current_shader->has_surface_transparent = true; if (info->has_surface_bssrdf) { @@ -1120,8 +1123,6 @@ void OSLCompiler::generate_nodes(const ShaderNodeSet &nodes) done.insert(node); if (current_type == SHADER_TYPE_SURFACE) { - if (node->has_surface_emission()) - current_shader->has_surface_emission = true; if (node->has_surface_transparent()) current_shader->has_surface_transparent = true; if (node->get_feature() & KERNEL_FEATURE_NODE_RAYTRACE) @@ -1213,7 +1214,6 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader) current_shader = shader; shader->has_surface = false; - shader->has_surface_emission = false; shader->has_surface_transparent = false; shader->has_surface_bssrdf = false; shader->has_bump = has_bump; @@ -1256,6 +1256,9 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader) } else shader->osl_displacement_ref = OSL::ShaderGroupRef(); + + /* Estimate emission for MIS. */ + shader->estimate_emission(); } /* push state to array for lookup */ diff --git a/intern/cycles/scene/shader.cpp b/intern/cycles/scene/shader.cpp index f176c19ec95..ca15d0e1c44 100644 --- a/intern/cycles/scene/shader.cpp +++ b/intern/cycles/scene/shader.cpp @@ -147,7 +147,17 @@ NODE_DEFINE(Shader) { NodeType *type = NodeType::add("shader", create); - SOCKET_BOOLEAN(use_mis, "Use MIS", true); + static NodeEnum emission_sampling_method_enum; + emission_sampling_method_enum.insert("none", EMISSION_SAMPLING_NONE); + emission_sampling_method_enum.insert("auto", EMISSION_SAMPLING_AUTO); + emission_sampling_method_enum.insert("front", EMISSION_SAMPLING_FRONT); + emission_sampling_method_enum.insert("back", EMISSION_SAMPLING_BACK); + emission_sampling_method_enum.insert("front_back", EMISSION_SAMPLING_FRONT_BACK); + SOCKET_ENUM(emission_sampling_method, + "Emission Sampling Method", + emission_sampling_method_enum, + EMISSION_SAMPLING_AUTO); + SOCKET_BOOLEAN(use_transparent_shadow, "Use Transparent Shadow", true); SOCKET_BOOLEAN(heterogeneous_volume, "Heterogeneous Volume", true); @@ -189,7 +199,6 @@ Shader::Shader() : Node(get_node_type()) has_surface = false; has_surface_transparent = false; - has_surface_emission = false; has_surface_raytrace = false; has_surface_bssrdf = false; has_volume = false; @@ -203,6 +212,10 @@ Shader::Shader() : Node(get_node_type()) has_volume_connected = false; prev_volume_step_rate = 0.0f; + emission_estimate = zero_float3(); + emission_sampling = EMISSION_SAMPLING_NONE; + emission_is_constant = true; + displacement_method = DISPLACE_BUMP; id = -1; @@ -217,50 +230,141 @@ Shader::~Shader() delete graph; } -bool Shader::is_constant_emission(float3 *emission) +static float3 output_estimate_emission(ShaderOutput *output, bool &is_constant) +{ + /* Only supports a few nodes for now, not arbitrary shader graphs. */ + ShaderNode *node = (output) ? output->parent : nullptr; + + if (node == nullptr) { + return zero_float3(); + } + else if (node->type == EmissionNode::get_node_type() || + node->type == BackgroundNode::get_node_type()) { + /* Emission and Background node. */ + ShaderInput *color_in = node->input("Color"); + ShaderInput *strength_in = node->input("Strength"); + + float3 estimate = one_float3(); + + if (color_in->link) { + is_constant = false; + } + else { + estimate *= node->get_float3(color_in->socket_type); + } + + if (strength_in->link) { + is_constant = false; + estimate *= output_estimate_emission(strength_in->link, is_constant); + } + else { + estimate *= node->get_float(strength_in->socket_type); + } + + return estimate; + } + else if (node->type == LightFalloffNode::get_node_type()) { + /* Light Falloff node. */ + ShaderInput *strength_in = node->input("Strength"); + is_constant = false; + + return (strength_in->link) ? output_estimate_emission(strength_in->link, is_constant) : + make_float3(node->get_float(strength_in->socket_type)); + } + else if (node->type == AddClosureNode::get_node_type()) { + /* Add Closure. */ + ShaderInput *closure1_in = node->input("Closure1"); + ShaderInput *closure2_in = node->input("Closure2"); + + const float3 estimate1 = (closure1_in->link) ? + output_estimate_emission(closure1_in->link, is_constant) : + zero_float3(); + const float3 estimate2 = (closure2_in->link) ? + output_estimate_emission(closure2_in->link, is_constant) : + zero_float3(); + + return estimate1 + estimate2; + } + else if (node->type == MixClosureNode::get_node_type()) { + /* Mix Closure. */ + ShaderInput *fac_in = node->input("Fac"); + ShaderInput *closure1_in = node->input("Closure1"); + ShaderInput *closure2_in = node->input("Closure2"); + + const float3 estimate1 = (closure1_in->link) ? + output_estimate_emission(closure1_in->link, is_constant) : + zero_float3(); + const float3 estimate2 = (closure2_in->link) ? + output_estimate_emission(closure2_in->link, is_constant) : + zero_float3(); + + if (fac_in->link) { + is_constant = false; + return estimate1 + estimate2; + } + else { + const float fac = node->get_float(fac_in->socket_type); + return (1.0f - fac) * estimate1 + fac * estimate2; + } + } + else { + /* Other nodes, potentially OSL nodes with arbitrary code for which all we can + * determine is if it has emission or not. */ + const bool has_emission = node->has_surface_emission(); + float3 estimate; + + if (output->type() == SocketType::CLOSURE) { + if (has_emission) { + estimate = one_float3(); + is_constant = false; + } + else { + estimate = zero_float3(); + } + + foreach (const ShaderInput *in, node->inputs) { + if (in->type() == SocketType::CLOSURE && in->link) { + estimate += output_estimate_emission(in->link, is_constant); + } + } + } + else { + estimate = one_float3(); + is_constant = false; + } + + return estimate; + } +} + +void Shader::estimate_emission() { /* If the shader has AOVs, they need to be evaluated, so we can't skip the shader. */ + emission_is_constant = true; + foreach (ShaderNode *node, graph->nodes) { if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) { - return false; + emission_is_constant = false; } } ShaderInput *surf = graph->output()->input("Surface"); + emission_estimate = output_estimate_emission(surf->link, emission_is_constant); - if (surf->link == NULL) { - return false; + if (is_zero(emission_estimate)) { + emission_sampling = EMISSION_SAMPLING_NONE; } - - if (surf->link->parent->type == EmissionNode::get_node_type()) { - EmissionNode *node = (EmissionNode *)surf->link->parent; - - assert(node->input("Color")); - assert(node->input("Strength")); - - if (node->input("Color")->link || node->input("Strength")->link) { - return false; - } - - *emission = node->get_color() * node->get_strength(); - } - else if (surf->link->parent->type == BackgroundNode::get_node_type()) { - BackgroundNode *node = (BackgroundNode *)surf->link->parent; - - assert(node->input("Color")); - assert(node->input("Strength")); - - if (node->input("Color")->link || node->input("Strength")->link) { - return false; - } - - *emission = node->get_color() * node->get_strength(); + else if (emission_sampling_method == EMISSION_SAMPLING_AUTO) { + /* Automatically disable MIS when emission is low, to avoid weakly emitting + * using a lot of memory in the light tree and potentially wasting samples + * where indirect light samples are sufficient. + * Possible optimization: estimate front and back emission separately. */ + emission_sampling = (reduce_max(emission_estimate) > 0.5f) ? EMISSION_SAMPLING_FRONT_BACK : + EMISSION_SAMPLING_NONE; } else { - return false; + emission_sampling = emission_sampling_method; } - - return true; } void Shader::set_graph(ShaderGraph *graph_) @@ -305,7 +409,7 @@ void Shader::tag_update(Scene *scene) /* if the shader previously was emissive, update light distribution, * if the new shader is emissive, a light manager update tag will be * done in the shader manager device update. */ - if (use_mis && has_surface_emission) + if (emission_sampling != EMISSION_SAMPLING_NONE) scene->light_manager->tag_update(scene, LightManager::SHADER_MODIFIED); /* Special handle of background MIS light for now: for some reason it @@ -491,9 +595,17 @@ void ShaderManager::device_update_common(Device * /*device*/, foreach (Shader *shader, scene->shaders) { uint flag = 0; - if (shader->get_use_mis()) - flag |= SD_USE_MIS; - if (shader->has_surface_emission) + if (shader->emission_sampling == EMISSION_SAMPLING_FRONT) { + flag |= SD_MIS_FRONT; + } + else if (shader->emission_sampling == EMISSION_SAMPLING_BACK) { + flag |= SD_MIS_BACK; + } + else if (shader->emission_sampling == EMISSION_SAMPLING_FRONT_BACK) { + flag |= SD_MIS_FRONT | SD_MIS_BACK; + } + + if (!is_zero(shader->emission_estimate)) flag |= SD_HAS_EMISSION; if (shader->has_surface_transparent && shader->get_use_transparent_shadow()) flag |= SD_HAS_TRANSPARENT_SHADOW; @@ -531,8 +643,7 @@ void ShaderManager::device_update_common(Device * /*device*/, flag |= SD_HAS_DISPLACEMENT; /* constant emission check */ - float3 constant_emission = zero_float3(); - if (shader->is_constant_emission(&constant_emission)) + if (shader->emission_is_constant) flag |= SD_HAS_CONSTANT_EMISSION; uint32_t cryptomatte_id = util_murmur_hash3(shader->name.c_str(), shader->name.length(), 0); @@ -540,9 +651,9 @@ void ShaderManager::device_update_common(Device * /*device*/, /* regular shader */ kshader->flags = flag; kshader->pass_id = shader->get_pass_id(); - kshader->constant_emission[0] = constant_emission.x; - kshader->constant_emission[1] = constant_emission.y; - kshader->constant_emission[2] = constant_emission.z; + kshader->constant_emission[0] = shader->emission_estimate.x; + kshader->constant_emission[1] = shader->emission_estimate.y; + kshader->constant_emission[2] = shader->emission_estimate.z; kshader->cryptomatte_id = util_hash_to_float(cryptomatte_id); kshader++; @@ -627,8 +738,8 @@ void ShaderManager::add_default(Scene *scene) shader->set_graph(graph); scene->default_volume = shader; shader->tag_update(scene); - /* No default reference for the volume to avoid compiling volume kernels if there are no actual - * volumes in the scene */ + /* No default reference for the volume to avoid compiling volume kernels if there are no + * actual volumes in the scene */ } /* default light */ diff --git a/intern/cycles/scene/shader.h b/intern/cycles/scene/shader.h index 69b22d2ad19..8f59eefae05 100644 --- a/intern/cycles/scene/shader.h +++ b/intern/cycles/scene/shader.h @@ -34,6 +34,7 @@ struct float3; enum ShadingSystem { SHADINGSYSTEM_OSL, SHADINGSYSTEM_SVM }; /* Keep those in sync with the python-defined enum. */ + enum VolumeSampling { VOLUME_SAMPLING_DISTANCE = 0, VOLUME_SAMPLING_EQUIANGULAR = 1, @@ -73,7 +74,7 @@ class Shader : public Node { NODE_SOCKET_API(int, pass_id) /* sampling */ - NODE_SOCKET_API(bool, use_mis) + NODE_SOCKET_API(EmissionSampling, emission_sampling_method) NODE_SOCKET_API(bool, use_transparent_shadow) NODE_SOCKET_API(bool, heterogeneous_volume) NODE_SOCKET_API(VolumeSampling, volume_sampling_method) @@ -101,7 +102,6 @@ class Shader : public Node { /* information about shader after compiling */ bool has_surface; - bool has_surface_emission; bool has_surface_transparent; bool has_surface_raytrace; bool has_volume; @@ -114,6 +114,10 @@ class Shader : public Node { bool has_volume_attribute_dependency; bool has_integrator_dependency; + float3 emission_estimate; + EmissionSampling emission_sampling; + bool emission_is_constant; + /* requested mesh attributes */ AttributeRequestSet attributes; @@ -131,11 +135,12 @@ class Shader : public Node { Shader(); ~Shader(); - /* Checks whether the shader consists of just a emission node with fixed inputs that's connected - * directly to the output. - * If yes, it sets the content of emission to the constant value (color * strength), which is - * then used for speeding up light evaluation. */ - bool is_constant_emission(float3 *emission); + /* Estimate emission of this shader based on the shader graph. This works only in very simple + * cases. But it helps improve light importance sampling in common cases. + * + * If the emission is fully constant, returns true, so that shader evaluation can be skipped + * entirely for a light. */ + void estimate_emission(); void set_graph(ShaderGraph *graph); void tag_update(Scene *scene); diff --git a/intern/cycles/scene/shader_graph.h b/intern/cycles/scene/shader_graph.h index afe841f0d11..fb4f8f71a29 100644 --- a/intern/cycles/scene/shader_graph.h +++ b/intern/cycles/scene/shader_graph.h @@ -74,15 +74,15 @@ class ShaderInput { { } - ustring name() + ustring name() const { return socket_type.ui_name; } - int flags() + int flags() const { return socket_type.flags; } - SocketType::Type type() + SocketType::Type type() const { return socket_type.type; } @@ -119,11 +119,11 @@ class ShaderOutput { { } - ustring name() + ustring name() const { return socket_type.ui_name; } - SocketType::Type type() + SocketType::Type type() const { return socket_type.type; } diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index c1189e3795c..f032c52c1af 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -7211,6 +7211,7 @@ void SetNormalNode::compile(OSLCompiler &compiler) OSLNode::OSLNode() : ShaderNode(new NodeType(NodeType::SHADER)) { special_type = SHADER_SPECIAL_TYPE_OSL; + has_emission = false; } OSLNode::~OSLNode() diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index de152fc6f56..fa9210ff5cc 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -1530,6 +1530,11 @@ class OSLNode final : public ShaderNode { SHADER_NODE_NO_CLONE_CLASS(OSLNode) + bool has_surface_emission() + { + return has_emission; + } + /* Ideally we could better detect this, but we can't query this now. */ bool has_spatial_varying() { @@ -1551,6 +1556,7 @@ class OSLNode final : public ShaderNode { string filepath; string bytecode_hash; + bool has_emission; }; class NormalMapNode : public ShaderNode { diff --git a/intern/cycles/scene/svm.cpp b/intern/cycles/scene/svm.cpp index ede3f87e7e3..0a71ed962b0 100644 --- a/intern/cycles/scene/svm.cpp +++ b/intern/cycles/scene/svm.cpp @@ -109,7 +109,7 @@ void SVMShaderManager::device_update_specific(Device *device, Shader *shader = scene->shaders[i]; shader->clear_modified(); - if (shader->get_use_mis() && shader->has_surface_emission) { + if (shader->emission_sampling != EMISSION_SAMPLING_NONE) { scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED); } @@ -516,8 +516,6 @@ void SVMCompiler::generate_closure_node(ShaderNode *node, CompilerState *state) mix_weight_offset = SVM_STACK_INVALID; if (current_type == SHADER_TYPE_SURFACE) { - if (node->has_surface_emission()) - current_shader->has_surface_emission = true; if (node->has_surface_transparent()) current_shader->has_surface_transparent = true; if (node->has_surface_bssrdf()) { @@ -873,7 +871,6 @@ void SVMCompiler::compile(Shader *shader, array &svm_nodes, int index, Sum current_shader = shader; shader->has_surface = false; - shader->has_surface_emission = false; shader->has_surface_transparent = false; shader->has_surface_raytrace = false; shader->has_surface_bssrdf = false; @@ -928,6 +925,9 @@ void SVMCompiler::compile(Shader *shader, array &svm_nodes, int index, Sum summary->peak_stack_usage = max_stack_use; summary->num_svm_nodes = svm_nodes.size() - start_num_svm_nodes; } + + /* Estimate emission for MIS. */ + shader->estimate_emission(); } /* Compiler summary implementation. */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 5709891893b..6555eb07d78 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -25,7 +25,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 2 +#define BLENDER_FILE_SUBVERSION 3 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file