aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2021-02-06 17:27:42 +0100
committerJulian T <julian@jtle.dk>2021-02-06 17:27:42 +0100
commit0d5e6bd9363d5ed5c4f28174819fc0f5fd9aa586 (patch)
treef9ecafe7350ad616486e509ed55904295f505f83
parent1e83ea211055eb234b89c69b5d03602e3fcb98fb (diff)
Reorganized scene module, and fixed bug in sphere intersect
-rw-r--r--src/camera/camera.rs24
-rw-r--r--src/camera/mod.rs2
-rw-r--r--src/core/vector3.rs14
-rw-r--r--src/lib.rs3
-rw-r--r--src/main.rs24
-rw-r--r--src/render.rs4
-rw-r--r--src/scene/mod.rs25
-rw-r--r--src/scene/scene.rs27
-rw-r--r--src/scene/shapes/mod.rs12
-rw-r--r--src/scene/shapes/sphere.rs30
-rw-r--r--src/trace/mod.rs3
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<T: Number> Mul<T> for Vector3<T> {
}
}
+impl<T: Number> Neg for Vector3<T> {
+ type Output = Self;
+
+ fn neg(self) -> Self::Output {
+ Self::Output::new_xyz(
+ -self.x,
+ -self.y,
+ -self.z,
+ )
+ }
+}
+
impl<T: Number> DivAssign<T> for Vector3<T> {
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<Output = Self> +
Add<Output = Self> +
Mul<Output = Self> +
+ Neg<Output = Self> +
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<Intersection>;
+}
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<Box<dyn Shape>>,
-}
+type Shape = Box<dyn Hittable>;
-pub struct Intersection<'a> {
- pub shp: &'a dyn Shape,
- pub t: Float,
+pub struct Scene {
+ shps: Vec<Shape>,
}
impl Scene {
@@ -18,17 +14,20 @@ impl Scene {
}
}
- pub fn add_shape(&mut self, shp: Box<dyn Shape>) {
+ pub fn add_shape(&mut self, shp: Shape) {
self.shps.push(shp);
}
+ pub fn add_shapes(&mut self, shps: Vec<Shape>) {
+ for shp in shps {
+ self.add_shape(shp);
+ }
+ }
+
pub fn intersect(&self, ray: &Ray) -> Option<Intersection> {
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<Float>;
-
- /// 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<Float> {
+ fn intersect(&self, ray: &Ray) -> Option<Intersection> {
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);
}