Cycles: add instancing support in light tree

Build a subtree for each unique mesh light.

Pull Request: #106683
This commit is contained in:
Weizhen Huang 2023-04-14 18:52:12 +02:00
parent 910f60de4c
commit bfd1836861
12 changed files with 728 additions and 262 deletions

@ -64,6 +64,7 @@ KERNEL_DATA_ARRAY(float2, light_background_conditional_cdf)
KERNEL_DATA_ARRAY(KernelLightTreeNode, light_tree_nodes)
KERNEL_DATA_ARRAY(KernelLightTreeEmitter, light_tree_emitters)
KERNEL_DATA_ARRAY(uint, light_to_tree)
KERNEL_DATA_ARRAY(uint, object_to_tree)
KERNEL_DATA_ARRAY(uint, object_lookup_offset)
KERNEL_DATA_ARRAY(uint, triangle_to_tree)

@ -56,7 +56,7 @@ ccl_device_noinline bool light_distribution_sample(KernelGlobals kg,
const int index = light_distribution_sample(kg, randn);
const float pdf_selection = kernel_data.integrator.distribution_pdf_lights;
return light_sample<in_volume_segment>(
kg, randu, randv, time, P, bounce, path_flag, index, pdf_selection, ls);
kg, randu, randv, time, P, bounce, path_flag, index, 0, pdf_selection, ls);
}
ccl_device_inline float light_distribution_pdf_lamp(KernelGlobals kg)

@ -108,6 +108,7 @@ ccl_device_noinline bool light_sample(KernelGlobals kg,
const int bounce,
const uint32_t path_flag,
const int emitter_index,
const int object_id,
const float pdf_selection,
ccl_private LightSample *ls)
{
@ -117,8 +118,9 @@ ccl_device_noinline bool light_sample(KernelGlobals kg,
if (kernel_data.integrator.use_light_tree) {
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
emitter_index);
prim = kemitter->prim_id;
mesh_light = kemitter->mesh_light;
prim = kemitter->light.id;
mesh_light.shader_flag = kemitter->mesh_light.shader_flag;
mesh_light.object_id = object_id;
}
else
#endif

@ -438,7 +438,9 @@ ccl_device_inline float light_sample_mis_weight_forward_surface(KernelGlobals kg
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
uint lookup_offset = kernel_data_fetch(object_lookup_offset, sd->object);
uint prim_offset = kernel_data_fetch(object_prim_offset, sd->object);
pdf *= light_tree_pdf(kg, ray_P, N, path_flag, sd->prim - prim_offset + lookup_offset);
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);
}
else
#endif
@ -462,7 +464,7 @@ 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, ~ls->lamp);
pdf *= light_tree_pdf(kg, P, N, path_flag, 0, kernel_data_fetch(light_to_tree, ls->lamp));
}
else
#endif
@ -496,7 +498,8 @@ ccl_device_inline float light_sample_mis_weight_forward_background(KernelGlobals
#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, ray_P, N, path_flag, ~kernel_data.background.light_index);
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);
}
else
#endif

@ -69,6 +69,59 @@ ccl_device float3 compute_v(
cos_phi0 * o0 + dot_o1_a * inv_len * o1;
}
ccl_device_inline bool is_light(const ccl_global KernelLightTreeEmitter *kemitter)
{
return kemitter->light.id < 0;
}
ccl_device_inline bool is_mesh(const ccl_global KernelLightTreeEmitter *kemitter)
{
return !is_light(kemitter) && kemitter->mesh_light.object_id == OBJECT_NONE;
}
ccl_device_inline bool is_triangle(const ccl_global KernelLightTreeEmitter *kemitter)
{
return !is_light(kemitter) && kemitter->mesh_light.object_id != OBJECT_NONE;
}
ccl_device_inline bool is_leaf(const ccl_global KernelLightTreeNode *knode)
{
/* The distant node is also considered o leaf node. */
return knode->type >= LIGHT_TREE_LEAF;
}
template<bool in_volume_segment>
ccl_device void light_tree_to_local_space(KernelGlobals kg,
const int object_id,
ccl_private float3 &P,
ccl_private float3 &N_or_D,
ccl_private float &t)
{
const int object_flag = kernel_data_fetch(object_flag, object_id);
if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
#ifdef __OBJECT_MOTION__
Transform itfm;
object_fetch_transform_motion_test(kg, object_id, 0.5f, &itfm);
#else
const Transform itfm = object_fetch_transform(kg, object_id, OBJECT_INVERSE_TRANSFORM);
#endif
P = transform_point(&itfm, P);
if (in_volume_segment) {
/* Transform direction. */
float3 D_local = transform_direction(&itfm, N_or_D);
float scale;
N_or_D = normalize_len(D_local, &scale);
t *= scale;
}
else if (!is_zero(N_or_D)) {
/* Transform normal. */
const Transform tfm = object_fetch_transform(kg, object_id, OBJECT_TRANSFORM);
N_or_D = normalize(transform_direction_transposed(&tfm, N_or_D));
}
}
}
/* This is the general function for calculating the importance of either a cluster or an emitter.
* Both of the specialized functions obtain the necessary data before calling this function. */
template<bool in_volume_segment>
@ -184,9 +237,8 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg,
ccl_private float3 &centroid,
ccl_private packed_float3 &dir)
{
const int prim_id = kemitter->prim_id;
if (prim_id < 0) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ~prim_id);
if (is_light(kemitter)) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ~(kemitter->light.id));
centroid = klight->co;
switch (klight->type) {
@ -213,19 +265,22 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg,
}
}
else {
kernel_assert(is_triangle(kemitter));
const int object = kemitter->mesh_light.object_id;
float3 vertices[3];
triangle_world_space_vertices(kg, object, prim_id, -1.0f, vertices);
triangle_vertices(kg, kemitter->triangle.id, vertices);
centroid = (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
const bool is_front_only = (kemitter->emission_sampling == EMISSION_SAMPLING_FRONT);
const bool is_back_only = (kemitter->emission_sampling == EMISSION_SAMPLING_BACK);
const bool is_front_only = (kemitter->triangle.emission_sampling == EMISSION_SAMPLING_FRONT);
const bool is_back_only = (kemitter->triangle.emission_sampling == EMISSION_SAMPLING_BACK);
if (is_front_only || is_back_only) {
dir = safe_normalize(cross(vertices[1] - vertices[0], vertices[2] - vertices[0]));
if (is_back_only) {
dir = -dir;
}
if (kernel_data_fetch(object_flag, object) & SD_OBJECT_NEGATIVE_SCALE) {
const int object_flag = kernel_data_fetch(object_flag, object);
if ((object_flag & SD_OBJECT_TRANSFORM_APPLIED) &&
(object_flag & SD_OBJECT_NEGATIVE_SCALE)) {
dir = -dir;
}
}
@ -237,6 +292,75 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg,
return true;
}
template<bool in_volume_segment>
ccl_device void light_tree_node_importance(KernelGlobals kg,
const float3 P,
const float3 N_or_D,
const float t,
const bool has_transmission,
const ccl_global KernelLightTreeNode *knode,
ccl_private float &max_importance,
ccl_private float &min_importance)
{
const BoundingCone bcone = knode->bcone;
const BoundingBox bbox = knode->bbox;
float3 point_to_centroid;
float cos_theta_u;
float distance;
if (knode->type == LIGHT_TREE_DISTANT) {
if (in_volume_segment) {
return;
}
point_to_centroid = -bcone.axis;
cos_theta_u = fast_cosf(bcone.theta_o);
distance = 1.0f;
}
else {
const float3 centroid = 0.5f * (bbox.min + bbox.max);
if (in_volume_segment) {
const float3 D = N_or_D;
const float3 closest_point = P + dot(centroid - P, D) * D;
/* Minimal distance of the ray to the cluster. */
distance = len(centroid - closest_point);
point_to_centroid = -compute_v(centroid, P, D, bcone.axis, t);
cos_theta_u = light_tree_cos_bounding_box_angle(bbox, closest_point, point_to_centroid);
}
else {
const float3 N = N_or_D;
const float3 bbox_extent = bbox.max - centroid;
const bool bbox_is_visible = has_transmission |
(dot(N, centroid - P) + dot(fabs(N), fabs(bbox_extent)) > 0);
/* If the node is guaranteed to be behind the surface we're sampling, and the surface is
* opaque, then we can give the node an importance of 0 as it contributes nothing to the
* surface. */
if (!bbox_is_visible) {
return;
}
point_to_centroid = normalize_len(centroid - P, &distance);
cos_theta_u = light_tree_cos_bounding_box_angle(bbox, P, point_to_centroid);
}
/* Clamp distance to half the radius of the cluster when splitting is disabled. */
distance = fmaxf(0.5f * len(centroid - bbox.max), distance);
}
/* TODO: currently max_distance = min_distance, max_importance = min_importance for the
* nodes. Do we need better weights for complex scenes? */
light_tree_importance<in_volume_segment>(N_or_D,
has_transmission,
point_to_centroid,
cos_theta_u,
bcone,
distance,
distance,
t,
knode->energy,
max_importance,
min_importance);
}
template<bool in_volume_segment>
ccl_device void light_tree_emitter_importance(KernelGlobals kg,
const float3 P,
@ -247,11 +371,21 @@ ccl_device void light_tree_emitter_importance(KernelGlobals kg,
ccl_private float &max_importance,
ccl_private float &min_importance)
{
max_importance = 0.0f;
min_importance = 0.0f;
const ccl_global KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
emitter_index);
max_importance = 0.0f;
min_importance = 0.0f;
if (is_mesh(kemitter)) {
const ccl_global KernelLightTreeNode *knode = &kernel_data_fetch(light_tree_nodes,
kemitter->mesh.node_id);
light_tree_node_importance<in_volume_segment>(
kg, P, N_or_D, t, has_transmission, knode, max_importance, min_importance);
return;
}
BoundingCone bcone;
bcone.theta_o = kemitter->theta_o;
bcone.theta_e = kemitter->theta_e;
@ -264,8 +398,6 @@ ccl_device void light_tree_emitter_importance(KernelGlobals kg,
return;
}
const int prim_id = kemitter->prim_id;
if (in_volume_segment) {
const float3 D = N_or_D;
/* Closest point. */
@ -279,9 +411,15 @@ ccl_device void light_tree_emitter_importance(KernelGlobals kg,
P_c = P;
}
/* Early out if the emitter is guaranteed to be invisible. */
bool is_visible;
if (prim_id < 0) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ~prim_id);
if (is_triangle(kemitter)) {
is_visible = triangle_light_tree_parameters<in_volume_segment>(
kg, kemitter, centroid, P_c, N_or_D, bcone, cos_theta_u, distance, point_to_centroid);
}
else {
kernel_assert(is_light(kemitter));
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ~(kemitter->light.id));
switch (klight->type) {
/* Function templates only modifies cos_theta_u when in_volume_segment = true. */
case LIGHT_SPOT:
@ -309,10 +447,6 @@ ccl_device void light_tree_emitter_importance(KernelGlobals kg,
return;
}
}
else { /* Mesh light. */
is_visible = triangle_light_tree_parameters<in_volume_segment>(
kg, kemitter, centroid, P_c, N_or_D, bcone, cos_theta_u, distance, point_to_centroid);
}
is_visible |= has_transmission;
if (!is_visible) {
@ -333,81 +467,31 @@ ccl_device void light_tree_emitter_importance(KernelGlobals kg,
}
template<bool in_volume_segment>
ccl_device void light_tree_node_importance(KernelGlobals kg,
const float3 P,
const float3 N_or_D,
const float t,
const bool has_transmission,
const ccl_global KernelLightTreeNode *knode,
ccl_private float &max_importance,
ccl_private float &min_importance)
ccl_device void light_tree_child_importance(KernelGlobals kg,
const float3 P,
const float3 N_or_D,
const float t,
const bool has_transmission,
const ccl_global KernelLightTreeNode *knode,
ccl_private float &max_importance,
ccl_private float &min_importance)
{
max_importance = 0.0f;
min_importance = 0.0f;
if (knode->num_emitters == 1) {
/* At a leaf node with only one emitter. */
light_tree_emitter_importance<in_volume_segment>(
kg, P, N_or_D, t, has_transmission, -knode->child_index, max_importance, min_importance);
light_tree_emitter_importance<in_volume_segment>(kg,
P,
N_or_D,
t,
has_transmission,
knode->leaf.first_emitter,
max_importance,
min_importance);
}
else if (knode->num_emitters != 0) {
const BoundingCone bcone = knode->bcone;
const BoundingBox bbox = knode->bbox;
float3 point_to_centroid;
float cos_theta_u;
float distance;
if (knode->bit_trail == 1) {
/* Distant light node. */
if (in_volume_segment) {
return;
}
point_to_centroid = -bcone.axis;
cos_theta_u = fast_cosf(bcone.theta_o);
distance = 1.0f;
}
else {
const float3 centroid = 0.5f * (bbox.min + bbox.max);
if (in_volume_segment) {
const float3 D = N_or_D;
const float3 closest_point = P + dot(centroid - P, D) * D;
/* Minimal distance of the ray to the cluster. */
distance = len(centroid - closest_point);
point_to_centroid = -compute_v(centroid, P, D, bcone.axis, t);
cos_theta_u = light_tree_cos_bounding_box_angle(bbox, closest_point, point_to_centroid);
}
else {
const float3 N = N_or_D;
const float3 bbox_extent = bbox.max - centroid;
const bool bbox_is_visible = has_transmission |
(dot(N, centroid - P) + dot(fabs(N), fabs(bbox_extent)) > 0);
/* If the node is guaranteed to be behind the surface we're sampling, and the surface is
* opaque, then we can give the node an importance of 0 as it contributes nothing to the
* surface. */
if (!bbox_is_visible) {
return;
}
point_to_centroid = normalize_len(centroid - P, &distance);
cos_theta_u = light_tree_cos_bounding_box_angle(bbox, P, point_to_centroid);
}
/* Clamp distance to half the radius of the cluster when splitting is disabled. */
distance = fmaxf(0.5f * len(centroid - bbox.max), distance);
}
/* TODO: currently max_distance = min_distance, max_importance = min_importance for the
* nodes. Do we need better weights for complex scenes? */
light_tree_importance<in_volume_segment>(N_or_D,
has_transmission,
point_to_centroid,
cos_theta_u,
bcone,
distance,
distance,
t,
knode->energy,
max_importance,
min_importance);
light_tree_node_importance<in_volume_segment>(
kg, P, N_or_D, t, has_transmission, knode, max_importance, min_importance);
}
}
@ -440,26 +524,30 @@ ccl_device void sample_resevoir(const int current_index,
template<bool in_volume_segment>
ccl_device int light_tree_cluster_select_emitter(KernelGlobals kg,
ccl_private float &rand,
const float3 P,
const float3 N_or_D,
const float t,
ccl_private float3 &P,
ccl_private float3 &N_or_D,
ccl_private float &t,
const bool has_transmission,
const ccl_global KernelLightTreeNode *knode,
ccl_private int *node_index,
ccl_private float *pdf_factor)
{
float selected_importance[2] = {0.0f, 0.0f};
float total_importance[2] = {0.0f, 0.0f};
int selected_index = -1;
const ccl_global KernelLightTreeNode *knode = &kernel_data_fetch(light_tree_nodes, *node_index);
*node_index = -1;
/* Mark emitters with zero importance. Used for resevoir when total minimum importance = 0. */
kernel_assert(knode->num_emitters <= sizeof(uint) * 8);
uint has_importance = 0;
const bool sample_max = (rand > 0.5f); /* Sampling using the maximum importance. */
rand = rand * 2.0f - float(sample_max);
if (knode->num_emitters > 1) {
rand = rand * 2.0f - float(sample_max);
}
for (int i = 0; i < knode->num_emitters; i++) {
int current_index = -knode->child_index + i;
int current_index = knode->leaf.first_emitter + i;
/* maximum importance = importance[0], minimum importance = importance[1] */
float importance[2];
light_tree_emitter_importance<in_volume_segment>(
@ -492,7 +580,7 @@ ccl_device int light_tree_cluster_select_emitter(KernelGlobals kg,
else {
selected_index = -1;
for (int i = 0; i < knode->num_emitters; i++) {
int current_index = -knode->child_index + i;
int current_index = knode->inner.right_child + i;
sample_resevoir(current_index,
float(has_importance & 1),
selected_index,
@ -508,8 +596,24 @@ ccl_device int light_tree_cluster_select_emitter(KernelGlobals kg,
}
}
*pdf_factor = 0.5f * (selected_importance[0] / total_importance[0] +
selected_importance[1] / total_importance[1]);
*pdf_factor *= 0.5f * (selected_importance[0] / total_importance[0] +
selected_importance[1] / total_importance[1]);
const ccl_global KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
selected_index);
if (is_mesh(kemitter)) {
/* Transform ray from world to local space. */
light_tree_to_local_space<in_volume_segment>(kg, kemitter->mesh.object_id, P, N_or_D, t);
*node_index = kemitter->mesh.node_id;
const ccl_global KernelLightTreeNode *knode = &kernel_data_fetch(light_tree_nodes,
*node_index);
if (knode->type == LIGHT_TREE_INSTANCE) {
/* Switch to the node with the subtree. */
*node_index = knode->instance.reference;
}
}
return selected_index;
}
@ -528,9 +632,9 @@ ccl_device bool get_left_probability(KernelGlobals kg,
const ccl_global KernelLightTreeNode *right = &kernel_data_fetch(light_tree_nodes, right_index);
float min_left_importance, max_left_importance, min_right_importance, max_right_importance;
light_tree_node_importance<in_volume_segment>(
light_tree_child_importance<in_volume_segment>(
kg, P, N_or_D, t, has_transmission, left, max_left_importance, min_left_importance);
light_tree_node_importance<in_volume_segment>(
light_tree_child_importance<in_volume_segment>(
kg, P, N_or_D, t, has_transmission, right, max_right_importance, min_right_importance);
const float total_max_importance = max_left_importance + max_right_importance;
@ -556,8 +660,8 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
const float randv,
const float time,
const float3 P,
const float3 N_or_D,
const float t,
float3 N_or_D,
float t,
const int shader_flags,
const int bounce,
const uint32_t path_flag,
@ -571,28 +675,38 @@ 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. */
float3 local_P = P;
/* Traverse the light tree until a leaf node is reached. */
while (true) {
const ccl_global KernelLightTreeNode *knode = &kernel_data_fetch(light_tree_nodes, node_index);
if (knode->child_index <= 0) {
if (is_leaf(knode)) {
/* At a leaf node, we pick an emitter. */
selected_emitter = light_tree_cluster_select_emitter<in_volume_segment>(
kg, randn, P, N_or_D, t, has_transmission, knode, &pdf_selection);
break;
kg, randn, local_P, N_or_D, t, has_transmission, &node_index, &pdf_selection);
if (node_index < 0) {
break;
}
else {
/* Continue with the picked mesh light. */
object = kernel_data_fetch(light_tree_emitters, selected_emitter).mesh.object_id;
continue;
}
}
/* At an interior node, the left child is directly after the parent, while the right child is
* stored as the child index. */
const int left_index = node_index + 1;
const int right_index = knode->child_index;
const int right_index = knode->inner.right_child;
float left_prob;
if (!get_left_probability<in_volume_segment>(
kg, P, N_or_D, t, has_transmission, left_index, right_index, left_prob)) {
kg, local_P, N_or_D, t, has_transmission, left_index, right_index, left_prob)) {
return false; /* Both child nodes have zero importance. */
}
@ -610,38 +724,104 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
pdf_selection *= pdf_leaf;
return light_sample<in_volume_segment>(
kg, randu, randv, time, P, bounce, path_flag, selected_emitter, pdf_selection, ls);
kg, randu, randv, time, P, bounce, path_flag, selected_emitter, object, 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, const float3 P, const float3 N, const int path_flag, const int emitter)
KernelGlobals kg, float3 P, float3 N, const int path_flag, const int object, const uint target)
{
const bool has_transmission = (path_flag & PATH_RAY_MIS_HAD_TRANSMISSION);
/* Target emitter info. */
const int target_emitter = (emitter >= 0) ? kernel_data_fetch(triangle_to_tree, emitter) :
kernel_data_fetch(light_to_tree, ~emitter);
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
target_emitter);
const int target_leaf = kemitter->parent_index;
ccl_global const KernelLightTreeNode *kleaf = &kernel_data_fetch(light_tree_nodes, target_leaf);
uint bit_trail = kleaf->bit_trail;
int node_index = 0; /* Root node. */
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
target);
int root_index, target_leaf;
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);
ccl_global const KernelLightTreeEmitter *kmesh = &kernel_data_fetch(light_tree_emitters,
target_emitter);
target_leaf = kmesh->parent_index;
root_index = kmesh->mesh.node_id;
ccl_global const KernelLightTreeNode *kroot = &kernel_data_fetch(light_tree_nodes, root_index);
bit_trail = kroot->bit_trail;
if (kroot->type == LIGHT_TREE_INSTANCE) {
root_index = kroot->instance.reference;
}
}
else {
root_index = 0;
target_leaf = kemitter->parent_index;
bit_trail = kernel_data_fetch(light_tree_nodes, target_leaf).bit_trail;
target_emitter = target;
}
float pdf = 1.0f;
int node_index = 0;
/* Traverse the light tree until we reach the target leaf node. */
while (true) {
const ccl_global KernelLightTreeNode *knode = &kernel_data_fetch(light_tree_nodes, node_index);
if (knode->child_index <= 0) {
break;
if (is_leaf(knode)) {
kernel_assert(node_index == target_leaf);
ccl_global const KernelLightTreeNode *kleaf = &kernel_data_fetch(light_tree_nodes,
target_leaf);
/* Iterate through leaf node to find the probability of sampling the target emitter. */
float target_max_importance = 0.0f;
float target_min_importance = 0.0f;
float total_max_importance = 0.0f;
float total_min_importance = 0.0f;
int num_has_importance = 0;
for (int i = 0; i < kleaf->num_emitters; i++) {
const int emitter = kleaf->leaf.first_emitter + i;
float max_importance, min_importance;
light_tree_emitter_importance<false>(
kg, P, N, 0, has_transmission, emitter, max_importance, min_importance);
num_has_importance += (max_importance > 0);
if (emitter == target_emitter) {
target_max_importance = max_importance;
target_min_importance = min_importance;
}
total_max_importance += max_importance;
total_min_importance += min_importance;
}
if (target_max_importance > 0.0f) {
pdf *= 0.5f * (target_max_importance / total_max_importance +
(total_min_importance > 0 ? target_min_importance / total_min_importance :
1.0f / num_has_importance));
}
else {
return 0.0f;
}
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);
node_index = root_index;
root_index = 0;
target_emitter = target;
target_leaf = kemitter->parent_index;
bit_trail = kernel_data_fetch(light_tree_nodes, target_leaf).bit_trail;
continue;
}
else {
kernel_assert(node_index == target_leaf);
return pdf;
}
}
/* Interior node. */
const int left_index = node_index + 1;
const int right_index = knode->child_index;
const int right_index = knode->inner.right_child;
float left_prob;
if (!get_left_probability<false>(
@ -658,36 +838,6 @@ ccl_device float light_tree_pdf(
return 0.0f;
}
}
kernel_assert(node_index == target_leaf);
/* Iterate through leaf node to find the probability of sampling the target emitter. */
float target_max_importance = 0.0f;
float target_min_importance = 0.0f;
float total_max_importance = 0.0f;
float total_min_importance = 0.0f;
int num_has_importance = 0;
for (int i = 0; i < kleaf->num_emitters; i++) {
const int emitter = -kleaf->child_index + i;
float max_importance, min_importance;
light_tree_emitter_importance<false>(
kg, P, N, 0, has_transmission, emitter, max_importance, min_importance);
num_has_importance += (max_importance > 0);
if (emitter == target_emitter) {
target_max_importance = max_importance;
target_min_importance = min_importance;
}
total_max_importance += max_importance;
total_min_importance += min_importance;
}
if (target_max_importance > 0.0f) {
return pdf * 0.5f *
(target_max_importance / total_max_importance +
(total_min_importance > 0 ? target_min_importance / total_min_importance :
1.0f / num_has_importance));
}
return 0.0f;
}
CCL_NAMESPACE_END

@ -304,9 +304,8 @@ ccl_device_forceinline bool triangle_light_tree_parameters(
cos_theta_u = FLT_MAX;
const int object = kemitter->mesh_light.object_id;
float3 vertices[3];
triangle_world_space_vertices(kg, object, kemitter->prim_id, -1.0f, vertices);
triangle_vertices(kg, kemitter->triangle.id, vertices);
bool shape_above_surface = false;
for (int i = 0; i < 3; i++) {

@ -1370,6 +1370,13 @@ using BoundingCone = struct BoundingCone {
float theta_e;
};
enum LightTreeNodeType : uint8_t {
LIGHT_TREE_INSTANCE = (1 << 0),
LIGHT_TREE_INNER = (1 << 1),
LIGHT_TREE_LEAF = (1 << 2),
LIGHT_TREE_DISTANT = (1 << 3),
};
typedef struct KernelLightTreeNode {
/* Bounding box. */
BoundingBox bbox;
@ -1380,17 +1387,25 @@ typedef struct KernelLightTreeNode {
/* Energy. */
float energy;
/* If this is 0 or less, we're at a leaf node
* and the negative value indexes into the first child of the light array.
* Otherwise, it's an index to the node's second child. */
int child_index;
int num_emitters; /* leaf nodes need to know the number of emitters stored. */
LightTreeNodeType type;
/* Leaf nodes need to know the number of emitters stored. */
int num_emitters;
union {
struct {
int first_emitter; /* The index of the first emitter. */
} leaf;
struct {
int right_child; /* The index of the right child. */
} inner;
struct {
int reference; /* A reference to the node with the subtree. */
} instance;
};
/* Bit trail. */
uint bit_trail;
/* Padding. */
int pad;
} KernelLightTreeNode;
static_assert_align(KernelLightTreeNode, 16);
@ -1402,10 +1417,23 @@ typedef struct KernelLightTreeEmitter {
/* Energy. */
float energy;
/* The location in the lights or triangles array. */
int prim_id;
union {
struct {
int id; /* The location in the triangles array. */
EmissionSampling emission_sampling;
} triangle;
struct {
int id; /* The location in the lights array. */
} light;
struct {
int object_id;
int node_id;
} mesh;
};
MeshLight mesh_light;
EmissionSampling emission_sampling;
/* Parent. */
int parent_index;

@ -24,6 +24,7 @@
#include "util/path.h"
#include "util/progress.h"
#include "util/task.h"
#include <stack>
CCL_NAMESPACE_BEGIN
@ -453,6 +454,7 @@ void LightManager::device_update_tree(Device *,
/* We want to create separate arrays corresponding to triangles and lights,
* which will be used to index back into the light tree for PDF calculations. */
uint *light_array = dscene->light_to_tree.alloc(kintegrator->num_lights);
uint *mesh_array = dscene->object_to_tree.alloc(scene->objects.size());
uint *triangle_array = dscene->triangle_to_tree.alloc(light_tree.num_triangles);
/* First initialize the light tree's nodes. */
@ -473,11 +475,39 @@ void LightManager::device_update_tree(Device *,
* Once finished visiting the left subtree, we retrieve the last stored pointer from
* `right_node_stack`, assign it to its parent (retrieved from `left_index_stack`), and repeat
* the process from there. */
int left_index_stack[32]; /* `sizeof(bit_trail) * 8 == 32`. */
LightTreeNode *right_node_stack[32];
int stack_id = 0;
const LightTreeNode *node = root;
std::stack<int> left_indices;
std::stack<LightTreeNode *> right_nodes;
/* Subtree. */
int top_level_stack_size = -1;
std::queue<LightTreeNode *> mesh_light_nodes;
std::unordered_map<LightTreeNode *, int> processed_mesh;
LightTreeNode *node = root;
for (int node_index = 0; node_index < light_tree.num_nodes; node_index++) {
if (node->is_instance()) {
KernelLightTreeEmitter *mesh_light = &light_tree_emitters[mesh_array[node->object_id]];
mesh_light->mesh.node_id = node_index;
node->bit_trail = light_tree_nodes[mesh_light->parent_index].bit_trail;
LightTreeNode *reference = node->get_reference();
auto map_it = processed_mesh.find(reference);
if (map_it != processed_mesh.end()) {
light_tree_nodes[node_index].instance.reference = map_it->second;
}
else {
if (node != reference) {
/* Flatten the node with the subtree first so the subsequent instances know the index. */
std::swap(node->type, reference->type);
std::swap(node->variant_type, reference->variant_type);
}
node->type &= ~LIGHT_TREE_INSTANCE;
processed_mesh[reference] = node_index;
}
}
light_tree_nodes[node_index].energy = node->measure.energy;
light_tree_nodes[node_index].bbox.min = node->measure.bbox.min;
@ -488,14 +518,22 @@ void LightManager::device_update_tree(Device *,
light_tree_nodes[node_index].bcone.theta_e = node->measure.bcone.theta_e;
light_tree_nodes[node_index].bit_trail = node->bit_trail;
light_tree_nodes[node_index].num_emitters = node->num_emitters;
light_tree_nodes[node_index].type = static_cast<LightTreeNodeType>(node->type);
/* Here we need to make a distinction between interior and leaf nodes. */
if (node->is_leaf()) {
light_tree_nodes[node_index].child_index = -node->first_emitter_index;
if (node->is_inner()) {
light_tree_nodes[node_index].num_emitters = -1;
/* Fill in the stacks. */
left_indices.push(node_index);
right_nodes.push(node->get_inner().children[LightTree::right].get());
node = node->get_inner().children[LightTree::left].get();
continue;
}
if (node->is_leaf() || node->is_distant()) {
light_tree_nodes[node_index].num_emitters = node->get_leaf().num_emitters;
light_tree_nodes[node_index].leaf.first_emitter = node->get_leaf().first_emitter_index;
for (int i = 0; i < node->num_emitters; i++) {
int emitter_index = i + node->first_emitter_index;
for (int i = 0; i < node->get_leaf().num_emitters; i++) {
int emitter_index = i + node->get_leaf().first_emitter_index;
const LightTreeEmitter &emitter = light_tree.get_emitter(emitter_index);
light_tree_emitters[emitter_index].energy = emitter.measure.energy;
@ -503,7 +541,6 @@ void LightManager::device_update_tree(Device *,
light_tree_emitters[emitter_index].theta_e = emitter.measure.bcone.theta_e;
if (emitter.is_triangle()) {
light_tree_emitters[emitter_index].mesh_light.object_id = emitter.object_id;
int shader_flag = 0;
Object *object = scene->objects[emitter.object_id];
@ -530,43 +567,61 @@ void LightManager::device_update_tree(Device *,
shader_flag |= SHADER_EXCLUDE_SHADOW_CATCHER;
}
light_tree_emitters[emitter_index].prim_id = emitter.prim_id + mesh->prim_offset;
light_tree_emitters[emitter_index].triangle.id = emitter.prim_id + mesh->prim_offset;
light_tree_emitters[emitter_index].mesh_light.shader_flag = shader_flag;
light_tree_emitters[emitter_index].emission_sampling = shader->emission_sampling;
light_tree_emitters[emitter_index].mesh_light.object_id = emitter.object_id;
light_tree_emitters[emitter_index].triangle.emission_sampling =
shader->emission_sampling;
triangle_array[emitter.prim_id + dscene->object_lookup_offset[emitter.object_id]] =
emitter_index;
}
else {
light_tree_emitters[emitter_index].prim_id = emitter.prim_id;
else if (emitter.is_light()) {
light_tree_emitters[emitter_index].light.id = emitter.light_id;
light_tree_emitters[emitter_index].mesh_light.shader_flag = 0;
light_tree_emitters[emitter_index].mesh_light.object_id = OBJECT_NONE;
light_tree_emitters[emitter_index].emission_sampling = EMISSION_SAMPLING_FRONT_BACK;
light_array[~emitter.prim_id] = emitter_index;
light_array[~emitter.light_id] = emitter_index;
}
else {
assert(emitter.is_mesh());
light_tree_emitters[emitter_index].mesh.object_id = emitter.object_id;
light_tree_emitters[emitter_index].mesh_light.shader_flag = 0;
light_tree_emitters[emitter_index].mesh_light.object_id = OBJECT_NONE;
mesh_array[emitter.object_id] = emitter_index;
mesh_light_nodes.push(emitter.root.get());
top_level_stack_size = left_indices.size();
}
light_tree_emitters[emitter_index].parent_index = node_index;
}
}
/* Retrieve from the stacks. */
if (stack_id == 0) {
break;
if (left_indices.empty()) {
break;
}
if (left_indices.size() == top_level_stack_size) {
if (!mesh_light_nodes.empty()) {
node = mesh_light_nodes.front();
mesh_light_nodes.pop();
continue;
}
stack_id--;
light_tree_nodes[left_index_stack[stack_id]].child_index = node_index + 1;
node = right_node_stack[stack_id];
}
else {
/* Fill in the stacks. */
left_index_stack[stack_id] = node_index;
right_node_stack[stack_id] = node->children[LightTree::right].get();
node = node->children[LightTree::left].get();
stack_id++;
/* Finished processing subtrees in the last leaf node, go back to the top level tree. */
top_level_stack_size = -1;
}
/* Retrieve from the stacks. */
light_tree_nodes[left_indices.top()].inner.right_child = node_index + 1;
node = right_nodes.top();
left_indices.pop();
right_nodes.pop();
}
/* Copy arrays to device. */
dscene->light_tree_nodes.copy_to_device();
dscene->light_tree_emitters.copy_to_device();
dscene->light_to_tree.copy_to_device();
dscene->object_to_tree.copy_to_device();
dscene->object_lookup_offset.copy_to_device();
dscene->triangle_to_tree.copy_to_device();
}
@ -1107,6 +1162,7 @@ void LightManager::device_free(Device *, DeviceScene *dscene, const bool free_ba
dscene->light_tree_nodes.free();
dscene->light_tree_emitters.free();
dscene->light_to_tree.free();
dscene->object_to_tree.free();
dscene->object_lookup_offset.free();
dscene->triangle_to_tree.free();

@ -74,7 +74,15 @@ OrientationBounds merge(const OrientationBounds &cone_a, const OrientationBounds
return OrientationBounds({new_axis, theta_o, theta_e});
}
LightTreeEmitter::LightTreeEmitter(Scene *scene, int prim_id, int object_id)
LightTreeEmitter::LightTreeEmitter(Object *object, int object_id) : object_id(object_id)
{
centroid = object->bounds.center();
}
LightTreeEmitter::LightTreeEmitter(Scene *scene,
int prim_id,
int object_id,
bool need_transformation)
: prim_id(prim_id), object_id(object_id)
{
if (is_triangle()) {
@ -88,10 +96,8 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene, int prim_id, int object_id)
vertices[i] = mesh->get_verts()[triangle.v[i]];
}
/* instanced mesh lights have not applied their transform at this point.
* in this case, these points have to be transformed to get the proper
* spatial bound. */
if (!mesh->transform_applied) {
if (need_transformation) {
assert(!mesh->transform_applied);
const Transform &tfm = object->get_tfm();
for (int i = 0; i < 3; i++) {
vertices[i] = transform_point(&tfm, vertices[i]);
@ -115,7 +121,8 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene, int prim_id, int object_id)
if (is_back_only) {
measure.bcone.axis = -measure.bcone.axis;
}
if (transform_negative_scale(object->get_tfm())) {
if ((need_transformation || mesh->transform_applied) &&
transform_negative_scale(object->get_tfm())) {
measure.bcone.axis = -measure.bcone.axis;
}
measure.bcone.theta_o = 0;
@ -132,6 +139,7 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene, int prim_id, int object_id)
}
}
else {
assert(is_light());
Light *lamp = scene->lights[object_id];
LightType type = lamp->get_light_type();
const float size = lamp->get_size();
@ -210,6 +218,28 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene, int prim_id, int object_id)
}
}
bool LightTree::triangle_usable_as_light(Mesh *mesh, int prim_id)
{
int shader_index = mesh->get_shader()[prim_id];
if (shader_index < mesh->get_used_shaders().size()) {
Shader *shader = static_cast<Shader *>(mesh->get_used_shaders()[shader_index]);
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
return true;
}
}
return false;
}
void LightTree::add_mesh(Scene *scene, Mesh *mesh, int object_id)
{
size_t mesh_num_triangles = mesh->num_triangles();
for (size_t i = 0; i < mesh_num_triangles; i++) {
if (triangle_usable_as_light(mesh, i)) {
emitters_.emplace_back(scene, i, object_id);
}
}
}
LightTree::LightTree(Scene *scene,
DeviceScene *dscene,
Progress &progress,
@ -218,10 +248,8 @@ LightTree::LightTree(Scene *scene,
{
KernelIntegrator *kintegrator = &dscene->data.integrator;
/* Add both lights and emissive triangles to this vector for light tree construction. */
emitters_.reserve(kintegrator->num_distribution);
local_lights_.reserve(kintegrator->num_lights - kintegrator->num_distant_lights);
distant_lights_.reserve(kintegrator->num_distant_lights);
uint *object_offsets = dscene->object_lookup_offset.alloc(scene->objects.size());
/* When we keep track of the light index, only contributing lights will be added to the device.
* Therefore, we want to keep track of the light's index on the device.
@ -234,7 +262,7 @@ LightTree::LightTree(Scene *scene,
distant_lights_.emplace_back(scene, ~device_light_index, scene_light_index);
}
else {
emitters_.emplace_back(scene, ~device_light_index, scene_light_index);
local_lights_.emplace_back(scene, ~device_light_index, scene_light_index);
}
device_light_index++;
@ -243,7 +271,7 @@ LightTree::LightTree(Scene *scene,
scene_light_index++;
}
/* Similarly, we also want to keep track of the index of triangles that are emissive. */
/* Similarly, we also want to keep track of the index of triangles of emissive objects. */
int object_id = 0;
for (Object *object : scene->objects) {
if (progress_.get_cancel()) {
@ -255,61 +283,120 @@ LightTree::LightTree(Scene *scene,
continue;
}
object_offsets[object_id] = num_triangles;
/* Count emissive triangles. */
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
size_t mesh_num_triangles = mesh->num_triangles();
for (size_t i = 0; i < mesh_num_triangles; i++) {
int shader_index = mesh->get_shader()[i];
Shader *shader = (shader_index < mesh->get_used_shaders().size()) ?
static_cast<Shader *>(mesh->get_used_shaders()[shader_index]) :
scene->default_surface;
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
emitters_.emplace_back(scene, i, object_id);
}
}
num_triangles += mesh_num_triangles;
mesh_lights_.emplace_back(object, object_id);
object_id++;
}
/* Copy array to device. */
dscene->object_lookup_offset.copy_to_device();
/* Only count unique meshes. */
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
auto map_it = offset_map_.find(mesh);
if (map_it == offset_map_.end()) {
offset_map_[mesh] = num_triangles;
num_triangles += mesh->num_triangles();
}
}
}
LightTreeNode *LightTree::build(Scene * /* scene */, DeviceScene * /* dscene */)
LightTreeNode *LightTree::build(Scene *scene, DeviceScene *dscene)
{
if (emitters_.empty() && distant_lights_.empty()) {
if (local_lights_.empty() && distant_lights_.empty() && mesh_lights_.empty()) {
return nullptr;
}
/* At this stage `emitters_` only contains local lights, the distant lights will be merged
* into `emitters_` when Light Tree building is finished. */
const int num_local_lights = emitters_.size();
const int num_mesh_lights = mesh_lights_.size();
int num_local_lights = local_lights_.size() + num_mesh_lights;
const int num_distant_lights = distant_lights_.size();
/* Create a node for each mesh light, and keep track of unique mesh lights. */
std::unordered_map<Mesh *, std::tuple<LightTreeNode *, int, int>> unique_mesh;
uint *object_offsets = dscene->object_lookup_offset.alloc(scene->objects.size());
emitters_.reserve(num_triangles + num_local_lights + num_distant_lights);
for (LightTreeEmitter &emitter : mesh_lights_) {
Object *object = scene->objects[emitter.object_id];
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
emitter.root = create_node(LightTreeMeasure::empty, 0);
auto map_it = unique_mesh.find(mesh);
if (map_it == unique_mesh.end()) {
const int start = emitters_.size();
add_mesh(scene, mesh, emitter.object_id);
const int end = emitters_.size();
unique_mesh[mesh] = std::make_tuple(emitter.root.get(), start, end);
emitter.root->object_id = emitter.object_id;
}
else {
emitter.root->make_instance(std::get<0>(map_it->second), emitter.object_id);
}
object_offsets[emitter.object_id] = offset_map_[mesh];
}
/* Build a subtree for each unique mesh light. */
parallel_for_each(unique_mesh, [this](auto &map_it) {
LightTreeNode *node = std::get<0>(map_it.second);
int start = std::get<1>(map_it.second);
int end = std::get<2>(map_it.second);
recursive_build(self, node, start, end, emitters_.data(), 0, 0);
node->type |= LIGHT_TREE_INSTANCE;
});
task_pool.wait_work();
/* Update measure. */
parallel_for_each(mesh_lights_, [&](LightTreeEmitter &emitter) {
Object *object = scene->objects[emitter.object_id];
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
LightTreeNode *reference = std::get<0>(unique_mesh.find(mesh)->second);
emitter.measure = emitter.root->measure = reference->measure;
/* Transform measure. The measure is only directly transformable if the transformation has
* uniform scaling, otherwise recount all the triangles in the mesh with transformation. */
/* NOTE: in theory only energy needs recalculating: #bbox is available via `object->bounds`,
* transformation of #bcone is possible. However, the computation involves eigendecomposition
* and solving a cubic equation (https://doi.org/10.1016/j.nima.2009.11.075 section 3.4), then
* the angle is derived from the major axis of the resulted right elliptic cone's base, which
* can be an overestimation. */
if (!mesh->transform_applied && !emitter.measure.transform(object->get_tfm())) {
emitter.measure.reset();
size_t mesh_num_triangles = mesh->num_triangles();
for (size_t i = 0; i < mesh_num_triangles; i++) {
if (triangle_usable_as_light(mesh, i)) {
emitter.measure.add(LightTreeEmitter(scene, i, emitter.object_id, true).measure);
}
}
}
});
for (LightTreeEmitter &emitter : mesh_lights_) {
emitter.root->measure = emitter.measure;
}
/* Could be different from `num_triangles` if only some triangles of an object are emissive. */
const int num_emissive_triangles = emitters_.size();
num_local_lights += num_emissive_triangles;
/* Build the top level tree. */
root_ = create_node(LightTreeMeasure::empty, 0);
/* All local lights are grouped to the left child as an inner node. */
recursive_build(left, root_.get(), 0, num_local_lights, emitters_.data(), 0, 1);
/* All local lights and mesh lights are grouped to the left child as an inner node. */
std::move(local_lights_.begin(), local_lights_.end(), std::back_inserter(emitters_));
std::move(mesh_lights_.begin(), mesh_lights_.end(), std::back_inserter(emitters_));
recursive_build(
left, root_.get(), num_emissive_triangles, num_local_lights, emitters_.data(), 0, 1);
task_pool.wait_work();
/* All distant lights are grouped to the right child as a leaf node. */
root_->children[right] = create_node(LightTreeMeasure::empty, 1);
root_->get_inner().children[right] = create_node(LightTreeMeasure::empty, 1);
for (int i = 0; i < num_distant_lights; i++) {
root_->children[right]->add(distant_lights_[i]);
root_->get_inner().children[right]->add(distant_lights_[i]);
}
root_->children[right]->make_leaf(num_local_lights, num_distant_lights);
/* Append distant lights to the end of `light_prims` */
root_->get_inner().children[right]->make_distant(num_local_lights, num_distant_lights);
std::move(distant_lights_.begin(), distant_lights_.end(), std::back_inserter(emitters_));
return root_.get();
}
void LightTree::recursive_build(const Child child,
LightTreeNode *parent,
LightTreeNode *inner,
const int start,
const int end,
LightTreeEmitter *emitters,
@ -320,8 +407,15 @@ void LightTree::recursive_build(const Child child,
return;
}
parent->children[child] = create_node(LightTreeMeasure::empty, bit_trail);
LightTreeNode *node = parent->children[child].get();
LightTreeNode *node;
if (child == self) {
/* Building subtree. */
node = inner;
}
else {
inner->get_inner().children[child] = create_node(LightTreeMeasure::empty, bit_trail);
node = inner->get_inner().children[child].get();
}
/* 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. */

@ -12,6 +12,8 @@
#include "util/types.h"
#include "util/vector.h"
#include <variant>
CCL_NAMESPACE_BEGIN
/* Orientation Bounds
@ -102,26 +104,61 @@ struct LightTreeMeasure {
float area_measure = area == 0 ? len(bbox.size()) : area;
return energy * area_measure * bcone.calculate_measure();
}
__forceinline void reset()
{
*this = {};
}
bool transform(const Transform &tfm)
{
float scale_squared;
if (transform_uniform_scale(tfm, scale_squared)) {
bbox = bbox.transformed(&tfm);
bcone.axis = transform_direction(&tfm, bcone.axis) * inversesqrtf(scale_squared);
energy *= scale_squared;
return true;
}
return false;
}
};
LightTreeMeasure operator+(const LightTreeMeasure &a, const LightTreeMeasure &b);
struct LightTreeNode;
/* Light Tree Emitter
* Struct that indexes into the scene's triangle and light arrays. */
* An emitter is a built-in light, an emissive mesh, or an emissive triangle. */
struct LightTreeEmitter {
/* `prim_id >= 0` is an index into an object's local triangle index,
* otherwise `-prim_id-1`(`~prim`) is an index into device lights array. */
int prim_id;
/* If the emitter is a mesh, point to the root node of its subtree. */
unique_ptr<LightTreeNode> root;
union {
int light_id; /* Index into device lights array. */
int prim_id; /* Index into an object's local triangle index. */
};
int object_id;
float3 centroid;
LightTreeMeasure measure;
LightTreeEmitter(Scene *scene, int prim_id, int object_id);
LightTreeEmitter(Object *object, int object_id); /* Mesh emitter. */
LightTreeEmitter(Scene *scene, int prim_id, int object_id, bool with_transformation = false);
__forceinline bool is_mesh() const
{
return root != nullptr;
};
__forceinline bool is_triangle() const
{
return prim_id >= 0;
return !is_mesh() && prim_id >= 0;
};
__forceinline bool is_light() const
{
return !is_mesh() && light_id < 0;
};
};
@ -152,34 +189,115 @@ LightTreeBucket operator+(const LightTreeBucket &a, const LightTreeBucket &b);
struct LightTreeNode {
LightTreeMeasure measure;
uint bit_trail;
/* The number of emitters a leaf node stores. A negative number indicates it is an inner node. */
int num_emitters = -1;
/* Leaf nodes contain an index to first emitter. */
int first_emitter_index;
/* Inner node has two children. */
unique_ptr<LightTreeNode> children[2];
int object_id;
LightTreeNode() = default;
/* A bitmask of `LightTreeNodeType`, as in the building process an instance node can also be a
* leaf or an inner node. */
int type;
struct Leaf {
/* The number of emitters a leaf node stores. */
int num_emitters = -1;
/* Index to first emitter. */
int first_emitter_index = -1;
};
struct Inner {
/* Inner node has two children. */
unique_ptr<LightTreeNode> children[2];
};
struct Instance {
LightTreeNode *reference = nullptr;
};
std::variant<Leaf, Inner, Instance> variant_type;
LightTreeNode(const LightTreeMeasure &measure, const uint &bit_trial)
: measure(measure), bit_trail(bit_trial)
: measure(measure), bit_trail(bit_trial), variant_type(Inner())
{
type = LIGHT_TREE_INNER;
}
~LightTreeNode() = default;
__forceinline void add(const LightTreeEmitter &emitter)
{
measure.add(emitter.measure);
}
__forceinline Leaf &get_leaf()
{
return std::get<Leaf>(variant_type);
}
__forceinline Inner &get_inner()
{
return std::get<Inner>(variant_type);
}
__forceinline Instance &get_instance()
{
return std::get<Instance>(variant_type);
}
void make_leaf(const int &first_emitter_index, const int &num_emitters)
{
this->first_emitter_index = first_emitter_index;
this->num_emitters = num_emitters;
variant_type = Leaf();
Leaf &leaf = get_leaf();
leaf.first_emitter_index = first_emitter_index;
leaf.num_emitters = num_emitters;
type = LIGHT_TREE_LEAF;
}
void make_distant(const int &first_emitter_index, const int &num_emitters)
{
variant_type = Leaf();
Leaf &leaf = get_leaf();
leaf.first_emitter_index = first_emitter_index;
leaf.num_emitters = num_emitters;
type = LIGHT_TREE_DISTANT;
}
void make_instance(LightTreeNode *reference, const int &object_id)
{
variant_type = Instance();
Instance &instance = get_instance();
instance.reference = reference;
this->object_id = object_id;
type = LIGHT_TREE_INSTANCE;
}
LightTreeNode *get_reference()
{
assert(is_instance());
if (type == LIGHT_TREE_INSTANCE) {
return get_instance().reference;
}
return this;
}
__forceinline bool is_instance() const
{
return type & LIGHT_TREE_INSTANCE;
}
__forceinline bool is_leaf() const
{
return num_emitters >= 0;
return type & LIGHT_TREE_LEAF;
}
__forceinline bool is_inner() const
{
return type & LIGHT_TREE_INNER;
}
__forceinline bool is_distant() const
{
return type == LIGHT_TREE_DISTANT;
}
};
@ -190,8 +308,14 @@ struct LightTreeNode {
class LightTree {
unique_ptr<LightTreeNode> root_;
/* Local lights, distant lights and mesh lights are added to separate vectors for light tree
* construction. They are all considered as `emitters_`. */
vector<LightTreeEmitter> emitters_;
vector<LightTreeEmitter> local_lights_;
vector<LightTreeEmitter> distant_lights_;
vector<LightTreeEmitter> mesh_lights_;
std::unordered_map<Mesh *, int> offset_map_;
Progress &progress_;
@ -201,8 +325,9 @@ class LightTree {
std::atomic<int> num_nodes = 0;
size_t num_triangles = 0;
/* Left or right child of an inner node. */
/* An inner node itself or its left and right child. */
enum Child {
self = -1,
left = 0,
right = 1,
};
@ -236,7 +361,7 @@ class LightTree {
enum { MIN_EMITTERS_PER_THREAD = 4096 };
void recursive_build(Child child,
LightTreeNode *parent,
LightTreeNode *inner,
int start,
int end,
LightTreeEmitter *emitters,
@ -249,6 +374,12 @@ class LightTree {
const int end,
LightTreeMeasure &measure,
int &split_dim);
/* Check whether the light tree can use this triangle as light-emissive. */
bool triangle_usable_as_light(Mesh *mesh, int prim_id);
/* Add all the emissive triangles of a mesh to the light tree. */
void add_mesh(Scene *scene, Mesh *mesh, int object_id);
};
CCL_NAMESPACE_END

@ -74,6 +74,7 @@ DeviceScene::DeviceScene(Device *device)
light_tree_nodes(device, "light_tree_nodes", MEM_GLOBAL),
light_tree_emitters(device, "light_tree_emitters", MEM_GLOBAL),
light_to_tree(device, "light_to_tree", MEM_GLOBAL),
object_to_tree(device, "object_to_tree", MEM_GLOBAL),
object_lookup_offset(device, "object_lookup_offset", MEM_GLOBAL),
triangle_to_tree(device, "triangle_to_tree", MEM_GLOBAL),
particles(device, "particles", MEM_GLOBAL),

@ -115,6 +115,7 @@ class DeviceScene {
device_vector<KernelLightTreeNode> light_tree_nodes;
device_vector<KernelLightTreeEmitter> light_tree_emitters;
device_vector<uint> light_to_tree;
device_vector<uint> object_to_tree;
device_vector<uint> object_lookup_offset;
device_vector<uint> triangle_to_tree;