Cycles: new setting and heuristics for mesh light importance sampling
Materials now have an enum to set the emission sampling method, to be either None, Auto, Front, Back or Front & Back. This replace the previous "Multiple Importance Sample" option. Auto is the new default, and uses a heuristic to estimate the emitted light intensity to determine of the mesh should be considered as a light for sampling. Shaders sometimes have a bit of emission but treating them as a light source is not worth the memory/performance overhead. The Front/Back settings are not important yet, but will help when a light tree is added. In that case setting emission to Front only on closed meshes can help ignore emission from inside the mesh interior that does not contribute anything. Includes contributions by Brecht Van Lommel and Alaska. Ref T77889
This commit is contained in:
parent
ac51d331df
commit
396b407c7d
Notes:
blender-bot
2023-02-14 10:21:15 +01:00
Referenced by issue #77889, Cycles: Many Lights Sampling
|
@ -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, "
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -271,7 +271,7 @@ void Geometry::tag_update(Scene *scene, bool rebuild)
|
|||
else {
|
||||
foreach (Node *node, used_shaders) {
|
||||
Shader *shader = static_cast<Shader *>(node);
|
||||
if (shader->has_surface_emission) {
|
||||
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
|
||||
scene->light_manager->tag_update(scene, LightManager::EMISSIVE_MESH_MODIFIED);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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<Shader *>(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<Shader *>(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<Shader *>(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;
|
||||
|
|
|
@ -231,7 +231,7 @@ void Object::tag_update(Scene *scene)
|
|||
|
||||
foreach (Node *node, geometry->get_used_shaders()) {
|
||||
Shader *shader = static_cast<Shader *>(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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<OSLNode *>(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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<int4> &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<int4> &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. */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue