Skip to content

Instantly share code, notes, and snippets.

@jdmichaud
Forked from joshmarinacci/main.rs
Last active October 22, 2018 10:23
Show Gist options
  • Save jdmichaud/759d5a1b6a396ccf285b27f96feb2e8b to your computer and use it in GitHub Desktop.
Save jdmichaud/759d5a1b6a396ccf285b27f96feb2e8b to your computer and use it in GitHub Desktop.
Simple ray tracer in Rust
#[derive(Copy,Clone,Debug)]
struct Vector {
x: f32,
y: f32,
z: f32
}
impl Vector {
fn new(x: f32,y: f32,z: f32) -> Vector {
Vector { x:x, y:y, z:z }
}
fn scale(&self, s: f32) -> Vector { Vector { x:self.x * s, y:self.y * s, z:self.z * s } }
fn plus(&self, b: &Vector) -> Vector { Vector::new(self.x + b.x, self.y + b.y, self.z + b.z) }
fn minus(&self, b: &Vector) -> Vector { Vector::new(self.x - b.x, self.y - b.y, self.z - b.z) }
fn dot(&self, b: &Vector) -> f32 { self.x * b.x + self.y * b.y + self.z * b.z }
fn magnitude(&self) -> f32 { (self.dot(self)).sqrt() }
fn normalize(&self) -> Vector { self.scale(1.0 / self.magnitude()) }
}
#[derive(Copy,Clone,Debug)]
struct Ray {
orig: Vector,
dir: Vector,
}
#[derive(Copy,Clone,Debug)]
struct Color {
r: f32,
g: f32,
b: f32,
}
impl Color {
fn scale (&self, s: f32) -> Color {
Color { r: self.r * s, g: self.g * s, b: self.b * s }
}
fn plus (&self, b: Color) -> Color {
Color { r: self.r + b.r, g: self.g + b.g, b: self.b + b.b }
}
}
#[derive(Copy,Clone,Debug)]
struct Sphere {
center: Vector,
radius: f32,
color: Color,
}
impl Sphere {
fn get_normal(&self, pt: Vector) -> Vector {
return pt.minus(&self.center).normalize();
}
}
struct Light {
position: Vector,
color: Color,
}
const WHITE: Color = Color { r:1.0, g:1.0, b:1.0};
#[allow(dead_code)]
const RED: Color = Color { r:1.0, g:0.0, b:0.0};
const GREEN: Color = Color { r:0.0, g:1.0, b:0.0};
#[allow(dead_code)]
const BLUE: Color = Color { r:0.0, g:0.0, b:1.0};
const LIGHT1:Light = Light {
position: Vector { x: 0.7, y: -1.0, z: 1.7} ,
color: WHITE
};
fn main() {
let lut = vec!(".","-","+","*","X","M");
let w = 80;
let h = w/2;
let scene = vec!(
Sphere{ center: Vector::new(-1.0, 0.0, 6.0), radius: 0.8, color: RED },
Sphere{ center: Vector::new( 0.0, 0.0, 4.0), radius: 0.8, color: GREEN },
Sphere{ center: Vector::new( 1.0, 0.0, 3.0), radius: 0.8, color: BLUE }
);
for j in 0..h {
println!("");
for i in 0..w {
//let tMax = 10000f32;
let fw: f32 = w as f32;
let fi: f32 = i as f32;
let fj: f32 = j as f32;
let fh: f32 = h as f32;
let ray = Ray {
orig: Vector::new(0.0, 0.0, 0.0),
dir: Vector::new(
(fi - fw / 2.0) / fw,
(fj - fh / 2.0) / fh,
1.0).normalize(),
};
let mut obj_hit_obj:Option<(Sphere,f32)> = None;
for obj in scene.iter() {
let ret = intersect_sphere(&ray, &obj.center, obj.radius);
if ret.hit {
obj_hit_obj = Some((*obj,ret.tval));
}
}
let pixel = match obj_hit_obj {
Some((obj,tval)) => lut[shade_pixel(ray, obj, tval)],
None => " "
};
print!("{}",pixel);
}
}
println!("");
}
fn shade_pixel(ray:Ray, obj:Sphere, tval: f32) -> usize {
let pi = ray.orig.plus(&ray.dir.scale(tval));
let color = diffuse_shading(pi, obj, LIGHT1);
let col = (color.r + color.g + color.b) / 3.0;
(col * 6.0) as usize
}
struct HitPoint {
hit:bool,
tval: f32,
}
fn intersect_sphere(ray:&Ray, center: &Vector, radius: f32) -> HitPoint {
let l = center.minus(&ray.orig);
let tca = l.dot(&ray.dir);
if tca < 0.0 {
return HitPoint { hit:false, tval:-1.0 };
}
let d2 = l.dot(&l) - tca*tca;
let r2 = radius*radius;
if d2 > r2 {
return HitPoint { hit: false, tval:-1.0 };
}
let thc = (r2-d2).sqrt();
let t0 = tca-thc;
//let t1 = tca+thc;
if t0 > 10000.0 {
return HitPoint { hit: false, tval: -1.0 };
}
return HitPoint { hit: true, tval: t0}
}
fn clamp(x: f32,a: f32,b: f32) -> f32{
if x < a { return a; }
if x > b { return b; }
return x;
}
fn diffuse_shading(pi: Vector, obj:Sphere, light:Light) -> Color{
let n = obj.get_normal(pi);
let lam1 = light.position.minus(&pi).normalize().dot(&n);
let lam2 = clamp(lam1,0.0,1.0);
light.color.scale(lam2*0.5).plus(obj.color.scale(0.3))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment