Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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);
	}
}