aboutsummaryrefslogtreecommitdiff
path: root/src/camera/film.rs
blob: 2ff72397e1eb54d12b4e7b00726c5a107f248365 (plain)
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
use crate::core::*;
use crate::Float;
use image::{RgbImage, Rgb};

/// Contains the necesary values when doing calculations
///
/// This is not the final RGB value
#[derive(Clone)]
pub struct Pixel {
    /// The sum of the collected samples
    rgb: Spectrum,
    /// The amount of samples collected
    samples: u32,
}

pub struct Film {
    size: Vector2i,
    pub frame: Bound2i,

    pixels: Vec<Pixel>,
}

/// FilmTile is a small version of the Film used when rendering
///
/// This means that multiple threads can work on the same area and commit their changed when they
/// are done.
pub struct FilmTile {
    pub bounds: Bound2i,
    pub size: Vector2i,

    pixels: Vec<Pixel>,
}

impl Pixel {
    fn new() -> Pixel {
        Pixel {
            rgb: Default::default(),
            samples: 0,
        }
    }

    fn add(&mut self, c: &Spectrum, weight: Float) {
        self.rgb += &(*c * weight);
        self.samples += 1;
    }

    fn finalize_rgb(&self) -> [u8; 3] {
        let spc = (self.rgb / (self.samples as Float)).gamma_correct();
        let (r, g, b) = spc.to_rgb(255.0);
        [
            r as u8,
            g as u8,
            b as u8,
        ]
    }
}

impl std::ops::AddAssign<&Self> for Pixel {
    fn add_assign(&mut self, op: &Self) {
        self.rgb += &op.rgb;
        self.samples += op.samples;
    }
}

impl Film {
    pub fn new(size: Vector2i) -> Film {
        let area = size.x * size.y;
        Film {
            size,
            frame: Bound2i::new(&Vector2i::new(0), &size),
            pixels: vec![Pixel::new(); area as usize],
        }
    }

    /// Creates a new FilmTile from the specified bounds
    ///
    /// This tile can later be commited with the commit_tile function
    pub fn get_tile(&self, bound: &Bound2i) -> FilmTile {
        FilmTile::new(
            bound,
        )

    }

    /// Commit a tile back on the film
    ///
    /// This will lock the Film while the changes from the Tile is written
    pub fn commit_tile(&mut self, tile: &FilmTile) {
        let offset = tile.bounds.min;

        for y in 0 .. tile.size.y {
            let rowindex = (offset.y + y) * self.size.x;
            let prowindex = y * tile.size.x;

            for x in 0 .. tile.size.x {
                let index = offset.x + x + rowindex;
                let pindex: i32 = x + prowindex;

                self.pixels[index as usize] += &tile.pixels[pindex as usize];
            }
        }
    }

    pub fn finalize_image(&self) -> RgbImage {
        let mut img = RgbImage::new(self.size.x as u32, self.size.y as u32);

        for y in 0..self.size.y {
            let index = y * self.size.x;
            for x in 0..self.size.x {
                img.put_pixel(
                    x as u32, 
                    y as u32, 
                    Rgb(self.pixels[(index + x) as usize].finalize_rgb()),
                    );
            }
        }

        img
    }
}

impl FilmTile {
    fn new(bounds: &Bound2i) -> FilmTile {
        FilmTile {
            bounds: bounds.clone(),
            pixels: vec![Pixel::new(); bounds.area() as usize],
            size: bounds.diagonal(),
        }
    }

    /// Add a single sample sampled from the scene
    pub fn add_sample(&mut self, inp: &Vector2f, c: Spectrum) {
        let point = Vector2i::from(inp.floor());
        // Subtract the offset
        let point = point - self.bounds.min;

        let index = point.x + point.y * self.size.x;

        let pixel = self.pixels.get_mut(index as usize).unwrap();
        pixel.add(&c, 1.0);
    }
}