예제 #1
0
	/*
	 * Generates a camera ray based on the given information.
	 */
	WorldRay generate_ray(float x, float y, float dx, float dy, float time, float u, float v) const {
		WorldRay wray;

		wray.type = WorldRay::CAMERA;
		wray.time = time;

		// Get time-interpolated camera settings
		const Transform transform = lerp_seq(time, transforms);
		const float tfov = lerp_seq(time, tfovs);
		const float aperture_radius = lerp_seq(time, aperture_radii);
		const float focus_distance = lerp_seq(time, focus_distances);

		// Ray origin
		wray.o.x = aperture_radius * ((u * 2) - 1);
		wray.o.y = aperture_radius * ((v * 2) - 1);
		wray.o.z = 0.0;
		square_to_circle(&wray.o.x, &wray.o.y);

		// Ray direction
		wray.d.x = (x * tfov) - (wray.o.x / focus_distance);
		wray.d.y = (y * tfov) - (wray.o.y / focus_distance);
		wray.d.z = 1.0;
		wray.d.normalize();

		// Ray image plane differentials
		wray.odx = Vec3(0.0f, 0.0f, 0.0f);
		wray.ody = Vec3(0.0f, 0.0f, 0.0f);
		wray.ddx = Vec3(dx*tfov, 0.0f, 0.0f);
		wray.ddy = Vec3(0.0f, dy*tfov, 0.0f);

		// Transform the ray
		return wray.transformed(transform);
	}
예제 #2
0
void LightTree::build(const Assembly& assembly_) {
	assembly = &assembly_;

	// Populate the build nodes
	for (size_t i = 0; i < assembly->instances.size(); ++i) {
		const auto& instance = assembly->instances[i]; // Shorthand

		// If it's an object
		if (instance.type == Instance::OBJECT) {
			const Object* obj = assembly->objects[instance.data_index].get(); // Shorthand

			if (obj->total_emitted_color().energy() > 0.0f) {
				build_nodes.push_back(BuildNode());
				build_nodes.back().instance_index = i;
				build_nodes.back().bbox = assembly->instance_bounds_at(0.5f, i);
				build_nodes.back().center = build_nodes.back().bbox.center();
				const Vec3 scale = assembly->instance_xform_at(0.5f, i).get_inv_scale();
				const float surface_scale = ((scale[0]*scale[1]) + (scale[0]*scale[2]) + (scale[1]*scale[2])) * 0.33333333f;
				build_nodes.back().energy = obj->total_emitted_color().energy() / surface_scale;

				++total_lights;
			}
		}
		// If it's an assembly
		else if (instance.type == Instance::ASSEMBLY) {
			const Assembly* asmb = assembly->assemblies[instance.data_index].get(); // Shorthand
			const auto count = asmb->light_accel.light_count();
			const float energy = asmb->light_accel.total_emitted_color().energy();

			if (count > 0 && energy > 0.0f) {
				build_nodes.push_back(BuildNode());
				build_nodes.back().instance_index = i;
				if (instance.transform_count > 0) {
					auto xstart = assembly->xforms.cbegin() + instance.transform_index;
					auto xend = xstart + instance.transform_count;
					build_nodes.back().bbox = lerp_seq(0.5f, asmb->light_accel.bounds()).inverse_transformed(lerp_seq(0.5f, xstart, xend));
				} else {
					build_nodes.back().bbox = lerp_seq(0.5f, asmb->light_accel.bounds());
				}
				build_nodes.back().center = build_nodes.back().bbox.center();
				const Vec3 scale = assembly->instance_xform_at(0.5f, i).get_inv_scale();
				const float surface_scale = ((scale[0]*scale[1]) + (scale[0]*scale[2]) + (scale[1]*scale[2])) * 0.33333333f;
				build_nodes.back().energy = energy / surface_scale;

				total_lights += count;
			}
		}
	}

	if (build_nodes.size() > 0) {
		recursive_build(build_nodes.begin(), build_nodes.end());
		bounds_ = nodes[0].bounds;
		total_energy = nodes[0].energy;
	} else {
		bounds_.clear();
		bounds_.emplace_back(BBox());
	}
}
예제 #3
0
void Tracer::trace_surface(Surface* surface, Ray* rays, Ray* end) {
	// Get parent transforms
	const auto parent_xforms = xform_stack.top_frame<Transform>();
	const size_t parent_xforms_count = std::distance(parent_xforms.first, parent_xforms.second);

	// Trace!
	for (auto ritr = rays; ritr != end; ++ritr) {
		Ray& ray = *ritr;  // Shorthand reference to the ray
		Intersection& inter = intersections[ritr->id()]; // Shorthand reference to ray's intersection

		// Test against the ray
		if (surface->intersect_ray(ray, &inter)) {
			inter.hit = true;
			inter.id = element_id;

			if (ray.is_occlusion()) {
				ray.set_done_true(); // Early out for shadow rays
			} else {
				ray.max_t = inter.t;
				inter.space = parent_xforms_count > 0 ? lerp_seq(ray.time, parent_xforms.first, parent_xforms.second) : Transform();

				// Do shading
				auto shader = surface_shader_stack.back();
				if (shader != nullptr) {
					shader->shade(&inter);
				} else {
					inter.surface_closure.init(EmitClosure(Color(1.0, 0.0, 1.0)));
				}
			}
		}
	}
}
예제 #4
0
float LightTree::node_prob(const LightQuery& lq, uint32_t index) const {
	const BBox bbox = lerp_seq(lq.time, nodes[index].bounds);
	const Vec3 d = bbox.center() - lq.pos;
	const float dist2 = d.length2();
	const float r = bbox.diagonal() * 0.5f;
	const float r2 = r * r;
	const float inv_surface_area = 1.0f / r2;

	float cos_theta_max;
	if (dist2 <= r2) {
		cos_theta_max = -1.0f;
	} else {
		const float sin_theta_max2 = std::min(1.0f, r2 / dist2);
		cos_theta_max = std::sqrt(1.0f - sin_theta_max2);
	}

	// Get the approximate amount of light contribution from the
	// composite light source.
	const float approx_contrib = std::max(0.0f, lq.bsdf->estimate_eval_over_solid_angle(lq.d, d, cos_theta_max, lq.nor, lq.wavelength));

	return nodes[index].energy * inv_surface_area * approx_contrib;
}
예제 #5
0
void Tracer::trace_lightsource(Light* light, Ray* rays, Ray* end) {
	// Get parent transforms
	const auto parent_xforms = xform_stack.top_frame<Transform>();
	const size_t parent_xforms_count = std::distance(parent_xforms.first, parent_xforms.second);

	// Trace!
	for (auto ritr = rays; ritr != end; ++ritr) {
		Ray& ray = *ritr;  // Shorthand reference to the ray
		Intersection& inter = intersections[ritr->id()]; // Shorthand reference to ray's intersection

		// Test against the ray
		if (light->intersect_ray(ray, &inter)) {
			inter.hit = true;
			inter.id = element_id;

			if (ray.is_occlusion()) {
				ray.set_done_true(); // Early out for shadow rays
			} else {
				ray.max_t = inter.t;
				inter.space = parent_xforms_count > 0 ? lerp_seq(ray.time, parent_xforms.first, parent_xforms.second) : Transform();
			}
		}
	}
}
예제 #6
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;
}
예제 #7
0
void Tracer::trace_assembly(Assembly* assembly, Ray* rays, Ray* rays_end) {
	BVH4StreamTraverser traverser;

	// Initialize traverser
	traverser.init_accel(assembly->object_accel);
	traverser.init_rays(rays, rays_end);

	// Trace rays one object at a time
	std::tuple<Ray*, Ray*, size_t> hits = traverser.next_object();
	while (std::get<0>(hits) != std::get<1>(hits)) {
		const auto& instance = assembly->instances[std::get<2>(hits)]; // Short-hand for the current instance

		// Push the current instance index onto the element id
		const auto element_id_bits = assembly->element_id_bits();
		element_id.push_back(std::get<2>(hits), element_id_bits);

		// Propagate transforms (if necessary)
		const auto parent_xforms = xform_stack.top_frame<Transform>();
		const size_t parent_xforms_count = std::distance(parent_xforms.first, parent_xforms.second);
		if (instance.transform_count > 0) {
			const auto xbegin = &(*(assembly->xforms.begin() + instance.transform_index));
			const auto xend = xbegin + instance.transform_count;
			const auto larger_xform_count = std::max(instance.transform_count, parent_xforms_count);

			// Push merged transforms onto transform stack
			auto xforms = xform_stack.push_frame<Transform>(larger_xform_count);
			merge(xforms.first, parent_xforms.first, parent_xforms.second, xbegin, xend);

			for (auto ray = std::get<0>(hits); ray != std::get<1>(hits); ++ray) {
				w_rays[ray->id()].update_ray(ray, lerp_seq(ray->time, xforms.first, xforms.second));
			}
		}

		// Check for shader on the instance, and push to shader stack if it
		// has one.
		if (instance.surface_shader != nullptr) {
			surface_shader_stack.emplace_back(instance.surface_shader);
		}

		// Trace against the object or assembly
		if (instance.type == Instance::OBJECT) {
			Object* obj = assembly->objects[instance.data_index].get(); // Short-hand for the current object
			// Branch to different code path based on object type
			switch (obj->get_type()) {
				case Object::SURFACE:
					trace_surface(reinterpret_cast<Surface*>(obj), std::get<0>(hits), std::get<1>(hits));
					break;
				case Object::COMPLEX_SURFACE:
					trace_complex_surface(reinterpret_cast<ComplexSurface*>(obj), std::get<0>(hits), std::get<1>(hits));
					break;
				case Object::PATCH_SURFACE:
					trace_patch_surface(reinterpret_cast<PatchSurface*>(obj), std::get<0>(hits), std::get<1>(hits));
					break;
				case Object::LIGHT:
					trace_lightsource(reinterpret_cast<Light*>(obj), std::get<0>(hits), std::get<1>(hits));
					break;
				default:
					//std::cout << "WARNING: unknown object type, skipping." << std::endl;
					break;
			}

			Global::Stats::object_ray_tests += std::distance(std::get<0>(hits), std::get<1>(hits));
		} else { /* Instance::ASSEMBLY */
			Assembly* asmb = assembly->assemblies[instance.data_index].get(); // Short-hand for the current object
			trace_assembly(asmb, std::get<0>(hits), std::get<1>(hits));
		}

		// Pop shader stack if we pushed onto it earlier
		if (instance.surface_shader != nullptr) {
			surface_shader_stack.pop_back();
		}

		// Un-transform rays if we transformed them earlier
		if (instance.transform_count > 0) {
			if (parent_xforms_count > 0) {
				for (auto ray = std::get<0>(hits); ray != std::get<1>(hits); ++ray) {
					w_rays[ray->id()].update_ray(ray, lerp_seq(ray->time, parent_xforms.first, parent_xforms.second));
				}
			} else {
				for (auto ray = std::get<0>(hits); ray != std::get<1>(hits); ++ray) {
					w_rays[ray->id()].update_ray(ray);
				}
			}

			// Pop top off of xform stack
			xform_stack.pop_frame();
		}

		// Pop the index of this instance off the element id
		element_id.pop_back(element_id_bits);

		// Get next object to test against
		hits = traverser.next_object();
	}
}
예제 #8
0
bool Sphere::intersect_ray(const Ray &ray, Intersection *intersection)
{
    // Get the center and radius of the sphere at the ray's time
    const Vec3 cent = lerp_seq(ray.time, center); // Center of the sphere
    const float radi = lerp_seq(ray.time, radius); // Radius of the sphere

    // Calculate the relevant parts of the ray for the intersection
    Vec3 o = ray.o - cent; // Ray origin relative to sphere center
    Vec3 d = ray.d;


    // Code taken shamelessly from https://github.com/Tecla/Rayito
    // Ray-sphere intersection can result in either zero, one or two points
    // of intersection.  It turns into a quadratic equation, so we just find
    // the solution using the quadratic formula.  Note that there is a
    // slightly more stable form of it when computing it on a computer, and
    // we use that method to keep everything accurate.

    // Calculate quadratic coeffs
    float a = d.length2();
    float b = 2.0f * dot(d, o);
    float c = o.length2() - radi * radi;

    float t0, t1, discriminant;
    discriminant = b * b - 4.0f * a * c;
    if (discriminant < 0.0f) {
        // Discriminant less than zero?  No solution => no intersection.
        return false;
    }
    discriminant = std::sqrt(discriminant);

    // Compute a more stable form of our param t (t0 = q/a, t1 = c/q)
    // q = -0.5 * (b - sqrt(b * b - 4.0 * a * c)) if b < 0, or
    // q = -0.5 * (b + sqrt(b * b - 4.0 * a * c)) if b >= 0
    float q;
    if (b < 0.0f) {
        q = -0.5f * (b - discriminant);
    } else {
        q = -0.5f * (b + discriminant);
    }

    // Get our final parametric values
    t0 = q / a;
    if (q != 0.0f) {
        t1 = c / q;
    } else {
        t1 = ray.max_t;
    }

    // Swap them so they are ordered right
    if (t0 > t1) {
        float temp = t1;
        t1 = t0;
        t0 = temp;
    }

    // Check our intersection for validity against this ray's extents
    if (t0 >= ray.max_t || t1 < 0.0001f)
        return false;

    float t;
    if (t0 >= 0.0001f) {
        t = t0;
    } else if (t1 < ray.max_t) {
        t = t1;
    } else {
        return false;
    }

    if (intersection && !ray.is_occlusion()) {
        intersection->t = t;

        intersection->geo.p = ray.o + (ray.d * t);
        intersection->geo.n = intersection->geo.p - cent;
        intersection->geo.n.normalize();

        intersection->backfacing = dot(intersection->geo.n, ray.d.normalized()) > 0.0f;

        // Calculate the latitude and longitude of the hit point on the sphere
        const Vec3 unit_p = intersection->geo.n;
        const Vec3 p = unit_p * radi;
        const float lat_cos = unit_p.z;
        const float lat_sin = std::sqrt((unit_p.x * unit_p.x) + (unit_p.y * unit_p.y));
        const float long_cos = unit_p.x / lat_sin;
        const float long_sin = unit_p.y / lat_sin;

        float latitude = std::acos(lat_cos);
        float longitude = 0.0f;
        if (unit_p.x != 0.0f || unit_p.y != 0.0f) {
            longitude = std::acos(long_cos);
            if (unit_p.y < 0.0f)
                longitude = (2.0f * M_PI) - longitude;
        }

        // UV
        const float pi2 = M_PI * 2;
        intersection->geo.u = longitude / pi2;
        intersection->geo.v = latitude / M_PI;

        // Differential position
        intersection->geo.dpdu = Vec3(p.y * -1.0f, p.x, 0.0f) * pi2;
        intersection->geo.dpdv = Vec3(p.z * long_cos, p.z * long_sin, -radi * lat_sin) * M_PI;

        // Differential normal
        // Calculate second derivatives
        const Vec3 d2pduu = Vec3(p.x, p.y, 0.0f) * (-pi2 * pi2);
        const Vec3 d2pduv = Vec3(-long_sin, long_cos, 0.0f) * M_PI * p.z * pi2;
        const Vec3 d2pdvv = Vec3(p.x, p.y, p.z) * (-M_PI * M_PI);
        // Calculate surface normal derivatives
        const float E = dot(intersection->geo.dpdu, intersection->geo.dpdu);
        const float F = dot(intersection->geo.dpdu, intersection->geo.dpdv);
        const float G = dot(intersection->geo.dpdv, intersection->geo.dpdv);
        const float e = dot(intersection->geo.n, d2pduu);
        const float f = dot(intersection->geo.n, d2pduv);
        const float g = dot(intersection->geo.n, d2pdvv);
        const float invEGF2 = 1.0f / ((E*G) - (F*F));
        intersection->geo.dndu = (((f*F) - (e*G)) * invEGF2 * intersection->geo.dpdu) + (((e*F) - (f*E)) * invEGF2 * intersection->geo.dpdv);
        intersection->geo.dndv = (((g*F) - (f*G)) * invEGF2 * intersection->geo.dpdu) + (((f*F) - (g*E)) * invEGF2 * intersection->geo.dpdv);

        intersection->offset = intersection->geo.n * 0.000001f;
    }

    return true;
}
예제 #9
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);
	}
}