summaryrefslogtreecommitdiff
path: root/src/material.rs
blob: 360f775451b7b903d975365ae5a0510c8e8619ff (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
use rand::Rng;
use crate::calculus::calculus::{Ray, Vec3};
use crate::common::Color;
use crate::object::HitRecord;

#[derive(Clone)]
pub enum MaterialType {
    Diffuse,
    Metallic(f32), // fuzz
    Transparent(f32), // refraction index
}

#[derive(Clone)]
pub struct Material {
    pub material_type: MaterialType,
    pub albedo: Color,
}

impl Material {
    pub fn new(albedo: Color, material_type: MaterialType) -> Self {
        let mut material_type = material_type;
        if let MaterialType::Metallic(fuzz) = material_type {
            let fuzz = if fuzz <  1.0 { fuzz } else { fuzz };
            material_type = MaterialType::Metallic(fuzz);
        }
        Self { albedo, material_type }
    }

    pub fn scatter(&self, r_in: &Ray, rec: &HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool {
        scattered.direction = match self.material_type {
            MaterialType::Diffuse => {
                let  scatter_direction = rec.normal.add(&Vec3::random_unit());
                if scatter_direction.is_near_zero() {
                    rec.normal.clone()
                } else {
                    scatter_direction
                }
            }
            MaterialType::Metallic(fuzz) => {
                let reflected = r_in.reflect(&rec.normal);
                reflected.add(&Vec3::random_unit().scalar_mul(fuzz))

            }
            MaterialType::Transparent(refraction_index) => {
                let ri = if rec.front_face {
                    1.0 / refraction_index
                } else {
                    refraction_index
                };
                let cos_theta = f32::min(
                    r_in.direction.unit().scalar_mul(-1.0).dot_prod(&rec.normal),
                    1.0
                );
                let sin_theta = (1.0 - cos_theta * cos_theta).sqrt();
                let cannot_refract = ri * sin_theta > 1.0;
                let random: f32 = rand::rng().random();
                if cannot_refract || Self::reflectance(cos_theta, ri) > random {
                    r_in.direction.unit().reflect(&rec.normal)
                } else {
                    r_in.refract(&rec.normal, ri)
                }
            }
        };
        scattered.origin = rec.position;
        attenuation.r = self.albedo.r;
        attenuation.g = self.albedo.g;
        attenuation.b = self.albedo.b;
        true
    }

    fn reflectance(cosine: f32, refraction_index: f32) -> f32 {
        let mut r0 = (1.0 - refraction_index) / (1.0 + refraction_index);
        r0 = r0 * r0;
        r0 + (1.0 - r0) * (1.0 - cosine).powi(5)
    }
}