|
|
|
@ -21,12 +21,18 @@ public final class Sphere implements Hittable, Target {
|
|
|
|
|
|
|
|
|
|
private final @NotNull AABB bbox;
|
|
|
|
|
|
|
|
|
|
private final @NotNull Vec3 normalizedCenter;
|
|
|
|
|
private final double invRadius;
|
|
|
|
|
|
|
|
|
|
public Sphere(@NotNull Vec3 center, double radius, @NotNull Material material) {
|
|
|
|
|
this.center = Objects.requireNonNull(center, "center");
|
|
|
|
|
this.material = Objects.requireNonNull(material, "material");
|
|
|
|
|
if (radius <= 0 || !Double.isFinite(radius)) throw new IllegalArgumentException("radius must be positive");
|
|
|
|
|
this.radius = radius;
|
|
|
|
|
|
|
|
|
|
this.invRadius = 1 / radius;
|
|
|
|
|
this.normalizedCenter = this.center.times(-this.invRadius);
|
|
|
|
|
|
|
|
|
|
this.bbox = new AABB(
|
|
|
|
|
center.minus(radius, radius, radius),
|
|
|
|
|
center.plus(radius, radius, radius)
|
|
|
|
@ -35,23 +41,11 @@ public final class Sphere implements Hittable, Target {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public @NotNull Optional<HitResult> hit(@NotNull Ray ray, @NotNull Range range) {
|
|
|
|
|
var oc = ray.origin().minus(center);
|
|
|
|
|
|
|
|
|
|
var a = ray.direction().squared();
|
|
|
|
|
var h = ray.direction().times(oc);
|
|
|
|
|
var c = oc.squared() - radius * radius;
|
|
|
|
|
|
|
|
|
|
var discriminant = h * h - a * c;
|
|
|
|
|
if (discriminant < 0) return Optional.empty();
|
|
|
|
|
|
|
|
|
|
var sd = Math.sqrt(discriminant);
|
|
|
|
|
|
|
|
|
|
double t = (- h - sd) / a;
|
|
|
|
|
if (!range.surrounds(t)) t = (- h + sd) / a;
|
|
|
|
|
if (!range.surrounds(t)) return Optional.empty();
|
|
|
|
|
var t = hit0(ray, range);
|
|
|
|
|
if (Double.isNaN(t)) return Optional.empty();
|
|
|
|
|
|
|
|
|
|
var position = ray.at(t);
|
|
|
|
|
var normal = position.minus(center).div(radius);
|
|
|
|
|
var normal = Vec3.fma(invRadius, position, normalizedCenter);
|
|
|
|
|
var frontFace = normal.times(ray.direction()) < 0;
|
|
|
|
|
|
|
|
|
|
double u;
|
|
|
|
@ -72,6 +66,25 @@ public final class Sphere implements Hittable, Target {
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double hit0(@NotNull Ray ray, @NotNull Range range) {
|
|
|
|
|
var oc = ray.origin().minus(center);
|
|
|
|
|
|
|
|
|
|
var a = ray.direction().squared();
|
|
|
|
|
var h = ray.direction().times(oc);
|
|
|
|
|
var c = oc.squared() - radius * radius;
|
|
|
|
|
|
|
|
|
|
var discriminant = h * h - a * c;
|
|
|
|
|
if (discriminant < 0) return Double.NaN;
|
|
|
|
|
|
|
|
|
|
var sd = Math.sqrt(discriminant);
|
|
|
|
|
|
|
|
|
|
double t = (- h - sd) / a;
|
|
|
|
|
if (!range.surrounds(t)) t = (- h + sd) / a;
|
|
|
|
|
if (!range.surrounds(t)) return Double.NaN;
|
|
|
|
|
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public @NotNull AABB getBoundingBox() {
|
|
|
|
|
return bbox;
|
|
|
|
@ -79,7 +92,7 @@ public final class Sphere implements Hittable, Target {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public double getProbabilityDensity(@NotNull Vec3 origin, @NotNull Vec3 direction) {
|
|
|
|
|
if (hit(new Ray(origin, direction)).isEmpty()) return 0;
|
|
|
|
|
if (Double.isNaN(hit0(new Ray(origin, direction), FORWARD))) return 0;
|
|
|
|
|
|
|
|
|
|
var cos = Math.sqrt(1 - radius * radius / (center.minus(origin).squared()));
|
|
|
|
|
var solidAngle = 2 * Math.PI * (1 - cos);
|
|
|
|
|