aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2021-02-03 15:34:17 +0100
committerJulian T <julian@jtle.dk>2021-02-03 15:34:17 +0100
commit8296be848319eecd43f94900d4d12414ec189166 (patch)
treed0adad5c05692a55e2957d02eb62666bd45e011f
parent32e719a517a6fea113f3e66e350c9aa60ddd98b9 (diff)
Add simple perspective camera
-rw-r--r--src/camera/camera.rs90
-rw-r--r--src/camera/mod.rs4
-rw-r--r--src/core/vector3.rs29
-rw-r--r--src/lib.rs4
-rw-r--r--src/main.rs13
5 files changed, 137 insertions, 3 deletions
diff --git a/src/camera/camera.rs b/src/camera/camera.rs
new file mode 100644
index 0000000..69fd4e0
--- /dev/null
+++ b/src/camera/camera.rs
@@ -0,0 +1,90 @@
+//! Generates rays from screen coordinates
+//!
+//! Generates rays in world space from screen coordinates.
+//! Future versions should also simulate depth of field.
+//!
+//! # Examples
+//!
+//! ```
+//! use pathtrace::camera::Camera;
+//! use pathtrace::core::{Vector3f, Vector2f};
+//!
+//! let cam = Camera::new(
+//! Vector3f::new(10.0),
+//! Vector3f::new(0.0),
+//! Vector3f::new_xyz(0.0, 1.0, 0.0),
+//! 90.0, Vector2f::new(10.0),
+//! );
+//!
+//! let (r, _) = cam.generate_ray(&Vector2f::new(5.0));
+//! let dir = r.direction;
+//!
+//! assert!(
+//! dir.x == -0.44792563 &&
+//! dir.y == -0.659974 &&
+//! dir.z == -0.6031559
+//! );
+//!
+//! ```
+use crate::Float;
+use crate::core::{Vector3f, Vector2f, Ray};
+
+/// A simple perspective camera
+pub struct Camera {
+ /// The camera origin in the screen
+ origin: Vector3f,
+ /// Vector from camera origin to the screen lower left corner
+ screen_origin: Vector3f,
+ /// Scaling vectors from screen_origin
+ qx: Vector3f,
+ qy: Vector3f,
+}
+
+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: Vector2f) -> Camera {
+ // Calculate translation vectors
+ let forward = (target - origin).norm();
+ let right = up.cross(&origin).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 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));
+
+ Camera {
+ origin,
+ screen_origin,
+ qx,
+ qy,
+ }
+ }
+
+ /// Generates a ray a screen space point
+ ///
+ /// The point coordinates should be between [0,0] (lower left corner) and [screensize.x,
+ /// screensize.y] (upper right corner)
+ ///
+ /// Will return a ray and a weight
+ ///
+ /// 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);
+ dir.norm_in();
+
+ (
+ Ray { origin: self.origin, direction: dir },
+ 1.0
+ )
+ }
+}
diff --git a/src/camera/mod.rs b/src/camera/mod.rs
index 4865a36..b7c982b 100644
--- a/src/camera/mod.rs
+++ b/src/camera/mod.rs
@@ -7,4 +7,6 @@
pub mod film;
//pub mod filter;
-//pub mod camera;
+pub mod camera;
+
+pub use camera::Camera;
diff --git a/src/core/vector3.rs b/src/core/vector3.rs
index 915765d..6f4d6ab 100644
--- a/src/core/vector3.rs
+++ b/src/core/vector3.rs
@@ -2,7 +2,8 @@
//!
//! Also add more 3d math things needed for shading and 3d calculations.
use crate::{Float, Number};
-use std::ops::{Sub, Add, DivAssign};
+use std::ops::{Mul, Sub, Add, DivAssign};
+use std::fmt;
#[derive(Clone, Copy)]
pub struct Vector3<T: Number> {
@@ -49,6 +50,17 @@ impl<T: Number> Add for Vector3<T> {
}
}
+impl<T: Number> Mul<T> for Vector3<T> {
+ type Output = Self;
+ fn mul(self, op: T) -> Self::Output {
+ Self::Output::new_xyz(
+ self.x * op,
+ self.y * op,
+ self.z * op,
+ )
+ }
+}
+
impl<T: Number> DivAssign<T> for Vector3<T> {
fn div_assign(&mut self, op: T) {
self.x /= op;
@@ -57,6 +69,12 @@ impl<T: Number> DivAssign<T> for Vector3<T> {
}
}
+impl<T: Number> fmt::Display for Vector3<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_fmt(format_args!("[{}, {}, {}]", self.x, self.y, self.z))
+ }
+}
+
impl Vector3f {
/// Calculates the length times itself
///
@@ -97,4 +115,13 @@ impl Vector3f {
new.norm_in();
new
}
+
+ pub fn cross(&self, op: &Self) -> Self {
+ Self::new_xyz(
+ self.y * op.z - self.z * op.y,
+ self.z * op.x - self.x * op.z,
+ self.x * op.y - self.y * op.x,
+ )
+
+ }
}
diff --git a/src/lib.rs b/src/lib.rs
index df9bc1d..c3025b3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,6 +4,7 @@ mod scene;
use std::ops::{Add, Sub, Mul, DivAssign};
use std::cmp;
+use std::fmt;
/// Trait used to implement generics
///
@@ -14,7 +15,8 @@ pub trait Number:
Sub<Output = Self> +
Add<Output = Self> +
Mul<Output = Self> +
- DivAssign
+ DivAssign +
+ fmt::Display
{}
impl Number for i32 {}
diff --git a/src/main.rs b/src/main.rs
index 524f613..22998dd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,17 @@
+use pathtrace::camera::Camera;
+use pathtrace::core::{Vector3f, Vector2f};
fn main() {
+
+ let cam = Camera::new(
+ Vector3f::new(10.0),
+ Vector3f::new(0.0),
+ Vector3f::new_xyz(0.0, 1.0, 0.0),
+ 90.0, Vector2f::new(10.0),
+ );
+
+ let (r, _) = cam.generate_ray(Vector2f::new(5.0));
+
+ println!("r: {}, norm: {}", r.direction, r.direction.norm());
}