Changeset View
Changeset View
Standalone View
Standalone View
intern/cycles/kernel/kernel_accumulate.h
| Show First 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | |||||
| /* -------------------------------------------------------------------- | /* -------------------------------------------------------------------- | ||||
| * Clamping | * Clamping | ||||
| * | * | ||||
| * Clamping is done on a per-contribution basis so that we can write directly | * Clamping is done on a per-contribution basis so that we can write directly | ||||
| * to render buffers instead of using per-thread memory, and to avoid the | * to render buffers instead of using per-thread memory, and to avoid the | ||||
| * impact of clamping on other contributions. */ | * impact of clamping on other contributions. */ | ||||
| ccl_device_forceinline void kernel_accum_clamp(ccl_global const KernelGlobals *kg, | ccl_device_forceinline void kernel_accum_clamp(KernelGlobals kg, ccl_private float3 *L, int bounce) | ||||
| ccl_private float3 *L, | |||||
| int bounce) | |||||
| { | { | ||||
| #ifdef __KERNEL_DEBUG_NAN__ | #ifdef __KERNEL_DEBUG_NAN__ | ||||
| if (!isfinite3_safe(*L)) { | if (!isfinite3_safe(*L)) { | ||||
| kernel_assert(!"Cycles sample with non-finite value detected"); | kernel_assert(!"Cycles sample with non-finite value detected"); | ||||
| } | } | ||||
| #endif | #endif | ||||
| /* Make sure all components are finite, allowing the contribution to be usable by adaptive | /* Make sure all components are finite, allowing the contribution to be usable by adaptive | ||||
| * sampling convergence check, but also to make it so render result never causes issues with | * sampling convergence check, but also to make it so render result never causes issues with | ||||
| Show All 11 Lines | |||||
| } | } | ||||
| /* -------------------------------------------------------------------- | /* -------------------------------------------------------------------- | ||||
| * Pass accumulation utilities. | * Pass accumulation utilities. | ||||
| */ | */ | ||||
| /* Get pointer to pixel in render buffer. */ | /* Get pointer to pixel in render buffer. */ | ||||
| ccl_device_forceinline ccl_global float *kernel_accum_pixel_render_buffer( | ccl_device_forceinline ccl_global float *kernel_accum_pixel_render_buffer( | ||||
| INTEGRATOR_STATE_CONST_ARGS, ccl_global float *ccl_restrict render_buffer) | KernelGlobals kg, ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer) | ||||
| { | { | ||||
| const uint32_t render_pixel_index = INTEGRATOR_STATE(path, render_pixel_index); | const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); | ||||
| const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * | const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * | ||||
| kernel_data.film.pass_stride; | kernel_data.film.pass_stride; | ||||
| return render_buffer + render_buffer_offset; | return render_buffer + render_buffer_offset; | ||||
| } | } | ||||
| /* -------------------------------------------------------------------- | /* -------------------------------------------------------------------- | ||||
| * Adaptive sampling. | * Adaptive sampling. | ||||
| */ | */ | ||||
| ccl_device_inline int kernel_accum_sample(INTEGRATOR_STATE_CONST_ARGS, | ccl_device_inline int kernel_accum_sample(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| ccl_global float *ccl_restrict render_buffer, | ccl_global float *ccl_restrict render_buffer, | ||||
| int sample) | int sample) | ||||
| { | { | ||||
| if (kernel_data.film.pass_sample_count == PASS_UNUSED) { | if (kernel_data.film.pass_sample_count == PASS_UNUSED) { | ||||
| return sample; | return sample; | ||||
| } | } | ||||
| ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, | ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); | ||||
| render_buffer); | |||||
| return atomic_fetch_and_add_uint32((uint *)(buffer) + kernel_data.film.pass_sample_count, 1); | return atomic_fetch_and_add_uint32((uint *)(buffer) + kernel_data.film.pass_sample_count, 1); | ||||
| } | } | ||||
| ccl_device void kernel_accum_adaptive_buffer(INTEGRATOR_STATE_CONST_ARGS, | ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| const float3 contribution, | const float3 contribution, | ||||
| ccl_global float *ccl_restrict buffer) | ccl_global float *ccl_restrict buffer) | ||||
| { | { | ||||
| /* Adaptive Sampling. Fill the additional buffer with the odd samples and calculate our stopping | /* Adaptive Sampling. Fill the additional buffer with the odd samples and calculate our stopping | ||||
| * criteria. This is the heuristic from "A hierarchical automatic stopping condition for Monte | * criteria. This is the heuristic from "A hierarchical automatic stopping condition for Monte | ||||
| * Carlo global illumination" except that here it is applied per pixel and not in hierarchical | * Carlo global illumination" except that here it is applied per pixel and not in hierarchical | ||||
| * tiles. */ | * tiles. */ | ||||
| if (kernel_data.film.pass_adaptive_aux_buffer == PASS_UNUSED) { | if (kernel_data.film.pass_adaptive_aux_buffer == PASS_UNUSED) { | ||||
| return; | return; | ||||
| } | } | ||||
| const int sample = INTEGRATOR_STATE(path, sample); | const int sample = INTEGRATOR_STATE(state, path, sample); | ||||
| if (sample_is_even(kernel_data.integrator.sampling_pattern, sample)) { | if (sample_is_even(kernel_data.integrator.sampling_pattern, sample)) { | ||||
| kernel_write_pass_float4( | kernel_write_pass_float4( | ||||
| buffer + kernel_data.film.pass_adaptive_aux_buffer, | buffer + kernel_data.film.pass_adaptive_aux_buffer, | ||||
| make_float4(contribution.x * 2.0f, contribution.y * 2.0f, contribution.z * 2.0f, 0.0f)); | make_float4(contribution.x * 2.0f, contribution.y * 2.0f, contribution.z * 2.0f, 0.0f)); | ||||
| } | } | ||||
| } | } | ||||
| /* -------------------------------------------------------------------- | /* -------------------------------------------------------------------- | ||||
| * Shadow catcher. | * Shadow catcher. | ||||
| */ | */ | ||||
| #ifdef __SHADOW_CATCHER__ | #ifdef __SHADOW_CATCHER__ | ||||
| /* Accumulate contribution to the Shadow Catcher pass. | /* Accumulate contribution to the Shadow Catcher pass. | ||||
| * | * | ||||
| * Returns truth if the contribution is fully handled here and is not to be added to the other | * Returns truth if the contribution is fully handled here and is not to be added to the other | ||||
| * passes (like combined, adaptive sampling). */ | * passes (like combined, adaptive sampling). */ | ||||
| ccl_device bool kernel_accum_shadow_catcher(INTEGRATOR_STATE_CONST_ARGS, | ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| const float3 contribution, | const float3 contribution, | ||||
| ccl_global float *ccl_restrict buffer) | ccl_global float *ccl_restrict buffer) | ||||
| { | { | ||||
| if (!kernel_data.integrator.has_shadow_catcher) { | if (!kernel_data.integrator.has_shadow_catcher) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED); | kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED); | ||||
| kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); | kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); | ||||
| /* Matte pass. */ | /* Matte pass. */ | ||||
| if (kernel_shadow_catcher_is_matte_path(INTEGRATOR_STATE_PASS)) { | if (kernel_shadow_catcher_is_matte_path(kg, state)) { | ||||
| kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher_matte, contribution); | kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher_matte, contribution); | ||||
| /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive | /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive | ||||
| * sampling is based on how noisy the combined pass is as if there were no catchers in the | * sampling is based on how noisy the combined pass is as if there were no catchers in the | ||||
| * scene. */ | * scene. */ | ||||
| } | } | ||||
| /* Shadow catcher pass. */ | /* Shadow catcher pass. */ | ||||
| if (kernel_shadow_catcher_is_object_pass(INTEGRATOR_STATE_PASS)) { | if (kernel_shadow_catcher_is_object_pass(kg, state)) { | ||||
| kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); | kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); | ||||
| return true; | return true; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| ccl_device bool kernel_accum_shadow_catcher_transparent(INTEGRATOR_STATE_CONST_ARGS, | ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| const float3 contribution, | const float3 contribution, | ||||
| const float transparent, | const float transparent, | ||||
| ccl_global float *ccl_restrict buffer) | ccl_global float *ccl_restrict buffer) | ||||
| { | { | ||||
| if (!kernel_data.integrator.has_shadow_catcher) { | if (!kernel_data.integrator.has_shadow_catcher) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED); | kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED); | ||||
| kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); | kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); | ||||
| if (INTEGRATOR_STATE(path, flag) & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { | if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| /* Matte pass. */ | /* Matte pass. */ | ||||
| if (kernel_shadow_catcher_is_matte_path(INTEGRATOR_STATE_PASS)) { | if (kernel_shadow_catcher_is_matte_path(kg, state)) { | ||||
| kernel_write_pass_float4( | kernel_write_pass_float4( | ||||
| buffer + kernel_data.film.pass_shadow_catcher_matte, | buffer + kernel_data.film.pass_shadow_catcher_matte, | ||||
| make_float4(contribution.x, contribution.y, contribution.z, transparent)); | make_float4(contribution.x, contribution.y, contribution.z, transparent)); | ||||
| /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive | /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive | ||||
| * sampling is based on how noisy the combined pass is as if there were no catchers in the | * sampling is based on how noisy the combined pass is as if there were no catchers in the | ||||
| * scene. */ | * scene. */ | ||||
| } | } | ||||
| /* Shadow catcher pass. */ | /* Shadow catcher pass. */ | ||||
| if (kernel_shadow_catcher_is_object_pass(INTEGRATOR_STATE_PASS)) { | if (kernel_shadow_catcher_is_object_pass(kg, state)) { | ||||
| /* NOTE: The transparency of the shadow catcher pass is ignored. It is not needed for the | /* NOTE: The transparency of the shadow catcher pass is ignored. It is not needed for the | ||||
| * calculation and the alpha channel of the pass contains numbers of samples contributed to a | * calculation and the alpha channel of the pass contains numbers of samples contributed to a | ||||
| * pixel of the pass. */ | * pixel of the pass. */ | ||||
| kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); | kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); | ||||
| return true; | return true; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| ccl_device void kernel_accum_shadow_catcher_transparent_only(INTEGRATOR_STATE_CONST_ARGS, | ccl_device void kernel_accum_shadow_catcher_transparent_only(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| const float transparent, | const float transparent, | ||||
| ccl_global float *ccl_restrict buffer) | ccl_global float *ccl_restrict buffer) | ||||
| { | { | ||||
| if (!kernel_data.integrator.has_shadow_catcher) { | if (!kernel_data.integrator.has_shadow_catcher) { | ||||
| return; | return; | ||||
| } | } | ||||
| kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); | kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); | ||||
| /* Matte pass. */ | /* Matte pass. */ | ||||
| if (kernel_shadow_catcher_is_matte_path(INTEGRATOR_STATE_PASS)) { | if (kernel_shadow_catcher_is_matte_path(kg, state)) { | ||||
| kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, transparent); | kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, transparent); | ||||
| } | } | ||||
| } | } | ||||
| #endif /* __SHADOW_CATCHER__ */ | #endif /* __SHADOW_CATCHER__ */ | ||||
| /* -------------------------------------------------------------------- | /* -------------------------------------------------------------------- | ||||
| * Render passes. | * Render passes. | ||||
| */ | */ | ||||
| /* Write combined pass. */ | /* Write combined pass. */ | ||||
| ccl_device_inline void kernel_accum_combined_pass(INTEGRATOR_STATE_CONST_ARGS, | ccl_device_inline void kernel_accum_combined_pass(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| const float3 contribution, | const float3 contribution, | ||||
| ccl_global float *ccl_restrict buffer) | ccl_global float *ccl_restrict buffer) | ||||
| { | { | ||||
| #ifdef __SHADOW_CATCHER__ | #ifdef __SHADOW_CATCHER__ | ||||
| if (kernel_accum_shadow_catcher(INTEGRATOR_STATE_PASS, contribution, buffer)) { | if (kernel_accum_shadow_catcher(kg, state, contribution, buffer)) { | ||||
| return; | return; | ||||
| } | } | ||||
| #endif | #endif | ||||
| if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { | if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { | ||||
| kernel_write_pass_float3(buffer + kernel_data.film.pass_combined, contribution); | kernel_write_pass_float3(buffer + kernel_data.film.pass_combined, contribution); | ||||
| } | } | ||||
| kernel_accum_adaptive_buffer(INTEGRATOR_STATE_PASS, contribution, buffer); | kernel_accum_adaptive_buffer(kg, state, contribution, buffer); | ||||
| } | } | ||||
| /* Write combined pass with transparency. */ | /* Write combined pass with transparency. */ | ||||
| ccl_device_inline void kernel_accum_combined_transparent_pass(INTEGRATOR_STATE_CONST_ARGS, | ccl_device_inline void kernel_accum_combined_transparent_pass(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| const float3 contribution, | const float3 contribution, | ||||
| const float transparent, | const float transparent, | ||||
| ccl_global float *ccl_restrict | ccl_global float *ccl_restrict | ||||
| buffer) | buffer) | ||||
| { | { | ||||
| #ifdef __SHADOW_CATCHER__ | #ifdef __SHADOW_CATCHER__ | ||||
| if (kernel_accum_shadow_catcher_transparent( | if (kernel_accum_shadow_catcher_transparent(kg, state, contribution, transparent, buffer)) { | ||||
| INTEGRATOR_STATE_PASS, contribution, transparent, buffer)) { | |||||
| return; | return; | ||||
| } | } | ||||
| #endif | #endif | ||||
| if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { | if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { | ||||
| kernel_write_pass_float4( | kernel_write_pass_float4( | ||||
| buffer + kernel_data.film.pass_combined, | buffer + kernel_data.film.pass_combined, | ||||
| make_float4(contribution.x, contribution.y, contribution.z, transparent)); | make_float4(contribution.x, contribution.y, contribution.z, transparent)); | ||||
| } | } | ||||
| kernel_accum_adaptive_buffer(INTEGRATOR_STATE_PASS, contribution, buffer); | kernel_accum_adaptive_buffer(kg, state, contribution, buffer); | ||||
| } | } | ||||
| /* Write background or emission to appropriate pass. */ | /* Write background or emission to appropriate pass. */ | ||||
| ccl_device_inline void kernel_accum_emission_or_background_pass(INTEGRATOR_STATE_CONST_ARGS, | ccl_device_inline void kernel_accum_emission_or_background_pass(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| float3 contribution, | float3 contribution, | ||||
| ccl_global float *ccl_restrict | ccl_global float *ccl_restrict | ||||
| buffer, | buffer, | ||||
| const int pass) | const int pass) | ||||
| { | { | ||||
| if (!(kernel_data.film.light_pass_flag & PASS_ANY)) { | if (!(kernel_data.film.light_pass_flag & PASS_ANY)) { | ||||
| return; | return; | ||||
| } | } | ||||
| #ifdef __PASSES__ | #ifdef __PASSES__ | ||||
| const int path_flag = INTEGRATOR_STATE(path, flag); | const int path_flag = INTEGRATOR_STATE(state, path, flag); | ||||
| int pass_offset = PASS_UNUSED; | int pass_offset = PASS_UNUSED; | ||||
| /* Denoising albedo. */ | /* Denoising albedo. */ | ||||
| # ifdef __DENOISING_FEATURES__ | # ifdef __DENOISING_FEATURES__ | ||||
| if (path_flag & PATH_RAY_DENOISING_FEATURES) { | if (path_flag & PATH_RAY_DENOISING_FEATURES) { | ||||
| if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { | if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { | ||||
| const float3 denoising_feature_throughput = INTEGRATOR_STATE(path, | const float3 denoising_feature_throughput = INTEGRATOR_STATE( | ||||
| denoising_feature_throughput); | state, path, denoising_feature_throughput); | ||||
| const float3 denoising_albedo = denoising_feature_throughput * contribution; | const float3 denoising_albedo = denoising_feature_throughput * contribution; | ||||
| kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); | kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); | ||||
| } | } | ||||
| } | } | ||||
| # endif /* __DENOISING_FEATURES__ */ | # endif /* __DENOISING_FEATURES__ */ | ||||
| if (!(path_flag & PATH_RAY_ANY_PASS)) { | if (!(path_flag & PATH_RAY_ANY_PASS)) { | ||||
| /* Directly visible, write to emission or background pass. */ | /* Directly visible, write to emission or background pass. */ | ||||
| pass_offset = pass; | pass_offset = pass; | ||||
| } | } | ||||
| else if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) { | else if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) { | ||||
| /* Indirectly visible through reflection. */ | /* Indirectly visible through reflection. */ | ||||
| const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ? | const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ? | ||||
| ((INTEGRATOR_STATE(path, bounce) == 1) ? | ((INTEGRATOR_STATE(state, path, bounce) == 1) ? | ||||
| kernel_data.film.pass_glossy_direct : | kernel_data.film.pass_glossy_direct : | ||||
| kernel_data.film.pass_glossy_indirect) : | kernel_data.film.pass_glossy_indirect) : | ||||
| ((INTEGRATOR_STATE(path, bounce) == 1) ? | ((INTEGRATOR_STATE(state, path, bounce) == 1) ? | ||||
| kernel_data.film.pass_transmission_direct : | kernel_data.film.pass_transmission_direct : | ||||
| kernel_data.film.pass_transmission_indirect); | kernel_data.film.pass_transmission_indirect); | ||||
| if (glossy_pass_offset != PASS_UNUSED) { | if (glossy_pass_offset != PASS_UNUSED) { | ||||
| /* Glossy is a subset of the throughput, reconstruct it here using the | /* Glossy is a subset of the throughput, reconstruct it here using the | ||||
| * diffuse-glossy ratio. */ | * diffuse-glossy ratio. */ | ||||
| const float3 ratio = INTEGRATOR_STATE(path, diffuse_glossy_ratio); | const float3 ratio = INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); | ||||
| const float3 glossy_contribution = (one_float3() - ratio) * contribution; | const float3 glossy_contribution = (one_float3() - ratio) * contribution; | ||||
| kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution); | kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution); | ||||
| } | } | ||||
| /* Reconstruct diffuse subset of throughput. */ | /* Reconstruct diffuse subset of throughput. */ | ||||
| pass_offset = (INTEGRATOR_STATE(path, bounce) == 1) ? kernel_data.film.pass_diffuse_direct : | pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ? | ||||
| kernel_data.film.pass_diffuse_direct : | |||||
| kernel_data.film.pass_diffuse_indirect; | kernel_data.film.pass_diffuse_indirect; | ||||
| if (pass_offset != PASS_UNUSED) { | if (pass_offset != PASS_UNUSED) { | ||||
| contribution *= INTEGRATOR_STATE(path, diffuse_glossy_ratio); | contribution *= INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); | ||||
| } | } | ||||
| } | } | ||||
| else if (path_flag & PATH_RAY_VOLUME_PASS) { | else if (path_flag & PATH_RAY_VOLUME_PASS) { | ||||
| /* Indirectly visible through volume. */ | /* Indirectly visible through volume. */ | ||||
| pass_offset = (INTEGRATOR_STATE(path, bounce) == 1) ? kernel_data.film.pass_volume_direct : | pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ? | ||||
| kernel_data.film.pass_volume_direct : | |||||
| kernel_data.film.pass_volume_indirect; | kernel_data.film.pass_volume_indirect; | ||||
| } | } | ||||
| /* Single write call for GPU coherence. */ | /* Single write call for GPU coherence. */ | ||||
| if (pass_offset != PASS_UNUSED) { | if (pass_offset != PASS_UNUSED) { | ||||
| kernel_write_pass_float3(buffer + pass_offset, contribution); | kernel_write_pass_float3(buffer + pass_offset, contribution); | ||||
| } | } | ||||
| #endif /* __PASSES__ */ | #endif /* __PASSES__ */ | ||||
| } | } | ||||
| /* Write light contribution to render buffer. */ | /* Write light contribution to render buffer. */ | ||||
| ccl_device_inline void kernel_accum_light(INTEGRATOR_STATE_CONST_ARGS, | ccl_device_inline void kernel_accum_light(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| ccl_global float *ccl_restrict render_buffer) | ccl_global float *ccl_restrict render_buffer) | ||||
| { | { | ||||
| /* The throughput for shadow paths already contains the light shader evaluation. */ | /* The throughput for shadow paths already contains the light shader evaluation. */ | ||||
| float3 contribution = INTEGRATOR_STATE(shadow_path, throughput); | float3 contribution = INTEGRATOR_STATE(state, shadow_path, throughput); | ||||
| kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(shadow_path, bounce)); | kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, shadow_path, bounce)); | ||||
| ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, | ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); | ||||
| render_buffer); | |||||
| kernel_accum_combined_pass(INTEGRATOR_STATE_PASS, contribution, buffer); | kernel_accum_combined_pass(kg, state, contribution, buffer); | ||||
| #ifdef __PASSES__ | #ifdef __PASSES__ | ||||
| if (kernel_data.film.light_pass_flag & PASS_ANY) { | if (kernel_data.film.light_pass_flag & PASS_ANY) { | ||||
| const int path_flag = INTEGRATOR_STATE(shadow_path, flag); | const int path_flag = INTEGRATOR_STATE(state, shadow_path, flag); | ||||
| int pass_offset = PASS_UNUSED; | int pass_offset = PASS_UNUSED; | ||||
| if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) { | if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) { | ||||
| /* Indirectly visible through reflection. */ | /* Indirectly visible through reflection. */ | ||||
| const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ? | const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ? | ||||
| ((INTEGRATOR_STATE(shadow_path, bounce) == 0) ? | ((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? | ||||
| kernel_data.film.pass_glossy_direct : | kernel_data.film.pass_glossy_direct : | ||||
| kernel_data.film.pass_glossy_indirect) : | kernel_data.film.pass_glossy_indirect) : | ||||
| ((INTEGRATOR_STATE(shadow_path, bounce) == 0) ? | ((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? | ||||
| kernel_data.film.pass_transmission_direct : | kernel_data.film.pass_transmission_direct : | ||||
| kernel_data.film.pass_transmission_indirect); | kernel_data.film.pass_transmission_indirect); | ||||
| if (glossy_pass_offset != PASS_UNUSED) { | if (glossy_pass_offset != PASS_UNUSED) { | ||||
| /* Glossy is a subset of the throughput, reconstruct it here using the | /* Glossy is a subset of the throughput, reconstruct it here using the | ||||
| * diffuse-glossy ratio. */ | * diffuse-glossy ratio. */ | ||||
| const float3 ratio = INTEGRATOR_STATE(shadow_path, diffuse_glossy_ratio); | const float3 ratio = INTEGRATOR_STATE(state, shadow_path, diffuse_glossy_ratio); | ||||
| const float3 glossy_contribution = (one_float3() - ratio) * contribution; | const float3 glossy_contribution = (one_float3() - ratio) * contribution; | ||||
| kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution); | kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution); | ||||
| } | } | ||||
| /* Reconstruct diffuse subset of throughput. */ | /* Reconstruct diffuse subset of throughput. */ | ||||
| pass_offset = (INTEGRATOR_STATE(shadow_path, bounce) == 0) ? | pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? | ||||
| kernel_data.film.pass_diffuse_direct : | kernel_data.film.pass_diffuse_direct : | ||||
| kernel_data.film.pass_diffuse_indirect; | kernel_data.film.pass_diffuse_indirect; | ||||
| if (pass_offset != PASS_UNUSED) { | if (pass_offset != PASS_UNUSED) { | ||||
| contribution *= INTEGRATOR_STATE(shadow_path, diffuse_glossy_ratio); | contribution *= INTEGRATOR_STATE(state, shadow_path, diffuse_glossy_ratio); | ||||
| } | } | ||||
| } | } | ||||
| else if (path_flag & PATH_RAY_VOLUME_PASS) { | else if (path_flag & PATH_RAY_VOLUME_PASS) { | ||||
| /* Indirectly visible through volume. */ | /* Indirectly visible through volume. */ | ||||
| pass_offset = (INTEGRATOR_STATE(shadow_path, bounce) == 0) ? | pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? | ||||
| kernel_data.film.pass_volume_direct : | kernel_data.film.pass_volume_direct : | ||||
| kernel_data.film.pass_volume_indirect; | kernel_data.film.pass_volume_indirect; | ||||
| } | } | ||||
| /* Single write call for GPU coherence. */ | /* Single write call for GPU coherence. */ | ||||
| if (pass_offset != PASS_UNUSED) { | if (pass_offset != PASS_UNUSED) { | ||||
| kernel_write_pass_float3(buffer + pass_offset, contribution); | kernel_write_pass_float3(buffer + pass_offset, contribution); | ||||
| } | } | ||||
| /* Write shadow pass. */ | /* Write shadow pass. */ | ||||
| if (kernel_data.film.pass_shadow != PASS_UNUSED && (path_flag & PATH_RAY_SHADOW_FOR_LIGHT) && | if (kernel_data.film.pass_shadow != PASS_UNUSED && (path_flag & PATH_RAY_SHADOW_FOR_LIGHT) && | ||||
| (path_flag & PATH_RAY_CAMERA)) { | (path_flag & PATH_RAY_CAMERA)) { | ||||
| const float3 unshadowed_throughput = INTEGRATOR_STATE(shadow_path, unshadowed_throughput); | const float3 unshadowed_throughput = INTEGRATOR_STATE( | ||||
| const float3 shadowed_throughput = INTEGRATOR_STATE(shadow_path, throughput); | state, shadow_path, unshadowed_throughput); | ||||
| const float3 shadowed_throughput = INTEGRATOR_STATE(state, shadow_path, throughput); | |||||
| const float3 shadow = safe_divide_float3_float3(shadowed_throughput, unshadowed_throughput) * | const float3 shadow = safe_divide_float3_float3(shadowed_throughput, unshadowed_throughput) * | ||||
| kernel_data.film.pass_shadow_scale; | kernel_data.film.pass_shadow_scale; | ||||
| kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow, shadow); | kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow, shadow); | ||||
| } | } | ||||
| } | } | ||||
| #endif | #endif | ||||
| } | } | ||||
| /* Write transparency to render buffer. | /* Write transparency to render buffer. | ||||
| * | * | ||||
| * Note that we accumulate transparency = 1 - alpha in the render buffer. | * Note that we accumulate transparency = 1 - alpha in the render buffer. | ||||
| * Otherwise we'd have to write alpha on path termination, which happens | * Otherwise we'd have to write alpha on path termination, which happens | ||||
| * in many places. */ | * in many places. */ | ||||
| ccl_device_inline void kernel_accum_transparent(INTEGRATOR_STATE_CONST_ARGS, | ccl_device_inline void kernel_accum_transparent(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| const float transparent, | const float transparent, | ||||
| ccl_global float *ccl_restrict render_buffer) | ccl_global float *ccl_restrict render_buffer) | ||||
| { | { | ||||
| ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, | ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); | ||||
| render_buffer); | |||||
| if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { | if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { | ||||
| kernel_write_pass_float(buffer + kernel_data.film.pass_combined + 3, transparent); | kernel_write_pass_float(buffer + kernel_data.film.pass_combined + 3, transparent); | ||||
| } | } | ||||
| kernel_accum_shadow_catcher_transparent_only(INTEGRATOR_STATE_PASS, transparent, buffer); | kernel_accum_shadow_catcher_transparent_only(kg, state, transparent, buffer); | ||||
| } | } | ||||
| /* Write background contribution to render buffer. | /* Write background contribution to render buffer. | ||||
| * | * | ||||
| * Includes transparency, matching kernel_accum_transparent. */ | * Includes transparency, matching kernel_accum_transparent. */ | ||||
| ccl_device_inline void kernel_accum_background(INTEGRATOR_STATE_CONST_ARGS, | ccl_device_inline void kernel_accum_background(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| const float3 L, | const float3 L, | ||||
| const float transparent, | const float transparent, | ||||
| const bool is_transparent_background_ray, | const bool is_transparent_background_ray, | ||||
| ccl_global float *ccl_restrict render_buffer) | ccl_global float *ccl_restrict render_buffer) | ||||
| { | { | ||||
| float3 contribution = INTEGRATOR_STATE(path, throughput) * L; | float3 contribution = INTEGRATOR_STATE(state, path, throughput) * L; | ||||
| kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(path, bounce) - 1); | kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); | ||||
| ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, | ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); | ||||
| render_buffer); | |||||
| if (is_transparent_background_ray) { | if (is_transparent_background_ray) { | ||||
| kernel_accum_transparent(INTEGRATOR_STATE_PASS, transparent, render_buffer); | kernel_accum_transparent(kg, state, transparent, render_buffer); | ||||
| } | } | ||||
| else { | else { | ||||
| kernel_accum_combined_transparent_pass( | kernel_accum_combined_transparent_pass(kg, state, contribution, transparent, buffer); | ||||
| INTEGRATOR_STATE_PASS, contribution, transparent, buffer); | |||||
| } | } | ||||
| kernel_accum_emission_or_background_pass( | kernel_accum_emission_or_background_pass( | ||||
| INTEGRATOR_STATE_PASS, contribution, buffer, kernel_data.film.pass_background); | kg, state, contribution, buffer, kernel_data.film.pass_background); | ||||
| } | } | ||||
| /* Write emission to render buffer. */ | /* Write emission to render buffer. */ | ||||
| ccl_device_inline void kernel_accum_emission(INTEGRATOR_STATE_CONST_ARGS, | ccl_device_inline void kernel_accum_emission(KernelGlobals kg, | ||||
| ConstIntegratorState state, | |||||
| const float3 throughput, | const float3 throughput, | ||||
| const float3 L, | const float3 L, | ||||
| ccl_global float *ccl_restrict render_buffer) | ccl_global float *ccl_restrict render_buffer) | ||||
| { | { | ||||
| float3 contribution = throughput * L; | float3 contribution = throughput * L; | ||||
| kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(path, bounce) - 1); | kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); | ||||
| ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS, | ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); | ||||
| render_buffer); | |||||
| kernel_accum_combined_pass(INTEGRATOR_STATE_PASS, contribution, buffer); | kernel_accum_combined_pass(kg, state, contribution, buffer); | ||||
| kernel_accum_emission_or_background_pass( | kernel_accum_emission_or_background_pass( | ||||
| INTEGRATOR_STATE_PASS, contribution, buffer, kernel_data.film.pass_emission); | kg, state, contribution, buffer, kernel_data.film.pass_emission); | ||||
| } | } | ||||
| CCL_NAMESPACE_END | CCL_NAMESPACE_END | ||||