void LightArray::sample(LightQuery* query) const { // Handle empty light accel if (light_indices.size() == 0 && assembly_lights.size() == 0) { query->spec_samp = SpectralSample(query->spec_samp.hero_wavelength, 0.0f); return; } const float local_prob = static_cast<double>(light_indices.size()) / (total_assembly_lights + light_indices.size()); const float child_prob = 1.0f - local_prob; // If we're sampling a light in this assembly if (query->n <= local_prob) { // Update probabilities query->n /= local_prob; // Get light instance const auto index = light_indices[static_cast<uint32_t>(query->n * light_indices.size()) % light_indices.size()]; const Instance& instance = assembly->instances[index]; // Shorthand // Get light data Light* light = dynamic_cast<Light*>(assembly->objects[instance.data_index].get()); /// Get transforms if any if (instance.transform_count > 0) { auto cbegin = assembly->xforms.cbegin() + instance.transform_index; auto cend = cbegin + instance.transform_count; auto instance_xform = lerp_seq(query->time, cbegin, cend); query->pos = instance_xform.pos_to(query->pos); query->nor = instance_xform.nor_to(query->nor).normalized(); query->xform *= instance_xform; } // Sample the light float p; query->spec_samp = light->sample(query->pos, query->u, query->v, query->wavelength, query->time, &(query->to_light), &p); query->to_light = query->xform.dir_from(query->to_light); query->light_sample_pdf = p; // FIll in the light's instance ID query->id.push_back(index, assembly->element_id_bits()); } // If we're sampling a light in a child assembly else { // Update probabilities query->n = (query->n - local_prob) / child_prob; // Select assembly // TODO: a binary search would be faster size_t index = 0; const size_t target_index = static_cast<size_t>(total_assembly_lights * query->n) % total_assembly_lights; for (const auto& al: assembly_lights) { if (std::get<0>(al) <= target_index && target_index < (std::get<0>(al) + std::get<1>(al))) { index = std::get<2>(al); break; } } // Get assembly instance shorthand const Instance& instance = assembly->instances[index]; // Get assembly Assembly* child_assembly = assembly->assemblies[instance.data_index].get(); // Get transforms if any if (instance.transform_count > 0) { auto cbegin = assembly->xforms.cbegin() + instance.transform_index; auto cend = cbegin + instance.transform_count; auto instance_xform = lerp_seq(query->time, cbegin, cend); query->pos = instance_xform.pos_to(query->pos); query->xform *= instance_xform; } // Push the assembly's instance ID query->id.push_back(index, assembly->element_id_bits()); // Traverse into child assembly child_assembly->light_accel.sample(query); } // Selection PDF is just one, since all lights have equal probability of // being selected. query->selection_pdf = 1.0f; }
void LightTree::sample(LightQuery* query) const { const Node* node = &(nodes[0]); float tot_prob = 1.0f; // Traverse down the tree, keeping track of the relative probabilities while (!node->is_leaf) { // Calculate the relative probabilities of the two children float p1 = node_prob(*query, node->index1); float p2 = node_prob(*query, node->index2); const float total = p1 + p2; if (total <= 0.0f) { p1 = 0.5f; p2 = 0.5f; } else { p1 /= total; p2 /= total; } if (query->n <= p1) { tot_prob *= p1; node = &(nodes[node->index1]); query->n /= p1; } else { tot_prob *= p2; node = &(nodes[node->index2]); query->n = (query->n - p1) / p2; } } // Instance shorthand const Instance& instance = assembly->instances[node->instance_index]; // Push the instance index onto the ID query->id.push_back(node->instance_index, assembly->element_id_bits()); // Get transforms if any if (instance.transform_count > 0) { auto cbegin = assembly->xforms.cbegin() + instance.transform_index; auto cend = cbegin + instance.transform_count; auto instance_xform = lerp_seq(query->time, cbegin, cend); query->pos = instance_xform.pos_to(query->pos); query->nor = instance_xform.nor_to(query->nor).normalized(); query->xform *= instance_xform; } // Do light sampling if (instance.type == Instance::OBJECT) { const Object* obj = assembly->objects[instance.data_index].get(); // Shorthand if (obj->get_type() == Object::LIGHT) { const Light* light = dynamic_cast<const Light*>(obj); float p = 1.0f; query->spec_samp = light->sample(query->pos, query->u, query->v, query->wavelength, query->time, &(query->to_light), &p); query->to_light = query->xform.dir_from(query->to_light); query->selection_pdf *= (tot_prob * light_count()); query->light_sample_pdf = p; } // TODO: handle non-light objects that emit light } else if (instance.type == Instance::ASSEMBLY) { const Assembly* asmb = assembly->assemblies[instance.data_index].get(); // Shorthand query->selection_pdf *= (tot_prob * light_count()) / asmb->light_accel.light_count(); asmb->light_accel.sample(query); } }