Cycles: Use correct light sampling PDF for MIS calculation with Branched Path Tracing
authorLukas Stockner <lukas.stockner@freenet.de>
Sun, 25 Sep 2016 20:11:50 +0000 (22:11 +0200)
committerLukas Stockner <lukas.stockner@freenet.de>
Sun, 25 Sep 2016 21:16:05 +0000 (23:16 +0200)
The light sampling functions calculate light sampling PDF for the case that the light has been randomly selected out of all lights.
However, since BPT handles lamps and meshlights separately, this isn't the case. So, to avoid a wrong result, the code just included the 0.5 factor in the throughput.

In theory, however, the correction should be made to the sampling probability, which needs to be doubled. Now, for the regular calculation, that's no real difference since the throughput is divided by the pdf.
However, it does matter for the MIS calculation - it's unbiased both ways, but including the factor in the PDF instead of the throughput should give slightly better results.

Reviewers: sergey, brecht, dingto, juicyfruit

Differential Revision: https://developer.blender.org/D2258

intern/cycles/kernel/kernel_path_surface.h
intern/cycles/kernel/kernel_path_volume.h

index 27acf8166b39869afe4a6201da4bf37f4ef12432..45f0f1cbfaadd74006731aff91fec8795042a3a9 100644 (file)
@@ -46,15 +46,17 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
                        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);
 
                                LightSample ls;
                                if(lamp_light_sample(kg, i, light_u, light_v, ccl_fetch(sd, P), &ls)) {
+                                       /* The sampling probability returned by lamp_light_sample assumes that all lights were sampled.
+                                        * However, this code only samples lamps, so if the scene also had mesh lights, the real probability is twice as high. */
+                                       if(kernel_data.integrator.pdf_triangles != 0.0f)
+                                               ls.pdf *= 2.0f;
+
                                        if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
                                                /* trace shadow ray */
                                                float3 shadow;
@@ -73,9 +75,6 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
                        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;
@@ -87,6 +86,10 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
 
                                LightSample ls;
                                if(light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls)) {
+                                       /* Same as above, probability needs to be corrected since the sampling was forced to select a mesh light. */
+                                       if(kernel_data.integrator.num_all_lights)
+                                               ls.pdf *= 2.0f;
+
                                        if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
                                                /* trace shadow ray */
                                                float3 shadow;
index 0bb8f1c517f00bd98fc309dd9f910b3475cb38f1..5ee1912c913cbb6151f352d70e0b4b9841ced9d2 100644 (file)
@@ -136,9 +136,6 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
                        float num_samples_inv = 1.0f/(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++) {
                                /* sample random position on given light */
                                float light_u, light_v;
@@ -161,6 +158,9 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
 
                                /* todo: split up light_sample so we don't have to call it again with new position */
                                if(lamp_light_sample(kg, i, light_u, light_v, sd->P, &ls)) {
+                                       if(kernel_data.integrator.pdf_triangles != 0.0f)
+                                               ls.pdf *= 2.0f;
+
                                        if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
                                                /* trace shadow ray */
                                                float3 shadow;
@@ -179,9 +179,6 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
                        int num_samples = kernel_data.integrator.mesh_light_samples;
                        float num_samples_inv = 1.0f/num_samples;
 
-                       if(kernel_data.integrator.num_all_lights)
-                               num_samples_inv *= 0.5f;
-
                        for(int j = 0; j < num_samples; j++) {
                                /* sample random position on random triangle */
                                float light_t = path_branched_rng_1D_for_decision(kg, rng, state, j, num_samples, PRNG_LIGHT);
@@ -209,6 +206,9 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
 
                                /* todo: split up light_sample so we don't have to call it again with new position */
                                if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
+                                       if(kernel_data.integrator.num_all_lights)
+                                               ls.pdf *= 2.0f;
+
                                        if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
                                                /* trace shadow ray */
                                                float3 shadow;