Пример #1
0
	bool Sphere::Intersect(const Ray& p_ray, DifferentialSurface& p_record) const
	{
		Vector3 temp = p_ray.origin - centre;

		double a = Vector3::Dot(p_ray.direction, p_ray.direction);
		double b = 2 * Vector3::Dot(p_ray.direction, temp);
		double c = Vector3::Dot(temp, temp) - radius * radius;

		double discriminant = b * b - 4.0f * a * c;

		if (discriminant < 0.0f) return false;

		discriminant = sqrt(discriminant);
		float q;
		if (b < 0)	q = -0.5f * (b - discriminant);		
		else q = -0.5f * (b + discriminant);

		float t0 = q / a;
		float t1 = c / q;

		if (t0 > t1)
		{
			std::swap(t0, t1);
		}

		/* Check for valid interval */
		if (t0 > p_ray.tmax || t1 < p_ray.tmin)	return false;
		float thit = t0;
		if (t0 < p_ray.tmin)
		{
			thit = t1;
			if (thit > p_ray.tmax)	return false;
		}


		auto phit = p_ray.PointAlongRay(thit);
		if (phit.x == 0.0f && phit.y == 0.0f)
		{
			phit.x = 1e-5f * radius;
		}
		auto phi = atan2f(phit.y, phit.x);
		if (phi < 0.0f)
		{
			phi += Maths::PiTwo;
		}

		/* Test sphere intersection against clipping parameters */
		if ((z_min > -radius && phit.z < z_min) ||
			(z_max <  radius && phit.z > z_max) || phi > phi_max) {
			if (thit == t1) return false;
			if (t1 > p_ray.tmax)
			{
				return false;
			}
			thit = t1;
			// Compute sphere hit position and $\phi$
			phit = p_ray.PointAlongRay(thit);
			if (phit.x == 0.f && phit.y == 0.f) phit.x = 1e-5f * radius;
			phi = atan2f(phit.y, phit.x);
			if (phi < 0.0f) phi += Maths::PiTwo;
			if ((z_min > -radius && phit.z < z_min) ||
				(z_max <  radius && phit.z > z_max) || phi > phi_max)
				return false;
		}

		/* Find parametric representation of sphere hit */
		float u = phi / phi_max;
		float theta = acosf(Maths::Clamp(phit.z / radius, -1.0f, -1.0f));
		float v = (theta - theta_min) / (theta_max - theta_min);
		/* Compute sphere dp/du and dp/dv */
		float z_radius = sqrtf(phit.x * phit.x + phit.y * phit.y);
		float inv_z_radius = 1.0f / z_radius;
		float cos_phi = phit.x * inv_z_radius;
		float sin_phi = phit.y * inv_z_radius;
		Vector3 dpdu{ -phi_max * phit.y, phi_max * phit.x, 0 };
		Vector3 dpdv = (theta_max - theta_min) * Vector3 { phit.z * cos_phi, phit.z * sin_phi, -radius * sinf(theta) };
		/* Compute sphere dn/du and dn/dv */
		Vector3 d2Pduu = -phi_max * phi_max * Vector3{ phit.x, phit.y, 0.0f };
		Vector3 d2Pduv = (theta_max - theta_min) * phit.z * phi_max * Vector3{ -sin_phi, cos_phi, 0.0f };
		Vector3 d2Pdvv = -(theta_max - theta_min) * (theta_max - theta_min) * Vector3(phit.x, phit.y, phit.z);
		/* Compute coefficients for fundamental form */
		float E = Vector3::Dot(dpdu, dpdu);
		float F = Vector3::Dot(dpdu, dpdv);
		float G = Vector3::Dot(dpdv, dpdv);
		Vector3 nn = (Vector3::Cross(dpdu, dpdv)).Normalize();
		float e = Vector3::Dot(nn, d2Pduu);
		float f = Vector3::Dot(nn, d2Pduv);
		float g = Vector3::Dot(nn, d2Pdvv);
		/* Compute dn/du and dn/dv from fundamental form coefficients */
		float invEGF2 = 1.0f / (E * G - F * F);
		Vector3 dndu = (Vector3{ (f * F - e * G) * invEGF2 * dpdu +
			(e * F - f * E) * invEGF2 * dpdv }).Normalize();
		Vector3 dndv = (Vector3{ (g * F - f * G) * invEGF2 * dpdu +
			(f * F - g * E) * invEGF2 * dpdv }).Normalize();


		/* We have a valid hit */
		/* Populate the differential surface object */
		p_record.shape = this;
		p_record.t = thit;
		p_record.point = phit;
		p_record.normal = (p_record.point - centre).Normalize();
		p_record.ray_epsilon = 5e-4f * thit;
		p_record.dpdu = dpdu;
		p_record.dndu = dndu;
		p_record.dpdv = dpdv;
		p_record.dndv = dndv;

		return true;
	}
Пример #2
0
	bool Sphere::IntersectP(const Ray& p_ray) const
	{
		Vector3 temp = p_ray.origin - centre;

		double a = Vector3::Dot(p_ray.direction, p_ray.direction);
		double b = 2 * Vector3::Dot(p_ray.direction, temp);
		double c = Vector3::Dot(temp, temp) - radius * radius;

		double discriminant = b * b - 4 * a * c;

		if (discriminant < 0.0f) return false;
		
			discriminant = sqrt(discriminant);
			float q;
			if (b < 0)
			{
				q = -0.5f * (b - discriminant);
			}
			else
			{
				q = -0.5f * (b + discriminant);
			}
			float t0 = q / a;
			float t1 = c / q;

			if (t0 > t1)
			{
				std::swap(t0, t1);
			}

			/* Check for valid interval */
			if (t0 > p_ray.tmax || t1 < p_ray.tmin)
			{
				return false;
			}
			float thit = t0;
			if (t0 < p_ray.tmin)
			{
				thit = t1;
				if (thit > p_ray.tmax)
				{
					return false;
				}
			}


			auto phit = p_ray.PointAlongRay(thit);
			if (phit.x == 0.0f && phit.y == 0.0f)
			{
				phit.x = 1e-5f * radius;
			}
			auto phi = atan2f(phit.y, phit.x);
			if (phi < 0.0f)
			{
				phi += Maths::PiTwo;
			}
			/* Test sphere intersection against clipping parameters */
			if ((z_min > -radius && phit.z < z_min) ||
				(z_max <  radius && phit.z > z_max) || phi > phi_max) {
				if (thit == t1) return false;
				if (t1 > p_ray.tmax)
				{
					return false;
				}
				thit = t1;
				// Compute sphere hit position and $\phi$
				phit = p_ray.PointAlongRay(thit);
				if (phit.x == 0.f && phit.y == 0.f) phit.x = 1e-5f * radius;
				phi = atan2f(phit.y, phit.x);
				if (phi < 0.) phi += Maths::PiTwo;
				if ((z_min > -radius && phit.z < z_min) ||
					(z_max <  radius && phit.z > z_max) || phi > phi_max)
					return false;
			}		
		return true;
	}