Light linking: Initial support of shadow linking #106501
|
@ -148,7 +148,10 @@ void BlenderSync::sync_light(BL::Object &b_parent,
|
|||
|
||||
/* Light group and linking. */
|
||||
light->set_lightgroup(ustring(b_ob_info.real_object.lightgroup()));
|
||||
light->set_light_link_set_membership(light_linking_get_set_membership(b_ob_info.real_object));
|
||||
light->set_light_set_membership(
|
||||
BlenderLightLink::get_light_set_membership(b_ob_info.real_object));
|
||||
light->set_shadow_set_membership(
|
||||
BlenderLightLink::get_shadow_set_membership(b_ob_info.real_object));
|
||||
|
||||
/* tag */
|
||||
light->tag_update(scene);
|
||||
|
|
|
@ -12,16 +12,28 @@ static const ::Object *get_blender_object(const BL::Object &object)
|
|||
return reinterpret_cast<::Object *>(object.ptr.data);
|
||||
}
|
||||
|
||||
uint64_t light_linking_get_set_membership(const BL::Object &object)
|
||||
uint64_t BlenderLightLink::get_light_set_membership(const BL::Object &object)
|
||||
{
|
||||
const ::Object *blender_object = get_blender_object(object);
|
||||
return blender_object->light_linking.runtime.set_membership;
|
||||
return blender_object->light_linking.runtime.light_set_membership;
|
||||
}
|
||||
|
||||
uint light_linking_get_receiver_set(const BL::Object &object)
|
||||
uint BlenderLightLink::get_receiver_light_set(const BL::Object &object)
|
||||
{
|
||||
const ::Object *blender_object = get_blender_object(object);
|
||||
return blender_object->light_linking.runtime.receiver_set;
|
||||
return blender_object->light_linking.runtime.receiver_light_set;
|
||||
}
|
||||
|
||||
uint64_t BlenderLightLink::get_shadow_set_membership(const BL::Object &object)
|
||||
{
|
||||
const ::Object *blender_object = get_blender_object(object);
|
||||
return blender_object->light_linking.runtime.shadow_set_membership;
|
||||
}
|
||||
|
||||
uint BlenderLightLink::get_blocker_shadow_set(const BL::Object &object)
|
||||
{
|
||||
const ::Object *blender_object = get_blender_object(object);
|
||||
return blender_object->light_linking.runtime.blocker_shadow_set;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
@ -10,7 +10,13 @@
|
|||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
uint64_t light_linking_get_set_membership(const BL::Object &object);
|
||||
uint light_linking_get_receiver_set(const BL::Object &object);
|
||||
class BlenderLightLink {
|
||||
public:
|
||||
static uint64_t get_light_set_membership(const BL::Object &object);
|
||||
static uint get_receiver_light_set(const BL::Object &object);
|
||||
|
||||
static uint64_t get_shadow_set_membership(const BL::Object &object);
|
||||
static uint get_blocker_shadow_set(const BL::Object &object);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
@ -349,8 +349,10 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
|
|||
|
||||
/* Light group and linking. */
|
||||
object->set_lightgroup(ustring(b_ob.lightgroup()));
|
||||
object->set_light_link_set_membership(light_linking_get_set_membership(b_ob));
|
||||
object->set_light_link_receiver_set(light_linking_get_receiver_set(b_ob));
|
||||
object->set_light_set_membership(BlenderLightLink::get_light_set_membership(b_ob));
|
||||
object->set_receiver_light_set(BlenderLightLink::get_receiver_light_set(b_ob));
|
||||
object->set_shadow_set_membership(BlenderLightLink::get_shadow_set_membership(b_ob));
|
||||
object->set_blocker_shadow_set(BlenderLightLink::get_blocker_shadow_set(b_ob));
|
||||
|
||||
object->tag_update(scene);
|
||||
}
|
||||
|
|
|
@ -144,6 +144,12 @@ ccl_device_inline
|
|||
continue;
|
||||
}
|
||||
|
||||
#ifdef __LIGHT_LINKING__
|
||||
if (intersection_skip_shadow_link(kg, ray, prim_object)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (type & PRIMITIVE_ALL) {
|
||||
case PRIMITIVE_TRIANGLE: {
|
||||
hit = triangle_intersect(
|
||||
|
|
|
@ -129,6 +129,12 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
|
|||
continue;
|
||||
}
|
||||
|
||||
#ifdef __LIGHT_LINKING__
|
||||
if (intersection_skip_shadow_link(kg, ray, prim_object)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (type & PRIMITIVE_ALL) {
|
||||
case PRIMITIVE_TRIANGLE: {
|
||||
if (triangle_intersect(kg,
|
||||
|
|
|
@ -232,4 +232,41 @@ ccl_device_inline bool intersection_skip_self_local(ccl_private const RaySelfPri
|
|||
return (self.prim == prim);
|
||||
}
|
||||
|
||||
#ifdef __LIGHT_LINKING__
|
||||
ccl_device_inline uint64_t ray_get_shadow_set_membership(KernelGlobals kg,
|
||||
ccl_private const Ray *ray)
|
||||
{
|
||||
if (ray->self.light != LAMP_NONE) {
|
||||
return kernel_data_fetch(lights, ray->self.light).shadow_set_membership;
|
||||
}
|
||||
|
||||
if (ray->self.light_object != OBJECT_NONE) {
|
||||
return kernel_data_fetch(objects, ray->self.light_object).shadow_set_membership;
|
||||
}
|
||||
|
||||
return LIGHT_LINK_MASK_ALL;
|
||||
}
|
||||
#endif
|
||||
|
||||
ccl_device_inline bool intersection_skip_shadow_link(KernelGlobals kg,
|
||||
ccl_private const Ray *ray,
|
||||
const int isect_object)
|
||||
{
|
||||
#ifdef __LIGHT_LINKING__
|
||||
if (!(kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_LINKING)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t set_membership = ray_get_shadow_set_membership(kg, ray);
|
||||
if (set_membership == LIGHT_LINK_MASK_ALL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint blocker_set = kernel_data_fetch(objects, isect_object).blocker_shadow_set;
|
||||
return ((uint64_t(1) << uint64_t(blocker_set)) & set_membership) == 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
@ -118,14 +118,19 @@ ccl_device_inline void kernel_embree_setup_rayhit(const Ray &ray,
|
|||
rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;
|
||||
}
|
||||
|
||||
ccl_device_inline int kernel_embree_get_hit_object(const RTCHit *hit)
|
||||
{
|
||||
return (hit->instID[0] != RTC_INVALID_GEOMETRY_ID ? hit->instID[0] : hit->geomID) / 2;
|
||||
}
|
||||
|
||||
ccl_device_inline bool kernel_embree_is_self_intersection(const KernelGlobals kg,
|
||||
const RTCHit *hit,
|
||||
const Ray *ray,
|
||||
const intptr_t prim_offset)
|
||||
{
|
||||
int object, prim;
|
||||
object = (hit->instID[0] != RTC_INVALID_GEOMETRY_ID ? hit->instID[0] : hit->geomID) / 2;
|
||||
const int object = kernel_embree_get_hit_object(hit);
|
||||
|
||||
int prim;
|
||||
if ((ray->self.object == object) || (ray->self.light_object == object)) {
|
||||
prim = hit->primID + prim_offset;
|
||||
}
|
||||
|
@ -149,7 +154,7 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg,
|
|||
{
|
||||
isect->t = ray->tfar;
|
||||
isect->prim = hit->primID + prim_offset;
|
||||
isect->object = hit->instID[0] != RTC_INVALID_GEOMETRY_ID ? hit->instID[0] / 2 : hit->geomID / 2;
|
||||
isect->object = kernel_embree_get_hit_object(hit);
|
||||
|
||||
const bool is_hair = hit->geomID & 1;
|
||||
if (is_hair) {
|
||||
|
@ -218,7 +223,15 @@ ccl_device void kernel_embree_filter_intersection_func(const RTCFilterFunctionNA
|
|||
if (kernel_embree_is_self_intersection(
|
||||
kg, hit, cray, reinterpret_cast<intptr_t>(args->geometryUserPtr))) {
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __LIGHT_LINKING__
|
||||
if (intersection_skip_shadow_link(kg, cray, kernel_embree_get_hit_object(hit))) {
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This gets called by Embree at every valid ray/object intersection.
|
||||
|
@ -246,6 +259,14 @@ ccl_device void kernel_embree_filter_occluded_func(const RTCFilterFunctionNArgum
|
|||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __LIGHT_LINKING__
|
||||
if (intersection_skip_shadow_link(kg, cray, current_isect.object)) {
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If no transparent shadows or max number of hits exceeded, all light is blocked. */
|
||||
const int flags = intersection_get_shader_flags(kg, current_isect.prim, current_isect.type);
|
||||
if (!(flags & (SD_HAS_TRANSPARENT_SHADOW)) || ctx->num_hits >= ctx->max_hits) {
|
||||
|
|
|
@ -183,6 +183,12 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
|
|||
return optixIgnoreIntersection();
|
||||
}
|
||||
|
||||
# ifdef __LIGHT_LINKING__
|
||||
if (intersection_skip_shadow_link(nullptr, ray, object)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
# endif
|
||||
|
||||
# ifndef __TRANSPARENT_SHADOWS__
|
||||
/* No transparent shadows support compiled in, make opaque. */
|
||||
optixSetPayload_5(true);
|
||||
|
@ -325,6 +331,12 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
|
|||
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
|
||||
|
||||
if (visibility & PATH_RAY_SHADOW_OPAQUE) {
|
||||
#ifdef __LIGHT_LINKING__
|
||||
if (intersection_skip_shadow_link(nullptr, ray, object)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (intersection_skip_self_shadow(ray->self, object, prim)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
|
|
|
@ -353,6 +353,7 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg,
|
|||
ray.self.prim = last_isect_prim;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
ray.self.light = LAMP_NONE;
|
||||
bool hit = scene_intersect(kg, &ray, visibility, &isect);
|
||||
|
||||
/* TODO: remove this and do it in the various intersection functions instead. */
|
||||
|
|
|
@ -147,6 +147,7 @@ ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowSt
|
|||
ray.self.prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, prim);
|
||||
ray.self.light_object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 1, object);
|
||||
ray.self.light_prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 1, prim);
|
||||
ray.self.light = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 2, object);
|
||||
/* Compute visibility. */
|
||||
const uint visibility = integrate_intersect_shadow_visibility(kg, state);
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
|
|||
volume_ray.self.prim = INTEGRATOR_STATE(state, isect, prim);
|
||||
volume_ray.self.light_object = OBJECT_NONE;
|
||||
volume_ray.self.light_prim = PRIM_NONE;
|
||||
volume_ray.self.light = LAMP_NONE;
|
||||
/* Store to avoid global fetches on every intersection step. */
|
||||
const uint volume_stack_size = kernel_data.volume_stack_size;
|
||||
|
||||
|
@ -84,6 +85,7 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s
|
|||
volume_ray.self.prim = PRIM_NONE;
|
||||
volume_ray.self.light_object = OBJECT_NONE;
|
||||
volume_ray.self.light_prim = PRIM_NONE;
|
||||
volume_ray.self.light = LAMP_NONE;
|
||||
|
||||
int stack_index = 0, enclosed_index = 0;
|
||||
|
||||
|
|
|
@ -407,6 +407,7 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg,
|
|||
Ray projection_ray;
|
||||
projection_ray.self.light_object = OBJECT_NONE;
|
||||
projection_ray.self.light_prim = PRIM_NONE;
|
||||
projection_ray.self.light = LAMP_NONE;
|
||||
projection_ray.dP = differential_make_compact(sd->dP);
|
||||
projection_ray.dD = differential_zero_compact();
|
||||
projection_ray.tmin = 0.0f;
|
||||
|
@ -823,6 +824,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
|
|||
Ray probe_ray;
|
||||
probe_ray.self.light_object = ls->object;
|
||||
probe_ray.self.light_prim = ls->prim;
|
||||
probe_ray.self.light = ls->lamp;
|
||||
probe_ray.tmin = 0.0f;
|
||||
probe_ray.dP = differential_make_compact(sd->dP);
|
||||
probe_ray.dD = differential_zero_compact();
|
||||
|
@ -923,6 +925,7 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg,
|
|||
probe_ray.self.prim = sd->prim;
|
||||
probe_ray.self.light_object = ls->object;
|
||||
probe_ray.self.light_prim = ls->prim;
|
||||
probe_ray.self.light = ls->lamp;
|
||||
probe_ray.P = sd->P;
|
||||
probe_ray.tmin = 0.0f;
|
||||
if (ls->t == FLT_MAX) {
|
||||
|
|
|
@ -75,6 +75,7 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg,
|
|||
ray.self.prim = PRIM_NONE;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
ray.self.light = LAMP_NONE;
|
||||
/* Modify ray position and length to match current segment. */
|
||||
ray.tmin = (hit == 0) ? ray.tmin : INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t);
|
||||
ray.tmax = (hit < num_recorded_hits) ? INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit, t) :
|
||||
|
|
|
@ -265,6 +265,7 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
|
|||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, prim) = ray.self.prim;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, object) = ray.self.light_object;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, prim) = ray.self.light_prim;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 2, object) = ray.self.light;
|
||||
|
||||
/* Copy state from main path to shadow path. */
|
||||
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
|
||||
|
@ -546,6 +547,7 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
|
|||
ray.self.prim = (skip_self) ? sd->prim : PRIM_NONE;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
ray.self.light = LAMP_NONE;
|
||||
ray.dP = differential_zero_compact();
|
||||
ray.dD = differential_zero_compact();
|
||||
|
||||
|
@ -562,6 +564,7 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
|
|||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, prim) = ray.self.prim;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, object) = ray.self.light_object;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, prim) = ray.self.light_prim;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 2, object) = ray.self.light;
|
||||
|
||||
/* Copy state from main path to shadow path. */
|
||||
const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce);
|
||||
|
|
|
@ -91,7 +91,8 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
|
|||
ray.self.object = OBJECT_NONE;
|
||||
ray.self.prim = PRIM_NONE;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
ray.self.light = LAMP_NONE;
|
||||
|
||||
/* Intersect with the same object. if multiple intersections are found it
|
||||
* will use at most BSSRDF_MAX_HITS hits, a random subset of all hits. */
|
||||
|
|
|
@ -197,6 +197,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
|
|||
ray.self.prim = prim;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
ray.self.light = LAMP_NONE;
|
||||
|
||||
/* Convert subsurface to volume coefficients.
|
||||
* The single-scattering albedo is named alpha to avoid confusion with the surface albedo. */
|
||||
|
|
|
@ -52,12 +52,11 @@ ccl_device_inline bool light_link_light_match(KernelGlobals kg,
|
|||
return true;
|
||||
}
|
||||
|
||||
const uint64_t set_membership =
|
||||
kernel_data_fetch(lights, light_emitter).light_link_set_membership;
|
||||
const uint64_t set_membership = kernel_data_fetch(lights, light_emitter).light_set_membership;
|
||||
if (set_membership == LIGHT_LINK_MASK_ALL) {
|
||||
return true;
|
||||
}
|
||||
const uint receiver_set = kernel_data_fetch(objects, object_receiver).light_link_receiver_set;
|
||||
const uint receiver_set = kernel_data_fetch(objects, object_receiver).receiver_light_set;
|
||||
return ((uint64_t(1) << uint64_t(receiver_set)) & set_membership) != 0;
|
||||
}
|
||||
|
||||
|
@ -72,12 +71,11 @@ ccl_device_inline bool light_link_object_match(KernelGlobals kg,
|
|||
return true;
|
||||
}
|
||||
|
||||
const uint64_t set_membership =
|
||||
kernel_data_fetch(objects, object_emitter).light_link_set_membership;
|
||||
const uint64_t set_membership = kernel_data_fetch(objects, object_emitter).light_set_membership;
|
||||
if (set_membership == LIGHT_LINK_MASK_ALL) {
|
||||
return true;
|
||||
}
|
||||
const uint receiver_set = kernel_data_fetch(objects, object_receiver).light_link_receiver_set;
|
||||
const uint receiver_set = kernel_data_fetch(objects, object_receiver).receiver_light_set;
|
||||
return ((uint64_t(1) << uint64_t(receiver_set)) & set_membership) != 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -250,6 +250,7 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri
|
|||
ray->self.prim = (skip_self) ? sd->prim : PRIM_NONE;
|
||||
ray->self.light_object = ls->object;
|
||||
ray->self.light_prim = ls->prim;
|
||||
ray->self.light = ls->lamp;
|
||||
}
|
||||
|
||||
/* Create shadow ray towards light sample. */
|
||||
|
|
|
@ -1676,6 +1676,7 @@ bool OSLRenderServices::trace(TraceOpt &options,
|
|||
ray.self.prim = PRIM_NONE;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
ray.self.light = LAMP_NONE;
|
||||
|
||||
if (options.mindist == 0.0f) {
|
||||
/* avoid self-intersections */
|
||||
|
|
|
@ -66,6 +66,7 @@ ccl_device float svm_ao(
|
|||
ray.self.prim = sd->prim;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
ray.self.light = LAMP_NONE;
|
||||
ray.dP = differential_zero_compact();
|
||||
ray.dD = differential_zero_compact();
|
||||
|
||||
|
|
|
@ -188,6 +188,7 @@ ccl_device float3 svm_bevel(
|
|||
ray.self.prim = PRIM_NONE;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
ray.self.light = LAMP_NONE;
|
||||
|
||||
/* Intersect with the same object. if multiple intersections are found it
|
||||
* will use at most LOCAL_MAX_HITS hits, a random subset of all hits. */
|
||||
|
|
|
@ -61,6 +61,7 @@ CCL_NAMESPACE_BEGIN
|
|||
#define __DENOISING_FEATURES__
|
||||
#define __DPDU__
|
||||
#define __HAIR__
|
||||
#define __LIGHT_LINKING__
|
||||
#define __LIGHT_TREE__
|
||||
#define __OBJECT_MOTION__
|
||||
#define __PASSES__
|
||||
|
@ -562,6 +563,7 @@ typedef struct RaySelfPrimitives {
|
|||
int object; /* Instance prim is a part of */
|
||||
int light_prim; /* Light primitive */
|
||||
int light_object; /* Light object */
|
||||
int light; /* Light ID (the light the shadow ray is traced towards to) */
|
||||
} RaySelfPrimitives;
|
||||
|
||||
typedef struct Ray {
|
||||
|
@ -1278,9 +1280,10 @@ typedef struct KernelObject {
|
|||
float velocity_scale;
|
||||
|
||||
/* TODO: separate array to avoid memory overhead when not used. */
|
||||
uint64_t light_link_set_membership;
|
||||
uint light_link_receiver_set;
|
||||
uint pad;
|
||||
uint64_t light_set_membership;
|
||||
uint receiver_light_set;
|
||||
uint64_t shadow_set_membership;
|
||||
uint blocker_shadow_set;
|
||||
} KernelObject;
|
||||
static_assert_align(KernelObject, 16);
|
||||
|
||||
|
@ -1346,8 +1349,8 @@ typedef struct KernelLight {
|
|||
KernelAreaLight area;
|
||||
KernelDistantLight distant;
|
||||
};
|
||||
uint64_t light_link_set_membership;
|
||||
uint64_t pad;
|
||||
uint64_t light_set_membership;
|
||||
uint64_t shadow_set_membership;
|
||||
} KernelLight;
|
||||
static_assert_align(KernelLight, 16);
|
||||
|
||||
|
|
|
@ -137,7 +137,8 @@ NODE_DEFINE(Light)
|
|||
SOCKET_NODE(shader, "Shader", Shader::get_node_type());
|
||||
|
||||
SOCKET_STRING(lightgroup, "Light Group", ustring());
|
||||
SOCKET_UINT64(light_link_set_membership, "Light Link Set Membership", 0);
|
||||
SOCKET_UINT64(light_set_membership, "Light Set Membership", 0);
|
||||
SOCKET_UINT64(shadow_set_membership, "Shadow Set Membership", 0);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
@ -1128,6 +1129,12 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
|
|||
klights[light_index].spot.spot_smooth = spot_smooth;
|
||||
}
|
||||
|
||||
/* Disable MIS if the light participates in the shadow linking, as it is not supported. */
|
||||
/* TODO(sergey): Support MIS with shadow linking. */
|
||||
if (light->shadow_set_membership) {
|
||||
shader_id &= ~SHADER_USE_MIS;
|
||||
}
|
||||
|
||||
klights[light_index].shader_id = shader_id;
|
||||
|
||||
klights[light_index].max_bounces = max_bounces;
|
||||
|
@ -1145,7 +1152,8 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
|
|||
klights[light_index].lightgroup = LIGHTGROUP_NONE;
|
||||
}
|
||||
|
||||
klights[light_index].light_link_set_membership = light->light_link_set_membership;
|
||||
klights[light_index].light_set_membership = light->light_set_membership;
|
||||
klights[light_index].shadow_set_membership = light->shadow_set_membership;
|
||||
|
||||
light_index++;
|
||||
}
|
||||
|
|
|
@ -73,7 +73,8 @@ class Light : public Node {
|
|||
NODE_SOCKET_API(uint, random_id)
|
||||
|
||||
NODE_SOCKET_API(ustring, lightgroup)
|
||||
NODE_SOCKET_API(uint64_t, light_link_set_membership);
|
||||
NODE_SOCKET_API(uint64_t, light_set_membership);
|
||||
NODE_SOCKET_API(uint64_t, shadow_set_membership);
|
||||
|
||||
void tag_update(Scene *scene);
|
||||
|
||||
|
|
|
@ -100,8 +100,10 @@ NODE_DEFINE(Object)
|
|||
SOCKET_FLOAT(ao_distance, "AO Distance", 0.0f);
|
||||
|
||||
SOCKET_STRING(lightgroup, "Light Group", ustring());
|
||||
SOCKET_UINT(light_link_receiver_set, "Light Link Set Index", 0);
|
||||
SOCKET_UINT64(light_link_set_membership, "Light Link Set Membership", 0);
|
||||
SOCKET_UINT(receiver_light_set, "Light Set Index", 0);
|
||||
SOCKET_UINT64(light_set_membership, "Light Set Membership", 0);
|
||||
SOCKET_UINT(blocker_shadow_set, "Shadow Set Index", 0);
|
||||
SOCKET_UINT64(shadow_set_membership, "Shadow Set Membership", 0);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
@ -429,10 +431,14 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
|
|||
kobject.particle_index = particle_index;
|
||||
kobject.motion_offset = 0;
|
||||
kobject.ao_distance = ob->ao_distance;
|
||||
kobject.light_link_receiver_set = ob->light_link_receiver_set >= LIGHT_LINK_SET_MAX ?
|
||||
0 :
|
||||
ob->light_link_receiver_set;
|
||||
kobject.light_link_set_membership = ob->light_link_set_membership;
|
||||
kobject.receiver_light_set = ob->receiver_light_set >= LIGHT_LINK_SET_MAX ?
|
||||
0 :
|
||||
ob->receiver_light_set;
|
||||
kobject.light_set_membership = ob->light_set_membership;
|
||||
kobject.blocker_shadow_set = ob->blocker_shadow_set >= LIGHT_LINK_SET_MAX ?
|
||||
0 :
|
||||
ob->blocker_shadow_set;
|
||||
kobject.shadow_set_membership = ob->shadow_set_membership;
|
||||
|
||||
if (geom->get_use_motion_blur()) {
|
||||
state->have_motion = true;
|
||||
|
|
|
@ -67,8 +67,10 @@ class Object : public Node {
|
|||
NODE_SOCKET_API(float, ao_distance)
|
||||
|
||||
NODE_SOCKET_API(ustring, lightgroup)
|
||||
NODE_SOCKET_API(uint, light_link_receiver_set)
|
||||
NODE_SOCKET_API(uint64_t, light_link_set_membership)
|
||||
NODE_SOCKET_API(uint, receiver_light_set)
|
||||
NODE_SOCKET_API(uint64_t, light_set_membership)
|
||||
NODE_SOCKET_API(uint, blocker_shadow_set)
|
||||
NODE_SOCKET_API(uint64_t, shadow_set_membership)
|
||||
|
||||
/* Set during device update. */
|
||||
bool intersects_volume;
|
||||
|
|
|
@ -542,7 +542,7 @@ void Scene::update_kernel_features()
|
|||
else if (geom->is_pointcloud()) {
|
||||
kernel_features |= KERNEL_FEATURE_POINTCLOUD;
|
||||
}
|
||||
if (object->get_light_link_set_membership()) {
|
||||
if (object->get_light_set_membership() || object->get_shadow_set_membership()) {
|
||||
kernel_features |= KERNEL_FEATURE_LIGHT_LINKING;
|
||||
}
|
||||
}
|
||||
|
@ -551,7 +551,7 @@ void Scene::update_kernel_features()
|
|||
if (light->get_use_caustics()) {
|
||||
has_caustics_light = true;
|
||||
}
|
||||
if (light->get_light_link_set_membership()) {
|
||||
if (light->get_light_set_membership() || light->get_shadow_set_membership()) {
|
||||
kernel_features |= KERNEL_FEATURE_LIGHT_LINKING;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,31 +50,6 @@ void eval_runtime_data(const ::Depsgraph *depsgraph, Object &object_eval)
|
|||
/** \name Internal builder API
|
||||
* \{ */
|
||||
|
||||
namespace blender::deg::light_linking {
|
||||
|
||||
namespace internal {
|
||||
|
||||
bool LightSet::operator==(const LightSet &other) const
|
||||
{
|
||||
return include_collection_mask == other.include_collection_mask &&
|
||||
exclude_collection_mask == other.exclude_collection_mask;
|
||||
}
|
||||
|
||||
uint64_t LightSet::hash() const
|
||||
{
|
||||
return get_default_hash_2(get_default_hash(include_collection_mask),
|
||||
get_default_hash(exclude_collection_mask));
|
||||
}
|
||||
|
||||
uint64_t EmitterData::get_set_membership() const
|
||||
{
|
||||
const uint64_t effective_included_mask = included_sets_mask ? included_sets_mask :
|
||||
SET_MEMBERSHIP_ALL;
|
||||
return effective_included_mask & ~excluded_sets_mask;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
namespace {
|
||||
|
||||
/* TODO(sergey): Move to a public API, solving the const-correctness. */
|
||||
|
@ -86,47 +61,19 @@ template<class T> static inline const T *get_original(const T *id)
|
|||
return reinterpret_cast<T *>(DEG_get_original_id(const_cast<ID *>(&id->id)));
|
||||
}
|
||||
|
||||
/* Iterate over all objects of the collection and invoke the given callback with two arguments:
|
||||
* the given collection light linking settings, and the object (passed as reference).
|
||||
*
|
||||
* Note that if an object is reachable from multiple children collection the callback is invoked
|
||||
* for all of them. */
|
||||
template<class Proc>
|
||||
static void foreach_light_collection_object_inner(
|
||||
const CollectionLightLinking &collection_light_linking,
|
||||
const Collection &collection,
|
||||
Proc &&callback)
|
||||
{
|
||||
LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
|
||||
foreach_light_collection_object_inner(
|
||||
collection_light_linking, *collection_child->collection, callback);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
|
||||
callback(collection_light_linking, *collection_object->ob);
|
||||
}
|
||||
}
|
||||
namespace blender::deg::light_linking {
|
||||
|
||||
/* Iterate over all objects of the collection and invoke the given callback with two arguments:
|
||||
* CollectionLightLinking and the actual Object (passed as reference).
|
||||
*
|
||||
* The CollectionLightLinking denotes the effective light linking settings of the object. It comes
|
||||
* from the first level of hierarchy from the given collection.
|
||||
*
|
||||
* Note that if an object is reachable from multiple children collection the callback is invoked
|
||||
* for all of them. */
|
||||
template<class Proc>
|
||||
static void foreach_light_collection_object(const Collection &collection, Proc &&callback)
|
||||
{
|
||||
LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
|
||||
foreach_light_collection_object_inner(
|
||||
collection_child->light_linking, *collection_child->collection, callback);
|
||||
}
|
||||
using LightSet = internal::LightSet;
|
||||
using EmitterData = internal::EmitterData;
|
||||
using EmitterDataMap = internal::EmitterDataMap;
|
||||
using EmitterSetMembership = internal::EmitterSetMembership;
|
||||
using LinkingData = internal::LinkingData;
|
||||
|
||||
LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
|
||||
callback(collection_object->light_linking, *collection_object->ob);
|
||||
}
|
||||
}
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Helper class which takes care of allocating an unique light set IDs, performing checks for
|
||||
* overflows. */
|
||||
|
@ -173,160 +120,38 @@ class LightSetIDManager {
|
|||
|
||||
} // namespace
|
||||
|
||||
void Cache::clear()
|
||||
/* LightSet */
|
||||
|
||||
bool LightSet::operator==(const LightSet &other) const
|
||||
{
|
||||
return include_collection_mask == other.include_collection_mask &&
|
||||
exclude_collection_mask == other.exclude_collection_mask;
|
||||
}
|
||||
|
||||
uint64_t LightSet::hash() const
|
||||
{
|
||||
return get_default_hash_2(get_default_hash(include_collection_mask),
|
||||
get_default_hash(exclude_collection_mask));
|
||||
}
|
||||
|
||||
/* EmitterSetMembership */
|
||||
|
||||
uint64_t EmitterSetMembership::get_mask() const
|
||||
{
|
||||
const uint64_t effective_included_mask = included_sets_mask ? included_sets_mask :
|
||||
SET_MEMBERSHIP_ALL;
|
||||
return effective_included_mask & ~excluded_sets_mask;
|
||||
}
|
||||
|
||||
/* EmitterDataMap. */
|
||||
|
||||
void EmitterDataMap::clear()
|
||||
{
|
||||
light_linked_sets_.clear();
|
||||
emitter_data_map_.clear();
|
||||
receiver_light_sets_.clear();
|
||||
next_collection_id_ = 0;
|
||||
}
|
||||
|
||||
void Cache::add_emitter(const Scene &scene, const Object &emitter)
|
||||
{
|
||||
BLI_assert(DEG_is_original_id(&emitter.id));
|
||||
|
||||
if (can_skip_emitter(emitter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const LightLinking &light_linking = emitter.light_linking;
|
||||
Collection *collection = light_linking.collection;
|
||||
|
||||
/* Add collection bit to all receivers affected by this emitter or any emitter with the same
|
||||
* receiver collection. */
|
||||
/* TODO: optimize by doing this non-recursively, and only going recursive in end_build()? */
|
||||
foreach_light_collection_object(
|
||||
*collection,
|
||||
[&](const CollectionLightLinking &collection_light_linking, const Object &receiver) {
|
||||
add_receiver_object(scene, emitter, collection_light_linking, receiver);
|
||||
});
|
||||
}
|
||||
|
||||
void Cache::add_receiver_object(const Scene &scene,
|
||||
const Object &emitter,
|
||||
const CollectionLightLinking &collection_light_linking,
|
||||
const Object &receiver)
|
||||
{
|
||||
BLI_assert(DEG_is_original_id(&emitter->id));
|
||||
BLI_assert(DEG_is_original_id(&receiver->id));
|
||||
|
||||
if (receiver.light_linking.collection != nullptr) {
|
||||
/* If the object has receiver collection configure do not consider it as a receiver, avoiding
|
||||
* dependency cycles. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Light linking membership. */
|
||||
if (collection_light_linking.light_state != COLLECTION_LIGHT_LINKING_STATE_NONE) {
|
||||
const EmitterData *emitter_data = ensure_emitter_data_if_possible(scene, emitter);
|
||||
|
||||
if (emitter_data) {
|
||||
LightSet &light_set = ensure_light_linked_set(receiver);
|
||||
|
||||
switch (collection_light_linking.light_state) {
|
||||
case COLLECTION_LIGHT_LINKING_STATE_NONE:
|
||||
break;
|
||||
|
||||
case COLLECTION_LIGHT_LINKING_STATE_INCLUDE:
|
||||
light_set.include_collection_mask |= emitter_data->collection_mask;
|
||||
light_set.exclude_collection_mask &= ~emitter_data->collection_mask;
|
||||
break;
|
||||
|
||||
case COLLECTION_LIGHT_LINKING_STATE_EXCLUDE:
|
||||
light_set.exclude_collection_mask |= emitter_data->collection_mask;
|
||||
light_set.include_collection_mask &= ~emitter_data->collection_mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO(sergey): Handle shadow linking. */
|
||||
}
|
||||
|
||||
void Cache::end_build(const Scene &scene)
|
||||
{
|
||||
if (!has_light_linking()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LightSetIDManager light_set_id_manager(scene);
|
||||
|
||||
for (const auto it : light_linked_sets_.items()) {
|
||||
const Object *receiver = it.key;
|
||||
LightSet &light_set = it.value;
|
||||
|
||||
uint64_t light_set_id;
|
||||
if (!light_set_id_manager.get(light_set, light_set_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint64_t light_set_mask = uint64_t(1) << light_set_id;
|
||||
|
||||
receiver_light_sets_.add(receiver, light_set_id);
|
||||
|
||||
for (EmitterData &emitter_data : emitter_data_map_.values()) {
|
||||
if (emitter_data.collection_mask & light_set.include_collection_mask) {
|
||||
emitter_data.included_sets_mask |= light_set_mask;
|
||||
}
|
||||
if (emitter_data.collection_mask & light_set.exclude_collection_mask) {
|
||||
emitter_data.excluded_sets_mask |= light_set_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
light_linked_sets_.clear_and_shrink();
|
||||
}
|
||||
|
||||
/* Set runtime data in light linking. */
|
||||
void Cache::eval_runtime_data(Object &object_eval) const
|
||||
{
|
||||
LightLinking &light_linking = object_eval.light_linking;
|
||||
|
||||
if (!has_light_linking()) {
|
||||
/* No light linking used in the scene. */
|
||||
light_linking.runtime.receiver_set = 0;
|
||||
light_linking.runtime.set_membership = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Receiver configuration. */
|
||||
const Object *object_orig = get_original(&object_eval);
|
||||
light_linking.runtime.receiver_set = receiver_light_sets_.lookup_default(object_orig,
|
||||
LightSet::DEFAULT_ID);
|
||||
|
||||
/* Emitter configuration. */
|
||||
const EmitterData *emitter_data = get_emitter_data(object_eval);
|
||||
if (emitter_data) {
|
||||
light_linking.runtime.set_membership = emitter_data->get_set_membership();
|
||||
}
|
||||
else {
|
||||
light_linking.runtime.set_membership = EmitterData::SET_MEMBERSHIP_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
internal::LightSet &Cache::ensure_light_linked_set(const Object &receiver)
|
||||
{
|
||||
BLI_assert(DEG_is_original_id(&receiver.id));
|
||||
|
||||
return light_linked_sets_.lookup_or_add_as(&receiver);
|
||||
}
|
||||
|
||||
bool Cache::can_skip_emitter(const Object &emitter) const
|
||||
{
|
||||
BLI_assert(DEG_is_original_id(&emitter.id));
|
||||
|
||||
const LightLinking &light_linking = emitter.light_linking;
|
||||
const Collection *collection = light_linking.collection;
|
||||
|
||||
if (!collection) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return emitter_data_map_.contains(collection);
|
||||
}
|
||||
|
||||
internal::EmitterData *Cache::ensure_emitter_data_if_possible(const Scene &scene,
|
||||
const Object &emitter)
|
||||
EmitterData *EmitterDataMap::ensure_data_if_possible(const Scene &scene, const Object &emitter)
|
||||
{
|
||||
BLI_assert(DEG_is_original_id(&emitter.id));
|
||||
BLI_assert(emitter.light_linking.collection);
|
||||
|
@ -370,7 +195,7 @@ internal::EmitterData *Cache::ensure_emitter_data_if_possible(const Scene &scene
|
|||
return &emitter_data;
|
||||
}
|
||||
|
||||
const internal::EmitterData *Cache::get_emitter_data(const Object &emitter) const
|
||||
const EmitterData *EmitterDataMap::get_data(const Object &emitter) const
|
||||
{
|
||||
const LightLinking &light_linking = emitter.light_linking;
|
||||
const Collection *collection_eval = light_linking.collection;
|
||||
|
@ -384,6 +209,257 @@ const internal::EmitterData *Cache::get_emitter_data(const Object &emitter) cons
|
|||
return emitter_data_map_.lookup_ptr(collection_orig);
|
||||
}
|
||||
|
||||
bool EmitterDataMap::can_skip_emitter(const Object &emitter) const
|
||||
{
|
||||
BLI_assert(DEG_is_original_id(&emitter.id));
|
||||
|
||||
const LightLinking &light_linking = emitter.light_linking;
|
||||
const Collection *collection = light_linking.collection;
|
||||
|
||||
if (!collection) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return emitter_data_map_.contains(collection);
|
||||
}
|
||||
|
||||
/* LinkingData */
|
||||
|
||||
void LinkingData::clear()
|
||||
{
|
||||
light_linked_sets_.clear();
|
||||
object_light_sets_.clear();
|
||||
}
|
||||
|
||||
void LinkingData::link_object(const EmitterData &emitter_data,
|
||||
const eCollectionLightLinkingState link_state,
|
||||
const Object &object)
|
||||
{
|
||||
if (link_state == COLLECTION_LIGHT_LINKING_STATE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
LightSet &light_set = ensure_light_set_for(object);
|
||||
|
||||
switch (link_state) {
|
||||
case COLLECTION_LIGHT_LINKING_STATE_NONE:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
|
||||
case COLLECTION_LIGHT_LINKING_STATE_INCLUDE:
|
||||
light_set.include_collection_mask |= emitter_data.collection_mask;
|
||||
light_set.exclude_collection_mask &= ~emitter_data.collection_mask;
|
||||
break;
|
||||
|
||||
case COLLECTION_LIGHT_LINKING_STATE_EXCLUDE:
|
||||
light_set.exclude_collection_mask |= emitter_data.collection_mask;
|
||||
light_set.include_collection_mask &= ~emitter_data.collection_mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LightSet &LinkingData::ensure_light_set_for(const Object &object)
|
||||
{
|
||||
BLI_assert(DEG_is_original_id(&object.id));
|
||||
|
||||
return light_linked_sets_.lookup_or_add_as(&object);
|
||||
}
|
||||
|
||||
void LinkingData::clear_after_build()
|
||||
{
|
||||
light_linked_sets_.clear_and_shrink();
|
||||
}
|
||||
|
||||
void LinkingData::end_build(const Scene &scene, EmitterDataMap &emitter_data_map)
|
||||
{
|
||||
LightSetIDManager light_set_id_manager(scene);
|
||||
|
||||
for (const auto it : light_linked_sets_.items()) {
|
||||
const Object *receiver = it.key;
|
||||
LightSet &light_set = it.value;
|
||||
|
||||
uint64_t light_set_id;
|
||||
if (!light_set_id_manager.get(light_set, light_set_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint64_t light_set_mask = uint64_t(1) << light_set_id;
|
||||
|
||||
object_light_sets_.add(receiver, light_set_id);
|
||||
|
||||
update_emitters_membership(emitter_data_map, light_set, light_set_mask);
|
||||
}
|
||||
|
||||
clear_after_build();
|
||||
}
|
||||
|
||||
void LinkingData::update_emitters_membership(EmitterDataMap &emitter_data_map,
|
||||
const LightSet &light_set,
|
||||
const uint64_t light_set_mask)
|
||||
{
|
||||
for (EmitterData &emitter_data : emitter_data_map.values()) {
|
||||
EmitterSetMembership &set_membership = get_emitter_set_membership(emitter_data);
|
||||
|
||||
if (emitter_data.collection_mask & light_set.include_collection_mask) {
|
||||
set_membership.included_sets_mask |= light_set_mask;
|
||||
}
|
||||
if (emitter_data.collection_mask & light_set.exclude_collection_mask) {
|
||||
set_membership.excluded_sets_mask |= light_set_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t LinkingData::get_light_set_for(const Object &object) const
|
||||
{
|
||||
const Object *object_orig = get_original(&object);
|
||||
return object_light_sets_.lookup_default(object_orig, LightSet::DEFAULT_ID);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
namespace {
|
||||
|
||||
/* Iterate over all objects of the collection and invoke the given callback with two arguments:
|
||||
* the given collection light linking settings, and the object (passed as reference).
|
||||
*
|
||||
* Note that if an object is reachable from multiple children collection the callback is invoked
|
||||
* for all of them. */
|
||||
template<class Proc>
|
||||
static void foreach_light_collection_object_inner(
|
||||
const CollectionLightLinking &collection_light_linking,
|
||||
const Collection &collection,
|
||||
Proc &&callback)
|
||||
{
|
||||
LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
|
||||
foreach_light_collection_object_inner(
|
||||
collection_light_linking, *collection_child->collection, callback);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
|
||||
callback(collection_light_linking, *collection_object->ob);
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate over all objects of the collection and invoke the given callback with two arguments:
|
||||
* CollectionLightLinking and the actual Object (passed as reference).
|
||||
*
|
||||
* The CollectionLightLinking denotes the effective light linking settings of the object. It comes
|
||||
* from the first level of hierarchy from the given collection.
|
||||
*
|
||||
* Note that if an object is reachable from multiple children collection the callback is invoked
|
||||
* for all of them. */
|
||||
template<class Proc>
|
||||
static void foreach_light_collection_object(const Collection &collection, Proc &&callback)
|
||||
{
|
||||
LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
|
||||
foreach_light_collection_object_inner(
|
||||
collection_child->light_linking, *collection_child->collection, callback);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
|
||||
callback(collection_object->light_linking, *collection_object->ob);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Cache::clear()
|
||||
{
|
||||
emitter_data_map_.clear();
|
||||
light_linking_.clear();
|
||||
shadow_linking_.clear();
|
||||
}
|
||||
|
||||
void Cache::add_emitter(const Scene &scene, const Object &emitter)
|
||||
{
|
||||
BLI_assert(DEG_is_original_id(&emitter.id));
|
||||
|
||||
if (emitter_data_map_.can_skip_emitter(emitter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const LightLinking &light_linking = emitter.light_linking;
|
||||
Collection *collection = light_linking.collection;
|
||||
|
||||
const EmitterData *emitter_data = emitter_data_map_.ensure_data_if_possible(scene, emitter);
|
||||
if (!emitter_data) {
|
||||
/* The number of possible emitters exceeded in the scene. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add collection bit to all receivers affected by this emitter or any emitter with the same
|
||||
* receiver collection. */
|
||||
foreach_light_collection_object(
|
||||
*collection,
|
||||
[&](const CollectionLightLinking &collection_light_linking, const Object &receiver) {
|
||||
add_receiver_object(*emitter_data, collection_light_linking, receiver);
|
||||
});
|
||||
}
|
||||
|
||||
void Cache::add_receiver_object(const EmitterData &emitter_data,
|
||||
const CollectionLightLinking &collection_light_linking,
|
||||
const Object &receiver)
|
||||
{
|
||||
BLI_assert(DEG_is_original_id(&receiver.id));
|
||||
|
||||
if (receiver.light_linking.collection != nullptr) {
|
||||
/* If the object has receiver collection configure do not consider it as a receiver, avoiding
|
||||
* dependency cycles. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Light linking. */
|
||||
light_linking_.link_object(
|
||||
emitter_data, eCollectionLightLinkingState(collection_light_linking.light_state), receiver);
|
||||
|
||||
/* Shadow linking. */
|
||||
shadow_linking_.link_object(
|
||||
emitter_data, eCollectionLightLinkingState(collection_light_linking.shadow_state), receiver);
|
||||
}
|
||||
|
||||
void Cache::end_build(const Scene &scene)
|
||||
{
|
||||
if (!has_light_linking()) {
|
||||
return;
|
||||
}
|
||||
|
||||
light_linking_.end_build(scene, emitter_data_map_);
|
||||
shadow_linking_.end_build(scene, emitter_data_map_);
|
||||
}
|
||||
|
||||
/* Set runtime data in light linking. */
|
||||
void Cache::eval_runtime_data(Object &object_eval) const
|
||||
{
|
||||
LightLinking &light_linking = object_eval.light_linking;
|
||||
|
||||
if (!has_light_linking()) {
|
||||
/* No light linking used in the scene. */
|
||||
|
||||
light_linking.runtime.receiver_light_set = 0;
|
||||
light_linking.runtime.light_set_membership = 0;
|
||||
|
||||
light_linking.runtime.blocker_shadow_set = 0;
|
||||
light_linking.runtime.shadow_set_membership = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Receiver and blocker configuration. */
|
||||
light_linking.runtime.receiver_light_set = light_linking_.get_light_set_for(object_eval);
|
||||
light_linking.runtime.blocker_shadow_set = shadow_linking_.get_light_set_for(object_eval);
|
||||
|
||||
/* Emitter configuration. */
|
||||
const EmitterData *emitter_data = emitter_data_map_.get_data(object_eval);
|
||||
if (emitter_data) {
|
||||
light_linking.runtime.light_set_membership = emitter_data->light_membership.get_mask();
|
||||
light_linking.runtime.shadow_set_membership = emitter_data->shadow_membership.get_mask();
|
||||
}
|
||||
else {
|
||||
light_linking.runtime.light_set_membership = EmitterSetMembership::SET_MEMBERSHIP_ALL;
|
||||
light_linking.runtime.shadow_set_membership = EmitterSetMembership::SET_MEMBERSHIP_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::deg::light_linking
|
||||
|
||||
/* \} */
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "DNA_collection_types.h" /* eCollectionLightLinkingState */
|
||||
|
||||
struct Collection;
|
||||
struct CollectionLightLinking;
|
||||
struct Object;
|
||||
|
@ -46,6 +48,19 @@ class LightSet {
|
|||
uint64_t exclude_collection_mask;
|
||||
};
|
||||
|
||||
class EmitterSetMembership {
|
||||
public:
|
||||
/* Bitmask which indicates the emitter belongs to all light sets. */
|
||||
static constexpr uint64_t SET_MEMBERSHIP_ALL = ~uint64_t(0);
|
||||
|
||||
/* Get final membership mask in the light sets, considering its inclusion and exclusion. */
|
||||
uint64_t get_mask() const;
|
||||
|
||||
/* Bit masks of the emitter membership in the light sets. */
|
||||
uint64_t included_sets_mask = 0;
|
||||
uint64_t excluded_sets_mask = 0;
|
||||
};
|
||||
|
||||
/* Packed information about emitter.
|
||||
* Emitter is actually corresponding to a light linking collection on an object. */
|
||||
class EmitterData {
|
||||
|
@ -54,20 +69,127 @@ class EmitterData {
|
|||
* The limitation is imposed by the fact that its identifier is converted to a bitmask. */
|
||||
static constexpr int MAX_COLLECTION_ID = 63;
|
||||
|
||||
/* Bitmask which indicates the emitter belongs to all light sets. */
|
||||
static constexpr uint64_t SET_MEMBERSHIP_ALL = ~uint64_t(0);
|
||||
|
||||
/* Get final emitter membership in the light sets, considering its inclusion and exclusion. */
|
||||
uint64_t get_set_membership() const;
|
||||
|
||||
/* Mask of a light linking collection this emitter uses in its configuration.
|
||||
* A single bit is set in this bitfield which corresponds to an identifier of a light linking
|
||||
* collection in the scene. */
|
||||
uint64_t collection_mask = 0;
|
||||
|
||||
/* Bit masks of the emitter membership in the light sets. */
|
||||
uint64_t included_sets_mask = 0;
|
||||
uint64_t excluded_sets_mask = 0;
|
||||
/* Membership masks for the light and shadow linking. */
|
||||
EmitterSetMembership light_membership;
|
||||
EmitterSetMembership shadow_membership;
|
||||
};
|
||||
|
||||
/* Helper class which deals with keeping per-emitter data. */
|
||||
class EmitterDataMap {
|
||||
using MapType = Map<const Collection *, EmitterData>;
|
||||
|
||||
public:
|
||||
/* Returns true if there is no information about emitters at all. */
|
||||
bool is_empty() const
|
||||
{
|
||||
return emitter_data_map_.is_empty();
|
||||
}
|
||||
|
||||
/* Entirely clear the state, become ready for a new light linking relations build. */
|
||||
void clear();
|
||||
|
||||
/* Ensure that the data exists for the given emitter.
|
||||
* The emitter must be original and have light linking collection.
|
||||
*
|
||||
* Note that there is limited number of emitters possible within a scene, When this number is
|
||||
* exceeded an error is printed and a nullptr is returned. */
|
||||
EmitterData *ensure_data_if_possible(const Scene &scene, const Object &emitter);
|
||||
|
||||
/* Get emitter data for the given original or evaluated object.
|
||||
* If the light linking is not configured for this emitted nullptr is returned. */
|
||||
const EmitterData *get_data(const Object &emitter) const;
|
||||
|
||||
/* Returns true if the underlying data of the light linking emitter has been handled, and there
|
||||
* is no need to handle the emitter.
|
||||
* The emitter must be original object. */
|
||||
bool can_skip_emitter(const Object &emitter) const;
|
||||
|
||||
/* Returns an iterator over all emitter data in the map. */
|
||||
MapType::MutableValueIterator values()
|
||||
{
|
||||
return emitter_data_map_.values();
|
||||
}
|
||||
|
||||
private:
|
||||
/* Emitter-centric information: indexed by an original emitter object, contains accumulated
|
||||
* information about emitter. */
|
||||
MapType emitter_data_map_;
|
||||
|
||||
/* Next unique light linking collection ID. */
|
||||
uint64_t next_collection_id_ = 0;
|
||||
};
|
||||
|
||||
/* Common part of receiver (for light linking) and blocker (for shadow lining) data. */
|
||||
class LinkingData {
|
||||
public:
|
||||
explicit LinkingData(const bool is_shadow) : is_shadow_(is_shadow) {}
|
||||
|
||||
/* Entirely clear the state, become ready for a new light linking relations build. */
|
||||
void clear();
|
||||
|
||||
/* Link the given object with the given light linking state. */
|
||||
void link_object(const EmitterData &emitter_data,
|
||||
eCollectionLightLinkingState link_state,
|
||||
const Object &object);
|
||||
|
||||
/* Compute unique sets of emitters used by receivers or blockers.
|
||||
*
|
||||
* This must be called at the end of depsgraph relations build after all emitters have been
|
||||
* added, and before runtime data can be set as part of evaluation. */
|
||||
void end_build(const Scene &scene, EmitterDataMap &emitter_data_map);
|
||||
|
||||
/* Get an unique index the given object is receiving light or casting shadow from.
|
||||
* The object can either be original or evaluated.
|
||||
*
|
||||
* If the object is not linked to any emitter LightSet::DEFAULT_ID is returned. */
|
||||
uint64_t get_light_set_for(const Object &object) const;
|
||||
|
||||
private:
|
||||
/* Ensure that the light set exists for the given receiver/blocker object.
|
||||
* The object must be original. */
|
||||
LightSet &ensure_light_set_for(const Object &object);
|
||||
|
||||
/* Update the emitter light/shadow set membership after the final unique light set identifier
|
||||
* is known.
|
||||
* The light_set_mask consists of a single bit set corresponding to the light set index. */
|
||||
void update_emitters_membership(EmitterDataMap &emitter_data_map,
|
||||
const LightSet &light_set,
|
||||
uint64_t light_set_mask);
|
||||
|
||||
/* Clear data which is only needed during the build. */
|
||||
void clear_after_build();
|
||||
|
||||
/* Get light set membership information of the emitter data depending whether this linking
|
||||
* data is a light or shadow linking. */
|
||||
/* TODO(sergey): Check whether such per-update_emitters_membership() call is fast enough, or
|
||||
* whether template specialization is preferred here. */
|
||||
inline EmitterSetMembership &get_emitter_set_membership(EmitterData &emitter_data)
|
||||
{
|
||||
if (is_shadow_) {
|
||||
return emitter_data.shadow_membership;
|
||||
}
|
||||
return emitter_data.light_membership;
|
||||
}
|
||||
|
||||
bool is_shadow_ = false;
|
||||
|
||||
/* Receiver/blocker-centric view of light sets: indexed by an original receiver object, contains
|
||||
* light set which defines from which emitters it receives light from or casts shadow when is lit
|
||||
* ny.
|
||||
*
|
||||
* NOTE: Only available during build. */
|
||||
Map<const Object *, LightSet> light_linked_sets_;
|
||||
|
||||
/* Map from an original receiver/blocker object: map to index of light set for this
|
||||
* receiver/blocker. */
|
||||
/* TODO(sergey): What is the generic term for receiver and blocker which is less generic than
|
||||
* object? */
|
||||
Map<const Object *, uint64_t> object_light_sets_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -80,8 +202,9 @@ class EmitterData {
|
|||
* This cache takes care of making it efficient to lookup emitter masks, emitters which affect
|
||||
* given receiver and so on. */
|
||||
class Cache {
|
||||
using LightSet = internal::LightSet;
|
||||
using EmitterData = internal::EmitterData;
|
||||
using EmitterDataMap = internal::EmitterDataMap;
|
||||
using LinkingData = internal::LinkingData;
|
||||
|
||||
public:
|
||||
/* Entirely clear the cache.
|
||||
|
@ -98,7 +221,7 @@ class Cache {
|
|||
* object leads to an undefined behavior. */
|
||||
void add_emitter(const Scene &scene, const Object &emitter);
|
||||
|
||||
/* Compute unique sets of emitters used by receivers.
|
||||
/* Compute unique sets of emitters used by receivers and blockers.
|
||||
*
|
||||
* This must be called at the end of depsgraph relations build after all emitters have been
|
||||
* added, and before runtime data can be set as part of evaluation. */
|
||||
|
@ -108,53 +231,26 @@ class Cache {
|
|||
void eval_runtime_data(Object &object_eval) const;
|
||||
|
||||
private:
|
||||
/* Add receiver object with the given light linking configuration. */
|
||||
void add_receiver_object(const Scene &scene,
|
||||
const Object &emitter,
|
||||
/* Add receiver or blocker object with the given light linking configuration.
|
||||
*
|
||||
* The term receiver here is meant in a wider meaning of it. For the light linking it is a
|
||||
* receiver of light, but for the shadow linking is it actually a shadow caster. */
|
||||
void add_receiver_object(const EmitterData &emitter_data,
|
||||
const CollectionLightLinking &collection_light_linking,
|
||||
const Object &receiver);
|
||||
|
||||
/* Ensure that the light set exists for the given receiver.
|
||||
* The receiver must be original. */
|
||||
LightSet &ensure_light_linked_set(const Object &receiver);
|
||||
|
||||
/* Returns true if the underlying data of the light linking emitter has been handled, and there
|
||||
* is no need to handle the emitter.
|
||||
* The emitter must be original object. */
|
||||
bool can_skip_emitter(const Object &emitter) const;
|
||||
|
||||
/* Ensure that the data exists for the given emitter.
|
||||
* The emitter must be original and have light linking collection.
|
||||
*
|
||||
* Note that there is limited number of emitters possible within a scene, When this number is
|
||||
* exceeded an error is printed and a nullptr is returned. */
|
||||
EmitterData *ensure_emitter_data_if_possible(const Scene &scene, const Object &emitter);
|
||||
|
||||
/* Get emitter data for the given original or evaluated object.
|
||||
* If the light linking is not configured for this emitted nullptr is returned. */
|
||||
const EmitterData *get_emitter_data(const Object &emitter) const;
|
||||
|
||||
/* Returns true if there is light linking configuration in the scene. */
|
||||
bool has_light_linking() const
|
||||
{
|
||||
return !emitter_data_map_.is_empty();
|
||||
}
|
||||
|
||||
/* Receiver-centric view of light sets: indexed by an original receiver object, contains light
|
||||
* set which defines from which emitters it receives light from.
|
||||
*
|
||||
* NOTE: Only available during build. */
|
||||
Map<const Object *, LightSet> light_linked_sets_;
|
||||
/* Per-emitter light and shadow linking information. */
|
||||
EmitterDataMap emitter_data_map_;
|
||||
|
||||
/* Emitter-centric information: indexed by an original emitter object, contains accumulated
|
||||
* information about emitter. */
|
||||
Map<const Collection *, EmitterData> emitter_data_map_;
|
||||
|
||||
/* Map from an original receiver object: map to index of light set for this receiver. */
|
||||
Map<const Object *, uint64_t> receiver_light_sets_;
|
||||
|
||||
/* Next unique light linking collection ID. */
|
||||
uint64_t next_collection_id_ = 0;
|
||||
/* Light and shadow linking data. */
|
||||
LinkingData light_linking_{false};
|
||||
LinkingData shadow_linking_{true};
|
||||
};
|
||||
|
||||
} // namespace blender::deg::light_linking
|
||||
|
|
|
@ -247,22 +247,40 @@ enum eObjectLineArt_Flags {
|
|||
OBJECT_LRT_OWN_INTERSECTION_PRIORITY = (1 << 1),
|
||||
};
|
||||
|
||||
/* Evaluated light linking state needed for the render engines integration. */
|
||||
typedef struct LightLinkingRuntime {
|
||||
/* For objects that emit light: a bitmask of light sets this emitter is part of.
|
||||
|
||||
/* For objects that emit light: a bitmask of light sets this emitter is part of for the light
|
||||
* linking.
|
||||
* A light set is a combination of emitters used by one or more receiver objects.
|
||||
*
|
||||
* If there is no light linking in the scene, this is assigned zero. If the emitter does
|
||||
* not specify light linking, it is a member of all light sets.
|
||||
*
|
||||
* NOTE: There can only be 64 light sets in a scene. */
|
||||
uint64_t set_membership;
|
||||
uint64_t light_set_membership;
|
||||
|
||||
/* For receiver objects, the index of the light set from which this object receives light.
|
||||
/* For objects that emit light: a bitmask of light sets this emitter is part of for the shadow
|
||||
* linking.
|
||||
* A light set is a combination of emitters from which a blocked object does not cast a shadow.
|
||||
*
|
||||
* If there is no shadow linking in the scene, this is assigned zero. If the emitter does
|
||||
* not specify light linking, it is a member of all light sets.
|
||||
*
|
||||
* NOTE: There can only be 64 light sets in a scene. */
|
||||
uint64_t shadow_set_membership;
|
||||
|
||||
/* For receiver objects: the index of the light set from which this object receives light.
|
||||
*
|
||||
* If there is no light linking in the scene, this is assigned zero. */
|
||||
uint8_t receiver_set;
|
||||
uint8_t receiver_light_set;
|
||||
|
||||
uint8_t _pad[7];
|
||||
/* For blocker objects: the index of the light set from which this object casts shadow from.
|
||||
*
|
||||
* If there is no light shadow in the scene, this is assigned zero. */
|
||||
uint8_t blocker_shadow_set;
|
||||
|
||||
uint8_t _pad[6];
|
||||
} LightLinkingRuntime;
|
||||
|
||||
typedef struct LightLinking {
|
||||
|
|
Loading…
Reference in New Issue