Skip to content

Instantly share code, notes, and snippets.

@JarrettBillingsley
Last active December 31, 2015 07:09
Show Gist options
  • Save JarrettBillingsley/7952202 to your computer and use it in GitHub Desktop.
Save JarrettBillingsley/7952202 to your computer and use it in GitHub Desktop.
Updated to 0.9-pre as of 11 december
// #[link_args = "-ffast_math"];
use std::os;
use std::unstable::intrinsics::sqrtf64;
use std::f64;
use std::from_str::{from_str};
use std::vec;
use std::path::{Path};
use std::io::{File, Open, Write};
use std::io::buffered::{BufferedWriter};
#[deriving(Clone)]
struct Vec { // Usage: time ./smallpt 5000 && xv image.ppm
x : f64,
y : f64,
z : f64
} // position, also color (r,g,b)
pub fn Vec(x: f64, y: f64, z: f64) -> Vec {
Vec {x: x, y: y, z: z}
}
impl Add<Vec, Vec> for Vec {
fn add(&self, b: &Vec) -> Vec { Vec { x: self.x+b.x, y: self.y+b.y, z: self.z+b.z } }
}
impl Sub<Vec, Vec> for Vec {
fn sub(&self, b: &Vec) -> Vec { Vec { x: self.x-b.x, y: self.y-b.y, z: self.z-b.z } }
}
impl Mul<f64, Vec> for Vec {
fn mul(&self, b: &f64) -> Vec { Vec { x: self.x* *b, y: self.y* *b, z: self.z* *b } }
}
impl Rem<Vec, Vec> for Vec {
fn rem(&self, b: &Vec) -> Vec { Vec{ x: self.y*b.z-self.z*b.y, y: self.z*b.x-self.x*b.z, z: self.x*b.y-self.y*b.x } }
}
impl Vec {
fn mult(&self, b: &Vec) -> Vec { Vec{ x: self.x*b.x, y: self.y*b.y, z: self.z*b.z } }
fn norm(&self) -> Vec { unsafe { *self * (1f64/sqrtf64(self.x*self.x+self.y*self.y+self.z*self.z)) } }
fn dot(&self, b: &Vec) -> f64 { self.x*b.x+self.y*b.y+self.z*b.z } // cross:
}
struct Ray { o : Vec, d : Vec }
enum Refl_t { DIFF, SPEC, REFR } // material types, used in radiance()
struct Sphere {
rad : f64,
p : Vec,
e : Vec,
c : Vec,
refl : Refl_t
}
impl Sphere {
fn intersect(&self, r : &Ray) -> f64 { // returns distance, 0 if nohit
let op = self.p - r.o; // Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0
let eps = 1e-4f64;
let b = op.dot(&r.d);
let mut det=b*b-op.dot(&op)+self.rad*self.rad;
if det < 0f64 { return 0f64; } else { unsafe { det = sqrtf64(det) } };
let mut t = b-det;
if ( t>eps ) { t } else {
t = b + det;
if t > eps { t } else { 0f64 }
}
}
}
fn clamp(x : f64) -> f64 { if x < 0f64 { 0f64 } else { if x > 1f64 { 1f64 } else { x } } }
fn toInt(x : f64) -> uint { return (f64::pow(clamp(x),1f64/2.2f64)*255f64+0.5f64) as uint; }
fn intersect (r: &Ray, t: &mut f64, id: &mut int, spheres: &[Sphere] ) -> bool {
let n = spheres.len();
let inf = 1e20f64;
*t = inf;
let mut i = n as i64;
while i > 0i64 {
i -= 1;
let d = spheres[i].intersect(r);
if d != 0.0f64 && d < *t { *t = d; *id = i as int; }
}
return *t < inf;
}
struct KissRng {
x : u32,
y : u32,
z : u32,
w : u32,
carry : u32,
}
impl KissRng {
fn gen_u32( &mut self ) -> u32 {
self.x = self.x * 69069 + 1;
self.y ^= self.y << 13;
self.y ^= self.y >> 17;
self.y ^= self.y << 5;
let k = (self.z >> 2) + (self.w >> 3) + (self.carry >> 2);
let m = self.w + self.w + self.z + self.carry;
self.z = self.w;
self.w = m;
self.carry = k >> 30;
return self.x + self.y + self.w;
}
fn gen_f64( &mut self ) -> f64 {
( self.gen_u32() as f64 ) / 4_294_967_295.0f64
}
}
pub fn KissRng( seed: u32 ) -> KissRng {
KissRng {
x: seed | 1,
y: seed | 2,
z: seed | 4,
w: seed | 8,
carry: 0,
}
}
fn radiance(r: &Ray, depth: int, Xi: &mut KissRng, spheres: &[Sphere]) -> Vec {
if depth > 100 {
return Vec( 0f64, 0f64, 0f64 );
}
let mut t = 0f64; // distance to intersection
let mut id : int = 0; // id of intersected object
if !intersect(r, &mut t, &mut id, spheres) { return Vec( 0f64, 0f64, 0f64 ); } // if miss, return black
let obj = &spheres[id]; // the hit object
let x =r.o+r.d*t;
let n=(x-obj.p).norm();
let nl=if n.dot(&r.d) < 0f64 { n } else { n * -1f64 };
let mut f=obj.c;
let p = if f.x>f.y && f.x>f.z { f.x } else { if f.y > f.z { f.y } else { f.z } }; // max refl
let depth = depth + 1;
if depth > 5 {
if Xi.gen_f64() < p {
f = f * (1f64 / p);
} else {
return obj.e;
}
} //R.R.
match obj.refl {
DIFF => { // Ideal DIFFUSE reflection
let r1 = 2f64 * 3.14159265f64 * Xi.gen_f64();
let r2 = Xi.gen_f64();
let r2s = unsafe { sqrtf64(r2) };
let w = nl;
let u = ((if f64::abs(w.x) > 0.1f64 { Vec(0f64,1f64,0f64) } else { Vec(1f64,0f64,0f64) }) %w).norm();
let v = w%u;
let d = unsafe { (u*f64::cos(r1)*r2s + v*f64::sin(r1)*r2s + w*sqrtf64(1f64-r2)).norm() };
let refl_ray = Ray{ o: x, d: d };
let refl_rad = radiance( &refl_ray,depth,Xi,spheres );
return obj.e + f.mult(&refl_rad);
}
SPEC => { // Ideal SPECULAR reflection
let refl_ray = Ray{ o: x, d: r.d-n*2f64*n.dot(&r.d) };
let refl_rad = radiance( &refl_ray, depth, Xi, spheres );
return obj.e + f.mult( &refl_rad );
}
_ => ()
}
let reflRay = Ray{ o: x, d: r.d-n*2f64*n.dot(&r.d) }; // Ideal dielectric REFRACTION
let into = n.dot(&nl) > 0f64; // Ray from outside going in?
let nc = 1f64;
let nt = 1.5f64;
let nnt = if into { nc/nt } else { nt/nc };
let ddn = r.d.dot(&nl);
let cos2t = 1f64-nnt*nnt*(1f64-ddn*ddn);
if cos2t < 0f64 { // Total internal reflection
let refl_rad = radiance(&reflRay,depth,Xi,spheres);
return obj.e + f.mult(&refl_rad);
}
let tdir = unsafe { (r.d*nnt - n*((if into { 1f64 } else { -1f64 } )*(ddn*nnt+sqrtf64(cos2t)))).norm() };
let a = nt-nc;
let b = nt+nc;
let R0=a*a/(b*b);
let c = 1f64-(if into {-ddn } else { tdir.dot(&n) });
let Re=R0+(1f64-R0)*c*c*c*c*c;
let Tr=1f64-Re;
let P=0.25f64+0.5f64*Re;
let RP=Re/P;
let TP=Tr/(1f64-P);
let refl_ray2 = Ray{ o: x, d: tdir };
let refl_rad = if depth>2 {
if Xi.gen_f64() < P { // Russian roulette
radiance(&reflRay,depth,Xi,spheres) * RP
} else {
radiance(&refl_ray2,depth,Xi,spheres) * TP
}
} else {
radiance(&reflRay,depth,Xi,spheres) * Re
+ radiance(&refl_ray2,depth,Xi,spheres) * Tr
};
return obj.e + f.mult(&refl_rad);
}
fn main() {
let w = 1024u;
let h = 768u;
let args = os::args();
let samps = if args.len() == 2u { from_str::<uint>( args[ 1 ] ).unwrap() / 4 } else { 1 };
let cam = Ray{ o:Vec(50f64,52f64,295.6f64), d:Vec(0f64,-0.042612f64,-1f64).norm() }; // cam pos, dir
let cx = Vec(w as f64 * 0.5135f64 / (h as f64), 0f64, 0f64);
let cy = (cx%cam.d).norm()*0.5135f64;
let mut r = Vec( 0f64, 0f64, 0f64 );
let mut c: ~[Vec] = vec::from_elem( w * h, r );
let spheres = ~[//Scene: radius, position, emission, color, material
Sphere{ rad: 1.0e5f64, p: Vec( 1e5f64+1f64,40.8f64,81.6f64), e: Vec(0f64, 0f64, 0f64),c: Vec(0.75f64,0.25f64,0.25f64),refl: DIFF},//Left
Sphere{ rad: 1.0e5f64, p: Vec(-1e5f64+99f64,40.8f64,81.6f64),e: Vec(0f64, 0f64, 0f64),c: Vec(0.25f64,0.25f64,0.75f64),refl: DIFF},//Rght
Sphere{ rad: 1.0e5f64, p: Vec(50f64,40.8, 1e5f64), e: Vec(0f64, 0f64, 0f64),c: Vec(0.75f64,0.75f64,0.75f64),refl: DIFF},//Back
Sphere{ rad: 1.0e5f64, p: Vec(50f64,40.8,-1e5f64+170f64), e: Vec(0f64, 0f64, 0f64),c: Vec(0f64, 0f64, 0f64), refl: DIFF},//Frnt
Sphere{ rad: 1.0e5f64, p: Vec(50f64, 1e5f64, 81.6f64), e: Vec(0f64, 0f64, 0f64),c: Vec(0.75f64,0.75f64,0.75f64),refl: DIFF},//Botm
Sphere{ rad: 1.0e5f64, p: Vec(50f64,-1e5f64+81.6f64,81.6f64),e: Vec(0f64, 0f64, 0f64),c: Vec(0.75f64,0.75f64,0.75f64),refl: DIFF},//Top
Sphere{ rad: 16.5f64, p:Vec(27f64,16.5f64,47f64), e: Vec(0f64, 0f64, 0f64),c: Vec(1f64,1f64,1f64)*0.999f64, refl: SPEC},//Mirr
Sphere{ rad: 16.5f64, p:Vec(73f64,16.5f64,78f64), e: Vec(0f64, 0f64, 0f64),c: Vec(1f64,1f64,1f64)*0.999f64, refl: REFR},//Glas
Sphere{ rad: 600.0f64, p: Vec(50f64,681.6f64-0.27f64,81.6f64),e: Vec(12f64,12f64,12f64), c: Vec(0f64, 0f64, 0f64), refl: DIFF},//Lite
];
let mut Xi = KissRng( 456213456u32 );//rand::Rng();
for y in range( 0u, h ) {
print!("\rRendering ({} spp) {}%", samps * 4, 100.0f64 * (y as f64) / ((h-1) as f64));
for x in range( 0u, w ) {
let i = (h-y-1)*w+x;
for sy in range( 0u, 2 ) {
for sx in range( 0u, 2 ) {
r = Vec( 0f64, 0f64, 0f64 );
for _ in range( 0u, samps ) {
let r1 = 2f64 * Xi.gen_f64();
let dx = if r1 < 1f64 { unsafe { sqrtf64(r1)-1f64 } } else { unsafe { 1f64-sqrtf64(2f64-r1) } };
let r2 = 2f64 * Xi.gen_f64();
let dy = if r2 < 1f64 { unsafe { sqrtf64(r2)-1f64 } } else { unsafe { 1f64-sqrtf64(2f64-r2) } };
let d = cx * (((sx as f64+0.5f64 + dx)/2f64 + x as f64)/(w as f64) - 0.5f64)
+ cy * (((sy as f64+0.5f64 + dy)/2f64 + y as f64)/(h as f64) - 0.5f64)
+ cam.d;
let sample_ray = Ray{ o: cam.o+d*140f64, d: d.norm()};
r = r + radiance(&sample_ray, 0, &mut Xi, spheres)*(1.0f64 / (samps as f64));
} // Camera rays are pushed ^^^^^ forward to start in interior
c[i] = c[i] + Vec(clamp(r.x),clamp(r.y),clamp(r.z))*0.25f64;
}
}
}
}
let mut file = BufferedWriter::new(File::open_mode(&Path::new("image_rust.ppm"), Open, Write));
let herp = format!( "P3\n{} {}\n{}\n", w, h, 255 );
file.write( herp.as_bytes() );
for i in range( 0u, w * h ) {
let derp = format!( "{} {} {} ", toInt(c[i].x), toInt(c[i].y), toInt(c[i].z));
file.write( derp.as_bytes() );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment