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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use crate::{color::Color, ray::cast_ray, scene_object::SceneObject, vec3::Vec3};
#[derive(Debug)]
pub struct RayMarcherConfig<C> {
pub width: usize,
pub height: usize,
pub camera_pos: Vec3,
pub look_at: Vec3,
pub light_pos: Vec3,
pub background_color: C,
pub camera_zoom: f64,
pub anti_aliasing_level: u32,
pub backplane_positions: Vec3,
pub specular_shininess: f64,
pub specular_color: C,
}
impl<C: Color> Default for RayMarcherConfig<C> {
fn default() -> Self {
RayMarcherConfig {
width: 10,
height: 10,
camera_pos: Vec3 {
x: 2.0,
y: 4.0,
z: 4.0,
},
look_at: Vec3 {
x: 0.0,
y: 0.0,
z: 0.0,
},
light_pos: Vec3 {
x: 2.0,
y: 4.0,
z: 4.0,
},
background_color: C::default(),
camera_zoom: 3.0,
anti_aliasing_level: 4u32,
backplane_positions: Vec3 {
x: 3.0,
y: 3.0,
z: 3.0,
},
specular_shininess: 50.0,
specular_color: C::white(),
}
}
}
pub struct RayMarcher<C, O: SceneObject<C>> {
pub object: O,
pub config: RayMarcherConfig<C>,
}
impl<C: Color, O: SceneObject<C>> RayMarcher<C, O> {
pub fn get_pixel_color(&self, x: usize, y: usize, t: f64) -> C {
let aa_level = self.config.anti_aliasing_level;
let subpixel_size = 1.0 / aa_level as f64;
let mut pixel_sum = C::default();
for subpixel_x in 0..aa_level {
for subpixel_y in 0..aa_level {
let ray_dir = self.camera_ray_direction(
x as f64 + subpixel_x as f64 * subpixel_size,
y as f64 + subpixel_y as f64 * subpixel_size,
);
pixel_sum =
pixel_sum + self.trace_with_lighting(self.config.camera_pos, ray_dir, t);
}
}
pixel_sum * (1.0 / (aa_level * aa_level) as f64)
}
fn trace_with_lighting(&self, point: Vec3, dir: Vec3, t: f64) -> C {
let res = cast_ray(&self.object, point, dir, self.config.backplane_positions, t);
let normal_backoff_dist = 1E-7;
match res {
Some(res) => {
let len = (res.hit_point - self.config.camera_pos).magnitude();
let len = (len / 2.0).powi(2);
let light_vec = (self.config.light_pos - res.hit_point).normalized();
let norm_point = res.hit_point - normal_backoff_dist * dir;
let norm = self.object.normal(norm_point, t);
let s_dot_n = norm.dot(light_vec);
let reflect_vec = (-light_vec).reflect(norm).normalized();
let view_vec = (self.config.camera_pos - res.hit_point).normalized();
let r_dot_v = reflect_vec.dot(view_vec.normalized());
let specular_term = r_dot_v.powf(self.config.specular_shininess);
let specular_term = if r_dot_v > 0.0 { specular_term } else { 0.0 };
self.object.get_color(t) * s_dot_n + self.config.specular_color * specular_term
}
None => self.config.background_color,
}
}
fn camera_ray_direction(&self, x: f64, y: f64) -> Vec3 {
let u = -(x as f64 / self.config.width as f64 * 2.0 - 1.0);
let v = y as f64 / self.config.height as f64 * 2.0 - 1.0;
let look_dir = (self.config.look_at - self.config.camera_pos).normalized();
let right_vec = Vec3::from((0, -1, 0)).cross(look_dir).normalized();
let down_vec = look_dir.cross(right_vec).normalized();
let zoomed_cam_pos = self.config.camera_pos + self.config.camera_zoom * look_dir;
let pix_pos = zoomed_cam_pos + u * right_vec + v * down_vec;
let dir = pix_pos - self.config.camera_pos;
dir.normalized()
}
}