// renderer.rs use std::f32; use nalgebra::*; use crate::camera::*; use crate::elements::*; const BLACK: Color = Color { red: 0.0, green: 0.0, blue: 0.0, }; pub struct Ray { pub pos: Vec3, pub dir: Vec3 } impl Ray { fn new(pos: Vec3, dir: Vec3) -> Ray { Ray { pos: pos, dir: dir } } fn at(&self, t: f64) -> Vec3 { self.pos + t * self.dir } } pub struct Intersection<'a> { pub distance: f64, pub object: &'a Element } impl<'a> Intersection<'a> { pub fn new<'b>(distance: f64, object: &'b Element) -> Intersection<'b> { Intersection { distance: distance, object: & object } } } fn create_reflection(normal: Vec3, incident: Vec3, hit_point: Vec3, bias: f64) -> Ray { Ray { pos: hit_point + (normal.normalize()), dir: incident - (2.0 * incident.dot(&normal) * normal), } } fn get_color(camera: &PerspectiveCamera, ray: &Ray, intersection: &Intersection, depth: u32) -> Color { let hit_point = ray.pos + (ray.dir * intersection.distance); let surface_normal = intersection.object.normal(hit_point); let material = intersection.object.material(); let mut color = shade_diffuse(camera, intersection.object, hit_point, surface_normal); if let SurfaceType::Reflective { reflectivity } = material.surface { let reflection_ray = create_reflection(surface_normal, ray.dir, hit_point, camera.shadow_bias); color = color * (1.0 - reflectivity); color = color + (cast_ray(&camera, &reflection_ray, depth + 1) * reflectivity); } color } fn shade_diffuse(camera: &PerspectiveCamera, object: &Element, hit_point: Vec3, surface_normal: Vec3) -> Color { let mut color = BLACK; // Light processing // TODO: Support multiple lights for light in camera.lights.iter() { let direction_to_light = light.pos - hit_point; let material = object.material(); // TODO: Change light intensity to take hit_point for some reason (read source) // https://github.com/bheisler/raytracer/blob/7130556181de7fc59eaa29346f5d4134db3e720e/src/rendering.rs#L195 let texture_coords = object.texture_coords(&hit_point); // Shadow stuff let shadow_ray = Ray { pos: hit_point + surface_normal.normalize(), dir: direction_to_light.normalize(), }; let shadow_intersection = camera.trace(&shadow_ray); let in_light = shadow_intersection.is_none() || shadow_intersection.unwrap().distance > light.distance(hit_point); let light_intensity = if in_light { light.intensity } else { 0.0 }; let light_power = (surface_normal.normalize().dot(&direction_to_light.normalize()) as f32).max(0.0); let light_reflected = material.albedo / f32::consts::PI; let light_color = light_intensity * light_power * light_reflected; color = color + (material.coloration.color(&texture_coords) * light_color); } return color; } pub fn cast_ray(camera: &PerspectiveCamera, ray: &Ray, depth: u32) -> Color { if depth >= camera.max_recursion_depth { return BLACK; } let intersection = camera.trace(&ray); intersection.map(|i| get_color(camera, &ray, &i, depth)).unwrap_or(BLACK) }