Cycles: new Microfacet-based Hair BSDF with elliptical cross-section support #105600

Merged
Weizhen Huang merged 114 commits from weizhen/blender:microfacet_hair into main 2023-08-18 12:46:20 +02:00
400 changed files with 6970 additions and 3960 deletions
Showing only changes of commit 778de47f31 - Show all commits

View File

@ -812,6 +812,16 @@ static void attr_create_generic(Scene *scene,
num_curves, num_keys, data, element, [&](int i) { return float(src[i]); });
break;
}
case BL::Attribute::data_type_INT32_2D: {
BL::Int2Attribute b_int2_attribute{b_attribute};
const int2 *src = static_cast<const int2 *>(b_int2_attribute.data[0].ptr.data);
Attribute *attr = attributes.add(name, TypeFloat2, element);
float2 *data = attr->data_float2();
fill_generic_attribute(num_curves, num_keys, data, element, [&](int i) {
return make_float2(float(src[i][0]), float(src[i][1]));
});
break;
}
case BL::Attribute::data_type_FLOAT_VECTOR: {
BL::FloatVectorAttribute b_vector_attribute{b_attribute};
const float(*src)[3] = static_cast<const float(*)[3]>(b_vector_attribute.data[0].ptr.data);

View File

@ -528,6 +528,19 @@ static void attr_create_generic(Scene *scene,
});
break;
}
case BL::Attribute::data_type_INT32_2D: {
BL::Int2Attribute b_int2_attribute{b_attribute};
if (b_int2_attribute.data.length() == 0) {
continue;
}
const int2 *src = static_cast<const int2 *>(b_int2_attribute.data[0].ptr.data);
Attribute *attr = attributes.add(name, TypeFloat2, element);
float2 *data = attr->data_float2();
fill_generic_attribute(b_mesh, data, b_domain, subdivision, [&](int i) {
return make_float2(float(src[i][0]), float(src[i][1]));
});
break;
}
default:
/* Not supported. */
break;

View File

@ -102,6 +102,16 @@ static void copy_attributes(PointCloud *pointcloud,
}
break;
}
case BL::Attribute::data_type_INT32_2D: {
BL::Int2Attribute b_int2_attribute{b_attribute};
const int2 *src = static_cast<const int2 *>(b_int2_attribute.data[0].ptr.data);
Attribute *attr = attributes.add(name, TypeFloat2, element);
float2 *data = attr->data_float2();
for (int i = 0; i < num_points; i++) {
data[i] = make_float2(float(src[i][0]), float(src[i][1]));
}
break;
}
case BL::Attribute::data_type_FLOAT_VECTOR: {
BL::FloatVectorAttribute b_vector_attribute{b_attribute};
const float(*src)[3] = static_cast<const float(*)[3]>(b_vector_attribute.data[0].ptr.data);

View File

@ -357,8 +357,12 @@ void PathTraceWorkCPU::guiding_push_sample_data_to_global_storage(
# if PATH_GUIDING_LEVEL >= 2
const bool use_direct_light = kernel_data.integrator.use_guiding_direct_light;
const bool use_mis_weights = kernel_data.integrator.use_guiding_mis_weights;
# if OPENPGL_VERSION_MINOR >= 5
kg->opgl_path_segment_storage->PrepareSamples(use_mis_weights, use_direct_light, false);
# else
kg->opgl_path_segment_storage->PrepareSamples(
false, nullptr, use_mis_weights, use_direct_light, false);
# endif
# endif
# ifdef WITH_CYCLES_DEBUG

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)
KERNEL_DATA_ARRAY(uint, object_to_tree)
KERNEL_DATA_ARRAY(uint, object_lookup_offset)
KERNEL_DATA_ARRAY(uint, triangle_to_tree)

View File

@ -454,8 +454,13 @@ ccl_device_forceinline bool guiding_bsdf_init(KernelGlobals kg,
ccl_private float &rand)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
# if OPENPGL_VERSION_MINOR >= 5
if (kg->opgl_surface_sampling_distribution->Init(
kg->opgl_guiding_field, guiding_point3f(P), rand)) {
# else
if (kg->opgl_surface_sampling_distribution->Init(
kg->opgl_guiding_field, guiding_point3f(P), rand, true)) {
# endif
kg->opgl_surface_sampling_distribution->ApplyCosineProduct(guiding_point3f(N));
return true;
}
@ -506,8 +511,13 @@ ccl_device_forceinline bool guiding_phase_init(KernelGlobals kg,
return false;
}
# if OPENPGL_VERSION_MINOR >= 5
if (kg->opgl_volume_sampling_distribution->Init(
kg->opgl_guiding_field, guiding_point3f(P), rand)) {
# else
if (kg->opgl_volume_sampling_distribution->Init(
kg->opgl_guiding_field, guiding_point3f(P), rand, true)) {
# endif
kg->opgl_volume_sampling_distribution->ApplySingleLobeHenyeyGreensteinProduct(guiding_vec3f(D),
g);
return true;

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

@ -1390,19 +1390,128 @@ ccl_device_extern void osl_noiseparams_set_impulses(ccl_private OSLNoiseOptions
res->y = n; \
res->z = n; \
} \
ccl_device_extern void name##_vv(ccl_private float3 *res, const float3 *v) \
ccl_device_extern void name##_vv(ccl_private float3 *res, ccl_private const float3 *v) \
{ \
const float n = name##_fv(v); \
res->x = n; \
res->y = n; \
res->z = n; \
} \
ccl_device_extern void name##_vvf(ccl_private float3 *res, const float3 *v, float w) \
ccl_device_extern void name##_vvf( \
ccl_private float3 *res, ccl_private const float3 *v, float w) \
{ \
const float n = name##_fvf(v, w); \
res->x = n; \
res->y = n; \
res->z = n; \
} \
ccl_device_extern void name##_dfdf(ccl_private float *res, ccl_private const float *x) \
{ \
res[0] = name##_ff(x[0]); \
res[1] = name##_ff(x[1]); \
res[2] = name##_ff(x[2]); \
} \
ccl_device_extern void name##_dfdff( \
ccl_private float *res, ccl_private const float *x, float y) \
{ \
res[0] = name##_fff(x[0], y); \
res[1] = name##_fff(x[1], y); \
res[2] = name##_fff(x[2], y); \
} \
ccl_device_extern void name##_dffdf( \
ccl_private float *res, float x, ccl_private const float *y) \
{ \
res[0] = name##_fff(x, y[0]); \
res[1] = name##_fff(x, y[1]); \
res[2] = name##_fff(x, y[2]); \
} \
ccl_device_extern void name##_dfdfdf( \
ccl_private float *res, ccl_private const float *x, ccl_private const float *y) \
{ \
res[0] = name##_fff(x[0], y[0]); \
res[1] = name##_fff(x[1], y[1]); \
res[2] = name##_fff(x[2], y[2]); \
} \
ccl_device_extern void name##_dfdv(ccl_private float *res, ccl_private const float3 *v) \
{ \
res[0] = name##_fv(&v[0]); \
res[1] = name##_fv(&v[1]); \
res[2] = name##_fv(&v[2]); \
} \
ccl_device_extern void name##_dfdvf( \
ccl_private float *res, ccl_private const float3 *v, float w) \
{ \
res[0] = name##_fvf(&v[0], w); \
res[1] = name##_fvf(&v[1], w); \
res[2] = name##_fvf(&v[2], w); \
} \
ccl_device_extern void name##_dfvdf( \
ccl_private float *res, ccl_private const float3 *v, ccl_private const float *w) \
{ \
res[0] = name##_fvf(v, w[0]); \
res[1] = name##_fvf(v, w[1]); \
res[2] = name##_fvf(v, w[2]); \
} \
ccl_device_extern void name##_dfdvdf( \
ccl_private float *res, ccl_private const float3 *v, ccl_private const float *w) \
{ \
res[0] = name##_fvf(&v[0], w[0]); \
res[1] = name##_fvf(&v[1], w[1]); \
res[2] = name##_fvf(&v[2], w[2]); \
} \
ccl_device_extern void name##_dvdf(ccl_private float3 *res, ccl_private const float *x) \
{ \
name##_vf(&res[0], x[0]); \
name##_vf(&res[1], x[1]); \
name##_vf(&res[2], x[2]); \
} \
ccl_device_extern void name##_dvdff( \
ccl_private float3 *res, ccl_private const float *x, float y) \
{ \
name##_vff(&res[0], x[0], y); \
name##_vff(&res[1], x[1], y); \
name##_vff(&res[2], x[2], y); \
} \
ccl_device_extern void name##_dvfdf( \
ccl_private float3 *res, float x, ccl_private const float *y) \
{ \
name##_vff(&res[0], x, y[0]); \
name##_vff(&res[1], x, y[1]); \
name##_vff(&res[2], x, y[2]); \
} \
ccl_device_extern void name##_dvdfdf( \
ccl_private float3 *res, ccl_private const float *x, ccl_private const float *y) \
{ \
name##_vff(&res[0], x[0], y[0]); \
name##_vff(&res[1], x[1], y[1]); \
name##_vff(&res[2], x[2], y[2]); \
} \
ccl_device_extern void name##_dvdv(ccl_private float3 *res, ccl_private const float3 *v) \
{ \
name##_vv(&res[0], &v[0]); \
name##_vv(&res[1], &v[1]); \
name##_vv(&res[2], &v[2]); \
} \
ccl_device_extern void name##_dvdvf( \
ccl_private float3 *res, ccl_private const float3 *v, float w) \
{ \
name##_vvf(&res[0], &v[0], w); \
name##_vvf(&res[1], &v[1], w); \
name##_vvf(&res[2], &v[2], w); \
} \
ccl_device_extern void name##_dvvdf( \
ccl_private float3 *res, ccl_private const float3 *v, ccl_private const float *w) \
{ \
name##_vvf(&res[0], v, w[0]); \
name##_vvf(&res[1], v, w[1]); \
name##_vvf(&res[2], v, w[2]); \
} \
ccl_device_extern void name##_dvdvdf( \
ccl_private float3 *res, ccl_private const float3 *v, ccl_private const float *w) \
{ \
name##_vvf(&res[0], &v[0], w[0]); \
name##_vvf(&res[1], &v[1], w[1]); \
name##_vvf(&res[2], &v[2], w[2]); \
}
ccl_device_forceinline float hashnoise_1d(float p)

View File

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

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;