Changeset View
Changeset View
Standalone View
Standalone View
intern/cycles/kernel/integrator/integrator_shade_volume.h
| Show First 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
| typedef struct VolumeShaderCoefficients { | typedef struct VolumeShaderCoefficients { | ||||
| float3 sigma_t; | float3 sigma_t; | ||||
| float3 sigma_s; | float3 sigma_s; | ||||
| float3 emission; | float3 emission; | ||||
| } VolumeShaderCoefficients; | } VolumeShaderCoefficients; | ||||
| /* Evaluate shader to get extinction coefficient at P. */ | /* Evaluate shader to get extinction coefficient at P. */ | ||||
| ccl_device_inline bool shadow_volume_shader_sample(INTEGRATOR_STATE_ARGS, | ccl_device_inline bool shadow_volume_shader_sample(KernelGlobals kg, | ||||
| IntegratorState state, | |||||
| ccl_private ShaderData *ccl_restrict sd, | ccl_private ShaderData *ccl_restrict sd, | ||||
| ccl_private float3 *ccl_restrict extinction) | ccl_private float3 *ccl_restrict extinction) | ||||
| { | { | ||||
| shader_eval_volume<true>(INTEGRATOR_STATE_PASS, sd, PATH_RAY_SHADOW, [=](const int i) { | shader_eval_volume<true>(kg, state, sd, PATH_RAY_SHADOW, [=](const int i) { | ||||
| return integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_PASS, i); | return integrator_state_read_shadow_volume_stack(state, i); | ||||
| }); | }); | ||||
| if (!(sd->flag & SD_EXTINCTION)) { | if (!(sd->flag & SD_EXTINCTION)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| const float density = object_volume_density(kg, sd->object); | const float density = object_volume_density(kg, sd->object); | ||||
| *extinction = sd->closure_transparent_extinction * density; | *extinction = sd->closure_transparent_extinction * density; | ||||
| return true; | return true; | ||||
| } | } | ||||
| /* Evaluate shader to get absorption, scattering and emission at P. */ | /* Evaluate shader to get absorption, scattering and emission at P. */ | ||||
| ccl_device_inline bool volume_shader_sample(INTEGRATOR_STATE_ARGS, | ccl_device_inline bool volume_shader_sample(KernelGlobals kg, | ||||
| IntegratorState state, | |||||
| ccl_private ShaderData *ccl_restrict sd, | ccl_private ShaderData *ccl_restrict sd, | ||||
| ccl_private VolumeShaderCoefficients *coeff) | ccl_private VolumeShaderCoefficients *coeff) | ||||
| { | { | ||||
| const int path_flag = INTEGRATOR_STATE(path, flag); | const int path_flag = INTEGRATOR_STATE(state, path, flag); | ||||
| shader_eval_volume<false>(INTEGRATOR_STATE_PASS, sd, path_flag, [=](const int i) { | shader_eval_volume<false>(kg, state, sd, path_flag, [=](const int i) { | ||||
| return integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); | return integrator_state_read_volume_stack(state, i); | ||||
| }); | }); | ||||
| if (!(sd->flag & (SD_EXTINCTION | SD_SCATTER | SD_EMISSION))) { | if (!(sd->flag & (SD_EXTINCTION | SD_SCATTER | SD_EMISSION))) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| coeff->sigma_s = zero_float3(); | coeff->sigma_s = zero_float3(); | ||||
| coeff->sigma_t = (sd->flag & SD_EXTINCTION) ? sd->closure_transparent_extinction : zero_float3(); | coeff->sigma_t = (sd->flag & SD_EXTINCTION) ? sd->closure_transparent_extinction : zero_float3(); | ||||
| Show All 12 Lines | ccl_device_inline bool volume_shader_sample(KernelGlobals kg, | ||||
| const float density = object_volume_density(kg, sd->object); | const float density = object_volume_density(kg, sd->object); | ||||
| coeff->sigma_s *= density; | coeff->sigma_s *= density; | ||||
| coeff->sigma_t *= density; | coeff->sigma_t *= density; | ||||
| coeff->emission *= density; | coeff->emission *= density; | ||||
| return true; | return true; | ||||
| } | } | ||||
| ccl_device_forceinline void volume_step_init(ccl_global const KernelGlobals *kg, | ccl_device_forceinline void volume_step_init(KernelGlobals kg, | ||||
| ccl_private const RNGState *rng_state, | ccl_private const RNGState *rng_state, | ||||
| const float object_step_size, | const float object_step_size, | ||||
| float t, | float t, | ||||
| ccl_private float *step_size, | ccl_private float *step_size, | ||||
| ccl_private float *step_shade_offset, | ccl_private float *step_shade_offset, | ||||
| ccl_private float *steps_offset, | ccl_private float *steps_offset, | ||||
| ccl_private int *max_steps) | ccl_private int *max_steps) | ||||
| { | { | ||||
| Show All 29 Lines | |||||
| /* Volume Shadows | /* Volume Shadows | ||||
| * | * | ||||
| * These functions are used to attenuate shadow rays to lights. Both absorption | * These functions are used to attenuate shadow rays to lights. Both absorption | ||||
| * and scattering will block light, represented by the extinction coefficient. */ | * and scattering will block light, represented by the extinction coefficient. */ | ||||
| # if 0 | # if 0 | ||||
| /* homogeneous volume: assume shader evaluation at the starts gives | /* homogeneous volume: assume shader evaluation at the starts gives | ||||
| * the extinction coefficient for the entire line segment */ | * the extinction coefficient for the entire line segment */ | ||||
| ccl_device void volume_shadow_homogeneous(INTEGRATOR_STATE_ARGS, | ccl_device void volume_shadow_homogeneous(KernelGlobals kg, IntegratorState state, | ||||
| ccl_private Ray *ccl_restrict ray, | ccl_private Ray *ccl_restrict ray, | ||||
| ccl_private ShaderData *ccl_restrict sd, | ccl_private ShaderData *ccl_restrict sd, | ||||
| ccl_global float3 *ccl_restrict throughput) | ccl_global float3 *ccl_restrict throughput) | ||||
| { | { | ||||
| float3 sigma_t = zero_float3(); | float3 sigma_t = zero_float3(); | ||||
| if (shadow_volume_shader_sample(INTEGRATOR_STATE_PASS, sd, &sigma_t)) { | if (shadow_volume_shader_sample(kg, state, sd, &sigma_t)) { | ||||
| *throughput *= volume_color_transmittance(sigma_t, ray->t); | *throughput *= volume_color_transmittance(sigma_t, ray->t); | ||||
| } | } | ||||
| } | } | ||||
| # endif | # endif | ||||
| /* heterogeneous volume: integrate stepping through the volume until we | /* heterogeneous volume: integrate stepping through the volume until we | ||||
| * reach the end, get absorbed entirely, or run out of iterations */ | * reach the end, get absorbed entirely, or run out of iterations */ | ||||
| ccl_device void volume_shadow_heterogeneous(INTEGRATOR_STATE_ARGS, | ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, | ||||
| IntegratorState state, | |||||
| ccl_private Ray *ccl_restrict ray, | ccl_private Ray *ccl_restrict ray, | ||||
| ccl_private ShaderData *ccl_restrict sd, | ccl_private ShaderData *ccl_restrict sd, | ||||
| ccl_private float3 *ccl_restrict throughput, | ccl_private float3 *ccl_restrict throughput, | ||||
| const float object_step_size) | const float object_step_size) | ||||
| { | { | ||||
| /* Load random number state. */ | /* Load random number state. */ | ||||
| RNGState rng_state; | RNGState rng_state; | ||||
| shadow_path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); | shadow_path_state_rng_load(state, &rng_state); | ||||
| float3 tp = *throughput; | float3 tp = *throughput; | ||||
| /* Prepare for stepping. | /* Prepare for stepping. | ||||
| * For shadows we do not offset all segments, since the starting point is | * For shadows we do not offset all segments, since the starting point is | ||||
| * already a random distance inside the volume. It also appears to create | * already a random distance inside the volume. It also appears to create | ||||
| * banding artifacts for unknown reasons. */ | * banding artifacts for unknown reasons. */ | ||||
| int max_steps; | int max_steps; | ||||
| Show All 18 Lines | for (int i = 0; i < max_steps; i++) { | ||||
| float new_t = min(ray->t, (i + steps_offset) * step_size); | float new_t = min(ray->t, (i + steps_offset) * step_size); | ||||
| float dt = new_t - t; | float dt = new_t - t; | ||||
| float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); | float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); | ||||
| float3 sigma_t = zero_float3(); | float3 sigma_t = zero_float3(); | ||||
| /* compute attenuation over segment */ | /* compute attenuation over segment */ | ||||
| sd->P = new_P; | sd->P = new_P; | ||||
| if (shadow_volume_shader_sample(INTEGRATOR_STATE_PASS, sd, &sigma_t)) { | if (shadow_volume_shader_sample(kg, state, sd, &sigma_t)) { | ||||
| /* Compute `expf()` only for every Nth step, to save some calculations | /* Compute `expf()` only for every Nth step, to save some calculations | ||||
| * because `exp(a)*exp(b) = exp(a+b)`, also do a quick #VOLUME_THROUGHPUT_EPSILON | * because `exp(a)*exp(b) = exp(a+b)`, also do a quick #VOLUME_THROUGHPUT_EPSILON | ||||
| * check then. */ | * check then. */ | ||||
| sum += (-sigma_t * dt); | sum += (-sigma_t * dt); | ||||
| if ((i & 0x07) == 0) { /* TODO: Other interval? */ | if ((i & 0x07) == 0) { /* TODO: Other interval? */ | ||||
| tp = *throughput * exp3(sum); | tp = *throughput * exp3(sum); | ||||
| /* stop if nearly all light is blocked */ | /* stop if nearly all light is blocked */ | ||||
| ▲ Show 20 Lines • Show All 266 Lines • ▼ Show 20 Lines | ccl_device_forceinline void volume_integrate_step_scattering( | ||||
| } | } | ||||
| } | } | ||||
| /* heterogeneous volume distance sampling: integrate stepping through the | /* heterogeneous volume distance sampling: integrate stepping through the | ||||
| * volume until we reach the end, get absorbed entirely, or run out of | * volume until we reach the end, get absorbed entirely, or run out of | ||||
| * iterations. this does probabilistically scatter or get transmitted through | * iterations. this does probabilistically scatter or get transmitted through | ||||
| * for path tracing where we don't want to branch. */ | * for path tracing where we don't want to branch. */ | ||||
| ccl_device_forceinline void volume_integrate_heterogeneous( | ccl_device_forceinline void volume_integrate_heterogeneous( | ||||
| INTEGRATOR_STATE_ARGS, | KernelGlobals kg, | ||||
| IntegratorState state, | |||||
| ccl_private Ray *ccl_restrict ray, | ccl_private Ray *ccl_restrict ray, | ||||
| ccl_private ShaderData *ccl_restrict sd, | ccl_private ShaderData *ccl_restrict sd, | ||||
| ccl_private const RNGState *rng_state, | ccl_private const RNGState *rng_state, | ||||
| ccl_global float *ccl_restrict render_buffer, | ccl_global float *ccl_restrict render_buffer, | ||||
| const float object_step_size, | const float object_step_size, | ||||
| const VolumeSampleMethod direct_sample_method, | const VolumeSampleMethod direct_sample_method, | ||||
| const float3 equiangular_light_P, | const float3 equiangular_light_P, | ||||
| ccl_private VolumeIntegrateResult &result) | ccl_private VolumeIntegrateResult &result) | ||||
| Show All 33 Lines | else { | ||||
| vstate.rscatter = (vstate.rscatter - 0.5f) * 2.0f; | vstate.rscatter = (vstate.rscatter - 0.5f) * 2.0f; | ||||
| vstate.direct_sample_method = VOLUME_SAMPLE_EQUIANGULAR; | vstate.direct_sample_method = VOLUME_SAMPLE_EQUIANGULAR; | ||||
| } | } | ||||
| } | } | ||||
| vstate.equiangular_pdf = 0.0f; | vstate.equiangular_pdf = 0.0f; | ||||
| vstate.distance_pdf = 1.0f; | vstate.distance_pdf = 1.0f; | ||||
| /* Initialize volume integration result. */ | /* Initialize volume integration result. */ | ||||
| const float3 throughput = INTEGRATOR_STATE(path, throughput); | const float3 throughput = INTEGRATOR_STATE(state, path, throughput); | ||||
| result.direct_throughput = throughput; | result.direct_throughput = throughput; | ||||
| result.indirect_throughput = throughput; | result.indirect_throughput = throughput; | ||||
| /* Equiangular sampling: compute distance and PDF in advance. */ | /* Equiangular sampling: compute distance and PDF in advance. */ | ||||
| if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) { | if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) { | ||||
| result.direct_t = volume_equiangular_sample( | result.direct_t = volume_equiangular_sample( | ||||
| ray, equiangular_light_P, vstate.rscatter, &vstate.equiangular_pdf); | ray, equiangular_light_P, vstate.rscatter, &vstate.equiangular_pdf); | ||||
| } | } | ||||
| # ifdef __DENOISING_FEATURES__ | # ifdef __DENOISING_FEATURES__ | ||||
| const bool write_denoising_features = (INTEGRATOR_STATE(path, flag) & | const bool write_denoising_features = (INTEGRATOR_STATE(state, path, flag) & | ||||
| PATH_RAY_DENOISING_FEATURES); | PATH_RAY_DENOISING_FEATURES); | ||||
| float3 accum_albedo = zero_float3(); | float3 accum_albedo = zero_float3(); | ||||
| # endif | # endif | ||||
| float3 accum_emission = zero_float3(); | float3 accum_emission = zero_float3(); | ||||
| for (int i = 0; i < max_steps; i++) { | for (int i = 0; i < max_steps; i++) { | ||||
| /* Advance to new position */ | /* Advance to new position */ | ||||
| vstate.end_t = min(ray->t, (i + steps_offset) * step_size); | vstate.end_t = min(ray->t, (i + steps_offset) * step_size); | ||||
| const float shade_t = vstate.start_t + (vstate.end_t - vstate.start_t) * step_shade_offset; | const float shade_t = vstate.start_t + (vstate.end_t - vstate.start_t) * step_shade_offset; | ||||
| sd->P = ray->P + ray->D * shade_t; | sd->P = ray->P + ray->D * shade_t; | ||||
| /* compute segment */ | /* compute segment */ | ||||
| VolumeShaderCoefficients coeff ccl_optional_struct_init; | VolumeShaderCoefficients coeff ccl_optional_struct_init; | ||||
| if (volume_shader_sample(INTEGRATOR_STATE_PASS, sd, &coeff)) { | if (volume_shader_sample(kg, state, sd, &coeff)) { | ||||
| const int closure_flag = sd->flag; | const int closure_flag = sd->flag; | ||||
| /* Evaluate transmittance over segment. */ | /* Evaluate transmittance over segment. */ | ||||
| const float dt = (vstate.end_t - vstate.start_t); | const float dt = (vstate.end_t - vstate.start_t); | ||||
| const float3 transmittance = (closure_flag & SD_EXTINCTION) ? | const float3 transmittance = (closure_flag & SD_EXTINCTION) ? | ||||
| volume_color_transmittance(coeff.sigma_t, dt) : | volume_color_transmittance(coeff.sigma_t, dt) : | ||||
| one_float3(); | one_float3(); | ||||
| ▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | # endif | ||||
| vstate.start_t = vstate.end_t; | vstate.start_t = vstate.end_t; | ||||
| if (vstate.start_t == ray->t) { | if (vstate.start_t == ray->t) { | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| /* Write accumulated emission. */ | /* Write accumulated emission. */ | ||||
| if (!is_zero(accum_emission)) { | if (!is_zero(accum_emission)) { | ||||
| kernel_accum_emission( | kernel_accum_emission(kg, state, result.indirect_throughput, accum_emission, render_buffer); | ||||
| INTEGRATOR_STATE_PASS, result.indirect_throughput, accum_emission, render_buffer); | |||||
| } | } | ||||
| # ifdef __DENOISING_FEATURES__ | # ifdef __DENOISING_FEATURES__ | ||||
| /* Write denoising features. */ | /* Write denoising features. */ | ||||
| if (write_denoising_features) { | if (write_denoising_features) { | ||||
| kernel_write_denoising_features_volume( | kernel_write_denoising_features_volume( | ||||
| INTEGRATOR_STATE_PASS, accum_albedo, result.indirect_scatter, render_buffer); | kg, state, accum_albedo, result.indirect_scatter, render_buffer); | ||||
| } | } | ||||
| # endif /* __DENOISING_FEATURES__ */ | # endif /* __DENOISING_FEATURES__ */ | ||||
| } | } | ||||
| # ifdef __EMISSION__ | # ifdef __EMISSION__ | ||||
| /* Path tracing: sample point on light and evaluate light shader, then | /* Path tracing: sample point on light and evaluate light shader, then | ||||
| * queue shadow ray to be traced. */ | * queue shadow ray to be traced. */ | ||||
| ccl_device_forceinline bool integrate_volume_sample_light( | ccl_device_forceinline bool integrate_volume_sample_light( | ||||
| INTEGRATOR_STATE_ARGS, | KernelGlobals kg, | ||||
| IntegratorState state, | |||||
| ccl_private const ShaderData *ccl_restrict sd, | ccl_private const ShaderData *ccl_restrict sd, | ||||
| ccl_private const RNGState *ccl_restrict rng_state, | ccl_private const RNGState *ccl_restrict rng_state, | ||||
| ccl_private LightSample *ccl_restrict ls) | ccl_private LightSample *ccl_restrict ls) | ||||
| { | { | ||||
| /* Test if there is a light or BSDF that needs direct light. */ | /* Test if there is a light or BSDF that needs direct light. */ | ||||
| if (!kernel_data.integrator.use_direct_light) { | if (!kernel_data.integrator.use_direct_light) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* Sample position on a light. */ | /* Sample position on a light. */ | ||||
| const int path_flag = INTEGRATOR_STATE(path, flag); | const int path_flag = INTEGRATOR_STATE(state, path, flag); | ||||
| const uint bounce = INTEGRATOR_STATE(path, bounce); | const uint bounce = INTEGRATOR_STATE(state, path, bounce); | ||||
| float light_u, light_v; | float light_u, light_v; | ||||
| 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); | ||||
| light_distribution_sample_from_volume_segment( | light_distribution_sample_from_volume_segment( | ||||
| kg, light_u, light_v, sd->time, sd->P, bounce, path_flag, ls); | kg, light_u, light_v, sd->time, sd->P, bounce, path_flag, ls); | ||||
| if (ls->shader & SHADER_EXCLUDE_SCATTER) { | if (ls->shader & SHADER_EXCLUDE_SCATTER) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| /* Path tracing: sample point on light and evaluate light shader, then | /* Path tracing: sample point on light and evaluate light shader, then | ||||
| * queue shadow ray to be traced. */ | * queue shadow ray to be traced. */ | ||||
| ccl_device_forceinline void integrate_volume_direct_light( | ccl_device_forceinline void integrate_volume_direct_light( | ||||
| INTEGRATOR_STATE_ARGS, | KernelGlobals kg, | ||||
| IntegratorState state, | |||||
| ccl_private const ShaderData *ccl_restrict sd, | ccl_private const ShaderData *ccl_restrict sd, | ||||
| ccl_private const RNGState *ccl_restrict rng_state, | ccl_private const RNGState *ccl_restrict rng_state, | ||||
| const float3 P, | const float3 P, | ||||
| ccl_private const ShaderVolumePhases *ccl_restrict phases, | ccl_private const ShaderVolumePhases *ccl_restrict phases, | ||||
| ccl_private const float3 throughput, | ccl_private const float3 throughput, | ||||
| ccl_private LightSample *ccl_restrict ls) | ccl_private LightSample *ccl_restrict ls) | ||||
| { | { | ||||
| PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_DIRECT_LIGHT); | PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_DIRECT_LIGHT); | ||||
| if (!kernel_data.integrator.use_direct_light) { | if (!kernel_data.integrator.use_direct_light) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Sample position on the same light again, now from the shading | /* Sample position on the same light again, now from the shading | ||||
| * point where we scattered. | * point where we scattered. | ||||
| * | * | ||||
| * TODO: decorrelate random numbers and use light_sample_new_position to | * TODO: decorrelate random numbers and use light_sample_new_position to | ||||
| * avoid resampling the CDF. */ | * avoid resampling the CDF. */ | ||||
| { | { | ||||
| const int path_flag = INTEGRATOR_STATE(path, flag); | const int path_flag = INTEGRATOR_STATE(state, path, flag); | ||||
| const uint bounce = INTEGRATOR_STATE(path, bounce); | const uint bounce = INTEGRATOR_STATE(state, path, bounce); | ||||
| float light_u, light_v; | float light_u, light_v; | ||||
| 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); | ||||
| if (!light_distribution_sample_from_position( | if (!light_distribution_sample_from_position( | ||||
| kg, light_u, light_v, sd->time, P, bounce, path_flag, ls)) { | kg, light_u, light_v, sd->time, P, bounce, path_flag, ls)) { | ||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| if (ls->shader & SHADER_EXCLUDE_SCATTER) { | if (ls->shader & SHADER_EXCLUDE_SCATTER) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Evaluate light shader. | /* Evaluate light shader. | ||||
| * | * | ||||
| * TODO: can we reuse sd memory? In theory we can move this after | * TODO: can we reuse sd memory? In theory we can move this after | ||||
| * integrate_surface_bounce, evaluate the BSDF, and only then evaluate | * integrate_surface_bounce, evaluate the BSDF, and only then evaluate | ||||
| * the light shader. This could also move to its own kernel, for | * the light shader. This could also move to its own kernel, for | ||||
| * non-constant light sources. */ | * non-constant light sources. */ | ||||
| ShaderDataTinyStorage emission_sd_storage; | ShaderDataTinyStorage emission_sd_storage; | ||||
| ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); | ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); | ||||
| const float3 light_eval = light_sample_shader_eval( | const float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, ls, sd->time); | ||||
| INTEGRATOR_STATE_PASS, emission_sd, ls, sd->time); | |||||
| if (is_zero(light_eval)) { | if (is_zero(light_eval)) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Evaluate BSDF. */ | /* Evaluate BSDF. */ | ||||
| BsdfEval phase_eval ccl_optional_struct_init; | BsdfEval phase_eval ccl_optional_struct_init; | ||||
| const float phase_pdf = shader_volume_phase_eval(kg, sd, phases, ls->D, &phase_eval); | const float phase_pdf = shader_volume_phase_eval(kg, sd, phases, ls->D, &phase_eval); | ||||
| Show All 11 Lines | ccl_device_forceinline void integrate_volume_direct_light( | ||||
| } | } | ||||
| /* Create shadow ray. */ | /* Create shadow ray. */ | ||||
| Ray ray ccl_optional_struct_init; | Ray ray ccl_optional_struct_init; | ||||
| light_sample_to_volume_shadow_ray(kg, sd, ls, P, &ray); | light_sample_to_volume_shadow_ray(kg, sd, ls, P, &ray); | ||||
| const bool is_light = light_sample_is_light(ls); | const bool is_light = light_sample_is_light(ls); | ||||
| /* Write shadow ray and associated state to global memory. */ | /* Write shadow ray and associated state to global memory. */ | ||||
| integrator_state_write_shadow_ray(INTEGRATOR_STATE_PASS, &ray); | integrator_state_write_shadow_ray(kg, state, &ray); | ||||
| /* Copy state from main path to shadow path. */ | /* Copy state from main path to shadow path. */ | ||||
| const uint16_t bounce = INTEGRATOR_STATE(path, bounce); | const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); | ||||
| const uint16_t transparent_bounce = INTEGRATOR_STATE(path, transparent_bounce); | const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); | ||||
| uint32_t shadow_flag = INTEGRATOR_STATE(path, flag); | uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag); | ||||
| shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; | shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; | ||||
| shadow_flag |= PATH_RAY_VOLUME_PASS; | shadow_flag |= PATH_RAY_VOLUME_PASS; | ||||
| const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval); | const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval); | ||||
| if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { | if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { | ||||
| const float3 diffuse_glossy_ratio = (bounce == 0) ? | const float3 diffuse_glossy_ratio = (bounce == 0) ? | ||||
| one_float3() : | one_float3() : | ||||
| INTEGRATOR_STATE(path, diffuse_glossy_ratio); | INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); | ||||
| INTEGRATOR_STATE_WRITE(shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; | INTEGRATOR_STATE_WRITE(state, shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; | ||||
| } | } | ||||
| INTEGRATOR_STATE_WRITE(shadow_path, flag) = shadow_flag; | INTEGRATOR_STATE_WRITE(state, shadow_path, flag) = shadow_flag; | ||||
| INTEGRATOR_STATE_WRITE(shadow_path, bounce) = bounce; | INTEGRATOR_STATE_WRITE(state, shadow_path, bounce) = bounce; | ||||
| INTEGRATOR_STATE_WRITE(shadow_path, transparent_bounce) = transparent_bounce; | INTEGRATOR_STATE_WRITE(state, shadow_path, transparent_bounce) = transparent_bounce; | ||||
| INTEGRATOR_STATE_WRITE(shadow_path, throughput) = throughput_phase; | INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) = throughput_phase; | ||||
| if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_PASS) { | if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_PASS) { | ||||
| INTEGRATOR_STATE_WRITE(shadow_path, unshadowed_throughput) = throughput; | INTEGRATOR_STATE_WRITE(state, shadow_path, unshadowed_throughput) = throughput; | ||||
| } | } | ||||
| integrator_state_copy_volume_stack_to_shadow(INTEGRATOR_STATE_PASS); | integrator_state_copy_volume_stack_to_shadow(kg, state); | ||||
| /* Branch off shadow kernel. */ | /* Branch off shadow kernel. */ | ||||
| INTEGRATOR_SHADOW_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); | INTEGRATOR_SHADOW_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); | ||||
| } | } | ||||
| # endif | # endif | ||||
| /* Path tracing: scatter in new direction using phase function */ | /* Path tracing: scatter in new direction using phase function */ | ||||
| ccl_device_forceinline bool integrate_volume_phase_scatter( | ccl_device_forceinline bool integrate_volume_phase_scatter( | ||||
| INTEGRATOR_STATE_ARGS, | KernelGlobals kg, | ||||
| IntegratorState state, | |||||
| ccl_private ShaderData *sd, | ccl_private ShaderData *sd, | ||||
| ccl_private const RNGState *rng_state, | ccl_private const RNGState *rng_state, | ||||
| ccl_private const ShaderVolumePhases *phases) | ccl_private const ShaderVolumePhases *phases) | ||||
| { | { | ||||
| PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INDIRECT_LIGHT); | PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INDIRECT_LIGHT); | ||||
| float phase_u, phase_v; | float phase_u, phase_v; | ||||
| path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &phase_u, &phase_v); | path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &phase_u, &phase_v); | ||||
| Show All 14 Lines | const int label = shader_volume_phase_sample(kg, | ||||
| &phase_domega_in, | &phase_domega_in, | ||||
| &phase_pdf); | &phase_pdf); | ||||
| if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) { | if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* Setup ray. */ | /* Setup ray. */ | ||||
| INTEGRATOR_STATE_WRITE(ray, P) = sd->P; | INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; | ||||
| INTEGRATOR_STATE_WRITE(ray, D) = normalize(phase_omega_in); | INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_omega_in); | ||||
| INTEGRATOR_STATE_WRITE(ray, t) = FLT_MAX; | INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX; | ||||
| # ifdef __RAY_DIFFERENTIALS__ | # ifdef __RAY_DIFFERENTIALS__ | ||||
| INTEGRATOR_STATE_WRITE(ray, dP) = differential_make_compact(sd->dP); | INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); | ||||
| INTEGRATOR_STATE_WRITE(ray, dD) = differential_make_compact(phase_domega_in); | INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(phase_domega_in); | ||||
| # endif | # endif | ||||
| /* Update throughput. */ | /* Update throughput. */ | ||||
| const float3 throughput = INTEGRATOR_STATE(path, throughput); | const float3 throughput = INTEGRATOR_STATE(state, path, throughput); | ||||
| const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval) / phase_pdf; | const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval) / phase_pdf; | ||||
| INTEGRATOR_STATE_WRITE(path, throughput) = throughput_phase; | INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput_phase; | ||||
| if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { | if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { | ||||
| INTEGRATOR_STATE_WRITE(path, diffuse_glossy_ratio) = one_float3(); | INTEGRATOR_STATE_WRITE(state, path, diffuse_glossy_ratio) = one_float3(); | ||||
| } | } | ||||
| /* Update path state */ | /* Update path state */ | ||||
| INTEGRATOR_STATE_WRITE(path, mis_ray_pdf) = phase_pdf; | INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf; | ||||
| INTEGRATOR_STATE_WRITE(path, mis_ray_t) = 0.0f; | INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; | ||||
| INTEGRATOR_STATE_WRITE(path, min_ray_pdf) = fminf(phase_pdf, | INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf( | ||||
| INTEGRATOR_STATE(path, min_ray_pdf)); | phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf)); | ||||
| path_state_next(INTEGRATOR_STATE_PASS, label); | path_state_next(kg, state, label); | ||||
| return true; | return true; | ||||
| } | } | ||||
| /* get the volume attenuation and emission over line segment defined by | /* get the volume attenuation and emission over line segment defined by | ||||
| * ray, with the assumption that there are no surfaces blocking light | * ray, with the assumption that there are no surfaces blocking light | ||||
| * between the endpoints. distance sampling is used to decide if we will | * between the endpoints. distance sampling is used to decide if we will | ||||
| * scatter or not. */ | * scatter or not. */ | ||||
| ccl_device VolumeIntegrateEvent volume_integrate(INTEGRATOR_STATE_ARGS, | ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg, | ||||
| IntegratorState state, | |||||
| ccl_private Ray *ccl_restrict ray, | ccl_private Ray *ccl_restrict ray, | ||||
| ccl_global float *ccl_restrict render_buffer) | ccl_global float *ccl_restrict render_buffer) | ||||
| { | { | ||||
| ShaderData sd; | ShaderData sd; | ||||
| shader_setup_from_volume(kg, &sd, ray); | shader_setup_from_volume(kg, &sd, ray); | ||||
| /* Load random number state. */ | /* Load random number state. */ | ||||
| RNGState rng_state; | RNGState rng_state; | ||||
| path_state_rng_load(INTEGRATOR_STATE_PASS, &rng_state); | path_state_rng_load(state, &rng_state); | ||||
| /* Sample light ahead of volume stepping, for equiangular sampling. */ | /* Sample light ahead of volume stepping, for equiangular sampling. */ | ||||
| /* TODO: distant lights are ignored now, but could instead use even distribution. */ | /* TODO: distant lights are ignored now, but could instead use even distribution. */ | ||||
| LightSample ls ccl_optional_struct_init; | LightSample ls ccl_optional_struct_init; | ||||
| const bool need_light_sample = !(INTEGRATOR_STATE(path, flag) & PATH_RAY_TERMINATE); | const bool need_light_sample = !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE); | ||||
| const bool have_equiangular_sample = need_light_sample && | const bool have_equiangular_sample = need_light_sample && | ||||
| integrate_volume_sample_light( | integrate_volume_sample_light( | ||||
| INTEGRATOR_STATE_PASS, &sd, &rng_state, &ls) && | kg, state, &sd, &rng_state, &ls) && | ||||
| (ls.t != FLT_MAX); | (ls.t != FLT_MAX); | ||||
| VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ? | VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ? | ||||
| volume_stack_sample_method(INTEGRATOR_STATE_PASS) : | volume_stack_sample_method(kg, state) : | ||||
| VOLUME_SAMPLE_DISTANCE; | VOLUME_SAMPLE_DISTANCE; | ||||
| /* Step through volume. */ | /* Step through volume. */ | ||||
| const float step_size = volume_stack_step_size(INTEGRATOR_STATE_PASS, [=](const int i) { | const float step_size = volume_stack_step_size( | ||||
| return integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i); | kg, state, [=](const int i) { return integrator_state_read_volume_stack(state, i); }); | ||||
| }); | |||||
| /* TODO: expensive to zero closures? */ | /* TODO: expensive to zero closures? */ | ||||
| VolumeIntegrateResult result = {}; | VolumeIntegrateResult result = {}; | ||||
| volume_integrate_heterogeneous(INTEGRATOR_STATE_PASS, | volume_integrate_heterogeneous(kg, | ||||
| state, | |||||
| ray, | ray, | ||||
| &sd, | &sd, | ||||
| &rng_state, | &rng_state, | ||||
| render_buffer, | render_buffer, | ||||
| step_size, | step_size, | ||||
| direct_sample_method, | direct_sample_method, | ||||
| ls.P, | ls.P, | ||||
| result); | result); | ||||
| /* Perform path termination. The intersect_closest will have already marked this path | /* Perform path termination. The intersect_closest will have already marked this path | ||||
| * to be terminated. That will shading evaluating to leave out any scattering closures, | * to be terminated. That will shading evaluating to leave out any scattering closures, | ||||
| * but emission and absorption are still handled for multiple importance sampling. */ | * but emission and absorption are still handled for multiple importance sampling. */ | ||||
| const uint32_t path_flag = INTEGRATOR_STATE(path, flag); | const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); | ||||
| const float probability = (path_flag & PATH_RAY_TERMINATE_IN_NEXT_VOLUME) ? | const float probability = (path_flag & PATH_RAY_TERMINATE_IN_NEXT_VOLUME) ? | ||||
| 0.0f : | 0.0f : | ||||
| path_state_continuation_probability(INTEGRATOR_STATE_PASS, | path_state_continuation_probability(kg, state, path_flag); | ||||
| path_flag); | |||||
| if (probability == 0.0f) { | if (probability == 0.0f) { | ||||
| return VOLUME_PATH_MISSED; | return VOLUME_PATH_MISSED; | ||||
| } | } | ||||
| /* Direct light. */ | /* Direct light. */ | ||||
| if (result.direct_scatter) { | if (result.direct_scatter) { | ||||
| const float3 direct_P = ray->P + result.direct_t * ray->D; | const float3 direct_P = ray->P + result.direct_t * ray->D; | ||||
| result.direct_throughput /= probability; | result.direct_throughput /= probability; | ||||
| integrate_volume_direct_light(INTEGRATOR_STATE_PASS, | integrate_volume_direct_light(kg, | ||||
| state, | |||||
| &sd, | &sd, | ||||
| &rng_state, | &rng_state, | ||||
| direct_P, | direct_P, | ||||
| &result.direct_phases, | &result.direct_phases, | ||||
| result.direct_throughput, | result.direct_throughput, | ||||
| &ls); | &ls); | ||||
| } | } | ||||
| /* Indirect light. | /* Indirect light. | ||||
| * | * | ||||
| * Only divide throughput by probability if we scatter. For the attenuation | * Only divide throughput by probability if we scatter. For the attenuation | ||||
| * case the next surface will already do this division. */ | * case the next surface will already do this division. */ | ||||
| if (result.indirect_scatter) { | if (result.indirect_scatter) { | ||||
| result.indirect_throughput /= probability; | result.indirect_throughput /= probability; | ||||
| } | } | ||||
| INTEGRATOR_STATE_WRITE(path, throughput) = result.indirect_throughput; | INTEGRATOR_STATE_WRITE(state, path, throughput) = result.indirect_throughput; | ||||
| if (result.indirect_scatter) { | if (result.indirect_scatter) { | ||||
| sd.P = ray->P + result.indirect_t * ray->D; | sd.P = ray->P + result.indirect_t * ray->D; | ||||
| if (integrate_volume_phase_scatter( | if (integrate_volume_phase_scatter(kg, state, &sd, &rng_state, &result.indirect_phases)) { | ||||
| INTEGRATOR_STATE_PASS, &sd, &rng_state, &result.indirect_phases)) { | |||||
| return VOLUME_PATH_SCATTERED; | return VOLUME_PATH_SCATTERED; | ||||
| } | } | ||||
| else { | else { | ||||
| return VOLUME_PATH_MISSED; | return VOLUME_PATH_MISSED; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| return VOLUME_PATH_ATTENUATED; | return VOLUME_PATH_ATTENUATED; | ||||
| } | } | ||||
| } | } | ||||
| #endif | #endif | ||||
| ccl_device void integrator_shade_volume(INTEGRATOR_STATE_ARGS, | ccl_device void integrator_shade_volume(KernelGlobals kg, | ||||
| IntegratorState state, | |||||
| ccl_global float *ccl_restrict render_buffer) | ccl_global float *ccl_restrict render_buffer) | ||||
| { | { | ||||
| PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_SETUP); | PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_SETUP); | ||||
| #ifdef __VOLUME__ | #ifdef __VOLUME__ | ||||
| /* Setup shader data. */ | /* Setup shader data. */ | ||||
| Ray ray ccl_optional_struct_init; | Ray ray ccl_optional_struct_init; | ||||
| integrator_state_read_ray(INTEGRATOR_STATE_PASS, &ray); | integrator_state_read_ray(kg, state, &ray); | ||||
| Intersection isect ccl_optional_struct_init; | Intersection isect ccl_optional_struct_init; | ||||
| integrator_state_read_isect(INTEGRATOR_STATE_PASS, &isect); | integrator_state_read_isect(kg, state, &isect); | ||||
| /* Set ray length to current segment. */ | /* Set ray length to current segment. */ | ||||
| ray.t = (isect.prim != PRIM_NONE) ? isect.t : FLT_MAX; | ray.t = (isect.prim != PRIM_NONE) ? isect.t : FLT_MAX; | ||||
| /* Clean volume stack for background rays. */ | /* Clean volume stack for background rays. */ | ||||
| if (isect.prim == PRIM_NONE) { | if (isect.prim == PRIM_NONE) { | ||||
| volume_stack_clean(INTEGRATOR_STATE_PASS); | volume_stack_clean(kg, state); | ||||
| } | } | ||||
| VolumeIntegrateEvent event = volume_integrate(INTEGRATOR_STATE_PASS, &ray, render_buffer); | VolumeIntegrateEvent event = volume_integrate(kg, state, &ray, render_buffer); | ||||
| if (event == VOLUME_PATH_SCATTERED) { | if (event == VOLUME_PATH_SCATTERED) { | ||||
| /* Queue intersect_closest kernel. */ | /* Queue intersect_closest kernel. */ | ||||
| INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, | INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, | ||||
| DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); | DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); | ||||
| return; | return; | ||||
| } | } | ||||
| else if (event == VOLUME_PATH_MISSED) { | else if (event == VOLUME_PATH_MISSED) { | ||||
| Show All 14 Lines | else if (isect.type & PRIMITIVE_LAMP) { | ||||
| return; | return; | ||||
| } | } | ||||
| else { | else { | ||||
| /* Hit a surface, continue with surface kernel unless terminated. */ | /* Hit a surface, continue with surface kernel unless terminated. */ | ||||
| const int shader = intersection_get_shader(kg, &isect); | const int shader = intersection_get_shader(kg, &isect); | ||||
| const int flags = kernel_tex_fetch(__shaders, shader).flags; | const int flags = kernel_tex_fetch(__shaders, shader).flags; | ||||
| integrator_intersect_shader_next_kernel<DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME>( | integrator_intersect_shader_next_kernel<DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME>( | ||||
| INTEGRATOR_STATE_PASS, &isect, shader, flags); | kg, state, &isect, shader, flags); | ||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| #endif /* __VOLUME__ */ | #endif /* __VOLUME__ */ | ||||
| } | } | ||||
| CCL_NAMESPACE_END | CCL_NAMESPACE_END | ||||