Add existing files

This commit is contained in:
bijan2005 2020-11-27 00:02:03 -05:00
commit 9d188ed692
2517 changed files with 58438 additions and 0 deletions

127
src/camera.rs Normal file
View file

@ -0,0 +1,127 @@
extern crate nalgebra as na;
use na::*;
use na::geometry::{Point2, Point3};
use crate::types::Ray;
#[derive(Debug)]
pub struct Camera {
matrix: Isometry3<f32>, // The transformation that stores the
// position and orientation of the camera. (Not actually a matrix, but w/e)
focal_length: f32, // The distance from the camera origin to the canvas.
canvas_size: Vector2<f32>, // The size of the canvas within the world space.
pub image_size: Vector2<u32> // The size of the final image in pixels.
}
impl Camera {
// Constructs a new camera from a position and viewing direction
// (assuming the camera is oriented upright).
pub fn new(pos: Point3<f32>, dir: Vector3<f32>, focal_length: f32,
canvas_x: f32, canvas_y: f32, image_x: u32, image_y: u32) -> Self {
let iso = Isometry3::face_towards(&pos, &(pos + dir), &Vector3::y());
Camera {
matrix: iso,
focal_length: focal_length,
canvas_size: Vector2::new(canvas_x, canvas_y),
image_size: Vector2::new(image_x, image_y)
}
}
pub fn pos(&self) -> Point3<f32> { Point3::from(self.matrix.translation.vector) }
// Takes a 2D point in the image space and
// maps it to the 3D point on the canvas.
fn project(&self, x: u32, y: u32) -> Point3<f32> {
// convert point from raster coordinates to center-based coordinates
let pixelndc = Point2::new(x as f32 + 0.5 - self.image_size.x as f32 * 0.5, -(y as f32 + 0.5) + self.image_size.y as f32 * 0.5);
let point: Point3<f32> = Point::from(pixelndc.coords.component_div(&self.image_size.map(|x| x as f32))
.component_mul(&self.canvas_size)
.fixed_resize(self.focal_length));
self.matrix * point
}
// Takes a 2D point in the image space and
// returns a ray in the world space, for use in raytracing.
pub fn raycast(&self, x: u32, y: u32) -> Ray {
Ray::from_points(self.pos(), self.project(x, y))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn round(point: Point3<f32>) -> Point3<f32> {
Point::from(point.coords.map(|x| x.round()))
}
#[test]
fn camera_pos() {
let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0),
Vector3::new(1.0, 0.0, 0.0),
1.0,
2.0, 2.0,
800, 800);
assert_eq!(camera.pos(), Point3::new(-5.0, 0.0, 0.0));
}
#[test]
fn camera_matrix1() {
let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0),
Vector3::new(1.0, 0.0, 0.0),
1.0,
2.0, 2.0,
800, 800);
let point = Point3::new(0.0, 0.0, 4.0);
let point = camera.matrix * point;
let point = round(point); // round to avoid errors
assert_eq!(point, Point3::new(-1.0, 0.0, 0.0));
}
#[test]
fn camera_matrix2() {
let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0),
Vector3::new(1.0, 0.0, 0.0),
1.0,
2.0, 2.0,
800, 800);
let point = Point3::new(4.0, 0.0, 0.0);
let point = camera.matrix * point;
let point = round(point); // round to avoid errors
assert_eq!(point, Point3::new(-5.0, 0.0, -4.0));
}
#[test]
fn camera_project1() {
let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0),
Vector3::new(1.0, 0.0, 0.0),
1.0,
2.0, 2.0,
800, 800);
let point = camera.project(400, 400);
let point = round(point); // round to avoid errors
assert_eq!(point, Point3::new(-4.0, 0.0, 0.0));
}
#[test]
fn camera_project2() {
let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0),
Vector3::new(1.0, 0.0, 0.0),
1.0,
2.0, 2.0,
800, 800);
let point = camera.project(0, 0);
let point = round(point); // round to avoid errors
assert_eq!(point, Point3::new(-4.0, 1.0, 1.0));
}
}

62
src/main.rs Normal file
View file

@ -0,0 +1,62 @@
extern crate nalgebra as na;
use std::cmp::Ordering;
use std::fs::File;
use std::io::Write;
use na::*;
use na::geometry::Point3;
mod camera; use camera::*;
mod types; use types::*;
mod object; use object::*;
fn cast_ray(ray: Ray, scene: &Scene) -> Color {
//Color::new(0.0, -ray.direction.x, ray.direction.y)
let closest = scene.iter()
.filter_map(|obj| obj.intersect(ray)
.map(|x| (obj, x)))
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal));
if closest.is_some() {
Color::new(1.0, 1.0, 1.0)
} else {
Color::new(0.0, 0.0, 0.0)
}
}
fn render(camera: &Camera, scene: &Scene, filename: &str) -> std::io::Result<()> {
let width = camera.image_size.x;
let height = camera.image_size.y;
let mut buffer: Vec<Color> = Vec::with_capacity((width * height) as usize);
for j in 0..height {
for i in 0..width {
let ray = camera.raycast(i, j);
buffer.push(cast_ray(ray, &scene));
}
// println!("Rendered row {}", j);
}
let mut file = File::create(filename)?;
file.set_len(0)?;
file.write_all(format!("P6\n{} {}\n255\n", width, height).as_bytes())?;
for color in buffer.into_iter() {
file.write_all(&color.to_byte_array())?;
}
Ok(())
}
fn main() -> std::io::Result<()> {
let camera = Camera::new(Point3::new(0.0,0.0,0.0), Vector3::new(0.0,0.0,-1.0), 1.0, 2.0, 2.0, 400, 400);
let scene = vec![
Object::Sphere(Sphere::new(0.0,0.0,-5.0,2.0)),
Object::Sphere(Sphere::new(-3.0,0.0,-8.0,2.5))
];
render(&camera, &scene, "out.ppm")
}

27
src/object.rs Normal file
View file

@ -0,0 +1,27 @@
mod sphere; pub use sphere::*;
mod triangle; pub use triangle::*;
use na::*;
use crate::types::Ray;
pub enum Object {
Sphere(Sphere)
}
impl Object {
pub fn intersect(&self, ray: Ray) -> Option<f32> {
match *self {
Object::Sphere(ref sphere) => sphere.intersect(ray)
}
}
pub fn normal(&self, ray: Ray) -> Unit<Vector3<f32>> {
match *self {
Object::Sphere(ref sphere) => sphere.normal(ray)
}
}
}
pub type Scene = Vec<Object>;

52
src/object/sphere.rs Normal file
View file

@ -0,0 +1,52 @@
extern crate nalgebra as na;
use na::*;
use na::geometry::Point3;
use crate::types::*;
pub struct Sphere {
pub center: Point3<f32>,
pub radius: f32
}
impl Sphere {
pub fn new(x: f32, y: f32, z: f32, radius: f32) -> Self {
Sphere {
center: Point3::new(x, y, z),
radius: radius
}
}
pub fn intersect(&self, ray: Ray) -> Option<f32> {
fn solve_quadratic(a: f32, b: f32, c: f32) -> Option<(f32, f32)> {
let discr = b * b - 4.0 * a * c;
if discr < 0.0 { None }
else if discr == 0.0 {
let x = -0.5 * b / a;
Some((x, x))
} else {
let q = if b > 0.0 { -0.5 * (b + discr.sqrt()) } else { -0.5 * (b - discr.sqrt()) };
Some((q / a, c / q))
}
}
let l = ray.origin - self.center;
let a = ray.direction.dot(&ray.direction);
let b = 2.0 * ray.direction.dot(&l);
let c = l.dot(&l) - self.radius * self.radius;
let (mut t0, mut t1) = solve_quadratic(a, b, c)?;
if t0 > t1 { std::mem::swap(&mut t0, &mut t1); }
if t0 > 0.0 { Some(t0) }
else if t1 > 0.0 { Some(t1) }
else { None }
}
pub fn normal(&self, ray: Ray) -> Unit<Vector3<f32>> {
unimplemented!()
}
}

0
src/object/triangle.rs Normal file
View file

50
src/types.rs Normal file
View file

@ -0,0 +1,50 @@
extern crate nalgebra as na;
use na::*;
use na::geometry::Point3;
#[derive(Clone, Copy, Debug)]
pub struct Ray {
pub origin: Point3<f32>,
pub direction: Unit<Vector3<f32>>
}
impl Ray {
pub fn from_parts(origin: Point3<f32>, direction: Unit<Vector3<f32>>) -> Self {
Ray {
origin: origin,
direction: direction
}
}
pub fn new(origin: Point3<f32>, direction: Vector3<f32>) -> Self { Ray::from_parts(origin, Unit::new_normalize(direction)) }
pub fn from_points(a: Point3<f32>, b: Point3<f32>) -> Self { Ray::new(a, b - a) }
pub fn project(&self, t: f32) -> Point3<f32> { self.origin + t * self.direction.into_inner() }
}
#[derive(Clone, Copy, Debug)]
pub struct Color {
pub red: f32,
pub green: f32,
pub blue: f32,
_private: () // Private field prevents direct construction
}
impl Color {
pub fn new(red: f32, green: f32, blue: f32) -> Self {
Color {
red: clamp(red, 0.0, 1.0),
green: clamp(green, 0.0, 1.0),
blue: clamp(blue, 0.0, 1.0),
_private: ()
}
}
pub fn to_byte_array(&self) -> [u8; 3] {
let red = (255.0 * self.red) as u8;
let green = (255.0 * self.green) as u8;
let blue = (255.0 * self.blue) as u8;
[red, green, blue]
}
}