mirror of
https://github.com/blender/blender
synced 2026-10-25 03:07:01 +03:00
The `shade_light_nee` kernel is used by both `shade_surface`, and by
shadowing linking in `intersect_dedicated_light`. For the latter case,
curves and point primitives can be intersected, but these were not
handled.
Issue introduced in 4b34743b4e.
Pull Request: https://projects.blender.org/blender/blender/pulls/164683
247 lines
9.5 KiB
C
247 lines
9.5 KiB
C
/* SPDX-FileCopyrightText: 2011-2023 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#pragma once
|
|
|
|
#include "kernel/integrator/state_flow.h"
|
|
#include "kernel/light/light.h"
|
|
#include "kernel/light/sample.h"
|
|
#include "kernel/light/sun.h"
|
|
|
|
#include "kernel/integrator/shade_surface.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
#ifdef __SHADOW_LINKING__
|
|
|
|
ccl_device_inline LightEval
|
|
shadow_linking_light_eval_from_intersection(KernelGlobals kg,
|
|
const ccl_private Intersection &ccl_restrict isect,
|
|
const ccl_private Ray &ccl_restrict ray,
|
|
const float3 N,
|
|
const uint32_t path_flag)
|
|
{
|
|
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, isect.prim);
|
|
const LightType type = LightType(klight->type);
|
|
|
|
return (type == LIGHT_SUN) ?
|
|
sun_light_eval_from_intersection(klight, ray.D) :
|
|
light_eval_from_intersection(kg, &isect, ray.P, ray.D, N, path_flag);
|
|
}
|
|
|
|
ccl_device_inline float shadow_linking_light_sample_mis_weight(
|
|
KernelGlobals kg,
|
|
IntegratorState state,
|
|
const PathRayVisibility path_visibility,
|
|
const uint32_t path_flag,
|
|
const int light_id,
|
|
const int object_id,
|
|
const float light_sample_pdf,
|
|
const float3 P)
|
|
{
|
|
if (kernel_data_fetch(lights, light_id).type == LIGHT_SUN) {
|
|
return light_sample_mis_weight_forward_distant(
|
|
kg, state, path_visibility, path_flag, object_id, light_sample_pdf);
|
|
}
|
|
|
|
return light_sample_mis_weight_forward_lamp(
|
|
kg, state, path_visibility, path_flag, object_id, light_sample_pdf, P);
|
|
}
|
|
|
|
/* Setup ray for the shadow path.
|
|
* Expects that the current state of the ray is the one calculated by the surface bounce, and the
|
|
* intersection corresponds to a point on an emitter. */
|
|
ccl_device void shadow_linking_setup_ray_from_intersection(
|
|
IntegratorState state,
|
|
ccl_private Ray *ccl_restrict ray,
|
|
const ccl_private Intersection *ccl_restrict isect)
|
|
{
|
|
/* The ray->tmin follows the value configured at the surface bounce.
|
|
* it is the same for the continued main path and for this shadow ray. There is no need to push
|
|
* it forward here. */
|
|
|
|
ray->tmax = isect->t;
|
|
|
|
/* Use the same self intersection primitives as the main path.
|
|
* Those are copied to the dedicated storage from the main intersection after the surface bounce,
|
|
* but before the main intersection is re-used to find light to trace a ray to. */
|
|
ray->self.object = INTEGRATOR_STATE(state, shadow_link, last_isect_object);
|
|
ray->self.prim = INTEGRATOR_STATE(state, shadow_link, last_isect_prim);
|
|
|
|
ray->self.light_object = isect->object;
|
|
ray->self.light_prim = isect->prim;
|
|
}
|
|
|
|
ccl_device bool shadow_linking_shade_light(KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_private Ray &ccl_restrict ray,
|
|
ccl_private Intersection &ccl_restrict isect,
|
|
ccl_private float &ccl_restrict light_weight,
|
|
ccl_private float &mis_weight,
|
|
ccl_private int &ccl_restrict light_group,
|
|
ccl_private int &ccl_restrict shader_id)
|
|
{
|
|
const PathRayVisibility path_visibility = INTEGRATOR_STATE(state, path, visibility);
|
|
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
|
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
|
|
const LightEval light_eval = shadow_linking_light_eval_from_intersection(
|
|
kg, isect, ray, N, path_flag);
|
|
if (light_eval.eval_fac == 0.0f) {
|
|
/* No light to be sampled, so no direct light contribution either. */
|
|
return SHADER_EVAL_EMPTY;
|
|
}
|
|
|
|
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, isect.prim);
|
|
|
|
if (!is_light_shader_visible_to_path(klight->shader_id, path_visibility, path_flag)) {
|
|
return false;
|
|
}
|
|
|
|
/* MIS weighting. */
|
|
mis_weight = shadow_linking_light_sample_mis_weight(
|
|
kg, state, path_visibility, path_flag, isect.prim, isect.object, light_eval.pdf, ray.P);
|
|
|
|
light_weight = light_eval.eval_fac * mis_weight *
|
|
INTEGRATOR_STATE(state, shadow_link, dedicated_light_weight);
|
|
light_group = object_lightgroup(kg, klight->object_id);
|
|
shader_id = klight->shader_id;
|
|
|
|
return SHADER_EVAL_OK;
|
|
}
|
|
|
|
ccl_device bool shadow_linking_shade_surface_emission(KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_private float &ccl_restrict light_weight,
|
|
ccl_private float &mis_weight,
|
|
ccl_private int &ccl_restrict light_group,
|
|
ccl_private int &ccl_restrict shader_id)
|
|
{
|
|
ShaderDataTinyStorage emission_sd_storage;
|
|
ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage);
|
|
|
|
const PathRayVisibility path_visibility = INTEGRATOR_STATE(state, path, visibility);
|
|
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
|
|
|
integrate_surface_shader_setup(kg, state, emission_sd);
|
|
|
|
# ifdef __VOLUME__
|
|
if (emission_sd->shader_flag & SD_HAS_ONLY_VOLUME) {
|
|
return SHADER_EVAL_EMPTY;
|
|
}
|
|
# endif
|
|
|
|
mis_weight = light_sample_mis_weight_forward_surface(
|
|
kg, state, path_visibility, path_flag, emission_sd);
|
|
|
|
light_weight = mis_weight * INTEGRATOR_STATE(state, shadow_link, dedicated_light_weight);
|
|
light_group = object_lightgroup(kg, emission_sd->object);
|
|
shader_id = emission_sd->shader;
|
|
|
|
return SHADER_EVAL_OK;
|
|
}
|
|
|
|
ccl_device void shadow_linking_shade(KernelGlobals kg, IntegratorState state)
|
|
{
|
|
/* Read intersection from integrator state into local memory. */
|
|
Intersection isect ccl_optional_struct_init;
|
|
integrator_state_read_isect(state, &isect);
|
|
|
|
/* Read ray from integrator state into local memory. */
|
|
Ray ray ccl_optional_struct_init;
|
|
integrator_state_read_ray(state, &ray);
|
|
|
|
float light_weight = 0.0f;
|
|
float mis_weight = 1.0f;
|
|
int light_group = LIGHTGROUP_NONE;
|
|
int shader_id = SHADER_NONE;
|
|
|
|
if (isect.type == PRIMITIVE_LAMP) {
|
|
if (!shadow_linking_shade_light(
|
|
kg, state, ray, isect, light_weight, mis_weight, light_group, shader_id))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
if (!shadow_linking_shade_surface_emission(
|
|
kg, state, light_weight, mis_weight, light_group, shader_id))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Evaluate constant part of light shader, rest will optionally be done in another kernel. */
|
|
Spectrum light_eval;
|
|
const bool is_constant_light_shader = light_sample_shader_eval_nee_constant(
|
|
kg, shader_id, isect.prim, isect.type == PRIMITIVE_LAMP, light_eval);
|
|
light_eval *= light_weight;
|
|
|
|
if (is_zero(light_eval)) {
|
|
return;
|
|
}
|
|
|
|
shadow_linking_setup_ray_from_intersection(state, &ray, &isect);
|
|
|
|
/* Branch off shadow kernel. */
|
|
IntegratorShadowState shadow_state = integrate_direct_light_shadow_init_common(
|
|
kg, state, &ray, light_eval, light_group, 0, is_constant_light_shader);
|
|
|
|
if (!is_constant_light_shader) {
|
|
integrator_state_write_shadow_light_isect(shadow_state, &isect);
|
|
}
|
|
|
|
/* The light is accumulated from the shade_surface kernel, which will make the clamping decision
|
|
* based on the actual value of the bounce. For the dedicated shadow ray we want to follow the
|
|
* main path clamping rules, which subtracts one from the bounds before accumulation. */
|
|
INTEGRATOR_STATE_WRITE(
|
|
shadow_state, shadow_path, bounce) = INTEGRATOR_STATE(shadow_state, shadow_path, bounce) - 1;
|
|
|
|
/* No need to update the volume stack as the surface bounce already performed enter-exit check.
|
|
*/
|
|
|
|
const uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
|
|
|
|
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
|
/* The diffuse and glossy pass weights are written into the main path as part of the path
|
|
* configuration at a surface bounce. */
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_diffuse_weight) = INTEGRATOR_STATE(
|
|
state, path, pass_diffuse_weight);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_glossy_weight) = INTEGRATOR_STATE(
|
|
state, path, pass_glossy_weight);
|
|
}
|
|
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, visibility) = INTEGRATOR_STATE(
|
|
state, path, visibility);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, flag) = shadow_flag |
|
|
PATH_RAY_SHADOW_FOR_LIGHT_LINKING;
|
|
|
|
# if defined(__PATH_GUIDING__)
|
|
if (kernel_data.integrator.train_guiding) {
|
|
INTEGRATOR_STATE(shadow_state, shadow_path, guiding_light_linking_mis_weight) = mis_weight;
|
|
}
|
|
# endif
|
|
}
|
|
|
|
#endif /* __SHADOW_LINKING__ */
|
|
|
|
ccl_device void integrator_shade_dedicated_light(KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_global float *ccl_restrict /*render_buffer*/)
|
|
{
|
|
PROFILING_INIT(kg, PROFILING_SHADE_DEDICATED_LIGHT);
|
|
|
|
#ifdef __SHADOW_LINKING__
|
|
shadow_linking_shade(kg, state);
|
|
|
|
/* Restore self-intersection check primitives in the main state before returning to the
|
|
* intersect_closest() state. */
|
|
shadow_linking_restore_last_primitives(state);
|
|
#else
|
|
kernel_assert(!"integrator_intersect_dedicated_light is not supposed to be scheduled");
|
|
#endif
|
|
|
|
integrator_shade_surface_next_kernel<DEVICE_KERNEL_INTEGRATOR_SHADE_DEDICATED_LIGHT>(state);
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|