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