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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
use std::f64::consts;
use std::sync::Arc;
use crate::ray::Ray;
use crate::vec3::Point3;
use crate::{
hittable::{HitRecord, Hittable, AABB},
material::Material,
vec3::Vec3,
};
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,
},
})
}
}
|