forked from bartvdbraak/blender
Cycles: add instancing support in light tree
Build a subtree for each unique mesh light. Pull Request: #106683
This commit is contained in:
parent
910f60de4c
commit
bfd1836861
@ -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 ¢roid,
|
||||
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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user