Created
November 9, 2019 18:03
-
-
Save mitchellvitez/e17789f49d2528f195d5c3e5462b2eb2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://www.realtimerendering.com/raytracing/Ray%20Tracing%20in%20a%20Weekend.pdf | |
use cgmath::Vector3; | |
use rand::prelude::*; | |
type Point = Vector3<f64>; | |
fn point(x: f64, y: f64, z: f64) -> Point { | |
return Vector3::new(x, y, z); | |
} | |
struct Camera { | |
origin: Point, | |
lower_left_corner: Point, | |
horizontal: Point, | |
vertical: Point, | |
} | |
fn get_ray(c: &Camera, u: f64, v: f64) -> Ray { | |
return Ray { | |
a: c.origin, | |
b: c.lower_left_corner + u*c.horizontal + v*c.vertical - c.origin | |
} | |
} | |
struct Lambertian { | |
albedo: Point, | |
} | |
struct Metal { | |
albedo: Point, | |
} | |
impl Material for Lambertian { | |
fn scatter(&self, r_in: &Ray, rec: &HitRecord, attenuation: &Point, scattered: &Ray) -> bool { | |
let target = rec.p + rec.normal + random_in_unit_sphere(); | |
let scattered = Ray {a: rec.p, b: target - rec.p}; | |
let attenuation = self.albedo; | |
return true; | |
} | |
} | |
impl Material for Metal { | |
fn scatter(&self, r_in: &Ray, rec: &HitRecord, attenuation: &Point, scattered: &Ray) -> bool { | |
let reflected = reflect(&normalize(r_in.direction()), &rec.normal); | |
let scattered = Ray {a: rec.p, b: reflected}; | |
let attenuation = self.albedo; | |
return dot(scattered.direction(), rec.normal) > 0.0; | |
} | |
} | |
fn reflect(v: &Point, n: &Point) -> Point { | |
return v - 2.0 * dot(*v, *n) * n; | |
} | |
struct HitRecord { | |
t: f64, | |
p: Point, | |
normal: Point, | |
material: Box<dyn Material>, | |
} | |
trait Hitable { | |
fn hit(&self, r: &Ray, t_min: f64, t_max: f64, rec: &mut HitRecord) -> bool; | |
} | |
trait Material { | |
fn scatter(&self, r_in: &Ray, rec: &HitRecord, attenuation: &Point, scattered: &Ray) -> bool; | |
} | |
struct Sphere { | |
center: Point, | |
radius: f64, | |
} | |
struct HitableList { | |
list: Vec<Box<dyn Hitable>>, | |
} | |
impl Hitable for HitableList { | |
fn hit(&self, r: &Ray, t_min: f64, t_max: f64, rec: &mut HitRecord) -> bool { | |
let mut hit_anything = false; | |
let mut closest_so_far = t_max; | |
for elt in self.list.iter() { | |
if elt.hit(r, t_min, closest_so_far, rec) { | |
hit_anything = true; | |
closest_so_far = rec.t; | |
} | |
} | |
return hit_anything; | |
} | |
} | |
impl Hitable for Sphere { | |
fn hit(&self, r: &Ray, t_min: f64, t_max: f64, rec: &mut HitRecord) -> bool { | |
let oc = r.origin() - self.center; | |
let a = dot(r.direction(), r.direction()); | |
let b = dot(oc, r.direction()); | |
let c = dot(oc, oc) - self.radius * self.radius; | |
let discriminant = b*b - a*c; | |
if discriminant > 0.0 { | |
let mut temp = (-b - discriminant.sqrt()) / a; | |
if t_min < temp && temp < t_max { | |
rec.t = temp; | |
rec.p = r.point_at_parameter(rec.t); | |
rec.normal = (rec.p - self.center) / self.radius; | |
return true; | |
} | |
temp = (-b + discriminant.sqrt()) / a; | |
if t_min < temp && temp < t_max { | |
rec.t = temp; | |
rec.p = r.point_at_parameter(rec.t); | |
rec.normal = (rec.p - self.center) / self.radius; | |
return true; | |
} | |
} | |
return false; | |
} | |
} | |
struct Ray { | |
a: Point, | |
b: Point, | |
} | |
impl Ray { | |
fn origin(&self) -> Point { | |
return self.a | |
} | |
fn direction(&self) -> Point { | |
return self.b | |
} | |
fn point_at_parameter(&self, t: f64) -> Point { | |
return self.a + t * self.b | |
} | |
} | |
fn normalize(vec: Point) -> Point { | |
return vec / magnitude(vec); | |
} | |
fn magnitude(vec: Point) -> f64 { | |
let x = vec.x; | |
let y = vec.y; | |
let z = vec.z; | |
return (x*x + y*y + z*z).sqrt(); | |
} | |
fn color(r: &Ray, world: &dyn Hitable) -> Point { | |
let mut rec = HitRecord { | |
t: 0.0, | |
p: point(0.0, 0.0, 0.0), | |
normal: point(0.0, 0.0, 0.0), | |
material: Box::new(Metal{albedo: point(0.0, 0.0, 0.0)}), | |
}; | |
if world.hit(r, 0.0, std::f64::MAX, &mut rec) { | |
let target = rec.p + rec.normal + random_in_unit_sphere(); | |
return 0.5 * color(&Ray {a: rec.p, b: target - rec.p}, world); | |
} | |
else { | |
let unit_direction = normalize(r.direction()); | |
let t: f64 = 0.5 * (unit_direction.y + 1.0); | |
return (1.0 - t) * point(1.0, 1.0, 1.0) + t * point(0.5, 0.7, 1.0); | |
} | |
} | |
fn rand_float() -> f64 { | |
random() | |
} | |
fn random_in_unit_sphere() -> Point { | |
loop { | |
let p = 2.0 * point(rand_float(), rand_float(), rand_float()) - point(1.0, 1.0, 1.0); | |
if magnitude(p) < 1.0 { return p } | |
} | |
} | |
fn dot(a: Point, b: Point) -> f64 { | |
return a.x * b.x + a.y * b.y + a.z * b.z; | |
} | |
fn main() { | |
let nx = 500; | |
let ny = 250; | |
let ns = 100; | |
println!("P3\n{} {}\n255", nx, ny); | |
let cam = Camera { | |
lower_left_corner: point(-2.0, -1.0, -1.0), | |
horizontal: point(4.0, 0.0, 0.0), | |
vertical: point(0.0, 2.0, 0.0), | |
origin: point(0.0, 0.0, 0.0), | |
}; | |
let world = HitableList { list: vec![ | |
Box::new(Sphere { center: point(0.0, 0.0, -1.0), radius: 0.5 }), | |
Box::new(Sphere { center: point(0.0, -100.5, -1.0), radius: 100.0 }), | |
]}; | |
for j in (0 .. ny).rev() { | |
for i in 0 .. nx { | |
let mut col = point(0.0, 0.0, 0.0); | |
for _s in 0..ns { | |
let a: f64 = random(); | |
let b: f64 = random(); | |
let u = (i as f64 + a) / nx as f64; | |
let v = (j as f64 + b) / ny as f64; | |
let r = get_ray(&cam, u, v); | |
col += color(&r, &world); | |
col += color(&r, &world); | |
} | |
col /= ns as f64; | |
let ir = 255.99 * col.x.sqrt(); | |
let ig = 255.99 * col.y.sqrt(); | |
let ib = 255.99 * col.z.sqrt(); | |
println!("{} {} {}", ir as u32, ig as u32, ib as u32); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment