Cycles: don't count volume boundaries as transparent bounces.

This is more important now that we will have tigther volume bounds that
we hit multiple times. It also avoids some noise due to RR previously
affecting these surfaces, which shouldn't have been the case and should
eventually be fixed for transparent BSDFs as well.

For non-volume scenes I found no performance impact on NVIDIA or AMD.
For volume scenes the noise decrease and fixed artifacts are worth the
little extra render time, when there is any.
This commit is contained in:
Brecht Van Lommel 2018-02-28 23:00:46 +01:00
parent 03d1070378
commit 7f86afec9d
8 changed files with 124 additions and 45 deletions

@ -445,8 +445,15 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
break; break;
} }
/* Setup and evaluate shader. */ /* Setup shader data. */
shader_setup_from_ray(kg, sd, &isect, ray); shader_setup_from_ray(kg, sd, &isect, ray);
/* Skip most work for volume bounding surface. */
#ifdef __VOLUME__
if(!(sd->flag & SD_HAS_ONLY_VOLUME)) {
#endif
/* Evaluate shader. */
shader_eval_surface(kg, sd, state, state->flag); shader_eval_surface(kg, sd, state, state->flag);
shader_prepare_closures(sd, state); shader_prepare_closures(sd, state);
@ -523,6 +530,10 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
} }
#endif /* defined(__EMISSION__) */ #endif /* defined(__EMISSION__) */
#ifdef __VOLUME__
}
#endif
if(!kernel_path_surface_bounce(kg, sd, &throughput, state, &L->state, ray)) if(!kernel_path_surface_bounce(kg, sd, &throughput, state, &L->state, ray))
break; break;
} }
@ -605,8 +616,15 @@ ccl_device_forceinline void kernel_path_integrate(
break; break;
} }
/* Setup and evaluate shader. */ /* Setup shader data. */
shader_setup_from_ray(kg, &sd, &isect, ray); shader_setup_from_ray(kg, &sd, &isect, ray);
/* Skip most work for volume bounding surface. */
#ifdef __VOLUME__
if(!(sd.flag & SD_HAS_ONLY_VOLUME)) {
#endif
/* Evaluate shader. */
shader_eval_surface(kg, &sd, state, state->flag); shader_eval_surface(kg, &sd, state, state->flag);
shader_prepare_closures(&sd, state); shader_prepare_closures(&sd, state);
@ -669,6 +687,10 @@ ccl_device_forceinline void kernel_path_integrate(
/* direct lighting */ /* direct lighting */
kernel_path_surface_connect_light(kg, &sd, emission_sd, throughput, state, L); kernel_path_surface_connect_light(kg, &sd, emission_sd, throughput, state, L);
#ifdef __VOLUME__
}
#endif
/* compute direct lighting and next bounce */ /* compute direct lighting and next bounce */
if(!kernel_path_surface_bounce(kg, &sd, &throughput, state, &L->state, ray)) if(!kernel_path_surface_bounce(kg, &sd, &throughput, state, &L->state, ray))
break; break;

@ -480,6 +480,12 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
/* Setup and evaluate shader. */ /* Setup and evaluate shader. */
shader_setup_from_ray(kg, &sd, &isect, &ray); shader_setup_from_ray(kg, &sd, &isect, &ray);
/* Skip most work for volume bounding surface. */
#ifdef __VOLUME__
if(!(sd.flag & SD_HAS_ONLY_VOLUME)) {
#endif
shader_eval_surface(kg, &sd, &state, state.flag); shader_eval_surface(kg, &sd, &state, state.flag);
shader_merge_closures(&sd); shader_merge_closures(&sd);
@ -533,37 +539,46 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
} }
#endif /* __SUBSURFACE__ */ #endif /* __SUBSURFACE__ */
if(!(sd.flag & SD_HAS_ONLY_VOLUME)) { PathState hit_state = state;
PathState hit_state = state;
#ifdef __EMISSION__ #ifdef __EMISSION__
/* direct light */ /* direct light */
if(kernel_data.integrator.use_direct_light) { if(kernel_data.integrator.use_direct_light) {
int all = (kernel_data.integrator.sample_all_lights_direct) || int all = (kernel_data.integrator.sample_all_lights_direct) ||
(state.flag & PATH_RAY_SHADOW_CATCHER); (state.flag & PATH_RAY_SHADOW_CATCHER);
kernel_branched_path_surface_connect_light(kg, kernel_branched_path_surface_connect_light(kg,
&sd, emission_sd, &hit_state, throughput, 1.0f, L, all); &sd, emission_sd, &hit_state, throughput, 1.0f, L, all);
} }
#endif /* __EMISSION__ */ #endif /* __EMISSION__ */
/* indirect light */ /* indirect light */
kernel_branched_path_surface_indirect_light(kg, kernel_branched_path_surface_indirect_light(kg,
&sd, &indirect_sd, emission_sd, throughput, 1.0f, &hit_state, L); &sd, &indirect_sd, emission_sd, throughput, 1.0f, &hit_state, L);
/* continue in case of transparency */ /* continue in case of transparency */
throughput *= shader_bsdf_transparency(kg, &sd); throughput *= shader_bsdf_transparency(kg, &sd);
if(is_zero(throughput)) if(is_zero(throughput))
break; break;
}
/* Update Path State */ /* Update Path State */
path_state_next(kg, &state, LABEL_TRANSPARENT); path_state_next(kg, &state, LABEL_TRANSPARENT);
#ifdef __VOLUME__
}
else {
/* For volume bounding meshes we pass through without counting transparent
* bounces, only sanity check in case self intersection gets us stuck. */
state.volume_bounds_bounce++;
if (state.volume_bounds_bounce > VOLUME_BOUNDS_MAX) {
break;
}
}
#endif
ray.P = ray_offset(sd.P, -sd.Ng); ray.P = ray_offset(sd.P, -sd.Ng);
ray.t -= sd.ray_length; /* clipping works through transparent */ ray.t -= sd.ray_length; /* clipping works through transparent */
#ifdef __RAY_DIFFERENTIALS__ #ifdef __RAY_DIFFERENTIALS__
ray.dP = sd.dP; ray.dP = sd.dP;
ray.dD.dx = -sd.dI.dx; ray.dD.dx = -sd.dI.dx;

@ -55,6 +55,7 @@ ccl_device_inline void path_state_init(KernelGlobals *kg,
#ifdef __VOLUME__ #ifdef __VOLUME__
state->volume_bounce = 0; state->volume_bounce = 0;
state->volume_bounds_bounce = 0;
if(kernel_data.integrator.use_volumes) { if(kernel_data.integrator.use_volumes) {
/* Initialize volume stack with volume we are inside of. */ /* Initialize volume stack with volume we are inside of. */

@ -329,10 +329,12 @@ ccl_device bool kernel_path_surface_bounce(KernelGlobals *kg,
} }
#ifdef __VOLUME__ #ifdef __VOLUME__
else if(sd->flag & SD_HAS_ONLY_VOLUME) { else if(sd->flag & SD_HAS_ONLY_VOLUME) {
/* no surface shader but have a volume shader? act transparent */ /* For volume bounding meshes we pass through without counting transparent
* bounces, only sanity check in case self intersection gets us stuck. */
/* update path state, count as transparent */ state->volume_bounds_bounce++;
path_state_next(kg, state, LABEL_TRANSPARENT); if (state->volume_bounds_bounce > VOLUME_BOUNDS_MAX) {
return false;
}
if(state->bounce == 0) if(state->bounce == 0)
ray->t -= sd->ray_length; /* clipping works through transparent */ ray->t -= sd->ray_length; /* clipping works through transparent */

@ -49,6 +49,8 @@ CCL_NAMESPACE_BEGIN
#define BSSRDF_MAX_BOUNCES 256 #define BSSRDF_MAX_BOUNCES 256
#define LOCAL_MAX_HITS 4 #define LOCAL_MAX_HITS 4
#define VOLUME_BOUNDS_MAX 1024
#define BECKMANN_TABLE_SIZE 256 #define BECKMANN_TABLE_SIZE 256
#define SHADER_NONE (~0) #define SHADER_NONE (~0)
@ -1107,6 +1109,7 @@ typedef struct PathState {
/* volume rendering */ /* volume rendering */
#ifdef __VOLUME__ #ifdef __VOLUME__
int volume_bounce; int volume_bounce;
int volume_bounds_bounce;
uint rng_congruential; uint rng_congruential;
VolumeStack volume_stack[VOLUME_STACK_SIZE]; VolumeStack volume_stack[VOLUME_STACK_SIZE];
#endif #endif
@ -1497,8 +1500,10 @@ enum RayState {
RAY_ACTIVE, RAY_ACTIVE,
/* Denotes ray has completed processing all samples and is inactive. */ /* Denotes ray has completed processing all samples and is inactive. */
RAY_INACTIVE, RAY_INACTIVE,
/* Denoted ray has exited path-iteration and needs to update output buffer. */ /* Denotes ray has exited path-iteration and needs to update output buffer. */
RAY_UPDATE_BUFFER, RAY_UPDATE_BUFFER,
/* Denotes ray needs to skip most surface shader work. */
RAY_HAS_ONLY_VOLUME,
/* Donotes ray has hit background */ /* Donotes ray has hit background */
RAY_HIT_BACKGROUND, RAY_HIT_BACKGROUND,
/* Denotes ray has to be regenerated */ /* Denotes ray has to be regenerated */

@ -53,39 +53,52 @@ ccl_device_inline void kernel_split_branched_indirect_light_init(KernelGlobals *
ADD_RAY_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_LIGHT_INDIRECT); ADD_RAY_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_LIGHT_INDIRECT);
} }
ccl_device void kernel_split_branched_indirect_light_end(KernelGlobals *kg, int ray_index) ccl_device void kernel_split_branched_transparent_bounce(KernelGlobals *kg, int ray_index)
{ {
kernel_split_branched_path_indirect_loop_end(kg, ray_index);
ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index];
ShaderData *sd = kernel_split_sd(sd, ray_index); ShaderData *sd = kernel_split_sd(sd, ray_index);
ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; ccl_global PathState *state = &kernel_split_state.path_state[ray_index];
ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; ccl_global Ray *ray = &kernel_split_state.ray[ray_index];
/* continue in case of transparency */ # ifdef __VOLUME__
*throughput *= shader_bsdf_transparency(kg, sd); if(!(sd->flag & SD_HAS_ONLY_VOLUME)) {
# endif
/* continue in case of transparency */
*throughput *= shader_bsdf_transparency(kg, sd);
if(is_zero(*throughput)) {
kernel_split_path_end(kg, ray_index);
return;
}
if(is_zero(*throughput)) {
kernel_split_path_end(kg, ray_index);
}
else {
/* Update Path State */ /* Update Path State */
path_state_next(kg, state, LABEL_TRANSPARENT); path_state_next(kg, state, LABEL_TRANSPARENT);
# ifdef __VOLUME__
}
else {
/* For volume bounding meshes we pass through without counting transparent
* bounces, only sanity check in case self intersection gets us stuck. */
state->volume_bounds_bounce++;
if (state->volume_bounds_bounce > VOLUME_BOUNDS_MAX) {
kernel_split_path_end(kg, ray_index);
return;
}
}
# endif
ray->P = ray_offset(sd->P, -sd->Ng); ray->P = ray_offset(sd->P, -sd->Ng);
ray->t -= sd->ray_length; /* clipping works through transparent */ ray->t -= sd->ray_length; /* clipping works through transparent */
# ifdef __RAY_DIFFERENTIALS__ # ifdef __RAY_DIFFERENTIALS__
ray->dP = sd->dP; ray->dP = sd->dP;
ray->dD.dx = -sd->dI.dx; ray->dD.dx = -sd->dI.dx;
ray->dD.dy = -sd->dI.dy; ray->dD.dy = -sd->dI.dy;
# endif /* __RAY_DIFFERENTIALS__ */ # endif /* __RAY_DIFFERENTIALS__ */
# ifdef __VOLUME__ # ifdef __VOLUME__
/* enter/exit volume */ /* enter/exit volume */
kernel_volume_stack_enter_exit(kg, sd, state->volume_stack); kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
# endif /* __VOLUME__ */ # endif /* __VOLUME__ */
}
} }
#endif /* __BRANCHED_PATH__ */ #endif /* __BRANCHED_PATH__ */
@ -121,6 +134,13 @@ ccl_device void kernel_next_iteration_setup(KernelGlobals *kg,
ccl_global char *ray_state = kernel_split_state.ray_state; ccl_global char *ray_state = kernel_split_state.ray_state;
# ifdef __VOLUME__
/* Reactivate only volume rays here, most surface work was skipped. */
if(IS_STATE(ray_state, ray_index, RAY_HAS_ONLY_VOLUME)) {
ASSIGN_RAY_STATE(ray_state, ray_index, RAY_ACTIVE);
}
# endif
bool active = IS_STATE(ray_state, ray_index, RAY_ACTIVE); bool active = IS_STATE(ray_state, ray_index, RAY_ACTIVE);
if(active) { if(active) {
ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index];
@ -138,6 +158,9 @@ ccl_device void kernel_next_iteration_setup(KernelGlobals *kg,
} }
#ifdef __BRANCHED_PATH__ #ifdef __BRANCHED_PATH__
} }
else if(sd->flag & SD_HAS_ONLY_VOLUME) {
kernel_split_branched_transparent_bounce(kg, ray_index);
}
else { else {
kernel_split_branched_indirect_light_init(kg, ray_index); kernel_split_branched_indirect_light_init(kg, ray_index);
@ -151,7 +174,8 @@ ccl_device void kernel_next_iteration_setup(KernelGlobals *kg,
ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED);
} }
else { else {
kernel_split_branched_indirect_light_end(kg, ray_index); kernel_split_branched_path_indirect_loop_end(kg, ray_index);
kernel_split_branched_transparent_bounce(kg, ray_index);
} }
} }
#endif /* __BRANCHED_PATH__ */ #endif /* __BRANCHED_PATH__ */
@ -196,7 +220,8 @@ ccl_device void kernel_next_iteration_setup(KernelGlobals *kg,
ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED);
} }
else { else {
kernel_split_branched_indirect_light_end(kg, ray_index); kernel_split_branched_path_indirect_loop_end(kg, ray_index);
kernel_split_branched_transparent_bounce(kg, ray_index);
} }
} }

@ -56,6 +56,7 @@ ccl_device void kernel_queue_enqueue(KernelGlobals *kg,
queue_number = QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS; queue_number = QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS;
} }
else if(IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE) || else if(IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE) ||
IS_STATE(kernel_split_state.ray_state, ray_index, RAY_HAS_ONLY_VOLUME) ||
IS_STATE(kernel_split_state.ray_state, ray_index, RAY_REGENERATED)) { IS_STATE(kernel_split_state.ray_state, ray_index, RAY_REGENERATED)) {
queue_number = QUEUE_ACTIVE_AND_REGENERATED_RAYS; queue_number = QUEUE_ACTIVE_AND_REGENERATED_RAYS;
} }

@ -59,12 +59,20 @@ ccl_device void kernel_shader_setup(KernelGlobals *kg,
if(IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE)) { if(IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE)) {
Intersection isect = kernel_split_state.isect[ray_index]; Intersection isect = kernel_split_state.isect[ray_index];
Ray ray = kernel_split_state.ray[ray_index]; Ray ray = kernel_split_state.ray[ray_index];
ShaderData *sd = kernel_split_sd(sd, ray_index);
shader_setup_from_ray(kg, shader_setup_from_ray(kg,
kernel_split_sd(sd, ray_index), sd,
&isect, &isect,
&ray); &ray);
#ifdef __VOLUME__
if(sd->flag & SD_HAS_ONLY_VOLUME) {
ASSIGN_RAY_STATE(kernel_split_state.ray_state, ray_index, RAY_HAS_ONLY_VOLUME);
}
#endif
} }
} }
CCL_NAMESPACE_END CCL_NAMESPACE_END