Last active
December 31, 2015 07:09
-
-
Save JarrettBillingsley/7952202 to your computer and use it in GitHub Desktop.
Updated to 0.9-pre as of 11 december
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
// #[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