/* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ #pragma once #include "kernel/film/light_passes.h" #include "kernel/integrator/surface_shader.h" #include "kernel/light/light.h" #include "kernel/light/sample.h" CCL_NAMESPACE_BEGIN ccl_device Spectrum integrator_eval_background_shader(KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer) { const int shader = kernel_data.background.surface_shader; const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); /* Use visibility flag to skip lights. */ if (shader & SHADER_EXCLUDE_ANY) { if (((shader & SHADER_EXCLUDE_DIFFUSE) && (path_flag & PATH_RAY_DIFFUSE)) || ((shader & SHADER_EXCLUDE_GLOSSY) && ((path_flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || ((shader & SHADER_EXCLUDE_TRANSMIT) && (path_flag & PATH_RAY_TRANSMIT)) || ((shader & SHADER_EXCLUDE_CAMERA) && (path_flag & PATH_RAY_CAMERA)) || ((shader & SHADER_EXCLUDE_SCATTER) && (path_flag & PATH_RAY_VOLUME_SCATTER))) return zero_spectrum(); } /* Use fast constant background color if available. */ Spectrum L = zero_spectrum(); if (surface_shader_constant_emission(kg, shader, &L)) { return L; } /* Evaluate background shader. */ /* TODO: does aliasing like this break automatic SoA in CUDA? * Should we instead store closures separate from ShaderData? */ ShaderDataTinyStorage emission_sd_storage; ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_LIGHT_SETUP); shader_setup_from_background(kg, emission_sd, INTEGRATOR_STATE(state, ray, P), INTEGRATOR_STATE(state, ray, D), INTEGRATOR_STATE(state, ray, time)); PROFILING_SHADER(emission_sd->object, emission_sd->shader); PROFILING_EVENT(PROFILING_SHADE_LIGHT_EVAL); surface_shader_eval( kg, state, emission_sd, render_buffer, path_flag | PATH_RAY_EMISSION); return surface_shader_background(emission_sd); } ccl_device_inline void integrate_background(KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer) { /* Accumulate transparency for transparent background. We can skip background * shader evaluation unless a background pass is used. */ bool eval_background = true; float transparent = 0.0f; const bool is_transparent_background_ray = kernel_data.background.transparent && (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TRANSPARENT_BACKGROUND); if (is_transparent_background_ray) { transparent = average(INTEGRATOR_STATE(state, path, throughput)); #ifdef __PASSES__ eval_background = (kernel_data.film.light_pass_flag & PASSMASK(BACKGROUND)); #else eval_background = false; #endif } #ifdef __MNEE__ if (INTEGRATOR_STATE(state, path, mnee) & PATH_MNEE_CULL_LIGHT_CONNECTION) { if (kernel_data.background.use_mis) { for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { /* This path should have been resolved with mnee, it will * generate a firefly for small lights since it is improbable. */ const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp); if (klight->type == LIGHT_BACKGROUND && klight->use_caustics) { eval_background = false; break; } } } } #endif /* __MNEE__ */ /* Evaluate background shader. */ Spectrum L = zero_spectrum(); if (eval_background) { L = integrator_eval_background_shader(kg, state, render_buffer); /* When using the ao bounces approximation, adjust background * shader intensity with ao factor. */ if (path_state_ao_bounce(kg, state)) { L *= kernel_data.integrator.ao_bounces_factor; } /* Background MIS weights. */ float mis_weight = 1.0f; /* Check if background light exists or if we should skip pdf. */ if (!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_MIS_SKIP) && kernel_data.background.use_mis) { const float3 ray_P = INTEGRATOR_STATE(state, ray, P); const float3 ray_D = INTEGRATOR_STATE(state, ray, D); const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); /* multiple importance sampling, get background light pdf for ray * direction, and compute weight with respect to BSDF pdf */ const float pdf = background_light_pdf(kg, ray_P, ray_D); mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf); } L *= mis_weight; } /* Write to render buffer. */ film_write_background(kg, state, L, transparent, is_transparent_background_ray, render_buffer); } ccl_device_inline void integrate_distant_lights(KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer) { const float3 ray_D = INTEGRATOR_STATE(state, ray, D); const float ray_time = INTEGRATOR_STATE(state, ray, time); LightSample ls ccl_optional_struct_init; for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { if (light_sample_from_distant_ray(kg, ray_D, lamp, &ls)) { /* Use visibility flag to skip lights. */ #ifdef __PASSES__ const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if (ls.shader & SHADER_EXCLUDE_ANY) { if (((ls.shader & SHADER_EXCLUDE_DIFFUSE) && (path_flag & PATH_RAY_DIFFUSE)) || ((ls.shader & SHADER_EXCLUDE_GLOSSY) && ((path_flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || ((ls.shader & SHADER_EXCLUDE_TRANSMIT) && (path_flag & PATH_RAY_TRANSMIT)) || ((ls.shader & SHADER_EXCLUDE_CAMERA) && (path_flag & PATH_RAY_CAMERA)) || ((ls.shader & SHADER_EXCLUDE_SCATTER) && (path_flag & PATH_RAY_VOLUME_SCATTER))) return; } #endif #ifdef __MNEE__ if (INTEGRATOR_STATE(state, path, mnee) & PATH_MNEE_CULL_LIGHT_CONNECTION) { /* This path should have been resolved with mnee, it will * generate a firefly for small lights since it is improbable. */ const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp); if (klight->use_caustics) return; } #endif /* __MNEE__ */ /* Evaluate light shader. */ /* TODO: does aliasing like this break automatic SoA in CUDA? */ ShaderDataTinyStorage emission_sd_storage; ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); Spectrum light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, ray_time); if (is_zero(light_eval)) { return; } /* MIS weighting. */ float mis_weight = 1.0f; if (!(path_flag & PATH_RAY_MIS_SKIP)) { /* multiple importance sampling, get regular light pdf, * and compute weight with respect to BSDF pdf */ const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, ls.pdf); } /* Write to render buffer. */ film_write_surface_emission( kg, state, light_eval, mis_weight, render_buffer, kernel_data.background.lightgroup); } } } ccl_device void integrator_shade_background(KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer) { PROFILING_INIT(kg, PROFILING_SHADE_LIGHT_SETUP); /* TODO: unify these in a single loop to only have a single shader evaluation call. */ integrate_distant_lights(kg, state, render_buffer); integrate_background(kg, state, render_buffer); #ifdef __SHADOW_CATCHER__ if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { /* Special case for shadow catcher where we want to fill the background pass * behind the shadow catcher but also continue tracing the path. */ INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_SHADOW_CATCHER_BACKGROUND; integrator_intersect_next_kernel_after_shadow_catcher_background< DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND>(kg, state); return; } #endif integrator_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); } CCL_NAMESPACE_END