1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
use std::sync::Arc;
use std::f64::consts;
use crate::{hittable::{HitRecord, Hittable, AABB}, material::Material, vec3::Vec3};
use crate::ray::Ray;
use crate::vec3::Point3;
pub struct Sphere {
pub center: Point3,
pub radius: f64,
pub material: Arc<dyn Material>,
}
impl Sphere {
pub fn get_sphere_uv(p: &Point3, u: &mut f64, v: &mut f64) {
let theta = (-p.y).acos();
let phi = -p.z.atan2(p.x) + consts::PI;
*u = phi / (2.0 * consts::PI);
*v = theta / consts::PI;
}
}
impl Hittable for Sphere {
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
let oc = &ray.origin - &self.center;
let a = ray.direction.length_squared();
let half_b = oc.dot(&ray.direction);
let c = oc.length_squared() - self.radius * self.radius;
let discriminant = half_b * half_b - a * c;
if discriminant < 0.0 {
return None;
}
let sqrtd = discriminant.sqrt();
// Find the nearest root that lies within acceptable range
let mut root = (-half_b - sqrtd) / a;
if root < t_min || t_max < root {
root = (-half_b + sqrtd) / a;
if root < t_min || t_max < root {
return None;
}
}
let mut hit_record = HitRecord::new();
hit_record.t = root;
hit_record.p = ray.at(hit_record.t);
let outward_normal = (&hit_record.p - &self.center) / self.radius;
hit_record.set_face_normal(ray, &outward_normal);
Self::get_sphere_uv(&outward_normal, &mut hit_record.u, &mut hit_record.v);
hit_record.material = Some(self.material.clone());
Some(hit_record)
}
fn bounding_box(&self, _: f64, _: f64) -> Option<AABB> {
Some(AABB {
minimum: &self.center - Vec3 { x: self.radius, y: self.radius, z: self.radius },
maximum: &self.center + Vec3 { x: self.radius, y: self.radius, z: self.radius },
})
}
}
|