From 0d5e6bd9363d5ed5c4f28174819fc0f5fd9aa586 Mon Sep 17 00:00:00 2001 From: Julian T Date: Sat, 6 Feb 2021 17:27:42 +0100 Subject: Reorganized scene module, and fixed bug in sphere intersect --- src/camera/camera.rs | 24 ++++++++++++++++-------- src/camera/mod.rs | 2 +- src/core/vector3.rs | 14 +++++++++++++- src/lib.rs | 3 ++- src/main.rs | 24 +++++++++++++----------- src/render.rs | 4 ++-- src/scene/mod.rs | 25 +++++++++++++++++++++++++ src/scene/scene.rs | 27 +++++++++++++-------------- src/scene/shapes/mod.rs | 12 ------------ src/scene/shapes/sphere.rs | 30 +++++++++++++++++++----------- src/trace/mod.rs | 3 +-- 11 files changed, 105 insertions(+), 63 deletions(-) diff --git a/src/camera/camera.rs b/src/camera/camera.rs index 7822e92..d49220b 100644 --- a/src/camera/camera.rs +++ b/src/camera/camera.rs @@ -40,31 +40,39 @@ pub struct Camera { qy: Vector3f, } +pub struct CameraSettings { + pub origin: Vector3f, + pub target: Vector3f, + pub up: Vector3f, + pub fov: Float, + pub screensize: Vector2i, +} + impl Camera { /// Create a new camera look at a target /// /// The field of view specifies how wide the image should be. /// Currently must be between [0; 180[. - pub fn new(origin: Vector3f, target: Vector3f, up: Vector3f, fov: Float, screensize: Vector2i) -> Camera { - let screensize = Vector2f::from(screensize); + pub fn new(set: &CameraSettings) -> Camera { + let screensize = Vector2f::from(set.screensize); // Calculate translation vectors - let forward = (target - origin).norm(); - let right = up.cross(&origin).norm(); + let forward = (set.target - set.origin).norm(); + let right = set.up.cross(&forward).norm(); let newup = forward.cross(&right).norm(); // Calculate screen size from fov let aspect = screensize.y / screensize.x; - let width = 2.0 * (fov / 2.0).to_radians().tan(); + let width = 2.0 * (set.fov / 2.0).to_radians().tan(); let height = aspect * width; // Calculate screen scaling vectors let qx = right * (width / (screensize.x - 1.0)); let qy = newup * (height / (screensize.y - 1.0)); - let screen_origin = forward - (right * (width/2.0)) - (newup * (height/2.0)); + let screen_origin = forward - (right * (width/2.0)) + (newup * (height/2.0)); Camera { - origin, + origin: set.origin, screen_origin, qx, qy, @@ -80,7 +88,7 @@ impl Camera { /// /// The direction of the returned way is normalized pub fn generate_ray(&self, point: &Vector2f) -> (Ray, Float) { - let mut dir = self.screen_origin + (self.qx * point.x) + (self.qy * point.y); + let mut dir = self.screen_origin + (self.qx * point.x) - (self.qy * point.y); dir.norm_in(); ( diff --git a/src/camera/mod.rs b/src/camera/mod.rs index 1391cea..7c36307 100644 --- a/src/camera/mod.rs +++ b/src/camera/mod.rs @@ -9,5 +9,5 @@ pub mod film; //pub mod filter; pub mod camera; -pub use camera::Camera; +pub use camera::{Camera, CameraSettings}; pub use film::Film; diff --git a/src/core/vector3.rs b/src/core/vector3.rs index 6f4d6ab..10de647 100644 --- a/src/core/vector3.rs +++ b/src/core/vector3.rs @@ -2,7 +2,7 @@ //! //! Also add more 3d math things needed for shading and 3d calculations. use crate::{Float, Number}; -use std::ops::{Mul, Sub, Add, DivAssign}; +use std::ops::{Mul, Sub, Add, DivAssign, Neg}; use std::fmt; #[derive(Clone, Copy)] @@ -61,6 +61,18 @@ impl Mul for Vector3 { } } +impl Neg for Vector3 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self::Output::new_xyz( + -self.x, + -self.y, + -self.z, + ) + } +} + impl DivAssign for Vector3 { fn div_assign(&mut self, op: T) { self.x /= op; diff --git a/src/lib.rs b/src/lib.rs index 5883167..0ec6700 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ pub mod scene; pub mod trace; pub mod sample; -use std::ops::{Add, Sub, Mul, DivAssign}; +use std::ops::{Add, Sub, Mul, DivAssign, Neg}; use std::cmp; use std::fmt; @@ -18,6 +18,7 @@ pub trait Number: Sub + Add + Mul + + Neg + DivAssign + fmt::Display {} diff --git a/src/main.rs b/src/main.rs index c1383ad..c3d11e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use pathtrace::camera::{Camera, Film}; +use pathtrace::camera::{Camera, Film, CameraSettings}; use pathtrace::scene::Scene; use pathtrace::trace::Tracer; use pathtrace::scene::shapes::Sphere; @@ -9,17 +9,19 @@ use pathtrace::sample::UniformSampler; fn main() { let res = Vector2i::new_xy(500, 500); - let cam = Camera::new( - Vector3f::new_xyz(10.0, 0.0, 0.0), - Vector3f::new(0.0), - Vector3f::new_xyz(0.0, 0.1, 0.0), - 90.0, res, - ); + let cam = Camera::new(&CameraSettings { + target: Vector3f::new_xyz(0.0, 0.0, -1.0), + origin: Vector3f::new_xyz(0.0, 0.0, 0.0), + up: Vector3f::new_xyz(0.0, 1.0, 0.0), + fov: 90.0, + screensize: res, + }); let mut scn = Scene::new(); - scn.add_shape( - Box::new(Sphere::new(4.0, Vector3f::new(0.0))), - ); + scn.add_shapes(vec![ + Box::new(Sphere::new(0.5, Vector3f::new_xyz(0.0, 0.0, -1.0))), + Box::new(Sphere::new(100.0, Vector3f::new_xyz(0.0, -100.5, -1.0))), + ]); let tracer = Tracer::new(); @@ -30,7 +32,7 @@ fn main() { let mut film = Film::new(res); let tile = film.get_tile(&film.frame); - let mut task = RenderTask::new(Box::new(tile), 500); + let mut task = RenderTask::new(Box::new(tile), 10); task.render(&ctx, &mut sampler); film.commit_tile(&task.tile); diff --git a/src/render.rs b/src/render.rs index c8a75da..9c9e924 100644 --- a/src/render.rs +++ b/src/render.rs @@ -41,8 +41,8 @@ impl RenderTask { pub fn render(&mut self, ctx: &RenderContext, sampler: &mut dyn Sampler) { let b = self.tile.bounds.clone(); - for x in b.min.x .. b.max.x { - for y in b.min.y .. b.max.y { + for y in b.min.y .. b.max.y { + for x in b.min.x .. b.max.x { self.render_at(ctx, x, y, sampler); } } diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 7c77412..f8c1e09 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -5,3 +5,28 @@ pub mod shapes; mod scene; pub use scene::*; + +use crate::core::{Ray, Vector3f}; +use crate::Float; + +/// Returns the context of a intersection +pub struct Intersection { + /// Normal vector at intersection + pub n: Vector3f, + pub p: Vector3f, +} + +impl Intersection { + pub fn norm_against_ray(&self, r: &Ray) -> Vector3f { + if self.n.dot(&r.direction) < 0.0 { + self.n + } else { + -self.n + } + } +} + +/// Defines a common trait for objects in the scene +pub trait Hittable { + fn intersect(&self, ray: &Ray) -> Option; +} diff --git a/src/scene/scene.rs b/src/scene/scene.rs index 33a7613..367003e 100644 --- a/src/scene/scene.rs +++ b/src/scene/scene.rs @@ -1,14 +1,10 @@ -use super::shapes::Shape; -use crate::Float; +use super::{Intersection, Hittable}; use crate::core::Ray; -pub struct Scene { - shps: Vec>, -} +type Shape = Box; -pub struct Intersection<'a> { - pub shp: &'a dyn Shape, - pub t: Float, +pub struct Scene { + shps: Vec, } impl Scene { @@ -18,17 +14,20 @@ impl Scene { } } - pub fn add_shape(&mut self, shp: Box) { + pub fn add_shape(&mut self, shp: Shape) { self.shps.push(shp); } + pub fn add_shapes(&mut self, shps: Vec) { + for shp in shps { + self.add_shape(shp); + } + } + pub fn intersect(&self, ray: &Ray) -> Option { for shp in self.shps.iter() { - if let Some(t) = shp.intersect(&ray) { - return Some(Intersection { - shp: shp.as_ref(), - t, - }) + if let Some(i) = shp.intersect(&ray) { + return Some(i) } } diff --git a/src/scene/shapes/mod.rs b/src/scene/shapes/mod.rs index 9b6bdfd..d7583ad 100644 --- a/src/scene/shapes/mod.rs +++ b/src/scene/shapes/mod.rs @@ -2,15 +2,3 @@ mod sphere; pub use sphere::Sphere; -use crate::core::{Vector3f, Ray}; -use crate::Float; - -pub trait Shape { - fn intersect(&self, ray: &Ray) -> Option; - - /// Calculates the normal at point - /// - /// Point is assumed to be on the circle. - /// The resulting vector is assumed to be normalized. - fn norm_at(&self, point: &Vector3f) -> Vector3f; -} diff --git a/src/scene/shapes/sphere.rs b/src/scene/shapes/sphere.rs index da7f321..acca2b7 100644 --- a/src/scene/shapes/sphere.rs +++ b/src/scene/shapes/sphere.rs @@ -3,7 +3,7 @@ //! Spheres are relatively easy to calculate intersections between use crate::Float; use crate::core::{Ray, Vector3f}; -use super::Shape; +use crate::scene::{Hittable, Intersection}; pub struct Sphere { radius: Float, @@ -17,11 +17,17 @@ impl Sphere { center, } } + + fn norm_at(&self, point: &Vector3f) -> Vector3f { + let mut v = *point - self.center; + v /= self.radius; + v + } } -impl Shape for Sphere { +impl Hittable for Sphere { // Implementation from ray tracing in a weekend - fn intersect(&self, ray: &Ray) -> Option { + fn intersect(&self, ray: &Ray) -> Option { let oc = ray.origin - self.center; let a = ray.direction.len_squared(); let half_b = oc.dot(&ray.direction); @@ -29,18 +35,20 @@ impl Shape for Sphere { let disc = half_b*half_b - a*c; if disc < 0.0 { - None + return None } else { - Some( (-half_b - disc.sqrt()) / a) + let distance = (-half_b - disc.sqrt()) / a; + if distance < 0.0 { + return None + } + let w = ray.at(distance); + Some(Intersection { + n: self.norm_at(&w), + p: w, + }) } } - - fn norm_at(&self, point: &Vector3f) -> Vector3f { - let mut v = *point - self.center; - v /= self.radius; - v - } } #[cfg(test)] diff --git a/src/trace/mod.rs b/src/trace/mod.rs index 90f12da..9c8b0b8 100644 --- a/src/trace/mod.rs +++ b/src/trace/mod.rs @@ -19,8 +19,7 @@ impl NormTracer { pub fn trace(&self, scn: &Scene, ray: &Ray) -> Spectrum { // Trace ray if let Some(i) = scn.intersect(ray) { - let p = ray.at(i.t); - let norm = i.shp.norm_at(&p) * 0.5 + Vector3f::new(0.5); + let norm = i.n * 0.5 + Vector3f::new(0.5); return Spectrum::new_rgb(norm.x, norm.y, norm.z); } -- cgit v1.2.3