Cycles: build specialized light trees for light linking #107561
|
@ -465,7 +465,8 @@ ccl_device_inline float light_sample_mis_weight_forward_surface(KernelGlobals kg
|
|||
uint prim_offset = kernel_data_fetch(object_prim_offset, sd->object);
|
||||
uint triangle = kernel_data_fetch(triangle_to_tree, sd->prim - prim_offset + lookup_offset);
|
||||
|
||||
pdf *= light_tree_pdf(kg, ray_P, N, path_flag, sd->object, triangle);
|
||||
pdf *= light_tree_pdf(
|
||||
kg, ray_P, N, path_flag, sd->object, triangle, light_link_receiver_forward(kg, state));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
@ -489,7 +490,13 @@ ccl_device_inline float light_sample_mis_weight_forward_lamp(KernelGlobals kg,
|
|||
#ifdef __LIGHT_TREE__
|
||||
if (kernel_data.integrator.use_light_tree) {
|
||||
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
|
||||
pdf *= light_tree_pdf(kg, P, N, path_flag, 0, kernel_data_fetch(light_to_tree, ls->lamp));
|
||||
pdf *= light_tree_pdf(kg,
|
||||
P,
|
||||
N,
|
||||
path_flag,
|
||||
0,
|
||||
kernel_data_fetch(light_to_tree, ls->lamp),
|
||||
light_link_receiver_forward(kg, state));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
@ -524,7 +531,8 @@ ccl_device_inline float light_sample_mis_weight_forward_background(KernelGlobals
|
|||
if (kernel_data.integrator.use_light_tree) {
|
||||
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
|
||||
uint light = kernel_data_fetch(light_to_tree, kernel_data.background.light_index);
|
||||
pdf *= light_tree_pdf(kg, ray_P, N, path_flag, 0, light);
|
||||
pdf *= light_tree_pdf(
|
||||
kg, ray_P, N, path_flag, 0, light, light_link_receiver_forward(kg, state));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
|
|
@ -656,6 +656,19 @@ ccl_device bool get_left_probability(KernelGlobals kg,
|
|||
return true;
|
||||
}
|
||||
|
||||
ccl_device int light_tree_root_node_index(KernelGlobals kg, const int object_receiver)
|
||||
{
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_LINKING) {
|
||||
const uint receiver_light_set =
|
||||
(object_receiver != OBJECT_NONE) ?
|
||||
kernel_data_fetch(objects, object_receiver).receiver_light_set :
|
||||
0;
|
||||
return kernel_data.light_link_sets[receiver_light_set].light_tree_root;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<bool in_volume_segment>
|
||||
ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
|
||||
float randn,
|
||||
|
@ -679,8 +692,8 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
|
|||
float pdf_leaf = 1.0f;
|
||||
float pdf_selection = 1.0f;
|
||||
int selected_emitter = -1;
|
||||
int object = 0;
|
||||
int node_index = 0; /* Root node. */
|
||||
int object_emitter = 0;
|
||||
int node_index = light_tree_root_node_index(kg, object_receiver);
|
||||
|
||||
float3 local_P = P;
|
||||
|
||||
|
@ -698,7 +711,7 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
|
|||
}
|
||||
else {
|
||||
/* Continue with the picked mesh light. */
|
||||
object = kernel_data_fetch(light_tree_emitters, selected_emitter).mesh.object_id;
|
||||
object_emitter = kernel_data_fetch(light_tree_emitters, selected_emitter).mesh.object_id;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -737,26 +750,31 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
|
|||
bounce,
|
||||
path_flag,
|
||||
selected_emitter,
|
||||
object,
|
||||
object_emitter,
|
||||
pdf_selection,
|
||||
ls);
|
||||
}
|
||||
|
||||
/* We need to be able to find the probability of selecting a given light for MIS. */
|
||||
ccl_device float light_tree_pdf(
|
||||
KernelGlobals kg, float3 P, float3 N, const int path_flag, const int object, const uint target)
|
||||
ccl_device float light_tree_pdf(KernelGlobals kg,
|
||||
float3 P,
|
||||
float3 N,
|
||||
const int path_flag,
|
||||
const int object_emitter,
|
||||
const uint index_emitter,
|
||||
const int object_receiver)
|
||||
{
|
||||
const bool has_transmission = (path_flag & PATH_RAY_MIS_HAD_TRANSMISSION);
|
||||
|
||||
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
|
||||
target);
|
||||
index_emitter);
|
||||
int root_index;
|
||||
uint bit_trail, target_emitter;
|
||||
|
||||
if (is_triangle(kemitter)) {
|
||||
/* If the target is an emissive triangle, first traverse the top level tree to find the mesh
|
||||
* light emitter, then traverse the subtree. */
|
||||
target_emitter = kernel_data_fetch(object_to_tree, object);
|
||||
target_emitter = kernel_data_fetch(object_to_tree, object_emitter);
|
||||
ccl_global const KernelLightTreeEmitter *kmesh = &kernel_data_fetch(light_tree_emitters,
|
||||
target_emitter);
|
||||
root_index = kmesh->mesh.node_id;
|
||||
|
@ -770,11 +788,11 @@ ccl_device float light_tree_pdf(
|
|||
else {
|
||||
root_index = 0;
|
||||
bit_trail = kemitter->bit_trail;
|
||||
target_emitter = target;
|
||||
target_emitter = index_emitter;
|
||||
}
|
||||
|
||||
float pdf = 1.0f;
|
||||
int node_index = 0;
|
||||
int node_index = light_tree_root_node_index(kg, object_receiver);
|
||||
|
||||
/* Traverse the light tree until we reach the target leaf node. */
|
||||
while (true) {
|
||||
|
@ -813,11 +831,11 @@ ccl_device float light_tree_pdf(
|
|||
if (root_index) {
|
||||
/* Arrived at the mesh light. Continue with the subtree. */
|
||||
float unused;
|
||||
light_tree_to_local_space<false>(kg, object, P, N, unused);
|
||||
light_tree_to_local_space<false>(kg, object_emitter, P, N, unused);
|
||||
|
||||
node_index = root_index;
|
||||
root_index = 0;
|
||||
target_emitter = target;
|
||||
target_emitter = index_emitter;
|
||||
bit_trail = kemitter->bit_trail;
|
||||
continue;
|
||||
}
|
||||
|
@ -838,13 +856,14 @@ ccl_device float light_tree_pdf(
|
|||
}
|
||||
|
||||
const bool go_left = (bit_trail & 1) == 0;
|
||||
bit_trail >>= 1;
|
||||
pdf *= go_left ? left_prob : (1.0f - left_prob);
|
||||
node_index = go_left ? left_index : right_index;
|
||||
pdf *= go_left ? left_prob : (1.0f - left_prob);
|
||||
|
||||
if (pdf == 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bit_trail >>= kernel_data_fetch(light_tree_nodes, node_index).bit_shift;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1216,6 +1216,10 @@ typedef struct KernelBake {
|
|||
} KernelBake;
|
||||
static_assert_align(KernelBake, 16);
|
||||
|
||||
typedef struct KernelLightLinkSet {
|
||||
uint light_tree_root;
|
||||
} KernelLightLinkSet;
|
||||
|
||||
typedef struct KernelData {
|
||||
/* Features and limits. */
|
||||
uint kernel_features;
|
||||
|
@ -1227,6 +1231,7 @@ typedef struct KernelData {
|
|||
KernelCamera cam;
|
||||
KernelBake bake;
|
||||
KernelTables tables;
|
||||
KernelLightLinkSet light_link_sets[LIGHT_LINK_SET_MAX];
|
||||
|
||||
/* Potentially specialized data members. */
|
||||
#define KERNEL_STRUCT_BEGIN(name, parent) name parent;
|
||||
|
@ -1428,6 +1433,12 @@ typedef struct KernelLightTreeNode {
|
|||
|
||||
/* Bit trail. */
|
||||
uint bit_trail;
|
||||
|
||||
/* Bit shift for bit trail, to skip nodes for specialized trees. */
|
||||
uint8_t bit_shift;
|
||||
|
||||
/* Padding. */
|
||||
uint8_t pad[15];
|
||||
} KernelLightTreeNode;
|
||||
static_assert_align(KernelLightTreeNode, 16);
|
||||
|
||||
|
|
|
@ -463,6 +463,7 @@ static void light_tree_node_copy_to_device(KernelLightTreeNode &knode,
|
|||
knode.bcone.theta_e = node.measure.bcone.theta_e;
|
||||
|
||||
knode.bit_trail = node.bit_trail;
|
||||
knode.bit_shift = 1;
|
||||
knode.type = static_cast<LightTreeNodeType>(node.type);
|
||||
|
||||
if (node.is_leaf() || node.is_distant()) {
|
||||
|
@ -616,6 +617,133 @@ static int light_tree_flatten(LightTreeFlatten &flatten,
|
|||
return node_index;
|
||||
}
|
||||
|
||||
static void light_tree_emitters_copy_and_flatten(LightTreeFlatten &flatten,
|
||||
const LightTreeNode *node,
|
||||
KernelLightTreeNode *knodes,
|
||||
KernelLightTreeEmitter *kemitters,
|
||||
int &next_node_index)
|
||||
{
|
||||
/* Convert only emitters to device representation. */
|
||||
if (node->is_leaf() || node->is_distant()) {
|
||||
light_tree_leaf_emitters_copy_and_flatten(flatten, *node, knodes, kemitters, next_node_index);
|
||||
}
|
||||
else {
|
||||
assert(node->is_inner());
|
||||
|
||||
light_tree_emitters_copy_and_flatten(flatten,
|
||||
node->get_inner().children[LightTree::left].get(),
|
||||
knodes,
|
||||
kemitters,
|
||||
next_node_index);
|
||||
light_tree_emitters_copy_and_flatten(flatten,
|
||||
node->get_inner().children[LightTree::right].get(),
|
||||
knodes,
|
||||
kemitters,
|
||||
next_node_index);
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<int, LightTreeMeasure> light_tree_specialize_nodes_flatten(
|
||||
const LightTreeFlatten &flatten,
|
||||
LightTreeNode *node,
|
||||
const uint64_t light_link_mask,
|
||||
const int depth,
|
||||
vector<KernelLightTreeNode> &knodes,
|
||||
int &next_node_index)
|
||||
{
|
||||
assert(!node->is_instance());
|
||||
|
||||
/* Convert inner nodes to device representation, specialized for light linking. */
|
||||
int node_index, child_index = -1;
|
||||
|
||||
LightTreeNode new_node(LightTreeMeasure::empty, node->bit_trail);
|
||||
|
||||
if (depth == 0 && !(node->light_link.set_membership & light_link_mask)) {
|
||||
/* Ensure there is always a root node. */
|
||||
node_index = next_node_index++;
|
||||
new_node.make_leaf(-1, 0);
|
||||
}
|
||||
else if (node->light_link.shareable && node->light_link.shared_node_index != -1) {
|
||||
/* Share subtree already built for another light link set. */
|
||||
return std::make_pair(node->light_link.shared_node_index, node->measure);
|
||||
}
|
||||
else if (node->is_leaf() || node->is_distant()) {
|
||||
/* Specialize leaf node. */
|
||||
node_index = next_node_index++;
|
||||
int first_emitter = -1;
|
||||
int num_emitters = 0;
|
||||
|
||||
for (int i = 0; i < node->get_leaf().num_emitters; i++) {
|
||||
const LightTreeEmitter &emitter = flatten.emitters[node->get_leaf().first_emitter_index + i];
|
||||
if (emitter.light_set_membership & light_link_mask) {
|
||||
/* Assumes emitters are consecutive due to LighTree::sort_leaf. */
|
||||
if (first_emitter == -1) {
|
||||
first_emitter = node->get_leaf().first_emitter_index + i;
|
||||
}
|
||||
num_emitters++;
|
||||
new_node.measure.add(emitter.measure);
|
||||
}
|
||||
}
|
||||
|
||||
assert(first_emitter != -1);
|
||||
new_node.make_leaf(first_emitter, num_emitters);
|
||||
}
|
||||
else {
|
||||
assert(node->is_inner());
|
||||
assert(new_node.is_inner());
|
||||
|
||||
/* Specialize inner node. */
|
||||
LightTreeNode *left_node = node->get_inner().children[LightTree::left].get();
|
||||
LightTreeNode *right_node = node->get_inner().children[LightTree::right].get();
|
||||
|
||||
/* Skip nodes that have only one child. We have a single bit trail for each
|
||||
* primitive, the bit shift is incremented to skip the bit for this node. */
|
||||
LightTreeNode *only_node = nullptr;
|
||||
if (!(left_node->light_link.set_membership & light_link_mask)) {
|
||||
only_node = right_node;
|
||||
}
|
||||
else if (!(right_node->light_link.set_membership & light_link_mask)) {
|
||||
only_node = left_node;
|
||||
}
|
||||
if (only_node) {
|
||||
const auto [only_index, only_measure] = light_tree_specialize_nodes_flatten(
|
||||
flatten, only_node, light_link_mask, depth + 1, knodes, next_node_index);
|
||||
|
||||
assert(only_index != -1);
|
||||
knodes[only_index].bit_shift++;
|
||||
return std::make_pair(only_index, only_measure);
|
||||
}
|
||||
|
||||
/* Create inner node. */
|
||||
node_index = next_node_index++;
|
||||
|
||||
const auto [left_index, left_measure] = light_tree_specialize_nodes_flatten(
|
||||
flatten, left_node, light_link_mask, depth + 1, knodes, next_node_index);
|
||||
const auto [right_index, right_measure] = light_tree_specialize_nodes_flatten(
|
||||
flatten, right_node, light_link_mask, depth + 1, knodes, next_node_index);
|
||||
|
||||
new_node.measure = left_measure;
|
||||
new_node.measure.add(right_measure);
|
||||
|
||||
/* Nodes are stored in depth first order so that the left child node
|
||||
* immediately follows the parent, and only the right child index needs
|
||||
* to be stored. */
|
||||
child_index = right_index;
|
||||
}
|
||||
|
||||
/* Convert to kernel node. */
|
||||
if (knodes.size() <= node_index) {
|
||||
knodes.resize(node_index + 1);
|
||||
}
|
||||
light_tree_node_copy_to_device(knodes[node_index], new_node, child_index);
|
||||
|
||||
if (node->light_link.shareable) {
|
||||
node->light_link.shared_node_index = node_index;
|
||||
}
|
||||
|
||||
return std::make_pair(node_index, new_node.measure);
|
||||
}
|
||||
|
||||
void LightManager::device_update_tree(Device *,
|
||||
DeviceScene *dscene,
|
||||
Scene *scene,
|
||||
|
@ -649,23 +777,67 @@ void LightManager::device_update_tree(Device *,
|
|||
flatten.mesh_array = dscene->object_to_tree.alloc(scene->objects.size());
|
||||
flatten.triangle_array = dscene->triangle_to_tree.alloc(light_tree.num_triangles);
|
||||
|
||||
/* Allocate emitters and nodes. */
|
||||
/* Allocate emitters */
|
||||
const size_t num_emitters = light_tree.num_emitters();
|
||||
KernelLightTreeEmitter *kemitters = dscene->light_tree_emitters.alloc(num_emitters);
|
||||
KernelLightTreeNode *knodes = dscene->light_tree_nodes.alloc(light_tree.num_nodes);
|
||||
|
||||
/* Update integrator state. */
|
||||
kintegrator->use_direct_light = num_emitters > 0;
|
||||
|
||||
/* Copy nodes and emitters. */
|
||||
if (root) {
|
||||
int next_node_index = 0;
|
||||
light_tree_flatten(flatten, root, knodes, kemitters, next_node_index);
|
||||
}
|
||||
/* Test if light linking is used. */
|
||||
const bool use_light_linking = root && root->light_link.set_membership != 0;
|
||||
KernelLightLinkSet *klight_link_sets = dscene->data.light_link_sets;
|
||||
memset(klight_link_sets, 0, sizeof(dscene->data.light_link_sets));
|
||||
|
||||
VLOG_INFO << "Use light tree with " << num_emitters << " emitters and " << light_tree.num_nodes
|
||||
<< " nodes.";
|
||||
|
||||
if (!use_light_linking) {
|
||||
/* Regular light tree without linking. */
|
||||
KernelLightTreeNode *knodes = dscene->light_tree_nodes.alloc(light_tree.num_nodes);
|
||||
|
||||
if (root) {
|
||||
int next_node_index = 0;
|
||||
light_tree_flatten(flatten, root, knodes, kemitters, next_node_index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
int next_node_index = 0;
|
||||
|
||||
vector<KernelLightTreeNode> light_link_nodes;
|
||||
|
||||
/* Write primitives, and any subtrees for instances. */
|
||||
if (root) {
|
||||
/* Reserve enough size of all instance subtrees, then shrink back to
|
||||
* actual number of nodes used. */
|
||||
light_link_nodes.resize(light_tree.num_nodes);
|
||||
light_tree_emitters_copy_and_flatten(
|
||||
flatten, root, light_link_nodes.data(), kemitters, next_node_index);
|
||||
light_link_nodes.resize(next_node_index);
|
||||
}
|
||||
|
||||
/* Specialized light trees for linking. */
|
||||
for (uint64_t tree_index = 0; tree_index < LIGHT_LINK_SET_MAX; tree_index++) {
|
||||
const uint64_t tree_mask = uint64_t(1) << tree_index;
|
||||
if (!(light_tree.light_link_receiver_used & tree_mask)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
klight_link_sets[tree_index].light_tree_root = next_node_index;
|
||||
if (root) {
|
||||
light_tree_specialize_nodes_flatten(
|
||||
flatten, root, tree_mask, 0, light_link_nodes, next_node_index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate and copy nodes into device array. */
|
||||
KernelLightTreeNode *knodes = dscene->light_tree_nodes.alloc(light_link_nodes.size());
|
||||
memcpy(knodes, light_link_nodes.data(), light_link_nodes.size() * sizeof(*knodes));
|
||||
|
||||
VLOG_INFO << "Specialized light tree for light linking, with "
|
||||
<< light_link_nodes.size() - light_tree.num_nodes << " additional nodes.";
|
||||
}
|
||||
|
||||
/* Copy arrays to device. */
|
||||
dscene->light_tree_nodes.copy_to_device();
|
||||
dscene->light_tree_emitters.copy_to_device();
|
||||
|
|
|
@ -77,6 +77,7 @@ OrientationBounds merge(const OrientationBounds &cone_a, const OrientationBounds
|
|||
LightTreeEmitter::LightTreeEmitter(Object *object, int object_id) : object_id(object_id)
|
||||
{
|
||||
centroid = object->bounds.center();
|
||||
light_set_membership = object->get_light_set_membership();
|
||||
}
|
||||
|
||||
LightTreeEmitter::LightTreeEmitter(Scene *scene,
|
||||
|
@ -138,6 +139,8 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene,
|
|||
for (int i = 0; i < 3; i++) {
|
||||
measure.bbox.grow(vertices[i]);
|
||||
}
|
||||
|
||||
light_set_membership = object->get_light_set_membership();
|
||||
}
|
||||
else {
|
||||
assert(is_light());
|
||||
|
@ -216,6 +219,20 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene,
|
|||
/* Use absolute value of energy so lights with negative strength are properly supported in the
|
||||
* light tree. */
|
||||
measure.energy = fabsf(average(strength));
|
||||
|
||||
light_set_membership = lamp->get_light_set_membership();
|
||||
}
|
||||
}
|
||||
|
||||
static void sort_leaf(const int start, const int end, LightTreeEmitter *emitters)
|
||||
{
|
||||
/* Sort primitive by light link mask so that specialized trees can use a subset of these. */
|
||||
if (end > start) {
|
||||
std::sort(emitters + start,
|
||||
emitters + end,
|
||||
[](const LightTreeEmitter &a, const LightTreeEmitter &b) {
|
||||
return a.light_set_membership < b.light_set_membership;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,6 +296,8 @@ LightTree::LightTree(Scene *scene,
|
|||
return;
|
||||
}
|
||||
|
||||
light_link_receiver_used |= (uint64_t(1) << object->get_receiver_light_set());
|
||||
|
||||
if (!object->usable_as_light()) {
|
||||
object_id++;
|
||||
continue;
|
||||
|
@ -390,7 +409,15 @@ LightTreeNode *LightTree::build(Scene *scene, DeviceScene *dscene)
|
|||
for (int i = 0; i < num_distant_lights; i++) {
|
||||
root_->get_inner().children[right]->add(distant_lights_[i]);
|
||||
}
|
||||
|
||||
sort_leaf(0, num_distant_lights, distant_lights_.data());
|
||||
root_->get_inner().children[right]->make_distant(num_local_lights, num_distant_lights);
|
||||
|
||||
root_->measure = root_->get_inner().children[left]->measure +
|
||||
root_->get_inner().children[right]->measure;
|
||||
root_->light_link = root_->get_inner().children[left]->light_link +
|
||||
root_->get_inner().children[right]->light_link;
|
||||
|
||||
std::move(distant_lights_.begin(), distant_lights_.end(), std::back_inserter(emitters_));
|
||||
|
||||
return root_.get();
|
||||
|
@ -421,7 +448,7 @@ void LightTree::recursive_build(const Child child,
|
|||
/* Find the best place to split the emitters into 2 nodes.
|
||||
* If the best split cost is no better than making a leaf node, make a leaf instead. */
|
||||
int split_dim = -1, middle;
|
||||
if (should_split(emitters, start, middle, end, node->measure, split_dim)) {
|
||||
if (should_split(emitters, start, middle, end, node->measure, node->light_link, split_dim)) {
|
||||
|
||||
if (split_dim != -1) {
|
||||
/* Partition the emitters between start and end based on the centroids. */
|
||||
|
@ -453,6 +480,7 @@ void LightTree::recursive_build(const Child child,
|
|||
}
|
||||
}
|
||||
else {
|
||||
sort_leaf(start, end, emitters);
|
||||
node->make_leaf(start, end - start);
|
||||
}
|
||||
}
|
||||
|
@ -462,13 +490,15 @@ bool LightTree::should_split(LightTreeEmitter *emitters,
|
|||
int &middle,
|
||||
const int end,
|
||||
LightTreeMeasure &measure,
|
||||
LightTreeLightLink &light_link,
|
||||
int &split_dim)
|
||||
{
|
||||
const int num_emitters = end - start;
|
||||
if (num_emitters < 2) {
|
||||
if (num_emitters) {
|
||||
/* Do not try to split if there is only one emitter. */
|
||||
measure = (emitters + start)->measure;
|
||||
measure = emitters[start].measure;
|
||||
light_link = LightTreeLightLink(emitters[start].light_set_membership);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -518,6 +548,7 @@ bool LightTree::should_split(LightTreeEmitter *emitters,
|
|||
if (dim == 0) {
|
||||
/* Calculate node measure by summing up the bucket measure. */
|
||||
measure = left_buckets.back().measure + buckets.back().measure;
|
||||
light_link = left_buckets.back().light_link + buckets.back().light_link;
|
||||
|
||||
/* Degenerate case with co-located emitters. */
|
||||
if (is_zero(centroid_bbox.size())) {
|
||||
|
@ -563,7 +594,14 @@ __forceinline LightTreeMeasure operator+(const LightTreeMeasure &a, const LightT
|
|||
|
||||
LightTreeBucket operator+(const LightTreeBucket &a, const LightTreeBucket &b)
|
||||
{
|
||||
return LightTreeBucket(a.measure + b.measure, a.count + b.count);
|
||||
return LightTreeBucket(a.measure + b.measure, a.light_link + b.light_link, a.count + b.count);
|
||||
}
|
||||
|
||||
LightTreeLightLink operator+(const LightTreeLightLink &a, const LightTreeLightLink &b)
|
||||
{
|
||||
LightTreeLightLink c(a);
|
||||
c.add(b);
|
||||
return c;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
@ -127,6 +127,48 @@ LightTreeMeasure operator+(const LightTreeMeasure &a, const LightTreeMeasure &b)
|
|||
|
||||
struct LightTreeNode;
|
||||
|
||||
/* Light Linking. */
|
||||
struct LightTreeLightLink {
|
||||
/* Bitmask for membership of primitives in this node. */
|
||||
uint64_t set_membership = 0;
|
||||
|
||||
/* When all primitives below this node have identical light set membership, this
|
||||
* part of the light tree can be shared between specialized trees. */
|
||||
bool shareable = true;
|
||||
int shared_node_index = -1;
|
||||
|
||||
LightTreeLightLink() = default;
|
||||
LightTreeLightLink(const uint64_t set_membership) : set_membership(set_membership) {}
|
||||
|
||||
void add(const uint64_t prim_set_membership)
|
||||
{
|
||||
if (set_membership == 0) {
|
||||
set_membership = prim_set_membership;
|
||||
}
|
||||
else if (prim_set_membership != set_membership) {
|
||||
set_membership |= prim_set_membership;
|
||||
shareable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void add(const LightTreeLightLink &other)
|
||||
{
|
||||
if (set_membership == 0) {
|
||||
set_membership = other.set_membership;
|
||||
shareable = other.shareable;
|
||||
}
|
||||
else if (other.set_membership != set_membership) {
|
||||
set_membership |= other.set_membership;
|
||||
shareable = false;
|
||||
}
|
||||
else if (!other.shareable) {
|
||||
shareable = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LightTreeLightLink operator+(const LightTreeLightLink &a, const LightTreeLightLink &b);
|
||||
|
||||
/* Light Tree Emitter
|
||||
* An emitter is a built-in light, an emissive mesh, or an emissive triangle. */
|
||||
struct LightTreeEmitter {
|
||||
|
@ -140,6 +182,7 @@ struct LightTreeEmitter {
|
|||
|
||||
int object_id;
|
||||
float3 centroid;
|
||||
uint64_t light_set_membership;
|
||||
|
||||
LightTreeMeasure measure;
|
||||
|
||||
|
@ -166,19 +209,23 @@ struct LightTreeEmitter {
|
|||
* Struct used to determine splitting costs in the light BVH. */
|
||||
struct LightTreeBucket {
|
||||
LightTreeMeasure measure;
|
||||
LightTreeLightLink light_link;
|
||||
int count = 0;
|
||||
static const int num_buckets = 12;
|
||||
|
||||
LightTreeBucket() = default;
|
||||
|
||||
LightTreeBucket(const LightTreeMeasure &measure, const int &count)
|
||||
: measure(measure), count(count)
|
||||
LightTreeBucket(const LightTreeMeasure &measure,
|
||||
const LightTreeLightLink &light_link,
|
||||
const int &count)
|
||||
: measure(measure), light_link(light_link), count(count)
|
||||
{
|
||||
}
|
||||
|
||||
void add(const LightTreeEmitter &emitter)
|
||||
{
|
||||
measure.add(emitter.measure);
|
||||
light_link.add(emitter.light_set_membership);
|
||||
count++;
|
||||
}
|
||||
};
|
||||
|
@ -188,6 +235,7 @@ LightTreeBucket operator+(const LightTreeBucket &a, const LightTreeBucket &b);
|
|||
/* Light Tree Node */
|
||||
struct LightTreeNode {
|
||||
LightTreeMeasure measure;
|
||||
LightTreeLightLink light_link;
|
||||
uint bit_trail;
|
||||
int object_id;
|
||||
|
||||
|
@ -256,7 +304,7 @@ struct LightTreeNode {
|
|||
return std::get<Instance>(variant_type);
|
||||
}
|
||||
|
||||
void make_leaf(const int &first_emitter_index, const int &num_emitters)
|
||||
void make_leaf(const int first_emitter_index, const int num_emitters)
|
||||
{
|
||||
variant_type = Leaf();
|
||||
Leaf &leaf = get_leaf();
|
||||
|
@ -266,7 +314,7 @@ struct LightTreeNode {
|
|||
type = LIGHT_TREE_LEAF;
|
||||
}
|
||||
|
||||
void make_distant(const int &first_emitter_index, const int &num_emitters)
|
||||
void make_distant(const int first_emitter_index, const int num_emitters)
|
||||
{
|
||||
variant_type = Leaf();
|
||||
Leaf &leaf = get_leaf();
|
||||
|
@ -276,7 +324,7 @@ struct LightTreeNode {
|
|||
type = LIGHT_TREE_DISTANT;
|
||||
}
|
||||
|
||||
void make_instance(LightTreeNode *reference, const int &object_id)
|
||||
void make_instance(LightTreeNode *reference, const int object_id)
|
||||
{
|
||||
variant_type = Instance();
|
||||
Instance &instance = get_instance();
|
||||
|
@ -340,6 +388,9 @@ class LightTree {
|
|||
std::atomic<int> num_nodes = 0;
|
||||
size_t num_triangles = 0;
|
||||
|
||||
/* Bitmask of receiver light sets used. Default set is always used. */
|
||||
uint64_t light_link_receiver_used = 1;
|
||||
|
||||
/* An inner node itself or its left and right child. */
|
||||
enum Child {
|
||||
self = -1,
|
||||
|
@ -388,6 +439,7 @@ class LightTree {
|
|||
int &middle,
|
||||
const int end,
|
||||
LightTreeMeasure &measure,
|
||||
LightTreeLightLink &light_link,
|
||||
int &split_dim);
|
||||
|
||||
/* Check whether the light tree can use this triangle as light-emissive. */
|
||||
|
|
Loading…
Reference in New Issue