diff --git a/src/consts.rs b/src/consts.rs index c9d728b..15cd97a 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -4,8 +4,10 @@ pub const COLOR_CHANNELS: usize = 4; pub const EPSILON: f64 = 1. / 1024.; pub const LIGHT_EPSILON: f64 = 1. / 512.; -pub const MAX_DIST: f64 = 16.; +//pub const MAX_DIST: f64 = 16.; +pub const MAX_DIST: f64 = 32.; pub const MAX_STEPS: u32 = 1024; +//pub const MAX_STEPS: u32 = u32::MAX; pub const DIST_FIX_CORRECTION: f64 = 0.5; pub const DIST_CORRECTION: f64 = 3.; @@ -35,8 +37,10 @@ pub const RAYS_PER_PIXEL: usize = 500; //pub const MAX_BOUNCES: u32 = 8; pub const MAX_BOUNCES: u32 = 10; +//pub const THREAD_COUNT: usize = 1; pub const THREAD_COUNT: usize = 12; -pub const SLICES_PER_THREAD: usize = 4; +pub const SLICES_PER_THREAD: usize = 16; +pub const REPORT_STATUS: bool = true; pub const UP: Vec3 = Y; pub const RIGHT: Vec3 = X; diff --git a/src/main.rs b/src/main.rs index ce3190c..bf4dba0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use crate::consts::*; use crate::structs::*; use crate::material::*; use image::{ColorType, ImageFormat}; +use crate::light::Light; fn default_cam() -> Cam { Cam::new_pointing(Y*3. - X*5., O, 0.5) @@ -64,6 +65,23 @@ fn default_scene2() -> Scene { Scene::new(Union::new(Union::new(sphere, Union::new(wall, backwall)), Union::new(light, mirror))) } +fn default_scene3() -> Scene { + //TODO fix this scene + let s1 = WithMaterial::new(Sphere::new_xyz(4., 0., 0., 1.), WHITE); + let s2 = WithMaterial::new(Sphere::new_xyz(3., 1., 1., 0.5), GREEN); + let navion = WithMaterial::new(Plane::new_xyz(0., 1., -1., 3.), BLUE); + let backwall = WithMaterial::new(Plane::new_xyz(-1., -1., -0.5, 8.), RED); + let boiboite = Cuboid::new_xyz(4., 1.1, -1.1, 0.5, 0.275, 0.275); + let decal = AffineTransform::new_translate(boiboite, Vec3::new(-0.25, 0., 0.5)); + let nope = Exclusion::new(s1, Sphere::new_xyz(3.75, 0.75, 0.75, 1.)); + let walls = Union::new(navion, backwall); + let spheres = Union::new(AffineTransform::new_linear(nope, scale(0.75)), s2); + let scene = Union::new(spheres, Union::new(walls, decal)); + let light = WithMaterial::new(Plane::new_xyz(0., -1., 0., 8.), BRIGHT_AF_LIGHTSOURCE); + let scene = WithLight::new_one(scene, Light::new(Y*3.-X*5., ColorVec::new([2., 3., 4., 0.]))); + Scene::new(Union::new(scene, light)) +} + fn main() { // get scene and camera let scene = default_scene2(); diff --git a/src/material.rs b/src/material.rs index 31dde01..c024aa7 100644 --- a/src/material.rs +++ b/src/material.rs @@ -71,6 +71,12 @@ pub const STRONG_LIGHTSOURCE: Material = Material::new_from_diagonal( Stop ); +pub const BRIGHT_AF_LIGHTSOURCE: Material = Material::new_from_diagonal( + ColorVec::new([25., 25., 25., 0.]), + COLOR_ZERO, + Stop +); + pub const UV_LIGHTSOURCE: Material = Material::new_from_diagonal( ColorVec::new_one(3), ColorVec::new([0.25, 0.25, 0.25, 1.]), diff --git a/src/object/exclusion.rs b/src/object/exclusion.rs new file mode 100644 index 0000000..e3320cf --- /dev/null +++ b/src/object/exclusion.rs @@ -0,0 +1,32 @@ +use crate::object::Obj; +use crate::light::Light; +use crate::structs::Vec3; +use std::vec; +use crate::material::Material; + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Exclusion { + a: A, + b: B +} + +impl Exclusion { + pub fn new(a: A, b: B) -> Exclusion { + Exclusion { a, b } + } +} + +impl Obj for Exclusion { + fn distance_to(&self, point: Vec3) -> f64 { + f64::max(self.a.distance_to(point), -self.b.distance_to(point)) + } + fn material_at(&self, point: Vec3) -> Material { + self.a.material_at(point) + } + fn get_lights(&self) -> vec::Vec { + self.a.get_lights() + } + fn node_count(&self) -> u32 { + self.a.node_count() + self.b.node_count() + 1 + } +} diff --git a/src/object/intersection.rs b/src/object/intersection.rs index 9d32df8..e48fbeb 100644 --- a/src/object/intersection.rs +++ b/src/object/intersection.rs @@ -20,13 +20,6 @@ impl Obj for Intersection { fn distance_to(&self, point: Vec3) -> f64 { f64::max(self.a.distance_to(point), self.b.distance_to(point)) } - fn normal_at(&self, point: Vec3) -> Vec3 { - if self.a.distance_to(point) > self.b.distance_to(point) { - self.a.normal_at(point) - } else { - self.b.normal_at(point) - } - } fn material_at(&self, point: Vec3) -> Material { if self.a.distance_to(point) > self.b.distance_to(point) { self.a.material_at(point) diff --git a/src/object/mod.rs b/src/object/mod.rs index 8d4ab87..d12c0df 100644 --- a/src/object/mod.rs +++ b/src/object/mod.rs @@ -9,6 +9,7 @@ mod sphere; mod plane; mod union; mod intersection; +mod exclusion; mod cuboid; mod cylinder; mod torus; @@ -36,7 +37,9 @@ pub trait Obj: Send + Sync { fn get_lights(&self) -> Vec { Vec::new() } - fn node_count(&self) -> u32 { 1 } + fn node_count(&self) -> u32 { + 1 + } } impl Obj for &T { @@ -61,6 +64,7 @@ pub use sphere::Sphere; pub use plane::Plane; pub use union::Union; pub use intersection::Intersection; +pub use exclusion::Exclusion; pub use cuboid::Cuboid; pub use cylinder::Cylinder; pub use torus::Torus; diff --git a/src/object/transform.rs b/src/object/transform.rs index 4523d73..264a8a6 100644 --- a/src/object/transform.rs +++ b/src/object/transform.rs @@ -63,7 +63,7 @@ impl Obj for AffineTransform { self.obj.distance_to(self.apply_rev(point)) } fn normal_at(&self, point: Vec3) -> Vec3 { - self.apply_fwd(self.obj.normal_at(self.apply_rev(point))) + self.apply_fwd(self.obj.normal_at(self.apply_rev(point))).unit() } fn material_at(&self, point: Vec3) -> Material { self.obj.material_at(self.apply_rev(point)) diff --git a/src/structs/cam.rs b/src/structs/cam.rs index f205c74..2f7ceaa 100644 --- a/src/structs/cam.rs +++ b/src/structs/cam.rs @@ -7,6 +7,7 @@ use std::f64::consts::PI; use rand::prelude::*; use crossbeam_channel::unbounded; use std::ops::{Mul, Add, Sub}; +use std::io::Write; #[derive(Debug, Copy, Clone, PartialEq)] pub struct Cam { @@ -99,10 +100,10 @@ fn shoot_ray_at(scene: &T, pos: Vec3, dest: Vec3) -> bool { let point = ray.point(dist); let sd = scene.distance_to(point); let dd = dest.distance_to(point); - if sd < 0. { + if sd <= 0. { return false; } - if dd < sd { + if dd <= sd { return true; } dist += if sd < EPSILON { EPSILON } else { sd }; @@ -121,6 +122,12 @@ impl Cam { } pub fn render_singlethreaded(&self, scene: &T) -> Image { + let mut stderr = std::io::stderr(); + if REPORT_STATUS { + stderr.write_all(format!("Rendering... 0/{} rows (0.00%)", IMG_HEIGHT).as_bytes()).unwrap(); + stderr.flush(); + } + let lights = scene.get_lights(); let mut pixels = [0; IMG_BYTE_SIZE]; for y in 0..IMG_HEIGHT { @@ -128,7 +135,18 @@ impl Cam { let field_pos = (x + y*IMG_WIDTH) * 3; self.render_single(scene, &lights, x, y, &mut pixels[field_pos..(field_pos+3)]); } + + if REPORT_STATUS { + stderr.write_all(format!("\x1b[1K\x1b[GRendering... {}/{} rows ({:.2}%)", y+1, IMG_HEIGHT, (y+1) as f64/IMG_HEIGHT as f64).as_bytes()); + stderr.flush(); + } } + + if REPORT_STATUS { + stderr.write_all(format!("\x1b[1K\x1b[GRendering... Done\n").as_bytes()); + stderr.flush(); + } + pixels } @@ -176,6 +194,7 @@ impl Cam { drop(data_tx); // send the slice data + let mut total_slices = 0u32; let mut y = 0; while y < IMG_HEIGHT { let mut x = 0; @@ -184,12 +203,23 @@ impl Cam { let h = usize::min(slice_height, IMG_HEIGHT - y); slice_tx.send(Slice { x, y, w, h }).unwrap(); x += slice_width; + + if REPORT_STATUS { + total_slices += 1; + } } y += slice_height; } drop(slice_tx); // merge stuff as we get it + let mut stderr = std::io::stderr(); + if REPORT_STATUS { + stderr.write_all(format!("Rendering... 0/{} slices (0.00%), 0.00% pixels", total_slices).as_bytes()).unwrap(); + stderr.flush(); + } + let mut rendered_slices = 0u32; + let mut rendered_pixels = 0u64; for (slice, data) in data_rx { let data = data.as_slice(); for sy in 0..slice.h { @@ -204,6 +234,19 @@ impl Cam { pixels[pi + 2] = data[si + 2]; } } + + if REPORT_STATUS { + rendered_slices += 1; + rendered_pixels += (slice.w*slice.h) as u64; + let pct_slices = rendered_slices as f64 / total_slices as f64 * 100.; + let pct_pixels = rendered_pixels as f64 / (IMG_WIDTH * IMG_HEIGHT) as f64 * 100.; + stderr.write_all(format!("\x1b[1K\x1b[GRendering... {}/{} slices ({:.02}%), {:.02}% pixels", rendered_slices, total_slices, pct_slices, pct_pixels).as_bytes()).unwrap(); + stderr.flush(); + } + } + if REPORT_STATUS { + stderr.write_all(format!("\x1b[1K\x1b[GRendering... Done\n").as_bytes()); + stderr.flush(); } }).unwrap(); @@ -269,7 +312,7 @@ impl Cam { match hit { Some(hit) => { i += 1; - pos = hit.pos - dir * EPSILON; + pos = hit.pos + (hit.normal - dir) * EPSILON; match hit.material.surface() { SurfaceType::Stop => break, SurfaceType::Diffuse => { @@ -281,11 +324,7 @@ impl Cam { let x = r1.cos()*r2.sqrt(); let y = r1.sin()*r2.sqrt(); let z = (1.-r2).sqrt(); - if w.x().abs() < 0.5 { - dir = (u*x + v*y + w*z).unit(); - } else { - dir = (u*x + v*y + w*z).unit(); - } + dir = (u*x + v*y + w*z).unit(); }, SurfaceType::Reflective => { dir = dir - hit.normal * (hit.normal * dir) * 2.;