From 5fa68133c986be521e06b1f2558a33e56d27b98b Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 7 Jun 2014 18:47:14 +0200 Subject: [PATCH] Cycles: volume sampling method can now be set per material/world. This gives you "Multiple Importance", "Distance" and "Equiangular" choices. What multiple importance sampling does is make things more robust to certain types of noise at the cost of a bit more noise in cases where the individual strategies are always better. So if you've got a pretty dense volume that's lit from far away then distance sampling is usually more efficient. If you've got a light inside or near the volume then equiangular sampling is better. If you have a combination of both, then the multiple importance sampling will be better. --- intern/cycles/blender/addon/properties.py | 29 ++++++++------ intern/cycles/blender/addon/ui.py | 46 +++++++++++++---------- intern/cycles/blender/blender_shader.cpp | 2 + intern/cycles/blender/blender_sync.cpp | 1 - intern/cycles/kernel/kernel_path.h | 13 ++++++- intern/cycles/kernel/kernel_types.h | 16 +++++--- intern/cycles/kernel/kernel_volume.h | 45 +++++++++++++++++++--- intern/cycles/render/integrator.cpp | 5 --- intern/cycles/render/shader.cpp | 5 +++ intern/cycles/render/shader.h | 1 + 10 files changed, 112 insertions(+), 51 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 12babd95ed8..1a2d264e12d 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -108,10 +108,10 @@ enum_integrator = ( ('PATH', "Path Tracing", "Pure path tracing integrator"), ) -enum_volume_homogeneous_sampling = ( - ('DISTANCE', "Distance", "Use Distance Sampling"), - ('EQUI_ANGULAR', "Equi-angular", "Use Equi-angular Sampling"), - ('MULTIPLE_IMPORTANCE', "Multiple Importance", "Combine distance and equi-angular sampling"), +enum_volume_sampling = ( + ('DISTANCE', "Distance", "Use distance sampling, best for dense volumes with lights far away"), + ('EQUIANGULAR', "Equiangular", "Use equiangular sampling, best for volumes with low density with light inside or near the volume"), + ('MULTIPLE_IMPORTANCE', "Multiple Importance", "Combine distance and equi-angular sampling for volumes where neither method is ideal"), ) @@ -147,13 +147,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): default='PATH', ) - cls.volume_homogeneous_sampling = EnumProperty( - name="Homogeneous Sampling", - description="Sampling method to use for homogeneous volumes", - items=enum_volume_homogeneous_sampling, - default='DISTANCE', - ) - cls.use_square_samples = BoolProperty( name="Square Samples", description="Square sampling values for easier artist control", @@ -237,7 +230,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): name="Volume Samples", description="Number of volume scattering samples to render for each AA sample", min=1, max=10000, - default=1, + default=0, ) cls.sampling_pattern = EnumProperty( @@ -603,6 +596,12 @@ class CyclesMaterialSettings(bpy.types.PropertyGroup): "(not using any textures), for faster rendering", default=False, ) + cls.volume_sampling = EnumProperty( + name="Volume Sampling", + description="Sampling method to use for volumes", + items=enum_volume_sampling, + default='DISTANCE', + ) @classmethod def unregister(cls): @@ -673,6 +672,12 @@ class CyclesWorldSettings(bpy.types.PropertyGroup): "(not using any textures), for faster rendering", default=False, ) + cls.volume_sampling = EnumProperty( + name="Volume Sampling", + description="Sampling method to use for volumes", + items=enum_volume_sampling, + default='EQUIANGULAR', + ) @classmethod def unregister(cls): diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index a6a3501b912..9dd5a4e81ce 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -176,16 +176,11 @@ class CyclesRender_PT_volume_sampling(CyclesButtonsPanel, Panel): scene = context.scene cscene = scene.cycles - split = layout.split(align=True) - - sub = split.column(align=True) - sub.label("Heterogeneous:") - sub.prop(cscene, "volume_step_size") - sub.prop(cscene, "volume_max_steps") - - sub = split.column(align=True) - sub.label("Homogeneous:") - sub.prop(cscene, "volume_homogeneous_sampling", text="") + row = layout.row() + row.label("Heterogeneous:") + row = layout.row() + row.prop(cscene, "volume_step_size") + row.prop(cscene, "volume_max_steps") class CyclesRender_PT_light_paths(CyclesButtonsPanel, Panel): @@ -829,8 +824,6 @@ class CyclesWorld_PT_volume(CyclesButtonsPanel, Panel): world = context.world panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Volume') - layout.prop(world.cycles, "homogeneous_volume") - class CyclesWorld_PT_ambient_occlusion(CyclesButtonsPanel, Panel): bl_label = "Ambient Occlusion" @@ -922,15 +915,24 @@ class CyclesWorld_PT_settings(CyclesButtonsPanel, Panel): cworld = world.cycles cscene = context.scene.cycles - col = layout.column() + split = layout.split() - col.prop(cworld, "sample_as_light") - sub = col.row(align=True) + col = split.column() + + col.label(text="Surface:") + col.prop(cworld, "sample_as_light", text="Multiple Importance") + + sub = col.column(align=True) sub.active = cworld.sample_as_light sub.prop(cworld, "sample_map_resolution") if cscene.progressive == 'BRANCHED_PATH': sub.prop(cworld, "samples") + col = split.column() + col.label(text="Volume:") + col.prop(cworld, "volume_sampling", text="") + col.prop(cworld, "homogeneous_volume", text="Homogeneous") + class CyclesMaterial_PT_preview(CyclesButtonsPanel, Panel): bl_label = "Preview" @@ -979,8 +981,6 @@ class CyclesMaterial_PT_volume(CyclesButtonsPanel, Panel): panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Volume') - layout.prop(cmat, "homogeneous_volume") - class CyclesMaterial_PT_displacement(CyclesButtonsPanel, Panel): bl_label = "Displacement" @@ -1023,10 +1023,18 @@ class CyclesMaterial_PT_settings(CyclesButtonsPanel, Panel): col.label() col.prop(mat, "pass_index") - col = layout.column() - col.prop(cmat, "sample_as_light") + split = layout.split() + + col = split.column() + col.label(text="Surface:") + col.prop(cmat, "sample_as_light", text="Multiple Importance") col.prop(cmat, "use_transparent_shadow") + col = split.column() + col.label(text="Volume:") + col.prop(cmat, "volume_sampling", text="") + col.prop(cmat, "homogeneous_volume", text="Homogeneous") + class CyclesTexture_PT_context(CyclesButtonsPanel, Panel): bl_label = "" diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index bce236530b9..3b783d36b1e 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -977,6 +977,7 @@ void BlenderSync::sync_materials(bool update_all) shader->use_mis = get_boolean(cmat, "sample_as_light"); shader->use_transparent_shadow = get_boolean(cmat, "use_transparent_shadow"); shader->heterogeneous_volume = !get_boolean(cmat, "homogeneous_volume"); + shader->volume_sampling_method = RNA_enum_get(&cmat, "volume_sampling"); shader->set_graph(graph); shader->tag_update(scene); @@ -1006,6 +1007,7 @@ void BlenderSync::sync_world(bool update_all) /* volume */ PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles"); shader->heterogeneous_volume = !get_boolean(cworld, "homogeneous_volume"); + shader->volume_sampling_method = RNA_enum_get(&cworld, "volume_sampling"); } else if(b_world) { ShaderNode *closure, *out; diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index bdb3e20632f..042bbca9497 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -177,7 +177,6 @@ void BlenderSync::sync_integrator() integrator->transparent_min_bounce = get_int(cscene, "transparent_min_bounces"); integrator->transparent_shadows = get_boolean(cscene, "use_transparent_shadows"); - integrator->volume_homogeneous_sampling = RNA_enum_get(&cscene, "volume_homogeneous_sampling"); integrator->volume_max_steps = get_int(cscene, "volume_max_steps"); integrator->volume_step_size = get_float(cscene, "volume_step_size"); diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index 22024483bfc..d9ad348a952 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -89,7 +89,8 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, Ray ray, volume_ray.t = (hit)? isect.t: FLT_MAX; bool heterogeneous = volume_stack_is_heterogeneous(kg, state.volume_stack); - bool decoupled = kernel_volume_use_decoupled(kg, heterogeneous, false); + int sampling_method = volume_stack_sampling_method(kg, state.volume_stack); + bool decoupled = kernel_volume_use_decoupled(kg, heterogeneous, false, sampling_method); if(decoupled) { /* cache steps along volume for repeated sampling */ @@ -99,6 +100,8 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, Ray ray, shader_setup_from_volume(kg, &volume_sd, &volume_ray, state.bounce, state.transparent_bounce); kernel_volume_decoupled_record(kg, &state, &volume_ray, &volume_sd, &volume_segment, heterogeneous); + + volume_segment.sampling_method = sampling_method; /* emission */ if(volume_segment.closure_flag & SD_EMISSION) @@ -468,7 +471,8 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample, volume_ray.t = (hit)? isect.t: FLT_MAX; bool heterogeneous = volume_stack_is_heterogeneous(kg, state.volume_stack); - bool decoupled = kernel_volume_use_decoupled(kg, heterogeneous, true); + int sampling_method = volume_stack_sampling_method(kg, state.volume_stack); + bool decoupled = kernel_volume_use_decoupled(kg, heterogeneous, true, sampling_method); if(decoupled) { /* cache steps along volume for repeated sampling */ @@ -479,6 +483,8 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample, kernel_volume_decoupled_record(kg, &state, &volume_ray, &volume_sd, &volume_segment, heterogeneous); + volume_segment.sampling_method = sampling_method; + /* emission */ if(volume_segment.closure_flag & SD_EMISSION) path_radiance_accum_emission(&L, throughput, volume_segment.accum_emission, state.bounce); @@ -814,7 +820,10 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in /* direct light sampling */ if(volume_segment.closure_flag & SD_SCATTER) { + volume_segment.sampling_method = volume_stack_sampling_method(kg, state.volume_stack); + bool all = kernel_data.integrator.sample_all_lights_direct; + kernel_branched_path_volume_connect_light(kg, rng, &volume_sd, throughput, &state, &L, 1.0f, all, &volume_ray, &volume_segment); diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index f5c73b72c49..3ea39882540 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -577,7 +577,8 @@ enum ShaderDataFlag { SD_AO = 512, /* have ao closure? */ SD_TRANSPARENT = 1024, /* have transparent closure? */ - SD_CLOSURE_FLAGS = (SD_EMISSION|SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY|SD_BSSRDF|SD_HOLDOUT|SD_ABSORPTION|SD_SCATTER|SD_AO), + SD_CLOSURE_FLAGS = (SD_EMISSION|SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY| + SD_BSSRDF|SD_HOLDOUT|SD_ABSORPTION|SD_SCATTER|SD_AO), /* shader flags */ SD_USE_MIS = 2048, /* direct light sample */ @@ -586,13 +587,17 @@ enum ShaderDataFlag { SD_HAS_ONLY_VOLUME = 16384, /* has only volume shader, no surface */ SD_HETEROGENEOUS_VOLUME = 32768, /* has heterogeneous volume */ SD_HAS_BSSRDF_BUMP = 65536, /* bssrdf normal uses bump */ + SD_VOLUME_EQUIANGULAR = 131072, /* use equiangular sampling */ + SD_VOLUME_MIS = 262144, /* use multiple importance sampling */ - 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_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), /* object flags */ - SD_HOLDOUT_MASK = 131072, /* holdout for camera rays */ - SD_OBJECT_MOTION = 262144, /* has object motion blur */ - SD_TRANSFORM_APPLIED = 524288, /* vertices have transform applied */ + SD_HOLDOUT_MASK = 524288, /* holdout for camera rays */ + SD_OBJECT_MOTION = 1048576, /* has object motion blur */ + SD_TRANSFORM_APPLIED = 2097152, /* vertices have transform applied */ SD_OBJECT_FLAGS = (SD_HOLDOUT_MASK|SD_OBJECT_MOTION|SD_TRANSFORM_APPLIED) }; @@ -894,7 +899,6 @@ typedef struct KernelIntegrator { int aa_samples; /* volume render */ - int volume_homogeneous_sampling; int use_volumes; int volume_max_steps; float volume_step_size; diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h index 61e4989147b..e3de0ff90c8 100644 --- a/intern/cycles/kernel/kernel_volume.h +++ b/intern/cycles/kernel/kernel_volume.h @@ -116,6 +116,36 @@ ccl_device bool volume_stack_is_heterogeneous(KernelGlobals *kg, VolumeStack *st return false; } +ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stack) +{ + if(kernel_data.integrator.num_all_lights == 0) + return 0; + + int method = -1; + + for(int i = 0; stack[i].shader != SHADER_NONE; i++) { + int shader_flag = kernel_tex_fetch(__shader_flag, (stack[i].shader & SHADER_MASK)*2); + + if(shader_flag & SD_VOLUME_MIS) { + return SD_VOLUME_MIS; + } + else if(shader_flag & SD_VOLUME_EQUIANGULAR) { + if(method == 0) + return SD_VOLUME_MIS; + + method = SD_VOLUME_EQUIANGULAR; + } + else { + if(method == SD_VOLUME_EQUIANGULAR) + return SD_VOLUME_MIS; + + method = 0; + } + } + + return method; +} + /* Volume Shadows * * These functions are used to attenuate shadow rays to lights. Both absorption @@ -562,6 +592,8 @@ typedef struct VolumeSegment { float3 accum_emission; /* accumulated emission at end of segment */ float3 accum_transmittance; /* accumulated transmittance at end of segment */ + + int sampling_method; /* volume sampling method */ } VolumeSegment; /* record volume steps to the end of the volume. @@ -709,6 +741,8 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter( { int closure_flag = segment->closure_flag; + /* XXX add probalistic scattering! */ + if(!(closure_flag & SD_SCATTER)) return VOLUME_PATH_MISSED; @@ -737,8 +771,8 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter( bool distance_sample = true; bool use_mis = false; - if(kernel_data.integrator.volume_homogeneous_sampling && light_P) { - if(kernel_data.integrator.volume_homogeneous_sampling == 2) { + if(segment->sampling_method && light_P) { + if(segment->sampling_method == SD_VOLUME_MIS) { /* multiple importance sample: randomly pick between * equiangular and distance sampling strategy */ if(xi < 0.5f) { @@ -872,7 +906,7 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter( } /* decide if we need to use decoupled or not */ -ccl_device bool kernel_volume_use_decoupled(KernelGlobals *kg, bool heterogeneous, bool direct) +ccl_device bool kernel_volume_use_decoupled(KernelGlobals *kg, bool heterogeneous, bool direct, int sampling_method) { /* decoupled ray marching for heterogenous volumes not supported on the GPU, * which also means equiangular and multiple importance sampling is not @@ -882,9 +916,8 @@ ccl_device bool kernel_volume_use_decoupled(KernelGlobals *kg, bool heterogeneou return false; #endif - /* equiangular sampling only implemented for decoupled */ - bool equiangular = kernel_data.integrator.volume_homogeneous_sampling != 0; - if(equiangular) + /* equiangular and multiple importance sampling only implemented for decoupled */ + if(sampling_method != 0) return true; /* for all light sampling use decoupled, reusing shader evaluations is diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp index ee3419b055c..4a8b490b1ad 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/render/integrator.cpp @@ -101,11 +101,6 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene if(!transparent_shadows) kintegrator->transparent_shadows = false; - if(kintegrator->num_all_lights > 0) - kintegrator->volume_homogeneous_sampling = volume_homogeneous_sampling; - else - kintegrator->volume_homogeneous_sampling = 0; - kintegrator->volume_max_steps = volume_max_steps; kintegrator->volume_step_size = volume_step_size; diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index 0f9a5a5d39d..662caed72f1 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -44,6 +44,7 @@ Shader::Shader() use_mis = true; use_transparent_shadow = true; heterogeneous_volume = true; + volume_sampling_method = 0; has_surface = false; has_surface_transparent = false; @@ -257,6 +258,10 @@ void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Sc flag |= SD_HAS_BSSRDF_BUMP; if(shader->has_converter_blackbody) has_converter_blackbody = true; + if(shader->volume_sampling_method == 1) + flag |= SD_VOLUME_EQUIANGULAR; + if(shader->volume_sampling_method == 2) + flag |= SD_VOLUME_MIS; /* regular shader */ shader_flag[i++] = flag; diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h index dc18b385cc3..84be4b469d8 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/render/shader.h @@ -68,6 +68,7 @@ public: bool use_mis; bool use_transparent_shadow; bool heterogeneous_volume; + int volume_sampling_method; /* synchronization */ bool need_update;