• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ekarpp / lumo / 6054336964

01 Sep 2023 10:00PM UTC coverage: 47.535% (-0.9%) from 48.466%
6054336964

push

github

web-flow
0.3.2 (#33)

- Rewrites sample gathering to use `FilmTiles`
- Fixes splat samples in BDPT
- Some BDPT medium bug fixes (Progresses #31)

Closes #32

180 of 180 new or added lines in 17 files covered. (100.0%)

2179 of 4584 relevant lines covered (47.53%)

6887198.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/renderer.rs
1
use crate::{
2
    Vec2, Float, TracerCli,
3
    samplers::JitteredSampler, ToneMap
4
};
5
use crate::tracer::{
6
    Camera, Film, FilmSample,
7
    Integrator, Scene, Filter, FilmTile
8
};
9
use glam::IVec2;
10
use rayon::iter::{IntoParallelIterator, ParallelIterator};
11
use std::{sync::Mutex, time::Instant};
12

13
type PxSampler = JitteredSampler;
14

15
const TILE_SIZE: i32 = 16;
16
const SAMPLES_INCREMENT: i32 = 256;
17

18
/// Configures the image to be rendered
19
pub struct Renderer {
20
    scene: Scene,
21
    camera: Camera,
22
    resolution: IVec2,
23
    num_samples: i32,
24
    integrator: Integrator,
25
    tone_map: ToneMap,
26
    filter: Filter,
27
}
28

29
impl Renderer {
30
    /// Constructs a new renderer. Defaults to 1000x1000 image with 1 sample
31
    /// per pixel and path tracing as the integrator. Configured through the CLI
32
    /// or the setter functions of the struct.
33
    pub fn new(scene: Scene, camera: Camera) -> Self {
×
34
        assert!(scene.num_lights() != 0);
×
35

36
        let cli_args: TracerCli = argh::from_env();
×
37
        cli_args.set_threads();
×
38

×
39
        let resolution = camera.get_resolution();
×
40

×
41
        Self {
×
42
            scene,
×
43
            camera,
×
44
            resolution,
×
45
            filter: Filter::Box,
×
46
            num_samples: cli_args.samples,
×
47
            integrator: cli_args.get_integrator(),
×
48
            tone_map: ToneMap::NoMap,
×
49
        }
×
50
    }
×
51

52
    /// Sets the tone mapping algorithm used
53
    pub fn set_tone_map(&mut self, tone_map: ToneMap) {
×
54
        self.tone_map = tone_map;
×
55
    }
×
56

57
    /// Sets the pixel filter
58
    pub fn set_filter(&mut self, filter: Filter) {
×
59
        self.filter = filter;
×
60
    }
×
61

62
    /// Sets number of samples per pixel
63
    pub fn set_samples(&mut self, samples: i32) {
×
64
        self.num_samples = samples;
×
65
    }
×
66

67
    /// Sets the integrator used to render the image
68
    pub fn set_integrator(&mut self, integrator: Integrator) {
×
69
        self.integrator = integrator;
×
70
    }
×
71

72
    /// Starts the rendering process and returns the rendered image
73
    pub fn render(&self) -> Film {
×
74
        println!(
×
75
            "Rendering scene as a {} x {} image \
×
76
                  with {} thread(s) and {} sample(s) per pixel using {}",
×
77
            self.resolution.x,
×
78
            self.resolution.y,
×
79
            rayon::current_num_threads(),
×
80
            self.num_samples,
×
81
            self.integrator,
×
82
        );
×
83

×
84
        let start = Instant::now();
×
85
        let mut film = Film::new(
×
86
            self.resolution.x,
×
87
            self.resolution.y,
×
88
            self.num_samples,
×
89
        );
×
90

×
91
        let mutex = Mutex::new(&mut film);
×
92

×
93
        let tiles_x = (self.resolution.x + TILE_SIZE - 1) / TILE_SIZE;
×
94
        let tiles_y = (self.resolution.y + TILE_SIZE - 1) / TILE_SIZE;
×
95
        let mut samples_taken = 0;
×
96
        while samples_taken < self.num_samples {
×
97
            let prev = samples_taken;
×
98
            samples_taken += SAMPLES_INCREMENT;
×
99
            samples_taken = samples_taken.min(self.num_samples);
×
100
            let samples = samples_taken - prev;
×
101

×
102
            (0..tiles_y).into_par_iter()
×
103
                .for_each(|y: i32| {
×
104
                    (0..tiles_x).for_each(|x: i32| {
×
105
                        let px_min = IVec2::new(x, y) * TILE_SIZE;
×
106
                        let px_max = px_min + TILE_SIZE;
×
107
                        let mut tile = self.get_tile(px_min, px_max);
×
108

109
                        for y in tile.px_min.y..tile.px_max.y {
×
110
                            for x in tile.px_min.x..tile.px_max.x {
×
111
                                self.get_samples(&mut tile, samples, x, y)
×
112
                            }
113
                        }
114

115
                        mutex.lock().unwrap().add_tile(tile);
×
116
                    })
×
117
                });
×
118
        }
×
119
        println!("Finished rendering in {:#?}", start.elapsed());
×
120
        film
×
121
    }
×
122

123
    fn get_tile(&self, px_min: IVec2, px_max: IVec2) -> FilmTile {
×
124
        FilmTile::new(px_min, px_max.min(self.resolution), self.filter)
×
125
    }
×
126

127
    /// Sends `num_samples` rays towards the given pixel and averages the result
128
    fn get_samples(&self, tile: &mut FilmTile, num_samples: i32, x: i32, y: i32) {
×
129
        let xy = Vec2::new(x as Float, y as Float);
×
130
        PxSampler::new(num_samples)
×
131
            .flat_map(|rand_sq: Vec2| {
×
132
                let raster_xy = xy + rand_sq;
×
133
                self.integrator.integrate(
×
134
                    &self.scene,
×
135
                    &self.camera,
×
136
                    raster_xy,
×
137
                    self.camera.generate_ray(raster_xy),
×
138
                )
×
139
            })
×
140
            .for_each(|mut sample: FilmSample| {
×
141
                sample.color = self.tone_map.map(sample.color);
×
142
                tile.add_sample(sample)
×
143
            })
×
144
    }
×
145
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc