diff --git a/src/main.rs b/src/main.rs index 4a30927..1bc9f6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use nalgebra::*; use bmp::Image; use bmp::Pixel; -struct Ray { +pub struct Ray { pos: Vec3, dir: Vec3 } @@ -40,12 +40,46 @@ impl LightSrc { intensity: intensity } } + + fn distance(&self, hit_point: Vec3) -> f64 { + let difference = self.pos - hit_point; + difference.norm() + } +} + +enum Element { + Sphere(Sphere), + Plane(Plane), +} + +impl Element { + fn pos(&self) -> Vec3 { + match *self { + Element::Sphere(ref s) => s.pos, + Element::Plane(ref p) => p.pos, + } + } + + fn color(&self) -> &Color { + match *self { + Element::Sphere(ref s) => &s.material.coloration, + Element::Plane(ref p) => &p.color, + } + } + + fn normal(&self, pos: Vec3) -> Vec3 { + match *self { + Element::Sphere(ref s) => pos - s.pos, + Element::Plane(ref p) => -p.normal, + } + } } struct OrthoCamera { pos: Vec3, - plane: bmp::Image, - spheres: Vec, + output_img: bmp::Image, + elements: Vec, + //spheres: Vec, light: LightSrc, shadow_bias: f64, @@ -54,8 +88,8 @@ struct OrthoCamera { impl OrthoCamera { fn trace(&self, ray: &Ray) -> Option { - self.spheres.iter() - .filter_map(|s| s.intersection(ray).map(|d| Intersection::new(d, s) )) + self.elements.iter() + .filter_map(|s| s.intersect(ray).map(|d| Intersection::new(d, s) )) .min_by(|i1, i2| i1.distance.partial_cmp(&i2.distance).unwrap()) } } @@ -98,16 +132,17 @@ impl Color { } } -struct Sphere { +pub struct Sphere { pos: Vec3, radius: f64, material: Material, + } -impl Sphere { +impl Intersectable for Sphere { // Implemented from // http://kylehalladay.com/blog/tutorial/math/2013/12/24/Ray-Sphere-Intersection.html - fn intersection(&self, ray: &Ray) -> Option { + fn intersect(&self, ray: &Ray) -> Option { let l = self.pos - ray.pos; let adj = l.dot(&ray.dir); let d2 = l.dot(&l) - (adj * adj); @@ -128,15 +163,42 @@ impl Sphere { let distance = if t0 < t1 { t0 } else { t1 }; Some(distance) } + +} + +pub struct Plane { + pos: Vec3, + normal: Vec3, + color: Color, + //material: Material, +} + +pub trait Intersectable { + fn intersect(&self, ray: &Ray) -> Option; +} + +impl Intersectable for Plane { + fn intersect(&self, ray: &Ray) -> Option { + let normal = &self.normal; + let denom = normal.dot(&ray.dir); + if denom > 1e-6 { + let v = self.pos - ray.pos; + let distance = v.dot(&normal) / denom; + if distance >= 0.0 { + return Some(distance); + } + } + None + } } struct Intersection<'a> { distance: f64, - object: &'a Sphere + object: &'a Element } impl<'a> Intersection<'a> { - fn new<'b>(distance: f64, object: &'b Sphere) -> Intersection<'b> { + fn new<'b>(distance: f64, object: &'b Element) -> Intersection<'b> { Intersection { distance: distance, object: & object @@ -144,6 +206,15 @@ impl<'a> Intersection<'a> { } } +impl Intersectable for Element { + fn intersect(&self, ray: &Ray) -> Option { + match *self { + Element::Sphere(ref s) => s.intersect(ray), + Element::Plane(ref p) => p.intersect(ray), + } + } +} + //fn get_color(camera: &OrthoCamera, ray: &Ray, intersection: &Intersection) -> Color { // let hit_point = ray.at(intersection.distance); @@ -161,9 +232,9 @@ impl<'a> Intersection<'a> { fn main() { let mut camera = OrthoCamera { pos: Vec3::new(0.0, 0.0, 0.0), - plane: Image::new(256,256), - spheres: Vec::new(), - light: LightSrc::new(Vec3::new(125.0, -100.0, 100.0), 20.0), + output_img: Image::new(2560,2560), + elements: Vec::new(), + light: LightSrc::new(Vec3::new(200.0, 200.0, 300.0), 5.0), shadow_bias: 1e-3, max_recursion_depth: 5 }; @@ -173,10 +244,10 @@ fn main() { // camera.spheres.push(Sphere::new(Vec3::new(0.0, 0.0, 100.0), 10.0)); for i in 0..15 { let mut rng = rand::thread_rng(); - let x: f64 = rng.gen::() * 250.0; - let y: f64 = rng.gen::() * 250.0; - let z: f64 = rng.gen::() * 250.0; - let radius: f64 = rng.gen::() * 40.0; + let x: f64 = rng.gen::() * 250.0 * 10.0; + let y: f64 = rng.gen::() * 250.0 * 10.0; + let z: f64 = rng.gen::() * 250.0 * 10.0; + let radius: f64 = rng.gen::() * 40.0 * 10.0; let red: f64 = rng.gen::() * 100.0; let green: f64 = rng.gen::() * 100.0; let blue: f64 = rng.gen::() * 100.0; @@ -185,42 +256,84 @@ fn main() { radius: radius, material: Material::new(Color::new(red, green, blue), 2.0, SurfaceType::Reflective { reflectivity: 1.0 }) }; - camera.spheres.push(sphere); + camera.elements.push(Element::Sphere(sphere)); //camera.spheres.push(Sphere::new(Vec3::new(x, y, 100.0), radius)); } - for (x, y) in camera.plane.coordinates() { - camera.plane.set_pixel(x, y, px!(20, 20, 20)); + let back_plane = Plane { + //pos: Vec3::new(0.0, 0.0, 100.0), + pos: Vec3::new(0.0, 0.0, 1500.0), + color: Color::new(20.0, 20.0, 255.0), + normal: Vec3::new(0.0, 0.0, 1.0), + }; + camera.elements.push(Element::Plane(back_plane)); + + let bottom_plane = Plane { + pos: Vec3::new(2500.0, 0.0, 1500.0), + color: Color::new(20.0, 20.0, 80.0), + normal: Vec3::new(0.0, 0.4, 1.0), + }; + camera.elements.push(Element::Plane(bottom_plane)); + + let center_sphere = Sphere { + pos: Vec3::new(1280.0, 1280.0, 500.0), + radius: 300.0, + material: Material::new(Color::new(255.0, 20.0, 20.0), 2.0, SurfaceType::Reflective { reflectivity: 1.0 }) + }; + camera.elements.push(Element::Sphere(center_sphere)); + + //let sky_sphere = Sphere { + // pos: Vec3::new(1280.0, 1280.0, 0.0), + // radius: 50000.0, + // material: Material::new(Color::new(255.0, 20.0, 20.0), 2.0, SurfaceType::Reflective { reflectivity: 1.0 }) + //}; + //camera.spheres.push(sky_sphere); + + println!("Raytracing ..."); + for (x, y) in camera.output_img.coordinates() { + camera.output_img.set_pixel(x, y, px!(20, 20, 20)); let ray = Ray::new(Vec3::new(x as f64, y as f64, camera.pos.z as f64), Vec3::new(0.0, 0.0, 1.0)); let result = camera.trace(&ray); match result { Some(intersection) => { let hit_point = ray.at(intersection.distance); - let normal = hit_point - intersection.object.pos; - let light_dir = hit_point - camera.light.pos; - let light_color = &intersection.object.material.coloration; + let object_pos = intersection.object.pos(); + //let normal = hit_point - object_pos; + let normal = intersection.object.normal(hit_point); + let light_dir = camera.light.pos - hit_point; //hit_point - camera.light.pos; + let light_color = &intersection.object.color(); //&intersection.object.material.coloration; let shadow_ray = Ray { pos: hit_point + (normal.normalize()), - dir: -light_dir.normalize() + dir: light_dir.normalize() }; - println!("{} {}", shadow_ray.pos, shadow_ray.dir); + //println!("{} {}", shadow_ray.pos, shadow_ray.dir); - let in_light = camera.trace(&shadow_ray).is_none(); + // TODO: Get shadow calculations working better + // Working code below + let shadow_intersection = camera.trace(&shadow_ray); + //println!("{} < {}", camera.light.distance(hit_point), shadow_intersection.as_ref().unwrap().distance); + let in_light = shadow_intersection.is_none() || shadow_intersection.unwrap().distance > camera.light.distance(hit_point); + //let in_light = true; let light_intensity = if in_light { camera.light.intensity } else { 0.0 }; - let light_power = (normal.normalize().dot(&-light_dir.normalize()) as f64).max(0.0) * light_intensity; + //let light_intensity = camera.light.intensity; + let light_power = (normal.normalize().dot(&light_dir.normalize()) as f64).max(0.0) * light_intensity; let light_reflected = 2.0 / std::f64::consts::PI; - let red = light_color.red * light_power * light_reflected; - let green = light_color.green * light_power * light_reflected; - let blue = light_color.blue * light_power * light_reflected; + let red = light_color.red * light_power;// * light_reflected; + let green = light_color.green * light_power;// * light_reflected; + let blue = light_color.blue * light_power;// * light_reflected; - camera.plane.set_pixel(x, y, px!(red, green, blue)) + //let red = light_color.red; + //let green = light_color.green; + //let blue = light_color.blue; + + camera.output_img.set_pixel(x, y, px!(red, green, blue)) }, None => { } } } - let _ = camera.plane.save("img.bmp"); + let _ = camera.output_img.save("img.bmp"); }