Cycles: Option to Sample all Lights in the Branched Path integrator for indirect...
authorThomas Dinges <blender@dingto.org>
Sun, 9 Mar 2014 21:19:27 +0000 (22:19 +0100)
committerThomas Dinges <blender@dingto.org>
Sun, 9 Mar 2014 21:20:01 +0000 (22:20 +0100)
This adds a new option "Sample All Lights" to the Sampling panel in Cycles (Branched Path). When enabled, Cycles will sample all the lights in the scene for the indirect samples, instead of randomly picking one. This is already happening for direct samples, now you can optionally enable it for indirect.

Example file and renders:
Blend file: http://www.pasteall.org/blend/27411
Random: http://www.pasteall.org/pic/show.php?id=68033
All: http://www.pasteall.org/pic/show.php?id=68034

Sampling all lights is a bit slower, but there is less variance, so it should help in situations with many lights.

Patch by myself with some tweaks by Brecht.
Differential Revision: https://developer.blender.org/D391

intern/cycles/blender/addon/properties.py
intern/cycles/blender/addon/ui.py
intern/cycles/blender/blender_sync.cpp
intern/cycles/kernel/kernel_path.h
intern/cycles/kernel/kernel_types.h
intern/cycles/render/integrator.cpp
intern/cycles/render/integrator.h

index e267ea48b736d32cd896dc58e3d177f77243f17f..875f9d36d6393949cac29d9f241f7fbf7b49431e 100644 (file)
@@ -252,6 +252,11 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
                 items=enum_use_layer_samples,
                 default='USE',
                 )
+        cls.sample_all_lights_indirect = BoolProperty(
+                name="Sample All Lights",
+                description="Sample all lights (for indirect samples), rather than randomly picking one",
+                default=False,
+                )
 
         cls.no_caustics = BoolProperty(
                 name="No Caustics",
index a3138266b20683bee3098ac24d0c07515165d9d9..da334e208b79615653dcd8c833125ec491e69ffa 100644 (file)
@@ -133,6 +133,8 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel):
             sub.label(text="AA Samples:")
             sub.prop(cscene, "aa_samples", text="Render")
             sub.prop(cscene, "preview_aa_samples", text="Preview")
+            sub.separator()
+            sub.prop(cscene, "sample_all_lights_indirect")
 
             col = split.column()
             sub = col.column(align=True)
index 1d507ed9b1dbbeff5d68a80ebe2ddbce6534f5b0..202eff48d534baff6b5a2a534349a0c2e59992bf 100644 (file)
@@ -197,6 +197,8 @@ void BlenderSync::sync_integrator()
 #endif
 
        integrator->method = (Integrator::Method)get_enum(cscene, "progressive");
+       
+       integrator->sample_all_lights_indirect = get_boolean(cscene, "sample_all_lights_indirect");
 
        int diffuse_samples = get_int(cscene, "diffuse_samples");
        int glossy_samples = get_int(cscene, "glossy_samples");
index 635201471e125e49eeaba64839b33a85f44528c0..000b241f544fea9fcf44ab2cd8eff4e2d5338308 100644 (file)
@@ -133,6 +133,93 @@ ccl_device_inline bool kernel_path_integrate_scatter_lighting(KernelGlobals *kg,
 
 #if defined(__BRANCHED_PATH__) || defined(__SUBSURFACE__)
 
+ccl_device void kernel_branched_path_integrate_direct_lighting(KernelGlobals *kg, RNG *rng,
+       ShaderData *sd, PathState *state, float3 throughput, float num_samples_adjust, PathRadiance *L, bool sample_all_lights)
+{
+       /* sample illumination from lights to find path contribution */
+       if(sd->flag & SD_BSDF_HAS_EVAL) {
+               Ray light_ray;
+               BsdfEval L_light;
+               bool is_lamp;
+
+#ifdef __OBJECT_MOTION__
+               light_ray.time = sd->time;
+#endif
+
+               if(sample_all_lights) {
+                       /* lamp sampling */
+                       for(int i = 0; i < kernel_data.integrator.num_all_lights; i++) {
+                               int num_samples = ceil_to_int(num_samples_adjust*light_select_num_samples(kg, i));
+                               float num_samples_inv = num_samples_adjust/(num_samples*kernel_data.integrator.num_all_lights);
+                               RNG lamp_rng = cmj_hash(*rng, i);
+
+                               if(kernel_data.integrator.pdf_triangles != 0.0f)
+                                       num_samples_inv *= 0.5f;
+
+                               for(int j = 0; j < num_samples; j++) {
+                                       float light_u, light_v;
+                                       path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
+
+                                       if(direct_emission(kg, sd, i, 0.0f, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp, state->bounce)) {
+                                               /* trace shadow ray */
+                                               float3 shadow;
+
+                                               if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+                                                       /* accumulate */
+                                                       path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
+                                               }
+                                       }
+                               }
+                       }
+
+                       /* mesh light sampling */
+                       if(kernel_data.integrator.pdf_triangles != 0.0f) {
+                               int num_samples = ceil_to_int(num_samples_adjust*kernel_data.integrator.mesh_light_samples);
+                               float num_samples_inv = num_samples_adjust/num_samples;
+
+                               if(kernel_data.integrator.num_all_lights)
+                                       num_samples_inv *= 0.5f;
+
+                               for(int j = 0; j < num_samples; j++) {
+                                       float light_t = path_branched_rng_1D(kg, rng, state, j, num_samples, PRNG_LIGHT);
+                                       float light_u, light_v;
+                                       path_branched_rng_2D(kg, rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
+
+                                       /* only sample triangle lights */
+                                       if(kernel_data.integrator.num_all_lights)
+                                               light_t = 0.5f*light_t;
+
+                                       if(direct_emission(kg, sd, -1, light_t, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp, state->bounce)) {
+                                               /* trace shadow ray */
+                                               float3 shadow;
+
+                                               if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+                                                       /* accumulate */
+                                                       path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
+                                               }
+                                       }
+                               }
+                       }
+               }
+               else {
+                       float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
+                       float light_u, light_v;
+                       path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
+
+                       /* sample random light */
+                       if(direct_emission(kg, sd, -1, light_t, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp, state->bounce)) {
+                               /* trace shadow ray */
+                               float3 shadow;
+
+                               if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+                                       /* accumulate */
+                                       path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
+                               }
+                       }
+               }
+       }
+}
+
 ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, Ray ray, ccl_global float *buffer,
        float3 throughput, int num_samples, PathState state, PathRadiance *L)
 {
@@ -302,36 +389,8 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, Ray ray, ccl_g
 
 #ifdef __EMISSION__
                if(kernel_data.integrator.use_direct_light) {
-                       /* sample illumination from lights to find path contribution */
-                       if(sd.flag & SD_BSDF_HAS_EVAL) {
-                               float light_t = path_state_rng_1D(kg, rng, &state, PRNG_LIGHT);
-#ifdef __MULTI_CLOSURE__
-                               float light_o = 0.0f;
-#else
-                               float light_o = path_state_rng_1D(kg, rng, &state, PRNG_LIGHT_F);
-#endif
-                               float light_u, light_v;
-                               path_state_rng_2D(kg, rng, &state, PRNG_LIGHT_U, &light_u, &light_v);
-
-                               Ray light_ray;
-                               BsdfEval L_light;
-                               bool is_lamp;
-
-#ifdef __OBJECT_MOTION__
-                               light_ray.time = sd.time;
-#endif
-
-                               /* sample random light */
-                               if(direct_emission(kg, &sd, -1, light_t, light_o, light_u, light_v, &light_ray, &L_light, &is_lamp, state.bounce)) {
-                                       /* trace shadow ray */
-                                       float3 shadow;
-
-                                       if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
-                                               /* accumulate */
-                                               path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state.bounce, is_lamp);
-                                       }
-                               }
-                       }
+                       bool all = kernel_data.integrator.sample_all_lights_indirect;
+                       kernel_branched_path_integrate_direct_lighting(kg, rng, &sd, &state, throughput, 1.0f, L, all);
                }
 #endif
 
@@ -898,70 +957,7 @@ ccl_device_noinline void kernel_branched_path_integrate_lighting(KernelGlobals *
        PathState *state, PathRadiance *L, ccl_global float *buffer)
 {
 #ifdef __EMISSION__
-       /* sample illumination from lights to find path contribution */
-       if(sd->flag & SD_BSDF_HAS_EVAL) {
-               Ray light_ray;
-               BsdfEval L_light;
-               bool is_lamp;
-
-#ifdef __OBJECT_MOTION__
-               light_ray.time = sd->time;
-#endif
-
-               /* lamp sampling */
-               for(int i = 0; i < kernel_data.integrator.num_all_lights; i++) {
-                       int num_samples = ceil_to_int(num_samples_adjust*light_select_num_samples(kg, i));
-                       float num_samples_inv = num_samples_adjust/(num_samples*kernel_data.integrator.num_all_lights);
-                       RNG lamp_rng = cmj_hash(*rng, i);
-
-                       if(kernel_data.integrator.pdf_triangles != 0.0f)
-                               num_samples_inv *= 0.5f;
-
-                       for(int j = 0; j < num_samples; j++) {
-                               float light_u, light_v;
-                               path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
-
-                               if(direct_emission(kg, sd, i, 0.0f, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp, state->bounce)) {
-                                       /* trace shadow ray */
-                                       float3 shadow;
-
-                                       if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
-                                               /* accumulate */
-                                               path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
-                                       }
-                               }
-                       }
-               }
-
-               /* mesh light sampling */
-               if(kernel_data.integrator.pdf_triangles != 0.0f) {
-                       int num_samples = ceil_to_int(num_samples_adjust*kernel_data.integrator.mesh_light_samples);
-                       float num_samples_inv = num_samples_adjust/num_samples;
-
-                       if(kernel_data.integrator.num_all_lights)
-                               num_samples_inv *= 0.5f;
-
-                       for(int j = 0; j < num_samples; j++) {
-                               float light_t = path_branched_rng_1D(kg, rng, state, j, num_samples, PRNG_LIGHT);
-                               float light_u, light_v;
-                               path_branched_rng_2D(kg, rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
-
-                               /* only sample triangle lights */
-                               if(kernel_data.integrator.num_all_lights)
-                                       light_t = 0.5f*light_t;
-
-                               if(direct_emission(kg, sd, -1, light_t, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp, state->bounce)) {
-                                       /* trace shadow ray */
-                                       float3 shadow;
-
-                                       if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
-                                               /* accumulate */
-                                               path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
-                                       }
-                               }
-                       }
-               }
-       }
+       kernel_branched_path_integrate_direct_lighting(kg, rng, sd, state, throughput, num_samples_adjust, L, true);
 #endif
 
        for(int i = 0; i< sd->num_closure; i++) {
index fc3e273014154bbbf515c7f7092795eb2a4decbc..bf79b0aa3bfb3ae00255c4b0a3f053e913bcc0b5 100644 (file)
@@ -834,6 +834,7 @@ typedef struct KernelIntegrator {
        int ao_samples;
        int mesh_light_samples;
        int subsurface_samples;
+       int sample_all_lights_indirect;
        
        /* mis */
        int use_lamp_mis;
@@ -847,7 +848,7 @@ typedef struct KernelIntegrator {
        int volume_max_steps;
        float volume_step_size;
        int volume_samples;
-       int pad1, pad2;
+       int pad1;
 } KernelIntegrator;
 
 typedef struct KernelBVH {
index 5b26440594a6120e8521ac436c9bb28249df4ba5..849157d9e91ed2b348f505ec66686b7534255a34 100644 (file)
@@ -130,6 +130,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
        kintegrator->mesh_light_samples = mesh_light_samples;
        kintegrator->subsurface_samples = subsurface_samples;
        kintegrator->volume_samples = volume_samples;
+       kintegrator->sample_all_lights_indirect = sample_all_lights_indirect;
 
        kintegrator->sampling_pattern = sampling_pattern;
 
@@ -197,7 +198,8 @@ bool Integrator::modified(const Integrator& integrator)
                subsurface_samples == integrator.subsurface_samples &&
                volume_samples == integrator.volume_samples &&
                motion_blur == integrator.motion_blur &&
-               sampling_pattern == integrator.sampling_pattern);
+               sampling_pattern == integrator.sampling_pattern &&
+               sample_all_lights_indirect == integrator.sample_all_lights_indirect);
 }
 
 void Integrator::tag_update(Scene *scene)
index 4a8240c3941014ad5c9ec9b9d281e60f893fb2b7..587968dc1e4efc11f2cf1a77775b5235e68d64eb 100644 (file)
@@ -63,6 +63,7 @@ public:
        int mesh_light_samples;
        int subsurface_samples;
        int volume_samples;
+       bool sample_all_lights_indirect;
 
        enum Method {
                BRANCHED_PATH = 0,