diff --git a/src/hittable.rs b/src/hittable.rs index e0215ce..56f3a91 100644 --- a/src/hittable.rs +++ b/src/hittable.rs @@ -50,7 +50,7 @@ impl HitRecord { Self::new( Point3::null(), Vec3::null(), - Arc::new(Metal::new(&Color::null())), + Arc::new(Metal::new(&Color::null(), 0.0)), 0.0, false, ) diff --git a/src/main.rs b/src/main.rs index 484e2a8..3f6ef1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ use std::env; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; use vec3::{Color, Point3, Vec3}; -use material::{Material, Lambertian, Metal, Mirror}; +use material::{Material, Lambertian, Metal, Dielectric}; fn ray_color(r: &Ray, world: &HittableList, depth: u32) -> Color { let mut rec = HitRecord::empty(); @@ -50,7 +50,7 @@ fn main() { // Image let aspect_ratio = 16.0 / 9.0; - let image_width = 1000; + let image_width = 1920; let image_height = (image_width as f64 / aspect_ratio) as u32; let samples_per_pixel = 100_u32; let max_depth = 50; @@ -60,9 +60,12 @@ fn main() { let material_ground = Arc::new(Lambertian::new(&Color::new(0.8, 0.8, 0.0))); let material_center = Arc::new(Lambertian::new(&Color::new(0.7, 0.1, 0.2))); + //let material_center = Arc::new(Dielectric::new(1.5)); let material_blue = Arc::new(Lambertian::new(&Color::new(0.2, 0.1, 0.7))); - let material_metal = Arc::new(Metal::new(&Color::new(0.8, 0.8, 0.8))); - let _material_mirror = Arc::new(Mirror::new(&Color::new(0.99, 0.99, 0.99))); + let material_metal = Arc::new(Metal::new(&Color::new(0.8, 0.8, 0.8), 0.1)); + let material_metal_fuzz = Arc::new(Metal::new(&Color::new(0.8, 0.8, 0.8), 1.0)); + let material_mirror = Arc::new(Metal::new(&Color::new(0.8, 0.8, 0.8), 0.0)); + let material_dielectric = Arc::new(Dielectric::new(1.5)); @@ -72,24 +75,24 @@ fn main() { material_ground.clone(), ))); world.add(Box::::new(Sphere::new( - Point3::new(0.0, -0.1, -1.0), - 0.4, + Point3::new(0.0, 0.0, -1.0), + 0.5, material_center.clone(), ))); world.add(Box::::new(Sphere::new( - Point3::new(-1.0, 0.0, -1.0), + Point3::new(-1.0, -0.15, -1.0), 0.5, - material_metal.clone(), + material_dielectric.clone(), ))); world.add(Box::::new(Sphere::new( - Point3::new(1.3, 0.3, -1.5), - 0.8, - material_metal.clone(), + Point3::new(1.0, 0.0, -1.0), + -0.4, + material_dielectric.clone(), ))); world.add(Box::::new(Sphere::new( Point3::new(-1.5, 1.3, -1.7), 0.4, - material_metal.clone(), + material_mirror.clone(), ))); world.add(Box::::new(Sphere::new( Point3::new(-0.5, 0.5, 1.0), diff --git a/src/material.rs b/src/material.rs index e2f0e93..690dbf1 100644 --- a/src/material.rs +++ b/src/material.rs @@ -12,12 +12,17 @@ pub struct Lambertian { pub struct Metal { albedo: Color, + fuzz: f64, } pub struct Mirror { albedo: Color, } +pub struct Dielectric { + ir: f64, +} + pub trait Material: Sync + Send { fn scatter(&self,r_in: &Ray, rec: &HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool; @@ -46,9 +51,10 @@ impl Material for Lambertian { } impl Metal { - pub fn new(a: &Color) -> Self { + pub fn new(a: &Color, f: f64) -> Self { Metal { albedo: *a, + fuzz: f, } } } @@ -57,7 +63,7 @@ impl Material for Metal { fn scatter(&self, r_in: &Ray, rec: &HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool { let reflected = Vec3::reflect(&Vec3::unit_vector(r_in.direction()), &rec.normal); - *scattered = Ray::new(rec.p, reflected); + *scattered = Ray::new(rec.p, reflected + self.fuzz*Vec3::random_in_unit_sphere()); *attenuation = self.albedo.clone(); return Vec3::dot(scattered.direction(), rec.normal) > 0.0; @@ -90,4 +96,37 @@ impl Material for Mirror { return true; }; } +} + +impl Dielectric { + pub fn new(index_of_refraction: f64) -> Self { + Dielectric { + ir: index_of_refraction, + } + } + + fn reflectance(cosine: f64, ref_idx: f64) -> f64 { + let r0 = ((1.0-ref_idx) / (1.0+ref_idx)).powi(2); + return r0 + (1.0-r0)*(1.0 - cosine).powi(5); + } +} + +impl Material for Dielectric { + fn scatter(&self, r_in: &Ray, rec: &HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool { + *attenuation = Color::new(1.0, 1.0, 1.0); + let refraction_ratio = if rec.front_face { 1.0 / self.ir } else { self.ir }; + + let unit_direction = Vec3::unit_vector(r_in.direction()); + let cos_theta = Vec3::dot(-1.0 * unit_direction, rec.normal).min(1.0); + let sin_theta = (1.0 - cos_theta*cos_theta).sqrt(); + + let mut direction = Vec3::refract(&unit_direction, &rec.normal, refraction_ratio); // can reflect + + if refraction_ratio * sin_theta > 1.0 || Dielectric::reflectance(cos_theta, refraction_ratio) > utility::random_f64() { // must reflect + direction = Vec3::reflect(&unit_direction, &rec.normal); + } + + *scattered = Ray::new(rec.p, direction); + return true; + } } \ No newline at end of file diff --git a/src/vec3.rs b/src/vec3.rs index 2e0bbed..01a1945 100644 --- a/src/vec3.rs +++ b/src/vec3.rs @@ -50,6 +50,13 @@ impl Vec3 { *v - 2.0*Self::dot(*v, *n) * *(n) } + pub fn refract(uv: &Self, n: &Self, etai_over_etat: f64) -> Self { + let cos_theta = Self::dot(-1.0 * *uv, *n).min(1.0); // Minimum aus 1.0 und Punktprodukt + let r_out_perp = etai_over_etat * (*uv + cos_theta * *n); + let r_out_parallel = -(1.0 - r_out_perp.length_squared()).abs().sqrt() * *n; + return r_out_perp + r_out_parallel; + } + pub fn near_zero(&self) -> bool { let s = 1e-8; return (self.e[0].abs() < s) && (self.e[1].abs() < s) && (self.e[2].abs() < s);