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
use rand::{rngs::ThreadRng, seq::SliceRandom, thread_rng};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use std::{
fs,
path::Path,
sync::{Arc, Mutex},
};
use crate::{
render::{Input, Output, Parameters},
rt::{Camera, Ray},
util::ProgressBar,
};
#[inline]
pub fn run<
T: Fn(&Input<'_>, &Camera, Ray, f64, [usize; 2], &mut Output, &mut ThreadRng) + Send + Sync + Copy,
>(
parameters: &Parameters,
output_dir: &Path,
sample: T,
) {
let settings = parameters.build_settings();
let meshes = parameters.load_meshes();
let gradients = parameters.load_gradients();
let attributes = parameters.load_attributes(&gradients);
let surfaces = parameters.load_surfaces(&meshes, &attributes);
let tree = parameters.build_tree(&surfaces);
let shader = parameters.build_shader(&gradients);
let camera = parameters.build_camera();
let runtime = Input::new(settings, shader, tree);
let tiles_output_dir = output_dir.join("tiles");
if tiles_output_dir.exists() {
fs::remove_dir_all(&tiles_output_dir).expect("Failed to initialise output directory.");
}
fs::create_dir_all(&tiles_output_dir).expect("Failed to create output directory.");
render(&tiles_output_dir, &runtime, &camera, sample);
}
#[allow(clippy::integer_division)]
#[inline]
fn render<
T: Fn(&Input<'_>, &Camera, Ray, f64, [usize; 2], &mut Output, &mut ThreadRng)
+ Send
+ Sync
+ Clone,
>(
output_dir: &Path,
input: &Input,
camera: &Camera,
sample: T,
) {
let tiles = input.settings.tiles;
let tile_res = [camera.res[0] / tiles[0], camera.res[1] / tiles[1]];
let mut tile_order = Vec::with_capacity(tiles[0] * tiles[1]);
for iy in 0..tiles[1] {
for ix in 0..tiles[0] {
tile_order.push((ix, iy));
}
}
tile_order.shuffle(&mut thread_rng());
let pb = Arc::new(Mutex::new(ProgressBar::new(
"Rendering image",
tiles[0] * tiles[1],
)));
let print_width = ((tiles[0].max(tiles[1])) as f64).log10() as usize + 1;
tile_order.par_iter().for_each(|&(ix, iy)| {
let offset = [tile_res[0] * ix, tile_res[1] * iy];
let data = render_tile(input, camera, offset, tile_res, sample.clone());
data.save(
&input.shader,
output_dir,
&format!(
"{:0>width$}_{:0>width$}",
ix,
tiles[1] - iy - 1,
width = print_width
),
);
pb.lock().expect("Could not lock progress bar.").tick();
});
pb.lock()
.expect("Could not lock progress bar.")
.finish_with_message("Rendering complete");
}
#[inline]
#[must_use]
fn render_tile<T: Fn(&Input<'_>, &Camera, Ray, f64, [usize; 2], &mut Output, &mut ThreadRng)>(
input: &Input,
camera: &Camera,
offset: [usize; 2],
sub_res: [usize; 2],
sample: T,
) -> Output {
let mut data = Output::new(sub_res);
let weight = 1.0 / (camera.ss_power * camera.ss_power) as f64;
let mut rng = thread_rng();
for px in 0..sub_res[0] {
for py in 0..sub_res[1] {
let rx = offset[0] + px;
let ry = offset[1] + py;
for ssx in 0..camera.ss_power {
for ssy in 0..camera.ss_power {
let ray = camera.emit([rx, ry], [ssx, ssy]);
sample(input, camera, ray, weight, [px, py], &mut data, &mut rng);
}
}
}
}
data
}