Cycles: Stop lamp sampling if the lamp isn't visible

Both spot and area light have large areas where they're not visible.
Therefore, this patch stops the light sampling code when one of these cases (outside of the spotlight cone or behind the area light) occurs, before the lamp shader is evaluated.
In the case of the area light, the solid angle sampling can also be skipped.

In a test scene with Sample All Lights and 18 Area lamps and 9 Spot lamps that all point away from the area that the camera sees, render time drops from 12sec to 5sec.

Reviewers: brecht, sergey, dingto, juicyfruit

Differential Revision: https://developer.blender.org/D2216
This commit is contained in:
Lukas Stockner 2016-09-09 15:51:40 +02:00
parent aae2cea28d
commit b459d9f46c
4 changed files with 104 additions and 108 deletions

@ -510,7 +510,7 @@ ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3
return t*t/cos_pi; return t*t/cos_pi;
} }
ccl_device_inline void lamp_light_sample(KernelGlobals *kg, ccl_device_inline bool lamp_light_sample(KernelGlobals *kg,
int lamp, int lamp,
float randu, float randv, float randu, float randv,
float3 P, float3 P,
@ -581,6 +581,9 @@ ccl_device_inline void lamp_light_sample(KernelGlobals *kg,
/* spot light attenuation */ /* spot light attenuation */
float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2); float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2);
ls->eval_fac *= spot_light_attenuation(data1, data2, ls); ls->eval_fac *= spot_light_attenuation(data1, data2, ls);
if(ls->eval_fac == 0.0f) {
return false;
}
} }
ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t); ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
} }
@ -593,6 +596,10 @@ ccl_device_inline void lamp_light_sample(KernelGlobals *kg,
float3 axisv = make_float3(data2.y, data2.z, data2.w); float3 axisv = make_float3(data2.y, data2.z, data2.w);
float3 D = make_float3(data3.y, data3.z, data3.w); float3 D = make_float3(data3.y, data3.z, data3.w);
if(dot(ls->P - P, D) > 0.0f) {
return false;
}
ls->pdf = area_light_sample(P, &ls->P, ls->pdf = area_light_sample(P, &ls->P,
axisu, axisv, axisu, axisv,
randu, randv, randu, randv,
@ -603,13 +610,12 @@ ccl_device_inline void lamp_light_sample(KernelGlobals *kg,
float invarea = data2.x; float invarea = data2.x;
ls->eval_fac = 0.25f*invarea; ls->eval_fac = 0.25f*invarea;
if(dot(ls->D, D) > 0.0f)
ls->pdf = 0.0f;
} }
ls->eval_fac *= kernel_data.integrator.inv_pdf_lights; ls->eval_fac *= kernel_data.integrator.inv_pdf_lights;
} }
return (ls->pdf > 0.0f);
} }
ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, float t, LightSample *ls) ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, float t, LightSample *ls)
@ -836,7 +842,7 @@ ccl_device bool light_select_reached_max_bounces(KernelGlobals *kg, int index, i
return (bounce > __float_as_int(data4.x)); return (bounce > __float_as_int(data4.x));
} }
ccl_device_noinline void light_sample(KernelGlobals *kg, ccl_device_noinline bool light_sample(KernelGlobals *kg,
float randt, float randt,
float randu, float randu,
float randv, float randv,
@ -857,21 +863,20 @@ ccl_device_noinline void light_sample(KernelGlobals *kg,
int shader_flag = __float_as_int(l.z); int shader_flag = __float_as_int(l.z);
triangle_light_sample(kg, prim, object, randu, randv, time, ls); triangle_light_sample(kg, prim, object, randu, randv, time, ls);
/* compute incoming direction, distance and pdf */ /* compute incoming direction, distance and pdf */
ls->D = normalize_len(ls->P - P, &ls->t); ls->D = normalize_len(ls->P - P, &ls->t);
ls->pdf = triangle_light_pdf(kg, ls->Ng, -ls->D, ls->t); ls->pdf = triangle_light_pdf(kg, ls->Ng, -ls->D, ls->t);
ls->shader |= shader_flag; ls->shader |= shader_flag;
return (ls->pdf > 0.0f);
} }
else { else {
int lamp = -prim-1; int lamp = -prim-1;
if(UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) { if(UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) {
ls->pdf = 0.0f; return false;
return;
} }
lamp_light_sample(kg, lamp, randu, randv, P, ls); return lamp_light_sample(kg, lamp, randu, randv, P, ls);
} }
} }

@ -54,15 +54,15 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v); path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
LightSample ls; LightSample ls;
lamp_light_sample(kg, i, light_u, light_v, ccl_fetch(sd, P), &ls); if(lamp_light_sample(kg, i, light_u, light_v, ccl_fetch(sd, P), &ls)) {
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* trace shadow ray */ /* accumulate */
float3 shadow; path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
}
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
} }
} }
} }
@ -86,15 +86,15 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
light_t = 0.5f*light_t; light_t = 0.5f*light_t;
LightSample ls; LightSample ls;
light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls); if(light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls)) {
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* trace shadow ray */ /* accumulate */
float3 shadow; path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
}
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
} }
} }
} }
@ -107,16 +107,16 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v); path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
LightSample ls; LightSample ls;
light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls); if(light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls)) {
/* sample random light */
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
/* sample random light */ if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { /* accumulate */
/* trace shadow ray */ path_radiance_accum_light(L, throughput*num_samples_adjust, &L_light, shadow, num_samples_adjust, state->bounce, is_lamp);
float3 shadow; }
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(L, throughput*num_samples_adjust, &L_light, shadow, num_samples_adjust, state->bounce, is_lamp);
} }
} }
} }
@ -206,15 +206,15 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, ccl_
#endif #endif
LightSample ls; LightSample ls;
light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls); if(light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls)) {
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* trace shadow ray */ /* accumulate */
float3 shadow; path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
}
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
} }
} }
#endif #endif

@ -46,17 +46,16 @@ ccl_device_inline void kernel_path_volume_connect_light(
light_ray.time = sd->time; light_ray.time = sd->time;
# endif # endif
light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls); if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls))
if(ls.pdf == 0.0f) {
return; if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* trace shadow ray */ /* accumulate */
float3 shadow; path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
}
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
} }
} }
#endif #endif
@ -161,18 +160,15 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
kernel_assert(result == VOLUME_PATH_SCATTERED); kernel_assert(result == VOLUME_PATH_SCATTERED);
/* todo: split up light_sample so we don't have to call it again with new position */ /* todo: split up light_sample so we don't have to call it again with new position */
lamp_light_sample(kg, i, light_u, light_v, sd->P, &ls); if(lamp_light_sample(kg, i, light_u, light_v, sd->P, &ls)) {
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
if(ls.pdf == 0.0f) if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
continue; /* accumulate */
path_radiance_accum_light(L, tp*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { }
/* trace shadow ray */
float3 shadow;
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(L, tp*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
} }
} }
} }
@ -212,18 +208,15 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
kernel_assert(result == VOLUME_PATH_SCATTERED); kernel_assert(result == VOLUME_PATH_SCATTERED);
/* todo: split up light_sample so we don't have to call it again with new position */ /* todo: split up light_sample so we don't have to call it again with new position */
light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls); if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
if(ls.pdf == 0.0f) if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
continue; /* accumulate */
path_radiance_accum_light(L, tp*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { }
/* trace shadow ray */
float3 shadow;
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(L, tp*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
} }
} }
} }
@ -251,19 +244,16 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
kernel_assert(result == VOLUME_PATH_SCATTERED); kernel_assert(result == VOLUME_PATH_SCATTERED);
/* todo: split up light_sample so we don't have to call it again with new position */ /* todo: split up light_sample so we don't have to call it again with new position */
light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls); if(light_sample(kg, light_t, 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)) {
/* trace shadow ray */
float3 shadow;
if(ls.pdf == 0.0f) if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
return; /* accumulate */
path_radiance_accum_light(L, tp, &L_light, shadow, 1.0f, state->bounce, is_lamp);
/* sample random light */ }
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(L, tp, &L_light, shadow, 1.0f, state->bounce, is_lamp);
} }
} }
} }

@ -74,30 +74,31 @@ ccl_device char kernel_direct_lighting(
path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v); path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
LightSample ls; LightSample ls;
light_sample(kg, if(light_sample(kg,
light_t, light_u, light_v, light_t, light_u, light_v,
ccl_fetch(sd, time), ccl_fetch(sd, time),
ccl_fetch(sd, P), ccl_fetch(sd, P),
state->bounce, state->bounce,
&ls); &ls)) {
Ray light_ray; Ray light_ray;
#ifdef __OBJECT_MOTION__ #ifdef __OBJECT_MOTION__
light_ray.time = ccl_fetch(sd, time); light_ray.time = ccl_fetch(sd, time);
#endif #endif
BsdfEval L_light; BsdfEval L_light;
bool is_lamp; bool is_lamp;
if(direct_emission(kg, sd, kg->sd_input, &ls, state, &light_ray, &L_light, &is_lamp)) { if(direct_emission(kg, sd, kg->sd_input, &ls, state, &light_ray, &L_light, &is_lamp)) {
/* Write intermediate data to global memory to access from /* Write intermediate data to global memory to access from
* the next kernel. * the next kernel.
*/ */
LightRay_coop[ray_index] = light_ray; LightRay_coop[ray_index] = light_ray;
BSDFEval_coop[ray_index] = L_light; BSDFEval_coop[ray_index] = L_light;
ISLamp_coop[ray_index] = is_lamp; ISLamp_coop[ray_index] = is_lamp;
/* Mark ray state for next shadow kernel. */ /* Mark ray state for next shadow kernel. */
ADD_RAY_FLAG(ray_state, ray_index, RAY_SHADOW_RAY_CAST_DL); ADD_RAY_FLAG(ray_state, ray_index, RAY_SHADOW_RAY_CAST_DL);
enqueue_flag = 1; enqueue_flag = 1;
}
} }
} }
#endif /* __EMISSION__ */ #endif /* __EMISSION__ */