-
Notifications
You must be signed in to change notification settings - Fork 0
/
materials.rs
104 lines (89 loc) · 2.76 KB
/
materials.rs
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use super::hittable::*;
use super::rng::*;
use super::types::*;
pub trait Material {
fn scatter(&self, r: &Ray, hit: &Hit, rng: &mut RttRng) -> Option<ScatteredRay>;
}
// Lambertian
pub struct Lambertian {
pub albedo: Vec4f,
}
fn near_zero(v: Vec4f) -> bool {
v.x.abs() < 1e-8 && v.y.abs() < 1e-8 && v.z.abs() < 1e-8 && v.w.abs() < 1e-8
}
impl Material for Lambertian {
fn scatter(&self, r: &Ray, hit: &Hit, rng: &mut RttRng) -> Option<ScatteredRay> {
let direction = hit.normal + Vec4f::gen_uniform_random_unit(rng);
Some(ScatteredRay {
r: Ray {
origin: hit.p,
direction: if near_zero(direction) {
hit.normal
} else {
direction
},
time: r.time,
},
attenuation: self.albedo,
})
}
}
// Metal
pub struct Metal {
pub albedo: Vec4f,
pub fuzz: f32,
}
impl Material for Metal {
fn scatter(&self, r: &Ray, hit: &Hit, rng: &mut RttRng) -> Option<ScatteredRay> {
let direction = r.direction.normalized().reflected(hit.normal)
+ self.fuzz * Vec4f::gen_uniform_random_in_unit_sphere(rng);
if direction.dot(hit.normal) > 0. {
Some(ScatteredRay {
r: Ray {
origin: hit.p,
direction,
time: r.time,
},
attenuation: self.albedo,
})
} else {
None
}
}
}
// Dielectric
fn reflectance(cosine: f32, ref_idx: f32) -> f32 {
// Use Schlick's approximation for reflectance.
let r0 = ((1. - ref_idx) / (1. + ref_idx)).powf(2.);
r0 + (1. - r0) * (1. - cosine).powf(5.)
}
pub struct Dielectric {
pub ref_idx: f32,
}
impl Material for Dielectric {
fn scatter(&self, r: &Ray, hit: &Hit, rng: &mut RttRng) -> Option<ScatteredRay> {
let refraction_ratio = if hit.front_face {
1. / self.ref_idx
} else {
self.ref_idx
};
let unit_direction = r.direction.normalized();
let cos_theta = (-unit_direction).dot(hit.normal).min(1.);
let sin_theta = (1. - (cos_theta * cos_theta)).sqrt();
let cannot_refract = refraction_ratio * sin_theta > 1.;
let direction =
if cannot_refract || reflectance(cos_theta, refraction_ratio) > rng.gen::<f32>() {
unit_direction.reflected(hit.normal)
} else {
unit_direction.refracted(hit.normal, refraction_ratio)
};
Some(ScatteredRay {
r: Ray {
origin: hit.p,
direction,
time: r.time,
},
attenuation: Vec4f::new(1., 1., 1., 0.),
})
}
}