summaryrefslogtreecommitdiff
path: root/src/camera.rs
blob: 87ba7c32b023ed704ecdb20a9b2c0a796008488a (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
use crate::calculus::calculus::{sample_square, Point3, Ray, Vec3};
use crate::global::{get_image_height, Color, DisplayBuffer, Pixel, ASPECT_RATIO, CAMERA_CENTER, FOCAL_LENGTH, IMG_WIDTH, SAMPLES_PER_PIXEL, VIEWPORT_HEIGHT};
use crate::interval::Interval;
use crate::object::{HitRecord, Hittable, HittableList};

pub struct Camera {
    pub aspect_ratio: f32,
    pub image_width: usize,
    pub samples_per_pixel: usize,
    image_height: usize,
    center: Point3,
    pixel_upper_left: Point3,
    delta_pixel_u: Vec3,
    delta_pixel_v: Vec3,
    pixel_samples_scale: f32,
    max_depth: usize,
}

impl Camera {
    pub fn new() -> Self {
        let aspect_ratio = ASPECT_RATIO;

        let image_width = IMG_WIDTH;
        let image_height = get_image_height(image_width, aspect_ratio);

        let center = CAMERA_CENTER;
        let focal_length = FOCAL_LENGTH;

        let samples_per_pixel = SAMPLES_PER_PIXEL;
        let pixel_samples_scale = 1.0 / samples_per_pixel as f32;

        let viewport_height = VIEWPORT_HEIGHT;
        let viewport_width = viewport_height * (image_width as f32 / image_height as f32);

        let viewport_hor_vector = Vec3{ x: viewport_width, y: 0.0, z: 0.0 };
        let viewport_ver_vector = Vec3 { x: 0.0, y: -1f32 * viewport_height, z: 0.0 };

        let delta_pixel_u = viewport_hor_vector.scalar_mul(1.0 / image_width as f32);
        let delta_pixel_v = viewport_ver_vector.scalar_mul(1.0 / image_height as f32);

        let viewport_upper_left = center
            .sub(&Vec3 { x: 0f32, y: 0f32, z: focal_length})
            .sub(&viewport_hor_vector.scalar_mul(0.5))
            .sub(&viewport_ver_vector.scalar_mul(0.5));
        let pixel_upper_left = viewport_upper_left.add(
            &delta_pixel_u.add(&delta_pixel_u).scalar_mul(0.5)
        );

        let max_depth = 50;

        Self {
            aspect_ratio,
            image_width,
            image_height,
            center,
            pixel_upper_left,
            delta_pixel_u,
            delta_pixel_v,
            samples_per_pixel,
            pixel_samples_scale,
            max_depth
        }
    }

    fn get_ray(&self, i: usize, j: usize) -> Ray {
        let offset = sample_square();
        let pixel_sample = self.pixel_upper_left
            .add(&self.delta_pixel_u.scalar_mul(i as f32 + offset.x))
            .add(&self.delta_pixel_v.scalar_mul(j as f32 + offset.y));

        let ray_origin = self.center;
        let ray_direction = pixel_sample.sub(&ray_origin);

        Ray {
            origin: ray_origin,
            direction: ray_direction,
        }
    }

    fn ray_color(&self, ray: &Ray, world: &HittableList, depth: usize) -> Color {
        if depth <= 0 {
            return Color::new(0.0, 0.0, 0.0);
        }

        let mut rec = HitRecord::default();
        let ray_t = Interval::new(0.001, f32::INFINITY);
        if world.hit(ray, ray_t, &mut rec) {
            // let direction = Vec3::random_on_hemisphere(&rec.normal);
            let direction = rec.normal.add(&Vec3::random_unit());
            let origin = rec.position.clone();
            let r = Ray { origin, direction };
            return self.ray_color(&r, world, depth - 1).mul_scalar(0.9);
        }

        let unit_direction = ray.direction.unit();
        let a = 0.5 * (unit_direction.y + 1.0);
        let color1 = Color::new(1.0, 1.0, 1.0).mul_scalar(1.0 - a);
        let color2 = Color::new(0.5, 0.7, 1.0).mul_scalar(a);
        color1.add(&color2)
    }

    pub fn render(&self, display_buffer: &mut DisplayBuffer, world: &HittableList) {
        (0..self.image_height).for_each(|j| {
            (0..self.image_width).for_each(|i| {
                println!("Processing line {} col {}", j, i);
                let mut pixel_color = Color::new(0.0, 0.0, 0.0);
                (0..self.samples_per_pixel).for_each(|_| {
                    let r = self.get_ray(i, j);
                    let ray_color = self.ray_color(&r, world, self.max_depth);
                    pixel_color = pixel_color.add(&ray_color);
                });
                pixel_color = pixel_color.mul_scalar(self.pixel_samples_scale);
                display_buffer[j][i] = Pixel::from_color(&pixel_color);
            })
        })
    }
}