From cd6c9e9e5f4726ab0b0b2c58249f98dab5c5d267 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 8 Sep 2017 01:42:14 +0200 Subject: Cycles: improve sample stratification on area lights for path tracing. Previously we used a 1D sequence to select a light, and another 2D sequence to sample a point on the light. For multiple lights this meant each light would get a random subset of a 2D stratified sequence, which is not guaranteed to be stratified anymore. Now we use only a 2D sequence, split into segments along the X axis, one for each light. The samples that fall within a segment then each are a stratified sequence, at least in the limit. So for example for two lights, we split up the unit square into two segments [0,0.5[ x [0,1[ and [0.5,1[ x [0,1[. This doesn't make much difference in most scenes, mainly helps if you have a few large area lights or some types of HDR backgrounds. --- intern/cycles/kernel/kernel_light.h | 26 ++++++++++++++-------- intern/cycles/kernel/kernel_path_surface.h | 11 ++++----- intern/cycles/kernel/kernel_path_volume.h | 15 +++++-------- intern/cycles/kernel/kernel_types.h | 2 +- .../cycles/kernel/split/kernel_direct_lighting.h | 3 +-- 5 files changed, 29 insertions(+), 28 deletions(-) (limited to 'intern') diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index 59db6cbd430..9f1a48f9bba 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -1013,20 +1013,21 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg, int prim, i /* Light Distribution */ -ccl_device int light_distribution_sample(KernelGlobals *kg, float randt) +ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu) { - /* this is basically std::upper_bound as used by pbrt, to find a point light or + /* This is basically std::upper_bound as used by pbrt, to find a point light or * triangle to emit from, proportional to area. a good improvement would be to * also sample proportional to power, though it's not so well defined with - * OSL shaders. */ + * arbitrary shaders. */ int first = 0; int len = kernel_data.integrator.num_distribution + 1; + float r = *randu; while(len > 0) { int half_len = len >> 1; int middle = first + half_len; - if(randt < kernel_tex_fetch(__light_distribution, middle).x) { + if(r < kernel_tex_fetch(__light_distribution, middle).x) { len = half_len; } else { @@ -1035,9 +1036,17 @@ ccl_device int light_distribution_sample(KernelGlobals *kg, float randt) } } - /* clamping should not be needed but float rounding errors seem to - * make this fail on rare occasions */ - return clamp(first-1, 0, kernel_data.integrator.num_distribution-1); + /* Clamping should not be needed but float rounding errors seem to + * make this fail on rare occasions. */ + int index = clamp(first-1, 0, kernel_data.integrator.num_distribution-1); + + /* Rescale to reuse random number. this helps the 2D samples within + * each area light be stratified as well. */ + float distr_min = kernel_tex_fetch(__light_distribution, index).x; + float distr_max = kernel_tex_fetch(__light_distribution, index+1).x; + *randu = (r - distr_min)/(distr_max - distr_min); + + return index; } /* Generic Light */ @@ -1049,7 +1058,6 @@ ccl_device bool light_select_reached_max_bounces(KernelGlobals *kg, int index, i } ccl_device_noinline bool light_sample(KernelGlobals *kg, - float randt, float randu, float randv, float time, @@ -1058,7 +1066,7 @@ ccl_device_noinline bool light_sample(KernelGlobals *kg, LightSample *ls) { /* sample index */ - int index = light_distribution_sample(kg, randt); + int index = light_distribution_sample(kg, &randu); /* fetch light data */ float4 l = kernel_tex_fetch(__light_distribution, index); diff --git a/intern/cycles/kernel/kernel_path_surface.h b/intern/cycles/kernel/kernel_path_surface.h index 6c3a444e48a..e798fcc6a2c 100644 --- a/intern/cycles/kernel/kernel_path_surface.h +++ b/intern/cycles/kernel/kernel_path_surface.h @@ -85,17 +85,16 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light( float num_samples_inv = num_samples_adjust/num_samples; for(int j = 0; j < num_samples; j++) { - float light_t = path_branched_rng_1D(kg, state->rng_hash, state, j, num_samples, PRNG_LIGHT); float light_u, light_v; path_branched_rng_2D(kg, state->rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v); float terminate = path_branched_rng_light_termination(kg, state->rng_hash, state, j, num_samples); /* only sample triangle lights */ if(kernel_data.integrator.num_all_lights) - light_t = 0.5f*light_t; + light_u = 0.5f*light_u; LightSample ls; - if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { + if(light_sample(kg, light_u, light_v, sd->time, 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; @@ -118,13 +117,12 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light( } else { /* sample one light at random */ - float light_t = path_state_rng_1D(kg, state, PRNG_LIGHT); float light_u, light_v; path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v); float terminate = path_state_rng_light_termination(kg, state); LightSample ls; - if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { + if(light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { /* sample random light */ if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { /* trace shadow ray */ @@ -238,7 +236,6 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, #endif /* sample illumination from lights to find path contribution */ - float light_t = path_state_rng_1D(kg, state, PRNG_LIGHT); float light_u, light_v; path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v); @@ -251,7 +248,7 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, #endif LightSample ls; - if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { + if(light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { float terminate = path_state_rng_light_termination(kg, state); if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { /* trace shadow ray */ diff --git a/intern/cycles/kernel/kernel_path_volume.h b/intern/cycles/kernel/kernel_path_volume.h index c9c7f447c42..fb6b8d56a38 100644 --- a/intern/cycles/kernel/kernel_path_volume.h +++ b/intern/cycles/kernel/kernel_path_volume.h @@ -31,7 +31,6 @@ ccl_device_inline void kernel_path_volume_connect_light( return; /* sample illumination from lights to find path contribution */ - float light_t = path_state_rng_1D(kg, state, PRNG_LIGHT); float light_u, light_v; path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v); @@ -45,7 +44,7 @@ ccl_device_inline void kernel_path_volume_connect_light( light_ray.time = sd->time; # endif - if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) + if(light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { float terminate = path_state_rng_light_termination(kg, state); if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { @@ -195,16 +194,15 @@ ccl_device void kernel_branched_path_volume_connect_light( for(int j = 0; j < num_samples; j++) { /* sample random position on random triangle */ - float light_t = path_branched_rng_1D_for_decision(kg, state->rng_hash, state, j, num_samples, PRNG_LIGHT); float light_u, light_v; path_branched_rng_2D(kg, state->rng_hash, 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; + light_u = 0.5f*light_u; LightSample ls; - light_sample(kg, light_t, light_u, light_v, sd->time, ray->P, state->bounce, &ls); + light_sample(kg, light_u, light_v, sd->time, ray->P, state->bounce, &ls); float3 tp = throughput; @@ -219,7 +217,7 @@ ccl_device void kernel_branched_path_volume_connect_light( kernel_assert(result == VOLUME_PATH_SCATTERED); /* 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(light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { if(kernel_data.integrator.num_all_lights) ls.pdf *= 2.0f; @@ -239,12 +237,11 @@ ccl_device void kernel_branched_path_volume_connect_light( } else { /* sample random position on random light */ - float light_t = path_state_rng_1D(kg, state, PRNG_LIGHT); float light_u, light_v; path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v); LightSample ls; - light_sample(kg, light_t, light_u, light_v, sd->time, ray->P, state->bounce, &ls); + light_sample(kg, light_u, light_v, sd->time, ray->P, state->bounce, &ls); float3 tp = throughput; @@ -259,7 +256,7 @@ ccl_device void kernel_branched_path_volume_connect_light( kernel_assert(result == VOLUME_PATH_SCATTERED); /* 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(light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { /* sample random light */ float terminate = path_state_rng_light_termination(kg, state); if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 8f65c00491c..2c7e9b82feb 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -292,7 +292,7 @@ enum PathTraceDimension { PRNG_BSDF_U = 0, PRNG_BSDF_V = 1, PRNG_BSDF = 2, - PRNG_LIGHT = 3, + PRNG_UNUSED3 = 3, PRNG_LIGHT_U = 4, PRNG_LIGHT_V = 5, PRNG_LIGHT_TERMINATE = 6, diff --git a/intern/cycles/kernel/split/kernel_direct_lighting.h b/intern/cycles/kernel/split/kernel_direct_lighting.h index 8e3f7555550..5009828df92 100644 --- a/intern/cycles/kernel/split/kernel_direct_lighting.h +++ b/intern/cycles/kernel/split/kernel_direct_lighting.h @@ -81,14 +81,13 @@ ccl_device void kernel_direct_lighting(KernelGlobals *kg, if(flag) { /* Sample illumination from lights to find path contribution. */ - float light_t = path_state_rng_1D(kg, state, PRNG_LIGHT); float light_u, light_v; path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v); float terminate = path_state_rng_light_termination(kg, state); LightSample ls; if(light_sample(kg, - light_t, light_u, light_v, + light_u, light_v, sd->time, sd->P, state->bounce, -- cgit v1.2.3