diff options
Diffstat (limited to 'intern/cycles/kernel/kernel_volume.h')
-rw-r--r-- | intern/cycles/kernel/kernel_volume.h | 507 |
1 files changed, 340 insertions, 167 deletions
diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h index faaa68e3309..ce20f20e75a 100644 --- a/intern/cycles/kernel/kernel_volume.h +++ b/intern/cycles/kernel/kernel_volume.h @@ -116,6 +116,36 @@ ccl_device bool volume_stack_is_heterogeneous(KernelGlobals *kg, VolumeStack *st return false; } +ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stack) +{ + if(kernel_data.integrator.num_all_lights == 0) + return 0; + + int method = -1; + + for(int i = 0; stack[i].shader != SHADER_NONE; i++) { + int shader_flag = kernel_tex_fetch(__shader_flag, (stack[i].shader & SHADER_MASK)*2); + + if(shader_flag & SD_VOLUME_MIS) { + return SD_VOLUME_MIS; + } + else if(shader_flag & SD_VOLUME_EQUIANGULAR) { + if(method == 0) + return SD_VOLUME_MIS; + + method = SD_VOLUME_EQUIANGULAR; + } + else { + if(method == SD_VOLUME_EQUIANGULAR) + return SD_VOLUME_MIS; + + method = 0; + } + } + + return method; +} + /* Volume Shadows * * These functions are used to attenuate shadow rays to lights. Both absorption @@ -136,7 +166,7 @@ ccl_device void kernel_volume_shadow_homogeneous(KernelGlobals *kg, PathState *s ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd, float3 *throughput) { float3 tp = *throughput; - const float tp_eps = 1e-10f; /* todo: this is likely not the right value */ + const float tp_eps = 1e-6f; /* todo: this is likely not the right value */ /* prepare for stepping */ int max_steps = kernel_data.integrator.volume_max_steps; @@ -146,6 +176,8 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, PathState /* compute extinction at the start */ float t = 0.0f; + float3 sum = make_float3(0.0f, 0.0f, 0.0f); + for(int i = 0; i < max_steps; i++) { /* advance to new position */ float new_t = min(ray->t, (i+1) * step); @@ -160,20 +192,26 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, PathState /* compute attenuation over segment */ if(volume_shader_extinction_sample(kg, sd, state, new_P, &sigma_t)) { - /* todo: we could avoid computing expf() for each step by summing, - * because exp(a)*exp(b) = exp(a+b), but we still want a quick - * tp_eps check too */ - tp *= volume_color_transmittance(sigma_t, new_t - t); - - /* stop if nearly all light blocked */ - if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps) - break; + /* Compute expf() only for every Nth step, to save some calculations + * because exp(a)*exp(b) = exp(a+b), also do a quick tp_eps check then. */ + + sum += (-sigma_t * (new_t - t)); + if((i & 0x07) == 0) { /* ToDo: Other interval? */ + tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z)); + + /* stop if nearly all light is blocked */ + if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps) + break; + } } /* stop if at the end of the volume */ t = new_t; - if(t == ray->t) + if(t == ray->t) { + /* Update throughput in case we haven't done it above */ + tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z)); break; + } } *throughput = tp; @@ -226,33 +264,6 @@ ccl_device float kernel_volume_equiangular_pdf(Ray *ray, float3 light_P, float s return pdf; } -ccl_device bool kernel_volume_equiangular_light_position(KernelGlobals *kg, PathState *state, Ray *ray, RNG *rng, float3 *light_P) -{ - /* light RNGs */ - 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); - - /* light sample */ - LightSample ls; - light_sample(kg, light_t, light_u, light_v, ray->time, ray->P, &ls); - if(ls.pdf == 0.0f) - return false; - - *light_P = ls.P; - return true; -} - -ccl_device float kernel_volume_decoupled_equiangular_pdf(KernelGlobals *kg, PathState *state, Ray *ray, RNG *rng, float sample_t) -{ - float3 light_P; - - if(!kernel_volume_equiangular_light_position(kg, state, ray, rng, &light_P)) - return 0.0f; - - return kernel_volume_equiangular_pdf(ray, light_P, sample_t); -} - /* Distance sampling */ ccl_device float kernel_volume_distance_sample(float max_t, float3 sigma_t, int channel, float xi, float3 *transmittance, float3 *pdf) @@ -312,7 +323,7 @@ ccl_device float3 kernel_volume_emission_integrate(VolumeShaderCoefficients *coe * the volume shading coefficient for the entire line segment */ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd, PathRadiance *L, float3 *throughput, - RNG *rng) + RNG *rng, bool probalistic_scatter) { VolumeShaderCoefficients coeff; @@ -323,6 +334,7 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGloba float t = ray->t; float3 new_tp; +#ifdef __VOLUME_SCATTER__ /* randomly scatter, and if we do t is shortened */ if(closure_flag & SD_SCATTER) { /* extinction coefficient */ @@ -330,43 +342,41 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGloba /* pick random color channel, we use the Veach one-sample * model with balance heuristic for the channels */ - float rphase = path_state_rng_1D(kg, rng, state, PRNG_PHASE); + float rphase = path_state_rng_1D_for_decision(kg, rng, state, PRNG_PHASE); int channel = (int)(rphase*3.0f); sd->randb_closure = rphase*3.0f - channel; - float xi = path_state_rng_1D(kg, rng, state, PRNG_SCATTER_DISTANCE); - /* decide if we will hit or miss */ - float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel); - float sample_transmittance = expf(-sample_sigma_t * t); + bool scatter = true; + float xi = path_state_rng_1D_for_decision(kg, rng, state, PRNG_SCATTER_DISTANCE); + + if(probalistic_scatter) { + float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel); + float sample_transmittance = expf(-sample_sigma_t * t); + + if(1.0f - xi >= sample_transmittance) { + scatter = true; + + /* rescale random number so we can reuse it */ + xi = 1.0f - (1.0f - xi - sample_transmittance)/(1.0f - sample_transmittance); - if(xi >= sample_transmittance) { + } + else + scatter = false; + } + + if(scatter) { /* scattering */ float3 pdf; float3 transmittance; float sample_t; - /* rescale random number so we can reuse it */ - xi = (xi - sample_transmittance)/(1.0f - sample_transmittance); - - if(kernel_data.integrator.volume_homogeneous_sampling == 0 || !kernel_data.integrator.num_all_lights) { - /* distance sampling */ - sample_t = kernel_volume_distance_sample(ray->t, sigma_t, channel, xi, &transmittance, &pdf); - } - else { - /* equiangular sampling */ - float3 light_P; - float equi_pdf; - if(!kernel_volume_equiangular_light_position(kg, state, ray, rng, &light_P)) - return VOLUME_PATH_MISSED; - - sample_t = kernel_volume_equiangular_sample(ray, light_P, xi, &equi_pdf); - transmittance = volume_color_transmittance(sigma_t, sample_t); - pdf = make_float3(equi_pdf, equi_pdf, equi_pdf); - } + /* distance sampling */ + sample_t = kernel_volume_distance_sample(ray->t, sigma_t, channel, xi, &transmittance, &pdf); /* modifiy pdf for hit/miss decision */ - pdf *= make_float3(1.0f, 1.0f, 1.0f) - volume_color_transmittance(sigma_t, t); + if(probalistic_scatter) + pdf *= make_float3(1.0f, 1.0f, 1.0f) - volume_color_transmittance(sigma_t, t); new_tp = *throughput * coeff.sigma_s * transmittance / average(pdf); t = sample_t; @@ -378,14 +388,16 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGloba new_tp = *throughput * transmittance / pdf; } } - else if(closure_flag & SD_ABSORPTION) { + else +#endif + if(closure_flag & SD_ABSORPTION) { /* absorption only, no sampling needed */ float3 transmittance = volume_color_transmittance(coeff.sigma_a, t); new_tp = *throughput * transmittance; } /* integrate emission attenuated by extinction */ - if(closure_flag & SD_EMISSION) { + if(L && (closure_flag & SD_EMISSION)) { float3 sigma_t = coeff.sigma_a + coeff.sigma_s; float3 transmittance = volume_color_transmittance(sigma_t, ray->t); float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, ray->t); @@ -408,13 +420,15 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGloba return VOLUME_PATH_ATTENUATED; } -/* heterogeneous volume: integrate stepping through the volume until we - * reach the end, get absorbed entirely, or run out of iterations */ -ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlobals *kg, +/* heterogeneous volume distance sampling: integrate stepping through the + * volume until we reach the end, get absorbed entirely, or run out of + * iterations. this does probalistically scatter or get transmitted through + * for path tracing where we don't want to branch. */ +ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd, PathRadiance *L, float3 *throughput, RNG *rng) { float3 tp = *throughput; - const float tp_eps = 1e-10f; /* todo: this is likely not the right value */ + const float tp_eps = 1e-6f; /* todo: this is likely not the right value */ /* prepare for stepping */ int max_steps = kernel_data.integrator.volume_max_steps; @@ -425,9 +439,12 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo float t = 0.0f; float3 accum_transmittance = make_float3(1.0f, 1.0f, 1.0f); - /* cache some constant variables */ - float xi; - int channel = -1; + /* pick random color channel, we use the Veach one-sample + * model with balance heuristic for the channels */ + float xi = path_state_rng_1D_for_decision(kg, rng, state, PRNG_SCATTER_DISTANCE); + float rphase = path_state_rng_1D_for_decision(kg, rng, state, PRNG_PHASE); + int channel = (int)(rphase*3.0f); + sd->randb_closure = rphase*3.0f - channel; bool has_scatter = false; for(int i = 0; i < max_steps; i++) { @@ -449,25 +466,14 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo float3 transmittance; bool scatter = false; - /* randomly scatter, and if we do dt and new_t are shortened */ + /* distance sampling */ +#ifdef __VOLUME_SCATTER__ if((closure_flag & SD_SCATTER) || (has_scatter && (closure_flag & SD_ABSORPTION))) { has_scatter = true; - /* average sigma_t and sigma_s over segment */ float3 sigma_t = coeff.sigma_a + coeff.sigma_s; float3 sigma_s = coeff.sigma_s; - /* lazily set up variables for sampling */ - if(channel == -1) { - /* pick random color channel, we use the Veach one-sample - * model with balance heuristic for the channels */ - xi = path_state_rng_1D(kg, rng, state, PRNG_SCATTER_DISTANCE); - - float rphase = path_state_rng_1D(kg, rng, state, PRNG_PHASE); - channel = (int)(rphase*3.0f); - sd->randb_closure = rphase*3.0f - channel; - } - /* compute transmittance over full step */ transmittance = volume_color_transmittance(sigma_t, dt); @@ -480,10 +486,12 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo float new_dt = -logf(1.0f - xi)/sample_sigma_t; new_t = t + new_dt; - /* transmittance, throughput */ + /* transmittance and pdf */ float3 new_transmittance = volume_color_transmittance(sigma_t, new_dt); - float pdf = average(sigma_t * new_transmittance); - new_tp = tp * sigma_s * new_transmittance / pdf; + float3 pdf = sigma_t * new_transmittance; + + /* throughput */ + new_tp = tp * sigma_s * new_transmittance / average(pdf); scatter = true; } else { @@ -495,7 +503,9 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo xi = 1.0f - (1.0f - xi)/sample_transmittance; } } - else if(closure_flag & SD_ABSORPTION) { + else +#endif + if(closure_flag & SD_ABSORPTION) { /* absorption only, no sampling needed */ float3 sigma_a = coeff.sigma_a; @@ -504,7 +514,7 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo } /* integrate emission attenuated by absorption */ - if(closure_flag & SD_EMISSION) { + if(L && (closure_flag & SD_EMISSION)) { float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, dt); path_radiance_accum_emission(L, tp, emission, state->bounce); } @@ -518,19 +528,19 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo tp = make_float3(0.0f, 0.0f, 0.0f); break; } + } - /* prepare to scatter to new direction */ - if(scatter) { - /* adjust throughput and move to new location */ - sd->P = ray->P + new_t*ray->D; - *throughput = tp; + /* prepare to scatter to new direction */ + if(scatter) { + /* adjust throughput and move to new location */ + sd->P = ray->P + new_t*ray->D; + *throughput = tp; - return VOLUME_PATH_SCATTERED; - } - else { - /* accumulate transmittance */ - accum_transmittance *= transmittance; - } + return VOLUME_PATH_SCATTERED; + } + else { + /* accumulate transmittance */ + accum_transmittance *= transmittance; } } @@ -545,14 +555,34 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo return VOLUME_PATH_ATTENUATED; } +/* get the volume attenuation and emission over line segment defined by + * ray, with the assumption that there are no surfaces blocking light + * between the endpoints. distance sampling is used to decide if we will + * scatter or not. */ +ccl_device_noinline VolumeIntegrateResult kernel_volume_integrate(KernelGlobals *kg, + PathState *state, ShaderData *sd, Ray *ray, PathRadiance *L, float3 *throughput, RNG *rng, bool heterogeneous) +{ + /* workaround to fix correlation bug in T38710, can find better solution + * in random number generator later, for now this is done here to not impact + * performance of rendering without volumes */ + RNG tmp_rng = cmj_hash(*rng, state->rng_offset); + + shader_setup_from_volume(kg, sd, ray, state->bounce, state->transparent_bounce); + + if(heterogeneous) + return kernel_volume_integrate_heterogeneous_distance(kg, state, ray, sd, L, throughput, &tmp_rng); + else + return kernel_volume_integrate_homogeneous(kg, state, ray, sd, L, throughput, &tmp_rng, true); +} + /* Decoupled Volume Sampling * * VolumeSegment is list of coefficients and transmittance stored at all steps * through a volume. This can then latter be used for decoupled sampling as in: - * "Importance Sampling Techniques for Path Tracing in Participating Media" */ - -/* CPU only because of malloc/free */ -#ifdef __KERNEL_CPU__ + * "Importance Sampling Techniques for Path Tracing in Participating Media" + * + * On the GPU this is only supported for homogeneous volumes (1 step), due to + * no support for malloc/free and too much stack usage with a fix size array. */ typedef struct VolumeStep { float3 sigma_s; /* scatter coefficient */ @@ -571,6 +601,8 @@ typedef struct VolumeSegment { float3 accum_emission; /* accumulated emission at end of segment */ float3 accum_transmittance; /* accumulated transmittance at end of segment */ + + int sampling_method; /* volume sampling method */ } VolumeSegment; /* record volume steps to the end of the volume. @@ -578,10 +610,12 @@ typedef struct VolumeSegment { * it would be nice if we could only record up to the point that we need to scatter, * but the entire segment is needed to do always scattering, rather than probalistically * hitting or missing the volume. if we don't know the transmittance at the end of the - * volume we can't generate stratitied distance samples up to that transmittance */ + * volume we can't generate stratified distance samples up to that transmittance */ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd, VolumeSegment *segment, bool heterogeneous) { + const float tp_eps = 1e-6f; /* todo: this is likely not the right value */ + /* prepare for volume stepping */ int max_steps; float step_size, random_jitter_offset; @@ -608,6 +642,7 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta segment->closure_flag = 0; segment->numsteps = 0; + segment->steps = (VolumeStep*)malloc(sizeof(VolumeStep)*max_steps); VolumeStep *step = segment->steps; @@ -669,6 +704,10 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta t = new_t; if(t == ray->t) break; + + /* stop if nearly all light blocked */ + if(accum_transmittance.x < tp_eps && accum_transmittance.y < tp_eps && accum_transmittance.z < tp_eps) + break; } /* store total emission and transmittance */ @@ -698,35 +737,70 @@ ccl_device void kernel_volume_decoupled_free(KernelGlobals *kg, VolumeSegment *s * scattering, they always scatter if there is any non-zero scattering * coefficient. * - * these also do not do emission or modify throughput. */ + * these also do not do emission or modify throughput. + * + * function is expected to return VOLUME_PATH_SCATTERED when probalistic_scatter is false */ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter( KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd, - float3 *throughput, RNG *rng, VolumeSegment *segment) + float3 *throughput, float rphase, float rscatter, + const VolumeSegment *segment, const float3 *light_P, bool probalistic_scatter) { - int closure_flag = segment->closure_flag; - - if(!(closure_flag & SD_SCATTER)) - return VOLUME_PATH_MISSED; + kernel_assert(segment->closure_flag & SD_SCATTER); /* pick random color channel, we use the Veach one-sample * model with balance heuristic for the channels */ - float rphase = path_state_rng_1D(kg, rng, state, PRNG_PHASE); int channel = (int)(rphase*3.0f); sd->randb_closure = rphase*3.0f - channel; + float xi = rscatter; - float xi = path_state_rng_1D(kg, rng, state, PRNG_SCATTER_DISTANCE); + /* probalistic scattering decision based on transmittance */ + if(probalistic_scatter) { + float sample_transmittance = kernel_volume_channel_get(segment->accum_transmittance, channel); + + if(1.0f - xi >= sample_transmittance) { + /* rescale random number so we can reuse it */ + xi = 1.0f - (1.0f - xi - sample_transmittance)/(1.0f - sample_transmittance); + } + else { + *throughput /= sample_transmittance; + return VOLUME_PATH_MISSED; + } + } VolumeStep *step; float3 transmittance; float pdf, sample_t; + float mis_weight = 1.0f; + bool distance_sample = true; + bool use_mis = false; + + if(segment->sampling_method && light_P) { + if(segment->sampling_method == SD_VOLUME_MIS) { + /* multiple importance sample: randomly pick between + * equiangular and distance sampling strategy */ + if(xi < 0.5f) { + xi *= 2.0f; + } + else { + xi = (xi - 0.5f)*2.0f; + distance_sample = false; + } + + use_mis = true; + } + else { + /* only equiangular sampling */ + distance_sample = false; + } + } /* distance sampling */ - if(kernel_data.integrator.volume_homogeneous_sampling == 0 || !kernel_data.integrator.num_all_lights) { + if(distance_sample) { /* find step in cdf */ step = segment->steps; float prev_t = 0.0f; - float3 step_pdf = make_float3(1.0f, 1.0f, 1.0f); + float3 step_pdf_distance = make_float3(1.0f, 1.0f, 1.0f); if(segment->numsteps > 1) { float prev_cdf = 0.0f; @@ -749,7 +823,7 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter( xi = (xi - prev_cdf)/(step_cdf - prev_cdf); /* pdf for picking step */ - step_pdf = step->cdf_distance - prev_cdf_distance; + step_pdf_distance = step->cdf_distance - prev_cdf_distance; } /* determine range in which we will sample */ @@ -758,35 +832,77 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter( /* sample distance and compute transmittance */ float3 distance_pdf; sample_t = prev_t + kernel_volume_distance_sample(step_t, step->sigma_t, channel, xi, &transmittance, &distance_pdf); - pdf = average(distance_pdf * step_pdf); + + /* modifiy pdf for hit/miss decision */ + if(probalistic_scatter) + distance_pdf *= make_float3(1.0f, 1.0f, 1.0f) - segment->accum_transmittance; + + pdf = average(distance_pdf * step_pdf_distance); + + /* multiple importance sampling */ + if(use_mis) { + float equi_pdf = kernel_volume_equiangular_pdf(ray, *light_P, sample_t); + mis_weight = 2.0f*power_heuristic(pdf, equi_pdf); + } } /* equi-angular sampling */ else { - /* pick position on light */ - float3 light_P; - if(!kernel_volume_equiangular_light_position(kg, state, ray, rng, &light_P)) - return VOLUME_PATH_MISSED; - /* sample distance */ - sample_t = kernel_volume_equiangular_sample(ray, light_P, xi, &pdf); + sample_t = kernel_volume_equiangular_sample(ray, *light_P, xi, &pdf); /* find step in which sampled distance is located */ step = segment->steps; float prev_t = 0.0f; + float3 step_pdf_distance = make_float3(1.0f, 1.0f, 1.0f); if(segment->numsteps > 1) { - /* todo: optimize using binary search */ - for(int i = 0; i < segment->numsteps-1; i++, step++) { - if(sample_t < step->t) + float3 prev_cdf_distance = make_float3(0.0f, 0.0f, 0.0f); + + int numsteps = segment->numsteps; + int high = numsteps - 1; + int low = 0; + int mid; + + while(low < high) { + mid = (low + high) >> 1; + + if(sample_t < step[mid].t) + high = mid; + else if(sample_t >= step[mid + 1].t) + low = mid + 1; + else { + /* found our interval in step[mid] .. step[mid+1] */ + prev_t = step[mid].t; + prev_cdf_distance = step[mid].cdf_distance; + step += mid+1; break; + } + } - prev_t = step->t; + if(low >= numsteps - 1) { + prev_t = step[numsteps - 1].t; + prev_cdf_distance = step[numsteps-1].cdf_distance; + step += numsteps - 1; } + + /* pdf for picking step with distance sampling */ + step_pdf_distance = step->cdf_distance - prev_cdf_distance; } - + + /* determine range in which we will sample */ + float step_t = step->t - prev_t; + float step_sample_t = sample_t - prev_t; + /* compute transmittance */ - transmittance = volume_color_transmittance(step->sigma_t, sample_t - prev_t); + transmittance = volume_color_transmittance(step->sigma_t, step_sample_t); + + /* multiple importance sampling */ + if(use_mis) { + float3 distance_pdf3 = kernel_volume_distance_pdf(step_t, step->sigma_t, step_sample_t); + float distance_pdf = average(distance_pdf3 * step_pdf_distance); + mis_weight = 2.0f*power_heuristic(pdf, distance_pdf); + } } /* compute transmittance up to this step */ @@ -794,7 +910,7 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter( transmittance *= (step-1)->accum_transmittance; /* modify throughput */ - *throughput *= step->sigma_s * transmittance / pdf; + *throughput *= step->sigma_s * transmittance * (mis_weight / pdf); /* evaluate shader to create closures at shading point */ if(segment->numsteps > 1) { @@ -810,40 +926,27 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter( return VOLUME_PATH_SCATTERED; } -#endif - -/* get the volume attenuation and emission over line segment defined by - * ray, with the assumption that there are no surfaces blocking light - * between the endpoints */ -ccl_device_noinline VolumeIntegrateResult kernel_volume_integrate(KernelGlobals *kg, - PathState *state, ShaderData *sd, Ray *ray, PathRadiance *L, float3 *throughput, RNG *rng) +/* decide if we need to use decoupled or not */ +ccl_device bool kernel_volume_use_decoupled(KernelGlobals *kg, bool heterogeneous, bool direct, int sampling_method) { - /* workaround to fix correlation bug in T38710, can find better solution - * in random number generator later, for now this is done here to not impact - * performance of rendering without volumes */ - RNG tmp_rng = cmj_hash(*rng, state->rng_offset); - bool heterogeneous = volume_stack_is_heterogeneous(kg, state->volume_stack); - -#if 0 - /* debugging code to compare decoupled ray marching */ - VolumeSegment segment; - - shader_setup_from_volume(kg, sd, ray, state->bounce, state->transparent_bounce); - kernel_volume_decoupled_record(kg, state, ray, sd, &segment, heterogeneous); - - VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg, state, ray, sd, throughput, &tmp_rng, &segment); - - kernel_volume_decoupled_free(kg, &segment); + /* decoupled ray marching for heterogenous volumes not supported on the GPU, + * which also means equiangular and multiple importance sampling is not + * support for that case */ +#ifdef __KERNEL_GPU__ + if(heterogeneous) + return false; +#endif - return result; -#else - shader_setup_from_volume(kg, sd, ray, state->bounce, state->transparent_bounce); + /* equiangular and multiple importance sampling only implemented for decoupled */ + if(sampling_method != 0) + return true; - if(heterogeneous) - return kernel_volume_integrate_heterogeneous(kg, state, ray, sd, L, throughput, &tmp_rng); + /* for all light sampling use decoupled, reusing shader evaluations is + * typically faster in that case */ + if(direct) + return kernel_data.integrator.sample_all_lights_direct; else - return kernel_volume_integrate_homogeneous(kg, state, ray, sd, L, throughput, &tmp_rng); -#endif + return kernel_data.integrator.sample_all_lights_indirect; } /* Volume Stack @@ -851,17 +954,88 @@ ccl_device_noinline VolumeIntegrateResult kernel_volume_integrate(KernelGlobals * This is an array of object/shared ID's that the current segment of the path * is inside of. */ -ccl_device void kernel_volume_stack_init(KernelGlobals *kg, VolumeStack *stack) +ccl_device void kernel_volume_stack_init(KernelGlobals *kg, + Ray *ray, + VolumeStack *stack) { - /* todo: this assumes camera is always in air, need to detect when it isn't */ - if(kernel_data.background.volume_shader == SHADER_NONE) { - stack[0].shader = SHADER_NONE; + /* NULL ray happens in the baker, does it need proper initialization of + * camera in volume? + */ + if(!kernel_data.cam.is_inside_volume || ray == NULL) { + /* Camera is guaranteed to be in the air, only take background volume + * into account in this case. + */ + if(kernel_data.background.volume_shader != SHADER_NONE) { + stack[0].shader = kernel_data.background.volume_shader; + stack[0].object = PRIM_NONE; + stack[1].shader = SHADER_NONE; + } + else { + stack[0].shader = SHADER_NONE; + } + return; } - else { + + Ray volume_ray = *ray; + volume_ray.t = FLT_MAX; + + int stack_index = 0, enclosed_index = 0; + int enclosed_volumes[VOLUME_STACK_SIZE]; + + while(stack_index < VOLUME_STACK_SIZE - 1 && + enclosed_index < VOLUME_STACK_SIZE - 1) + { + Intersection isect; + if(!scene_intersect_volume(kg, &volume_ray, &isect)) { + break; + } + + ShaderData sd; + shader_setup_from_ray(kg, &sd, &isect, &volume_ray, 0, 0); + if(sd.flag & SD_HAS_VOLUME) { + if(sd.flag & SD_BACKFACING) { + /* If ray exited the volume and never entered to that volume + * it means that camera is inside such a volume. + */ + bool is_enclosed = false; + for(int i = 0; i < enclosed_index; ++i) { + if(enclosed_volumes[i] == sd.object) { + is_enclosed = true; + break; + } + } + if(is_enclosed == false) { + stack[stack_index].object = sd.object; + stack[stack_index].shader = sd.shader; + ++stack_index; + } + } + else { + /* If ray from camera enters the volume, this volume shouldn't + * be added to the stak on exit. + */ + enclosed_volumes[enclosed_index++] = sd.object; + } + } + + /* Move ray forward. */ + volume_ray.P = ray_offset(sd.P, -sd.Ng); + } + /* stack_index of 0 means quick checks outside of the kernel gave false + * positive, nothing to worry about, just we've wasted quite a few of + * ticks just to come into conclusion that camera is in the air. + * + * In this case we're doing the same above -- check whether background has + * volume. + */ + if(stack_index == 0 && kernel_data.background.volume_shader == SHADER_NONE) { stack[0].shader = kernel_data.background.volume_shader; stack[0].object = PRIM_NONE; stack[1].shader = SHADER_NONE; } + else { + stack[stack_index].shader = SHADER_NONE; + } } ccl_device void kernel_volume_stack_enter_exit(KernelGlobals *kg, ShaderData *sd, VolumeStack *stack) @@ -910,4 +1084,3 @@ ccl_device void kernel_volume_stack_enter_exit(KernelGlobals *kg, ShaderData *sd } CCL_NAMESPACE_END - |