Skip to content

Instantly share code, notes, and snippets.

@mitchellvitez
Created November 9, 2019 18:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mitchellvitez/e17789f49d2528f195d5c3e5462b2eb2 to your computer and use it in GitHub Desktop.
Save mitchellvitez/e17789f49d2528f195d5c3e5462b2eb2 to your computer and use it in GitHub Desktop.
// 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