This is an intermediate steps towards making lights actual geometry. Light is now a subclass of Geometry, which simplifies some code. The geometry is not added to the BVH yet, which would be the next step and improve light intersection performance with many lights. This makes object attributes work on lights. Co-authored-by: Lukas Stockner <lukas@lukasstockner.de> Pull Request: https://projects.blender.org/blender/blender/pulls/134846
244 lines
9.0 KiB
C
244 lines
9.0 KiB
C
/* SPDX-FileCopyrightText: 2011-2023 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#pragma once
|
|
|
|
#include "kernel/light/distant.h"
|
|
#include "kernel/light/light.h"
|
|
#include "kernel/light/sample.h"
|
|
|
|
#include "kernel/integrator/shade_surface.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
#ifdef __SHADOW_LINKING__
|
|
|
|
ccl_device_inline bool shadow_linking_light_sample_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,
|
|
ccl_private LightSample *ccl_restrict ls)
|
|
{
|
|
const int lamp = isect.prim;
|
|
|
|
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp);
|
|
const LightType type = LightType(klight->type);
|
|
|
|
if (type == LIGHT_DISTANT) {
|
|
return distant_light_sample_from_intersection(kg, ray.D, lamp, ls);
|
|
}
|
|
|
|
return light_sample_from_intersection(kg, &isect, ray.P, ray.D, N, path_flag, ls);
|
|
}
|
|
|
|
ccl_device_inline float shadow_linking_light_sample_mis_weight(KernelGlobals kg,
|
|
IntegratorState state,
|
|
const uint32_t path_flag,
|
|
const ccl_private LightSample *ls,
|
|
const float3 P)
|
|
{
|
|
if (ls->type == LIGHT_DISTANT) {
|
|
return light_sample_mis_weight_forward_distant(kg, state, path_flag, ls);
|
|
}
|
|
|
|
return light_sample_mis_weight_forward_lamp(kg, state, path_flag, ls, 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 ShaderData *emission_sd,
|
|
ccl_private Spectrum &ccl_restrict bsdf_spectrum,
|
|
ccl_private float &mis_weight,
|
|
ccl_private int &ccl_restrict light_group)
|
|
{
|
|
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
|
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
|
|
LightSample ls ccl_optional_struct_init;
|
|
const bool use_light_sample = shadow_linking_light_sample_from_intersection(
|
|
kg, isect, ray, N, path_flag, &ls);
|
|
if (!use_light_sample) {
|
|
/* No light to be sampled, so no direct light contribution either. */
|
|
return false;
|
|
}
|
|
|
|
const Spectrum light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, ray.time);
|
|
if (is_zero(light_eval)) {
|
|
return false;
|
|
}
|
|
|
|
if (!is_light_shader_visible_to_path(ls.shader, path_flag)) {
|
|
return false;
|
|
}
|
|
|
|
/* MIS weighting. */
|
|
mis_weight = shadow_linking_light_sample_mis_weight(kg, state, path_flag, &ls, ray.P);
|
|
|
|
bsdf_spectrum = light_eval * mis_weight *
|
|
INTEGRATOR_STATE(state, shadow_link, dedicated_light_weight);
|
|
light_group = ls.group;
|
|
|
|
return true;
|
|
}
|
|
|
|
ccl_device bool shadow_linking_shade_surface_emission(KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_private ShaderData *emission_sd,
|
|
ccl_global float *ccl_restrict render_buffer,
|
|
ccl_private Spectrum &ccl_restrict
|
|
bsdf_spectrum,
|
|
ccl_private float &mis_weight,
|
|
ccl_private int &ccl_restrict light_group)
|
|
{
|
|
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
|
|
|
integrate_surface_shader_setup(kg, state, emission_sd);
|
|
|
|
# ifdef __VOLUME__
|
|
if (emission_sd->flag & SD_HAS_ONLY_VOLUME) {
|
|
return false;
|
|
}
|
|
# endif
|
|
|
|
surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT>(
|
|
kg, state, emission_sd, render_buffer, path_flag | PATH_RAY_EMISSION);
|
|
|
|
if ((emission_sd->flag & SD_EMISSION) == 0) {
|
|
return false;
|
|
}
|
|
|
|
const Spectrum L = surface_shader_emission(emission_sd);
|
|
|
|
mis_weight = light_sample_mis_weight_forward_surface(kg, state, path_flag, emission_sd);
|
|
|
|
bsdf_spectrum = L * mis_weight * INTEGRATOR_STATE(state, shadow_link, dedicated_light_weight);
|
|
light_group = object_lightgroup(kg, emission_sd->object);
|
|
|
|
return true;
|
|
}
|
|
|
|
ccl_device void shadow_linking_shade(KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_global float *ccl_restrict render_buffer)
|
|
{
|
|
/* 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);
|
|
|
|
ShaderDataTinyStorage emission_sd_storage;
|
|
ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage);
|
|
|
|
Spectrum bsdf_spectrum;
|
|
float mis_weight = 1.0f;
|
|
int light_group = LIGHTGROUP_NONE;
|
|
|
|
if (isect.type == PRIMITIVE_LAMP) {
|
|
if (!shadow_linking_shade_light(
|
|
kg, state, ray, isect, emission_sd, bsdf_spectrum, mis_weight, light_group))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
if (!shadow_linking_shade_surface_emission(
|
|
kg, state, emission_sd, render_buffer, bsdf_spectrum, mis_weight, light_group))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (is_zero(bsdf_spectrum)) {
|
|
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, bsdf_spectrum, light_group, 0);
|
|
|
|
/* 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, flag) = shadow_flag;
|
|
|
|
# ifdef __PATH_GUIDING__
|
|
if (kernel_data.integrator.train_guiding) {
|
|
guiding_record_light_surface_segment(kg, state, &isect);
|
|
INTEGRATOR_STATE(shadow_state, shadow_path, guiding_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, render_buffer);
|
|
|
|
/* 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>(kg, state);
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|