Skip to content

Instantly share code, notes, and snippets.

@axelmagn
Created April 17, 2014 01:12
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 axelmagn/10946425 to your computer and use it in GitHub Desktop.
Save axelmagn/10946425 to your computer and use it in GitHub Desktop.
extern crate rand;
use std::f64;
use std::iter::range_step_inclusive;
use std::io;
use std::num;
use rand::{task_rng,Rng};
static G : [int, ..9] = [247570,280596,280600,249748,18578,18577,231184,16,16];
struct Vector3 {
x: f64,
y: f64,
z: f64
}
impl Vector3 {
fn scale(&self, other: &f64) -> Vector3 {
Vector3 {x: self.x * *other, y: self.y * *other, z: self.z * *other}
}
fn dot(&self, o: &Vector3) -> f64 {
self.x * o.x + self.y * o.y + self.z * o.z
}
fn cross(&self, o: &Vector3) -> Vector3 {
Vector3 {
x: self.y * o.z - self.z * o.y,
y: self.z * o.x - self.x * o.z,
z: self.x * o.y - self.y * o.x
}
}
fn norm(&self) -> Vector3 {
self.scale(&(1.0 / num::sqrt(self.dot(self))))
}
}
impl Add<Vector3, Vector3> for Vector3 {
fn add(&self, other: &Vector3) -> Vector3 {
Vector3 {x: self.x + other.x, y: self.y + other.y, z: self.z + other.z}
}
}
impl Sub<Vector3, Vector3> for Vector3 {
fn sub(&self, other: &Vector3) -> Vector3 {
Vector3 {x: self.x - other.x, y: self.y - other.y, z: self.z - other.z}
}
}
// Vector scaling
impl Mul<f64, Vector3> for Vector3 {
fn mul(&self, o: &f64) -> Vector3 {
self.scale(o)
}
}
// Vector dot product
impl Rem<Vector3, f64> for Vector3 {
fn rem(&self, o: &Vector3) -> f64 {
self.dot(o)
}
}
// Cross product for ^
impl BitXor<Vector3, Vector3> for Vector3 {
fn bitxor(&self, o: &Vector3) -> Vector3 {
self.cross(o)
}
}
// Normalize vector
impl Not<Vector3> for Vector3 {
fn not(&self) -> Vector3 {
self.norm()
}
}
fn randNorm() -> f64 {
let mut rng = task_rng();
let n: f64 = rng.gen_range(0.0, 1.0);
return n;
}
// sample the world and return the pixel color for a ray
fn sample(origin: &Vector3, dir: &Vector3) -> Vector3 {
let(m, t, n) = trace(origin, dir);
if m == 0 {
// no sphere found and ray goes upward: generate sky color
return Vector3{ x: 0.7, y: 0.6, z: 1.0} * num::pow(1.0-dir.z, 4);
}
// A sphere was maybe hit
let mut intersectCoord = origin + dir * t;
let lightDir = !(Vector3{x: 9.0 + randNorm(), y: 9.0 + randNorm(), z: 16.0} + intersectCoord * -1.0);
let halfVect = dir + n * (n % *dir * -2.0);
// calculate lambertian factor
let mut lambertFactor = lightDir % n;
// calculate the illumination factor
let illumFlag =
match (lambertFactor, trace(&intersectCoord, &lightDir)) {
(b, (a, _, _)) if b < 0.0 || a != 0 => { lambertFactor = 0.0; 0 }
_ => { 1 }
};
let p = num::pow(lightDir % halfVect * illumFlag as f64, 99);
if(m == 1) {
intersectCoord = intersectCoord * 0.2;
let patch = ((f64::ceil(intersectCoord.x) + f64::ceil(intersectCoord.y)) as int) & 1;
return match patch {
1 => Vector3{x: 3.0, y: 1.0, z: 1.0},
_ => Vector3{x: 3.0, y: 3.0, z: 3.0} * (lambertFactor * 0.2 + 0.1)
};
}
return Vector3{x: p, y: p, z: p} + sample(&intersectCoord, &halfVect) * 0.5;
}
// The intersection test for line [o,v]
// Return 2 if a hit was found
// Return 0 if no hit was found but ray goes upward
// Return 1 if no hit was found but ray goes downward
fn trace(origin: &Vector3, dir: &Vector3) -> (int, f64, Vector3) {
let mut t = 1e9;
let mut m = 0;
let p = -origin.z / dir.z;
let mut n = Vector3{x: 0.0, y: 0.0, z: 0.0};
if 0.01 < p {
t = p;
n = Vector3{x: 0.0, y: 0.0, z: 1.0};
m = 1;
}
// the world is encoded in G, with 9 lines and 19 columns
for k in range_step_inclusive(18, 0, -1) {
for j in range_step_inclusive(8, 0, -1) {
// for this line j, is there a sphere at column i?
if G[j] & 1 << k != 0 {
let v = origin + Vector3{x: -k as f64,y: 0.0,z: (-j as f64) - 4.0};
let b = v % *dir;
let c = v % v - 1.0;
let q = b * b - c;
// does the ray hit the sphere
if q > 0.0 {
let s = -b - num::sqrt(q);
if s < t && s > 0.1 {
t = s;
n = !(v + dir * t);
m = 2;
}
}
}
}
}
(m, t, n)
}
fn main() {
// PPM header
print!("P6 512 512 255 ");
// Camera direction
let cameraDir = !Vector3{x: -6.0, y: -16.0, z: 0.0};
// Camera up vector
let cameraUp = !(Vector3{x: 0.0, y: 0.0, z: 1.0} ^ cameraDir) * 0.002;
// The right vector
let cameraRight = !(cameraDir ^ cameraUp) * 0.002;
let c=(cameraUp+cameraRight)* -256.0 + cameraDir;
let mut out = io::stdout();
for y in range_step_inclusive(511, 0, -1) {
for x in range_step_inclusive(511, 0, -1) {
// reuse the vector class to store RGB pixels
// default pixel is almost pitch black
let mut p = Vector3{x: 13.0, y: 13.0, z: 13.0};
for r in range_step_inclusive(63, 0, -1) {
// delta applied to the origin for depth of field blur
let cameraDelta = cameraDir * (randNorm() - 0.5) * 99.0 + cameraRight * (randNorm() - 0.5) * 99.0;
// set the camera focal point to v(17,16,8) and Cast the ray
let focalPoint = Vector3{x: 17.0, y: 16.0, z: 8.0};
let rayDirection = !(cameraDelta * -1.0 +
(cameraUp * (randNorm() + (x as f64)) +
cameraRight * ((y as f64) + randNorm()) + c) * 16.0);
// Accumulate the color returned in the p variable
p = sample( &(focalPoint + cameraDelta), &rayDirection) * 3.5 + p;
}
// print!("{:s}{:s}{:s}", (p.x as u8).to_ascii().to_str(), (p.y as u8).to_ascii().to_str(), (p.z as u8).to_ascii().to_str());
// print!("{:t}{:t}{:t}", (p.x as u8), (p.y as u8), (p.z as u8));
out.write(&[(p.x as u16),(p.x as u16),(p.x as u16)]);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment