Cycles: add instancing support in light tree #106683

Closed
Weizhen Huang wants to merge 10 commits from weizhen:light_tree_instance into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
12 changed files with 728 additions and 262 deletions

View File

@ -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)
weizhen marked this conversation as resolved Outdated

What is the difference between lamp and light?

This is something confusing in the current state of kernel. Is it possible to stick to light? Or somewhere have a comment about whet the difference is?

What is the difference between lamp and light? This is something confusing in the current state of kernel. Is it possible to stick to `light`? Or somewhere have a comment about whet the difference is?

This array stores indices of the build-in lights (area light, spot light, sun light, point light), these lights are often called "lamps" throughout the code. Besides that we have mesh lights, which are kept in a separate array called mesh_to_light. Because both types are called "lights", I thought it would be less confusing to rename the other one to lamp_to_tree.
I can add a comment explaining this.

This array stores indices of the build-in lights (area light, spot light, sun light, point light), these lights are often called "lamps" throughout the code. Besides that we have mesh lights, which are kept in a separate array called `mesh_to_light`. Because both types are called "lights", I thought it would be less confusing to rename the other one to `lamp_to_tree`. I can add a comment explaining this.

The code indeed often refers to the light sources as lamps. And this is exactly what causes quite some confusion.

Like, KernelLight *klight = &kernel_data_fetch(lights, lamp). So, is the lamp different from light, how can they be used interchangeably.

We can probably also ignore the problem until the lights are actually a real objects in Cycles, and then everything is just an object.

The code indeed often refers to the light sources as lamps. And this is exactly what causes quite some confusion. Like, `KernelLight *klight = &kernel_data_fetch(lights, lamp)`. So, is the lamp different from light, how can they be used interchangeably. We can probably also ignore the problem until the lights are actually a real objects in Cycles, and then everything is just an object.

I think we should get rid of the term "lamp". It was used in Blender before and renamed to light in 2.8, but some usage of lamp stuck around in Cycles.

Maybe best to use "light" for area/spot/sun/point, and then "emitter" for lights and emissive triangles. Though not as part of this patch of course. I don't mind it being called lamp as long as that is still used in other places.

I think we should get rid of the term "lamp". It was used in Blender before and renamed to light in 2.8, but some usage of lamp stuck around in Cycles. Maybe best to use "light" for area/spot/sun/point, and then "emitter" for lights and emissive triangles. Though not as part of this patch of course. I don't mind it being called lamp as long as that is still used in other places.
KERNEL_DATA_ARRAY(uint, object_to_tree)
weizhen marked this conversation as resolved Outdated

Isn't it more like object_to_tree, as it is indexed by object?

Also, maybe object_to_light_tree?`

Isn't it more like `object_to_tree`, as it is indexed by object? Also, maybe `object_to_light_tree`?`
KERNEL_DATA_ARRAY(uint, object_lookup_offset)
KERNEL_DATA_ARRAY(uint, triangle_to_tree)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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++) {

View File

@ -1370,6 +1370,13 @@ using BoundingCone = struct BoundingCone {
float theta_e;
};
enum LightTreeNodeType : uint8_t {
weizhen marked this conversation as resolved Outdated

Not sure why such syntax is needed.

For being C-style (which matches the legacy roots of the OpenCL days in the file) it is typedef LightTreeNodeType { ... } LightTreeNodeType;.

For the C++ you can simply do enum LightTreeNodeType{ ... };

Not sure why such syntax is needed. For being C-style (which matches the legacy roots of the OpenCL days in the file) it is `typedef LightTreeNodeType { ... } LightTreeNodeType;`. For the C++ you can simply do `enum LightTreeNodeType{ ... };`
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;

We don't use enum types in struct like this and instead use an integer type, because we don't know that the size is the same on the CPU and GPU.

Maybe we can start using them with an explicit size in the declaration like enum LightTreeNodeType : int { or enum LightTreeNodeType : uint8_t {? Would need to be tested on the buildbot, not sure it works for all GPU compilers.

We don't use enum types in struct like this and instead use an integer type, because we don't know that the size is the same on the CPU and GPU. Maybe we can start using them with an explicit size in the declaration like `enum LightTreeNodeType : int {` or `enum LightTreeNodeType : uint8_t {`? Would need to be tested on the buildbot, not sure it works for all GPU compilers.
/* 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;
weizhen marked this conversation as resolved Outdated

I think it would be a bit more clear like this:

union {
   struct { int first_emitter; } leaf;
   struct { int right_child; } inner;
   struct { int reference; } instance;
};
I think it would be a bit more clear like this: ``` union { struct { int first_emitter; } leaf; struct { int right_child; } inner; struct { int reference; } instance; }; ```
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;
weizhen marked this conversation as resolved

Same suggestion as for KernelLightTreeNode, could be more explicit about which emitter type the data belongs to.

Same suggestion as for `KernelLightTreeNode`, could be more explicit about which emitter type the data belongs to.
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;

View File

@ -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();

View File

@ -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. */

View File

@ -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;
weizhen marked this conversation as resolved Outdated

Add a note that this is a bitmask of LightTreeNodeType.

Add a note that this is a bitmask of `LightTreeNodeType`.
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

View File

@ -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),

View File

@ -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;