From 2554b4f13deabbcb01956cfd403d473b344b5394 Mon Sep 17 00:00:00 2001 From: Mikhail Glushenkov Date: Fri, 13 Jul 2012 21:58:44 +0200 Subject: [PATCH 1/4] The 'cont' keyword is now called 'again'. --- model.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model.rs b/model.rs index 7642946..fbb7cc3 100644 --- a/model.rs +++ b/model.rs @@ -274,7 +274,7 @@ fn read_polysoup(fname: str) -> polysoup { while !reader.eof() { let line : str = reader.read_line(); if str::is_empty(line) { - cont; + again; } let mut num_texcoords = 0u; From 0aa12239abb1b58933119518cdac40416c51109f Mon Sep 17 00:00:00 2001 From: Mikhail Glushenkov Date: Fri, 13 Jul 2012 21:59:24 +0200 Subject: [PATCH 2/4] Move all constants to a separate module. Apparently, mutually recursive modules are no longer supported. --- consts.rs | 10 ++++++++++ main.rs | 12 +----------- raytracer.rs | 2 +- rustray.rc | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 consts.rs diff --git a/consts.rs b/consts.rs new file mode 100644 index 0000000..5701084 --- /dev/null +++ b/consts.rs @@ -0,0 +1,10 @@ +// TODO: These things should all be overridable by the command line +const WIDTH : uint = 256u; +const HEIGHT : uint = 256u; +const FOV : f32 = 3.14159f32 / 3f32 ; +const SAMPLE_GRID_SIZE : uint = 1u; +const NUM_GI_SAMPLES_SQRT: uint = 8u; +const NUM_LIGHT_SAMPLES : uint = 32u; +const MAX_TRACE_DEPTH : uint = 3u; +const USE_SMOOTH_NORMALS_FOR_GI : bool = true; +const USE_SMOOTH_NORMALS_FOR_DIRECT_LIGHTING : bool = true; diff --git a/main.rs b/main.rs index 0f6aa5b..f865480 100644 --- a/main.rs +++ b/main.rs @@ -1,18 +1,8 @@ import io::writer_util; import io::writer; +import consts::*; import raytracer::*; -// TODO: These things should all be overridable by the command line -const WIDTH : uint = 256u; -const HEIGHT : uint = 256u; -const FOV : f32 = 3.14159f32 / 3f32 ; -const SAMPLE_GRID_SIZE : uint = 1u; -const NUM_GI_SAMPLES_SQRT: uint = 8u; -const NUM_LIGHT_SAMPLES : uint = 32u; -const MAX_TRACE_DEPTH : uint = 3u; -const USE_SMOOTH_NORMALS_FOR_GI : bool = true; -const USE_SMOOTH_NORMALS_FOR_DIRECT_LIGHTING : bool = true; - fn write_ppm( fname: str, width: uint, height: uint, pixels: [color] ){ let writer = result::get( io::file_writer( fname, [io::create, io::truncate] ) ); diff --git a/raytracer.rs b/raytracer.rs index 6d82214..91c0df8 100644 --- a/raytracer.rs +++ b/raytracer.rs @@ -1,7 +1,7 @@ import std::*; import math3d::*; -import main::*; // for the consts.. ugh... make the constants go away +import consts::*; // for the consts.. ugh... make the constants go away type color = { r:u8, g:u8, b:u8 }; diff --git a/rustray.rc b/rustray.rc index 708597f..a4bf96e 100644 --- a/rustray.rc +++ b/rustray.rc @@ -14,4 +14,4 @@ mod main; mod model; mod raytracer; mod math3d; - +mod consts; From 2fef36ce5d0f17dfd40a8c21b3da01a8d5be12b4 Mon Sep 17 00:00:00 2001 From: Mikhail Glushenkov Date: Fri, 13 Jul 2012 22:41:30 +0200 Subject: [PATCH 3/4] Whitespace cleanup. --- main.rs | 52 +-- math3d.rs | 220 ++++++----- model.rs | 591 ++++++++++++++-------------- raytracer.rs | 1063 +++++++++++++++++++++++++------------------------- 4 files changed, 960 insertions(+), 966 deletions(-) diff --git a/main.rs b/main.rs index f865480..4e690c3 100644 --- a/main.rs +++ b/main.rs @@ -5,12 +5,12 @@ import raytracer::*; fn write_ppm( fname: str, width: uint, height: uint, pixels: [color] ){ - let writer = result::get( io::file_writer( fname, [io::create, io::truncate] ) ); + let writer = result::get( io::file_writer( fname, [io::create, io::truncate] ) ); - writer.write_str(#fmt("P6\n%u %u\n255\n", width, height)); - for pixels.each |pixel| { - writer.write([pixel.r, pixel.g, pixel.b]); - }; + writer.write_str(#fmt("P6\n%u %u\n255\n", width, height)); + for pixels.each |pixel| { + writer.write([pixel.r, pixel.g, pixel.b]); + }; } fn main( args: [str] ) @@ -25,29 +25,29 @@ fn main( args: [str] ) io::println(""); fail; } - - let start = std::time::precise_time_s(); - + let start = std::time::precise_time_s(); + + io::println("Reading \"" + args[1] + "\"..."); - let model = model::read_mesh( args[1] ); - let {depth,count} = model::count_kd_tree_nodes( model.kd_tree ); - - io::println(#fmt("Done.\nLoaded model.\n\tVerts: %u, Tris: %u\n\tKD-tree depth: %u, #nodes: %u", - vec::len(model.polys.vertices), - vec::len(model.polys.indices)/3u, - depth, count)); - - io::print("Tracing rays... "); - let start_tracing = std::time::precise_time_s(); - let pixels = raytracer::generate_raytraced_image(model, FOV, WIDTH, HEIGHT, SAMPLE_GRID_SIZE); - io::println("Done!"); - + let model = model::read_mesh( args[1] ); + let {depth,count} = model::count_kd_tree_nodes( model.kd_tree ); + + io::println(#fmt("Done.\nLoaded model.\n\tVerts: %u, Tris: %u\n\tKD-tree depth: %u, #nodes: %u", + vec::len(model.polys.vertices), + vec::len(model.polys.indices)/3u, + depth, count)); + + io::print("Tracing rays... "); + let start_tracing = std::time::precise_time_s(); + let pixels = raytracer::generate_raytraced_image(model, FOV, WIDTH, HEIGHT, SAMPLE_GRID_SIZE); + io::println("Done!"); + let outputfile = "./oput.ppm"; - io::print("Writing \"" + outputfile + "\"..."); - write_ppm( outputfile, WIDTH, HEIGHT, pixels ); - io::println("Done!"); - - let end = std::time::precise_time_s(); + io::print("Writing \"" + outputfile + "\"..."); + write_ppm( outputfile, WIDTH, HEIGHT, pixels ); + io::println("Done!"); + + let end = std::time::precise_time_s(); io::print(#fmt("Total time: %3.3fs, of which tracing: %3.3f\n", end - start, end - start_tracing) ); } diff --git a/math3d.rs b/math3d.rs index 22f9df1..f90715d 100644 --- a/math3d.rs +++ b/math3d.rs @@ -1,79 +1,77 @@ - - type vec3 = { x:f32, y:f32, z:f32 }; type mtx33 = { r0:vec3, r1:vec3, r2:vec3 }; -#[inline(always)] +#[inline(always)] fn vec3(x:f32, y:f32, z:f32) -> vec3{ - {x:x, y:y, z:z} + {x:x, y:y, z:z} } -#[inline(always)] +#[inline(always)] fn dot(a:vec3, b:vec3) -> f32 { - a.x*b.x + a.y*b.y + a.z*b.z + a.x*b.x + a.y*b.y + a.z*b.z } -#[inline(always)] +#[inline(always)] fn lerp(a:vec3, b:vec3, t:f32) -> vec3{ - add( a, scale(sub(b,a),t) ) + add( a, scale(sub(b,a),t) ) } -#[inline(always)] +#[inline(always)] fn scale(v:vec3, s:f32) -> vec3 { - { x:v.x*s, y:v.y*s, z:v.z*s } + { x:v.x*s, y:v.y*s, z:v.z*s } } -#[inline(always)] +#[inline(always)] fn length_sq(v:vec3) -> f32 { - dot(v,v) + dot(v,v) } -#[inline(always)] +#[inline(always)] fn length(v:vec3) -> f32 { - f32::sqrt(length_sq(v)) + f32::sqrt(length_sq(v)) } -#[inline(always)] +#[inline(always)] fn normalized(v:vec3) -> vec3 { - scale(v, 1.0f32 / length(v)) + scale(v, 1.0f32 / length(v)) } -#[inline(always)] +#[inline(always)] fn recip(a:vec3) -> vec3{ - vec3(1f32/a.x, 1f32/a.y, 1f32/a.z) + vec3(1f32/a.x, 1f32/a.y, 1f32/a.z) } -#[inline(always)] +#[inline(always)] fn mul(a:vec3, b:vec3) -> vec3{ - vec3(a.x*b.x, a.y*b.y, a.z*b.z) + vec3(a.x*b.x, a.y*b.y, a.z*b.z) } -#[inline(always)] +#[inline(always)] fn add(a:vec3, b:vec3) -> vec3 { - {x:a.x+b.x, y:a.y+b.y, z:a.z+b.z} + {x:a.x+b.x, y:a.y+b.y, z:a.z+b.z} } -#[inline(always)] +#[inline(always)] fn sub(a:vec3, b:vec3) -> vec3 { - add(a, scale(b, -1.0f32)) + add(a, scale(b, -1.0f32)) } -#[inline(always)] +#[inline(always)] fn cross(a:vec3, b:vec3) -> vec3 { - { x: a.y*b.z - b.y*a.z, - y: a.z*b.x - b.z*a.x, - z: a.x*b.y - b.x*a.y } + { x: a.y*b.z - b.y*a.z, + y: a.z*b.x - b.z*a.x, + z: a.x*b.y - b.x*a.y } } -#[inline(always)] +#[inline(always)] fn min(a: vec3, b: vec3) -> vec3 { - vec3( f32::fmin(a.x,b.x), f32::fmin(a.y, b.y), f32::fmin(a.z, b.z) ) + vec3( f32::fmin(a.x,b.x), f32::fmin(a.y, b.y), f32::fmin(a.z, b.z) ) } -#[inline(always)] +#[inline(always)] fn max(a: vec3, b: vec3) -> vec3 { - vec3( f32::fmax(a.x,b.x), f32::fmax(a.y, b.y), f32::fmax(a.z, b.z) ) + vec3( f32::fmax(a.x,b.x), f32::fmax(a.y, b.y), f32::fmax(a.z, b.z) ) } type ray = { origin:vec3, dir:vec3 }; @@ -82,116 +80,116 @@ type triangle = { p1: vec3, p2: vec3, p3: vec3 }; type hit_result = { barycentric: vec3, t: f32 }; -#[inline(always)] +#[inline(always)] fn ray_triangle_intersect( r:ray, t:triangle ) -> option { - let e1 = sub(t.p2, t.p1); - let e2 = sub(t.p3, t.p1); - let s1 = cross(r.dir, e2); - let divisor = dot(s1,e1); - - if divisor == 0f32 { - ret option::none; - } - - // compute first barycentric coordinate - let inv_divisor = 1.0f32 / divisor; - let d = sub(r.origin, t.p1); - - let b1 = dot(d, s1) * inv_divisor; - if b1 < 0.0f32 || b1 > 1.0f32 { - ret option::none; // outside triangle - } - - // and second barycentric coordinate - let s2 = cross(d, e1); - let b2 = dot(r.dir, s2)*inv_divisor; - if b2 < 0.0f32 || b1+b2 > 1.0f32 { - ret option::none; // outside triangle - } - - let t = dot(e2, s2) * inv_divisor; - if t < 0.0f32 { - ret option::none; // behind viewer - } - - option::some( {barycentric: { x:b1, y: b2, z: 1.0f32-b1-b2 }, t: t} ) + let e1 = sub(t.p2, t.p1); + let e2 = sub(t.p3, t.p1); + let s1 = cross(r.dir, e2); + let divisor = dot(s1,e1); + + if divisor == 0f32 { + ret option::none; + } + + // compute first barycentric coordinate + let inv_divisor = 1.0f32 / divisor; + let d = sub(r.origin, t.p1); + + let b1 = dot(d, s1) * inv_divisor; + if b1 < 0.0f32 || b1 > 1.0f32 { + ret option::none; // outside triangle + } + + // and second barycentric coordinate + let s2 = cross(d, e1); + let b2 = dot(r.dir, s2)*inv_divisor; + if b2 < 0.0f32 || b1+b2 > 1.0f32 { + ret option::none; // outside triangle + } + + let t = dot(e2, s2) * inv_divisor; + if t < 0.0f32 { + ret option::none; // behind viewer + } + + option::some( {barycentric: { x:b1, y: b2, z: 1.0f32-b1-b2 }, t: t} ) } type aabb = { min: vec3, max: vec3 }; -#[inline(always)] +#[inline(always)] fn ray_aabb_check( r:ray, max_dist: f32, box: aabb ) -> bool { - let inv_dir = recip(r.dir); - let (tx1,tx2,ty1,ty2,tz1,tz2) = ( - (box.min.x - r.origin.x)*inv_dir.x, - (box.max.x - r.origin.x)*inv_dir.x, - (box.min.y - r.origin.y)*inv_dir.y, - (box.max.y - r.origin.y)*inv_dir.y, - (box.min.z - r.origin.z)*inv_dir.z, - (box.max.z - r.origin.z)*inv_dir.z - ); - - let (minx, maxx) = (f32::fmin(tx1,tx2), f32::fmax(tx1,tx2)); - let (miny, maxy) = (f32::fmin(ty1,ty2), f32::fmax(ty1,ty2)); - let (minz, maxz) = (f32::fmin(tz1,tz2), f32::fmax(tz1,tz2)); - - let tmin = f32::fmax(minx, f32::fmax(miny, minz)); - let tmax = f32::fmin(maxx, f32::fmin(maxy, maxz)); - - tmax >= 0f32 && tmin <= tmax && tmin <= max_dist + let inv_dir = recip(r.dir); + let (tx1,tx2,ty1,ty2,tz1,tz2) = ( + (box.min.x - r.origin.x)*inv_dir.x, + (box.max.x - r.origin.x)*inv_dir.x, + (box.min.y - r.origin.y)*inv_dir.y, + (box.max.y - r.origin.y)*inv_dir.y, + (box.min.z - r.origin.z)*inv_dir.z, + (box.max.z - r.origin.z)*inv_dir.z + ); + + let (minx, maxx) = (f32::fmin(tx1,tx2), f32::fmax(tx1,tx2)); + let (miny, maxy) = (f32::fmin(ty1,ty2), f32::fmax(ty1,ty2)); + let (minz, maxz) = (f32::fmin(tz1,tz2), f32::fmax(tz1,tz2)); + + let tmin = f32::fmax(minx, f32::fmax(miny, minz)); + let tmax = f32::fmin(maxx, f32::fmin(maxy, maxz)); + + tmax >= 0f32 && tmin <= tmax && tmin <= max_dist } // Gives a cosine hemisphere sample from two uniform f32s // in [0,1) range. -#[inline(always)] +#[inline(always)] fn cosine_hemisphere_sample( u: f32, v: f32 ) -> vec3 { - let r_sqrt = f32::sqrt(u); - let theta = 2f32 * f32::consts::pi * v; - vec3( r_sqrt*f32::cos(theta), f32::sqrt(1f32-u), r_sqrt*f32::sin(theta)) + let r_sqrt = f32::sqrt(u); + let theta = 2f32 * f32::consts::pi * v; + vec3( r_sqrt*f32::cos(theta), f32::sqrt(1f32-u), r_sqrt*f32::sin(theta)) } -#[inline(always)] +#[inline(always)] fn rotate_to_up( up_vec: vec3 ) -> mtx33 { - let perp = if up_vec == vec3(0f32,1f32,0f32) { vec3(1f32,0f32,0f32) } else { vec3(0f32,1f32,0f32) }; - let right = cross( up_vec, perp ); - let fwd = cross(right, up_vec ); - transposed( { r0: right, r1: up_vec, r2: fwd } ) + let perp = if up_vec == vec3(0f32,1f32,0f32) { vec3(1f32,0f32,0f32) } else { vec3(0f32,1f32,0f32) }; + let right = cross( up_vec, perp ); + let fwd = cross(right, up_vec ); + transposed( { r0: right, r1: up_vec, r2: fwd } ) } #[inline(always)] fn rotate_y(theta: f32) -> mtx33{ - let ct = f32::cos(theta); - let st = f32::sin(theta); - { r0: vec3(ct,0f32,st), r1: vec3(0f32,1f32,0f32), r2: vec3(-st, 0f32, ct) } + let ct = f32::cos(theta); + let st = f32::sin(theta); + { r0: vec3(ct,0f32,st), r1: vec3(0f32,1f32,0f32), r2: vec3(-st, 0f32, ct) } } -#[inline(always)] +#[inline(always)] fn transform( m: mtx33, v: vec3 ) -> vec3 { - vec3( dot( m.r0, v ), dot( m.r1, v ), dot( m.r2, v ) ) + vec3( dot( m.r0, v ), dot( m.r1, v ), dot( m.r2, v ) ) } -#[inline(always)] +#[inline(always)] fn mul_mtx33( a: mtx33, b: mtx33 ) -> mtx33 { - let b_t = transposed(b); - { r0: vec3( dot(a.r0,b_t.r0), dot(a.r0,b_t.r1), dot(a.r0,b_t.r2) ), - r1: vec3( dot(a.r1,b_t.r0), dot(a.r1,b_t.r1), dot(a.r1,b_t.r2) ), - r2: vec3( dot(a.r2,b_t.r0), dot(a.r2,b_t.r1), dot(a.r2,b_t.r2) ) } + let b_t = transposed(b); + { r0: vec3( dot(a.r0,b_t.r0), dot(a.r0,b_t.r1), dot(a.r0,b_t.r2) ), + r1: vec3( dot(a.r1,b_t.r0), dot(a.r1,b_t.r1), dot(a.r1,b_t.r2) ), + r2: vec3( dot(a.r2,b_t.r0), dot(a.r2,b_t.r1), dot(a.r2,b_t.r2) ) } } -#[inline(always)] +#[inline(always)] fn transposed( m: mtx33 ) -> mtx33 { - { r0: vec3( m.r0.x, m.r1.x, m.r2.x ), - r1: vec3( m.r0.y, m.r1.y, m.r2.y ), - r2: vec3( m.r0.z, m.r1.z, m.r2.z ) } + { r0: vec3( m.r0.x, m.r1.x, m.r2.x ), + r1: vec3( m.r0.y, m.r1.y, m.r2.y ), + r2: vec3( m.r0.z, m.r1.z, m.r2.z ) } } #[test] fn intersection_test() { - let ray : ray = { origin: vec3(0f32, 0f32, 0f32), dir: vec3(0.0f32,0.0f32,-1.0f32) }; - let tri : triangle = { p1: vec3(-1f32, -1f32, -1f32), - p2: vec3(1f32, -1f32, -1f32), - p3: vec3(0f32, 2f32, -1f32) }; + let ray : ray = { origin: vec3(0f32, 0f32, 0f32), dir: vec3(0.0f32,0.0f32,-1.0f32) }; + let tri : triangle = { p1: vec3(-1f32, -1f32, -1f32), + p2: vec3(1f32, -1f32, -1f32), + p3: vec3(0f32, 2f32, -1f32) }; - assert option::is_some( ray_triangle_intersect( ray, tri ) ); + assert option::is_some( ray_triangle_intersect( ray, tri ) ); } diff --git a/model.rs b/model.rs index fbb7cc3..0f05f18 100644 --- a/model.rs +++ b/model.rs @@ -8,190 +8,190 @@ type polysoup = { vertices: [vec3], indices: [uint], normals: [vec3] }; type mesh = { polys: polysoup, kd_tree: kd_tree, bounding_box: aabb }; enum axis { - x, - y, - z + x, + y, + z } type kd_tree = { nodes : [kd_tree_node], root: uint }; enum kd_tree_node { - leaf( u32, u32 ), - node( axis, f32, u32 ) + leaf( u32, u32 ), + node( axis, f32, u32 ) } fn find_split_plane( distances: [f32], indices: [uint], faces: [uint] ) -> f32 { - - let mut face_distances = []; - for faces.each |f| { - face_distances += [distances[indices[f*3u]]]; - face_distances += [distances[indices[f*3u+1u]]]; - face_distances += [distances[indices[f*3u+2u]]]; - } - - let mut sorted_distances = sort::merge_sort( {|a,b| a {l:[uint],r:[uint]} { - let mut l = []; - let mut r = []; - - for faces.each |f| { - let d0 = distances[indices[f*3u ]]; - let d1 = distances[indices[f*3u+1u]]; - let d2 = distances[indices[f*3u+2u]]; - - let maxdist = f32::fmax(d0, f32::fmax(d1, d2)); - let mindist = f32::fmin(d0, f32::fmin(d1, d2)); - - if mindist <= splitter { - l += [f]; - } - - if maxdist >= splitter { - r += [f]; - } - } + let mut l = []; + let mut r = []; + + for faces.each |f| { + let d0 = distances[indices[f*3u ]]; + let d1 = distances[indices[f*3u+1u]]; + let d2 = distances[indices[f*3u+2u]]; + + let maxdist = f32::fmax(d0, f32::fmax(d1, d2)); + let mindist = f32::fmin(d0, f32::fmin(d1, d2)); + + if mindist <= splitter { + l += [f]; + } + + if maxdist >= splitter { + r += [f]; + } + } {l:l, r:r} } fn build_leaf( - &kd_tree_nodes : [mut kd_tree_node], - &new_indices: [uint], - indices: [uint], - faces: [uint] + &kd_tree_nodes : [mut kd_tree_node], + &new_indices: [uint], + indices: [uint], + faces: [uint] ) -> uint { - let next_face_ix : u32 = (vec::len(new_indices) as u32) / 3u32; - vec::push(kd_tree_nodes, leaf( next_face_ix, (vec::len(faces) as u32) )); - - for faces.each |f| { - new_indices += [ indices[f*3u], indices[f*3u+1u], indices[f*3u+2u] ]; - } - ret vec::len(kd_tree_nodes) - 1u; + let next_face_ix : u32 = (vec::len(new_indices) as u32) / 3u32; + vec::push(kd_tree_nodes, leaf( next_face_ix, (vec::len(faces) as u32) )); + + for faces.each |f| { + new_indices += [ indices[f*3u], indices[f*3u+1u], indices[f*3u+2u] ]; + } + ret vec::len(kd_tree_nodes) - 1u; } -fn build_kd_tree( - &kd_tree_nodes : [mut kd_tree_node], - &new_indices: [uint], - maxdepth: uint, - xdists: [f32], - ydists: [f32], - zdists: [f32], - aabbmin: vec3, - aabbmax: vec3, - indices: [uint], - faces: [uint] ) -> uint { - - if maxdepth == 0u || vec::len(faces) <= 15u { - ret build_leaf( kd_tree_nodes, new_indices, indices, faces ); - } - - let extent = sub(aabbmax, aabbmin); +fn build_kd_tree( + &kd_tree_nodes : [mut kd_tree_node], + &new_indices: [uint], + maxdepth: uint, + xdists: [f32], + ydists: [f32], + zdists: [f32], + aabbmin: vec3, + aabbmax: vec3, + indices: [uint], + faces: [uint] ) -> uint { + + if maxdepth == 0u || vec::len(faces) <= 15u { + ret build_leaf( kd_tree_nodes, new_indices, indices, faces ); + } + + let extent = sub(aabbmax, aabbmin); let axis = if extent.x > extent.y && extent.x > extent.z { x } else { if extent.y > extent.z { y } else { z } }; - let mut dists; - alt axis { - x() { dists = xdists } - y() { dists = ydists } - z() { dists = zdists } - }; - - let s = find_split_plane( dists, indices, faces ); - let {l,r} = split_triangles( s, dists, indices, faces ); - - // Stop when there's too much overlap between the two halves - if (vec::len(l) + vec::len(r)) as f32 > vec::len(faces) as f32 * 1.5f32 { - ret build_leaf( kd_tree_nodes, new_indices, indices, faces ); - } - - // adjust bounding boxes for children - let mut left_aabbmax; - let mut right_aabbmin; - alt axis { - x() { - left_aabbmax = { x: s with aabbmax }; - right_aabbmin = { x: s with aabbmin }; - } - y() { - left_aabbmax = { y: s with aabbmax }; - right_aabbmin = { y: s with aabbmin }; - } - z() { - left_aabbmax = { z: s with aabbmax }; - right_aabbmin = { z: s with aabbmin }; - } - }; - - // allocate node from nodes-array, and recursively build children - let ix = vec::len(kd_tree_nodes); - vec::push( kd_tree_nodes, node(axis,0f32,0u32)); - - build_kd_tree( - kd_tree_nodes, - new_indices, - maxdepth - 1u, - xdists, - ydists, - zdists, - aabbmin, - left_aabbmax, - indices, - l - ); - // left child ix is implied to be ix+1 - - let right_child_ix = build_kd_tree( - kd_tree_nodes, - new_indices, - maxdepth - 1u, - xdists, - ydists, - zdists, - right_aabbmin, - aabbmax, - indices, - r - ); - - kd_tree_nodes[ix] = node(axis, s as f32, right_child_ix as u32); - - ret ix; + let mut dists; + alt axis { + x() { dists = xdists } + y() { dists = ydists } + z() { dists = zdists } + }; + + let s = find_split_plane( dists, indices, faces ); + let {l,r} = split_triangles( s, dists, indices, faces ); + + // Stop when there's too much overlap between the two halves + if (vec::len(l) + vec::len(r)) as f32 > vec::len(faces) as f32 * 1.5f32 { + ret build_leaf( kd_tree_nodes, new_indices, indices, faces ); + } + + // adjust bounding boxes for children + let mut left_aabbmax; + let mut right_aabbmin; + alt axis { + x() { + left_aabbmax = { x: s with aabbmax }; + right_aabbmin = { x: s with aabbmin }; + } + y() { + left_aabbmax = { y: s with aabbmax }; + right_aabbmin = { y: s with aabbmin }; + } + z() { + left_aabbmax = { z: s with aabbmax }; + right_aabbmin = { z: s with aabbmin }; + } + }; + + // allocate node from nodes-array, and recursively build children + let ix = vec::len(kd_tree_nodes); + vec::push( kd_tree_nodes, node(axis,0f32,0u32)); + + build_kd_tree( + kd_tree_nodes, + new_indices, + maxdepth - 1u, + xdists, + ydists, + zdists, + aabbmin, + left_aabbmax, + indices, + l + ); + // left child ix is implied to be ix+1 + + let right_child_ix = build_kd_tree( + kd_tree_nodes, + new_indices, + maxdepth - 1u, + xdists, + ydists, + zdists, + right_aabbmin, + aabbmax, + indices, + r + ); + + kd_tree_nodes[ix] = node(axis, s as f32, right_child_ix as u32); + + ret ix; } fn count_kd_tree_nodes( t: kd_tree ) -> {depth:uint, count:uint} { - alt t.nodes[t.root] { - node(_,_,r) { - let {depth:d0,count:c0} = count_kd_tree_nodes( {root: t.root+1u with t} ); - let {depth:d1,count:c1} = count_kd_tree_nodes( {root: (r as uint) with t} ); - ret {depth: uint::max(d0, d1)+1u, count: c0+c1+1u }; - } - leaf(_, _) { - ret { depth:1u, count:1u }; - } - } + alt t.nodes[t.root] { + node(_,_,r) { + let {depth:d0,count:c0} = count_kd_tree_nodes( {root: t.root+1u with t} ); + let {depth:d1,count:c1} = count_kd_tree_nodes( {root: (r as uint) with t} ); + ret {depth: uint::max(d0, d1)+1u, count: c0+c1+1u }; + } + leaf(_, _) { + ret { depth:1u, count:1u }; + } + } } fn read_mesh(fname: str) -> mesh { - io::print("Reading model file..."); - let polys = read_polysoup( fname ); - - io::print("Building kd-tree... "); - - // just create a vector of 0..N-1 as our face array - let max_tri_ix = vec::len(polys.indices)/3u -1u; - check( uint::le(0u, max_tri_ix) ); + io::print("Reading model file..."); + let polys = read_polysoup( fname ); + + io::print("Building kd-tree... "); + + // just create a vector of 0..N-1 as our face array + let max_tri_ix = vec::len(polys.indices)/3u -1u; + check( uint::le(0u, max_tri_ix) ); let mut faces = []; let mut fii = 0u; while fii < max_tri_ix { @@ -199,156 +199,153 @@ fn read_mesh(fname: str) -> mesh { fii += 1u } - let mut aabbmin = vec3(f32::infinity, f32::infinity, f32::infinity); - let mut aabbmax = vec3(f32::neg_infinity, f32::neg_infinity, f32::neg_infinity); - for polys.vertices.each |v| { - aabbmin = math3d::min(v, aabbmin); - aabbmax = math3d::max(v, aabbmax); - } - - let downscale = 1.0f32 / math3d::length(sub(aabbmax,aabbmin)); - let offset = scale(add(aabbmin, aabbmax), 0.5f32); - - let mut transformed_verts = []; - - - for polys.vertices.each |v| { - transformed_verts += [scale(sub(v, offset), downscale)]; - } - - aabbmin = scale(sub(aabbmin, offset), downscale); - aabbmax = scale(sub(aabbmax, offset), downscale); - - // de-mux vertices for easier access later - let mut xdists = []; - let mut ydists = []; - let mut zdists = []; - - for transformed_verts.each |v| { - xdists += [v.x]; - ydists += [v.y]; - zdists += [v.z]; - } - - let mut nodes = [mut]; - let mut new_indices = []; - let rootnode = build_kd_tree( - nodes, - new_indices, - 100u, - xdists, - ydists, - zdists, - aabbmin, - aabbmax, - polys.indices, - faces); - - { polys: {vertices: transformed_verts, indices: new_indices with polys}, kd_tree: { nodes: vec::from_mut(nodes), root: rootnode } , bounding_box: {min: aabbmin, max: aabbmax} } + let mut aabbmin = vec3(f32::infinity, f32::infinity, f32::infinity); + let mut aabbmax = vec3(f32::neg_infinity, f32::neg_infinity, f32::neg_infinity); + for polys.vertices.each |v| { + aabbmin = math3d::min(v, aabbmin); + aabbmax = math3d::max(v, aabbmax); + } + + let downscale = 1.0f32 / math3d::length(sub(aabbmax,aabbmin)); + let offset = scale(add(aabbmin, aabbmax), 0.5f32); + + let mut transformed_verts = []; + + + for polys.vertices.each |v| { + transformed_verts += [scale(sub(v, offset), downscale)]; + } + + aabbmin = scale(sub(aabbmin, offset), downscale); + aabbmax = scale(sub(aabbmax, offset), downscale); + + // de-mux vertices for easier access later + let mut xdists = []; + let mut ydists = []; + let mut zdists = []; + + for transformed_verts.each |v| { + xdists += [v.x]; + ydists += [v.y]; + zdists += [v.z]; + } + + let mut nodes = [mut]; + let mut new_indices = []; + let rootnode = build_kd_tree( + nodes, + new_indices, + 100u, + xdists, + ydists, + zdists, + aabbmin, + aabbmax, + polys.indices, + faces); + + { polys: {vertices: transformed_verts, indices: new_indices with polys}, kd_tree: { nodes: vec::from_mut(nodes), root: rootnode } , bounding_box: {min: aabbmin, max: aabbmax} } } -#[inline(always)] +#[inline(always)] fn parse_faceindex(s: str) -> uint { - - // check for '/', the vertex index is the first - let ix_str = alt str::find_char(s, '/'){ - option::some(slash_ix) { - str::substr(s, 0u, slash_ix) - } - _ { - s - } - }; - - option::get(uint::from_str(ix_str))-1u + + // check for '/', the vertex index is the first + let ix_str = alt str::find_char(s, '/'){ + option::some(slash_ix) { + str::substr(s, 0u, slash_ix) + } + _ { + s + } + }; + + option::get(uint::from_str(ix_str))-1u } fn read_polysoup(fname: str) -> polysoup { - let reader = result::get( io::file_reader( fname ) ); - let mut vertices = []; - let mut indices = []; - - let mut vert_normals : [mut vec3]= [mut]; - - while !reader.eof() { - let line : str = reader.read_line(); - if str::is_empty(line) { - again; - } - - let mut num_texcoords = 0u; - let tokens = str::split_char(line, ' '); - - if tokens[0] == "v"{ - assert vec::len(tokens) == 4u; - let v = vec3( option::get(float::from_str(tokens[1])) as f32, - option::get(float::from_str(tokens[2])) as f32, - option::get(float::from_str(tokens[3])) as f32); - assert v.x != f32::NaN; - assert v.y != f32::NaN; - assert v.z != f32::NaN; - - vertices += [v]; - vert_normals += [mut vec3(0f32,0f32,0f32)]; - - } else if tokens[0] == "f" { - if vec::len(tokens) == 4u || vec::len(tokens) == 5u { - let mut face_triangles = []; - - if vec::len(tokens) == 4u { - let (i0,i1,i2) = ( parse_faceindex(tokens[1]), - parse_faceindex(tokens[2]), - parse_faceindex(tokens[3]) ); - - face_triangles += [ (i0, i1, i2) ]; - } else { - assert vec::len(tokens) == 5u; - // quad, triangulate - let (i0,i1,i2,i3) = ( parse_faceindex(tokens[1]), - parse_faceindex(tokens[2]), - parse_faceindex(tokens[3]), - parse_faceindex(tokens[4]) ); - - face_triangles += [ (i0,i1,i2), - (i0,i2,i3) ]; - } - - for face_triangles.each |t| { - let (i0,i1,i2) = t; - - indices += [i0,i1,i2]; - - let e1 = math3d::sub(vertices[i1], vertices[i0]); - let e2 = math3d::sub(vertices[i2], vertices[i0]); - let n = normalized(cross(e1,e2)); - - vert_normals[i0] = math3d::add( vert_normals[i0], n ); - vert_normals[i1] = math3d::add( vert_normals[i1], n ); - vert_normals[i2] = math3d::add( vert_normals[i2], n ); - } - } else { - io::println(#fmt("Polygon with %u vertices found. Ignored. Currently rustray only supports 4 vertices", vec::len(tokens) - 1u)); - } - } else if tokens[0] == "vt" { - num_texcoords += 1u; - } else if tokens[0] != "#" { - io::println(#fmt("Unrecognized line in .obj file: %s", line)); - } - - if num_texcoords > 0u { - io::println(#fmt("%u texture coordinates ignored", num_texcoords)); - } - } - - //for v in vert_normals { - // v = normalized(v); - //} - - ret { vertices: vertices, - indices: indices, - normals: vec::map( vec::from_mut(vert_normals), |v| normalized(v) ) }; + let reader = result::get( io::file_reader( fname ) ); + let mut vertices = []; + let mut indices = []; + + let mut vert_normals : [mut vec3]= [mut]; + + while !reader.eof() { + let line : str = reader.read_line(); + if str::is_empty(line) { + again; + } + + let mut num_texcoords = 0u; + let tokens = str::split_char(line, ' '); + + if tokens[0] == "v"{ + assert vec::len(tokens) == 4u; + let v = vec3( option::get(float::from_str(tokens[1])) as f32, + option::get(float::from_str(tokens[2])) as f32, + option::get(float::from_str(tokens[3])) as f32); + assert v.x != f32::NaN; + assert v.y != f32::NaN; + assert v.z != f32::NaN; + + vertices += [v]; + vert_normals += [mut vec3(0f32,0f32,0f32)]; + + } else if tokens[0] == "f" { + if vec::len(tokens) == 4u || vec::len(tokens) == 5u { + let mut face_triangles = []; + + if vec::len(tokens) == 4u { + let (i0,i1,i2) = ( parse_faceindex(tokens[1]), + parse_faceindex(tokens[2]), + parse_faceindex(tokens[3]) ); + + face_triangles += [ (i0, i1, i2) ]; + } else { + assert vec::len(tokens) == 5u; + // quad, triangulate + let (i0,i1,i2,i3) = ( parse_faceindex(tokens[1]), + parse_faceindex(tokens[2]), + parse_faceindex(tokens[3]), + parse_faceindex(tokens[4]) ); + + face_triangles += [ (i0,i1,i2), + (i0,i2,i3) ]; + } + + for face_triangles.each |t| { + let (i0,i1,i2) = t; + + indices += [i0,i1,i2]; + + let e1 = math3d::sub(vertices[i1], vertices[i0]); + let e2 = math3d::sub(vertices[i2], vertices[i0]); + let n = normalized(cross(e1,e2)); + + vert_normals[i0] = math3d::add( vert_normals[i0], n ); + vert_normals[i1] = math3d::add( vert_normals[i1], n ); + vert_normals[i2] = math3d::add( vert_normals[i2], n ); + } + } else { + io::println(#fmt("Polygon with %u vertices found. Ignored. Currently rustray only supports 4 vertices", vec::len(tokens) - 1u)); + } + } else if tokens[0] == "vt" { + num_texcoords += 1u; + } else if tokens[0] != "#" { + io::println(#fmt("Unrecognized line in .obj file: %s", line)); + } + + if num_texcoords > 0u { + io::println(#fmt("%u texture coordinates ignored", num_texcoords)); + } + } + + //for v in vert_normals { + // v = normalized(v); + //} + + ret { vertices: vertices, + indices: indices, + normals: vec::map( vec::from_mut(vert_normals), |v| normalized(v) ) }; } - - - diff --git a/raytracer.rs b/raytracer.rs index 91c0df8..df993ee 100644 --- a/raytracer.rs +++ b/raytracer.rs @@ -5,624 +5,623 @@ import consts::*; // for the consts.. ugh... make the constants go away type color = { r:u8, g:u8, b:u8 }; -#[inline(always)] +#[inline(always)] fn for_each_pixel( width: uint, height: uint, f : fn (x: uint, y: uint) -> color ) -> [color]{ - let mut img_pixels = []; + let mut img_pixels = []; - for uint::range( 0u, height ) |row| { - for uint::range(0u, width) |column| { - img_pixels += [f(column,row)]; - } - } - ret img_pixels; + for uint::range( 0u, height ) |row| { + for uint::range(0u, width) |column| { + img_pixels += [f(column,row)]; + } + } + ret img_pixels; } -#[inline(always)] -fn get_ray( horizontalFOV: f32, width: uint, height: uint, x: uint, y: uint, sample_jitter : (f32,f32)) -> ray{ - let (jitterx,jittery) = sample_jitter; - let dirx = (x as f32) - ((width/2u) as f32) + jitterx; - let diry = -((y as f32) - ((height/2u) as f32)) + jittery; - let dirz = -((width/2u) as f32) / f32::tan(horizontalFOV*0.5f32); - { origin: vec3(0f32, 0f32, 1f32), - dir: normalized( vec3( dirx, diry, dirz) ) } +#[inline(always)] +fn get_ray( horizontalFOV: f32, width: uint, height: uint, x: uint, y: uint, sample_jitter : (f32,f32)) -> ray{ + let (jitterx,jittery) = sample_jitter; + let dirx = (x as f32) - ((width/2u) as f32) + jitterx; + let diry = -((y as f32) - ((height/2u) as f32)) + jittery; + let dirz = -((width/2u) as f32) / f32::tan(horizontalFOV*0.5f32); + { origin: vec3(0f32, 0f32, 1f32), + dir: normalized( vec3( dirx, diry, dirz) ) } } type rand_env = { rng: rand::rng, floats: [f32], disk_samples: [(f32,f32)], hemicos_samples: [vec3] }; #[incline(always)] fn get_rand_env() -> rand_env{ - let gen = rand::rng(); - - let disk_samples = do vec::from_fn(513u) |_x| { - // compute random position on light disk - let r_sqrt = f32::sqrt(gen.gen_f32()); - let theta = gen.gen_f32() * 2f32 * f32::consts::pi; - (r_sqrt * f32::cos(theta), r_sqrt*f32::sin(theta)) - }; - - let mut hemicos_samples = []; - - for uint::range(0u, NUM_GI_SAMPLES_SQRT) |x| { - for uint::range(0u, NUM_GI_SAMPLES_SQRT) |y| { - let (u,v) = ( ( (x as f32) + gen.gen_f32() ) / (NUM_GI_SAMPLES_SQRT as f32), - ( (y as f32) + gen.gen_f32() ) / (NUM_GI_SAMPLES_SQRT as f32) ); - hemicos_samples += [cosine_hemisphere_sample(u,v)]; - } - }; - - { rng: gen, - floats: vec::from_fn(513u, |_x| gen.gen_f32() ), - disk_samples: disk_samples, - hemicos_samples: hemicos_samples } + let gen = rand::rng(); + + let disk_samples = do vec::from_fn(513u) |_x| { + // compute random position on light disk + let r_sqrt = f32::sqrt(gen.gen_f32()); + let theta = gen.gen_f32() * 2f32 * f32::consts::pi; + (r_sqrt * f32::cos(theta), r_sqrt*f32::sin(theta)) + }; + + let mut hemicos_samples = []; + + for uint::range(0u, NUM_GI_SAMPLES_SQRT) |x| { + for uint::range(0u, NUM_GI_SAMPLES_SQRT) |y| { + let (u,v) = ( ( (x as f32) + gen.gen_f32() ) / (NUM_GI_SAMPLES_SQRT as f32), + ( (y as f32) + gen.gen_f32() ) / (NUM_GI_SAMPLES_SQRT as f32) ); + hemicos_samples += [cosine_hemisphere_sample(u,v)]; + } + }; + + { rng: gen, + floats: vec::from_fn(513u, |_x| gen.gen_f32() ), + disk_samples: disk_samples, + hemicos_samples: hemicos_samples } } /* #[incline(always)] fn sample_floats( rnd: rand_env, num: uint, body: fn(f32) ) { - let mut ix = rnd.rng.next() % vec::len(rnd.floats); // start at random location - iter::repeat(num) { - body( rnd.floats[ix] ); - ix = (ix + 1u) % vec::len(rnd.floats); - } + let mut ix = rnd.rng.next() % vec::len(rnd.floats); // start at random location + iter::repeat(num) { + body( rnd.floats[ix] ); + ix = (ix + 1u) % vec::len(rnd.floats); + } } */ #[incline(always)] fn sample_floats_2d_offset( offset: uint, rnd: rand_env, num: uint, body: fn(f32,f32) ) { - let mut ix = offset % vec::len(rnd.floats); - for iter::repeat(num) { - let r1 = rnd.floats[ix]; - ix = (ix + 1u) % vec::len(rnd.floats); - let r2 = rnd.floats[ix]; - body(r1,r2); - ix = (ix + 1u) % vec::len(rnd.floats); - } + let mut ix = offset % vec::len(rnd.floats); + for iter::repeat(num) { + let r1 = rnd.floats[ix]; + ix = (ix + 1u) % vec::len(rnd.floats); + let r2 = rnd.floats[ix]; + body(r1,r2); + ix = (ix + 1u) % vec::len(rnd.floats); + } } #[inline(always)] -fn sample_disk( rnd: rand_env, num: uint, body: fn(f32,f32) ){ - - if ( num == 1u ) { - body(0f32,0f32); - } else { - let mut ix = (rnd.rng.next() as uint) % vec::len(rnd.disk_samples); // start at random location - for iter::repeat(num) { - let (u,v) = rnd.disk_samples[ix]; - body(u,v); - ix = (ix + 1u) % vec::len(rnd.disk_samples); - }; - } +fn sample_disk( rnd: rand_env, num: uint, body: fn(f32,f32) ){ + + if ( num == 1u ) { + body(0f32,0f32); + } else { + let mut ix = (rnd.rng.next() as uint) % vec::len(rnd.disk_samples); // start at random location + for iter::repeat(num) { + let (u,v) = rnd.disk_samples[ix]; + body(u,v); + ix = (ix + 1u) % vec::len(rnd.disk_samples); + }; + } } // Sample 2 floats at a time, starting with an offset that's passed in #[incline(always)] -fn sample_floats_2d( rnd: rand_env, num: uint, body: fn(f32,f32) ) { - sample_floats_2d_offset( rnd.rng.next() as uint, rnd, num, body); +fn sample_floats_2d( rnd: rand_env, num: uint, body: fn(f32,f32) ) { + sample_floats_2d_offset( rnd.rng.next() as uint, rnd, num, body); } #[incline(always)] fn sample_stratified_2d( rnd: rand_env, m: uint, n : uint, body: fn(f32,f32) ) { - let m_inv = 1f32/(m as f32); - let n_inv = 1f32/(n as f32); - - let start_offset = rnd.rng.next(); - for uint::range( 0u, m ) |samplex| { - // sample one "row" of 2d floats - let mut sampley = 0u; - do sample_floats_2d_offset( (start_offset as uint) + (n*samplex as uint), rnd, n ) |u,v| { - body( ((samplex as f32) + u) * m_inv, - ((sampley as f32) + v) * n_inv ); - sampley += 1u; - }; - } + let m_inv = 1f32/(m as f32); + let n_inv = 1f32/(n as f32); + + let start_offset = rnd.rng.next(); + for uint::range( 0u, m ) |samplex| { + // sample one "row" of 2d floats + let mut sampley = 0u; + do sample_floats_2d_offset( (start_offset as uint) + (n*samplex as uint), rnd, n ) |u,v| { + body( ((samplex as f32) + u) * m_inv, + ((sampley as f32) + v) * n_inv ); + sampley += 1u; + }; + } } #[incline(always)] fn sample_cosine_hemisphere( rnd: rand_env, n: vec3, body: fn(vec3) ) { - let rot_to_up = rotate_to_up(n); - let random_rot = rotate_y( rnd.floats[ rnd.rng.next() as uint % vec::len(rnd.floats) ] ); // random angle about y - let m = mul_mtx33(rot_to_up, random_rot); + let rot_to_up = rotate_to_up(n); + let random_rot = rotate_y( rnd.floats[ rnd.rng.next() as uint % vec::len(rnd.floats) ] ); // random angle about y + let m = mul_mtx33(rot_to_up, random_rot); for rnd.hemicos_samples.each |s| { - body(transform(m,s)); - } + body(transform(m,s)); + } } -#[inline(always)] +#[inline(always)] fn get_triangle( m : model::polysoup, ix : uint ) -> triangle{ - { p1: m.vertices[ m.indices[ix*3u ] ], - p2: m.vertices[ m.indices[ix*3u+1u] ], - p3: m.vertices[ m.indices[ix*3u+2u] ] } + { p1: m.vertices[ m.indices[ix*3u ] ], + p2: m.vertices[ m.indices[ix*3u+1u] ], + p3: m.vertices[ m.indices[ix*3u+2u] ] } } -#[inline(always)] +#[inline(always)] fn clamp( x: f32, lo: f32, hi: f32 ) -> f32{ - f32::fmin(hi, f32::fmax( x, lo )) + f32::fmin(hi, f32::fmax( x, lo )) } -#[inline(always)] -fn trace_kd_tree( - polys: model::polysoup, - kd_tree_nodes: [model::kd_tree_node], - kd_tree_root: uint, - r: ray, - inv_dir: vec3, - inmint: f32, - inmaxt: f32 ) - -> option<(hit_result, uint)> { - - let mut res : option<(hit_result, uint)> = option::none; - let mut closest_hit = inmaxt; - - let mut stack : [(uint, f32, f32)] = []; - let mut mint = inmint; - let mut maxt = inmaxt; - let mut cur_node = kd_tree_root; - - loop { - - // skip any nodes that have been superceded - // by a closer hit. - while mint >= closest_hit { - if ( vec::len(stack) > 0u ){ - let (n,mn,mx) = vec::pop(stack); - cur_node = n; - mint = mn; - maxt = mx; - } else { - ret res; - } - } - - alt kd_tree_nodes[cur_node] { - model::leaf(tri_begin, tri_count) { - - let mut tri_index : u32 = tri_begin; - while tri_index < tri_begin+tri_count { - - let t = get_triangle( polys, tri_index as uint ); - let new_hit_result = ray_triangle_intersect( r, t ); - - alt (res, new_hit_result){ - (option::none(), option::some(hr)) { - res = option::some((hr,tri_index as uint)); - closest_hit = hr.t; - } - (option::some((hr1,_)), option::some(hr2)) if hr1.t > hr2.t { - res = option::some((hr2,tri_index as uint)); - closest_hit = hr2.t; - } - _ {} - } - - tri_index += 1u32; - } - - - - if ( vec::len(stack) > 0u ){ - let (n,mn,mx) = vec::pop(stack); - cur_node = n; - mint = mn; - maxt = mx; - } else { - ret res; - } - - } - model::node(axis, splitter, right_tree) { - - // find the scalar direction/origin for the current axis - let (inv_dir_scalar, origin) = alt axis { - model::x() { (inv_dir.x, r.origin.x) } - model::y() { (inv_dir.y, r.origin.y) } - model::z() { (inv_dir.z, r.origin.z) } - }; - - // figure out which side of the spliting plane the ray origin is - // i.e. which child we need to test first. - - let (near,far) = if origin < splitter || (origin == splitter && inv_dir_scalar >= 0f32) { - ((cur_node+1u) as uint,right_tree as uint) - } else { - (right_tree as uint, (cur_node+1u) as uint) - }; - - // find intersection with plane - // origin + dir*plane_dist = splitter - let plane_dist = (splitter - origin) * inv_dir_scalar; - - if plane_dist > maxt || plane_dist <= 0f32 { - cur_node = near; - } else if plane_dist < mint { - cur_node = far; - } else{ - vec::push(stack, (far, plane_dist, maxt) ); - - cur_node = near; - maxt = plane_dist; - } - } - } - } - - +#[inline(always)] +fn trace_kd_tree( + polys: model::polysoup, + kd_tree_nodes: [model::kd_tree_node], + kd_tree_root: uint, + r: ray, + inv_dir: vec3, + inmint: f32, + inmaxt: f32 ) + -> option<(hit_result, uint)> { + + let mut res : option<(hit_result, uint)> = option::none; + let mut closest_hit = inmaxt; + + let mut stack : [(uint, f32, f32)] = []; + let mut mint = inmint; + let mut maxt = inmaxt; + let mut cur_node = kd_tree_root; + + loop { + + // skip any nodes that have been superceded + // by a closer hit. + while mint >= closest_hit { + if ( vec::len(stack) > 0u ){ + let (n,mn,mx) = vec::pop(stack); + cur_node = n; + mint = mn; + maxt = mx; + } else { + ret res; + } + } + + alt kd_tree_nodes[cur_node] { + model::leaf(tri_begin, tri_count) { + + let mut tri_index : u32 = tri_begin; + while tri_index < tri_begin+tri_count { + + let t = get_triangle( polys, tri_index as uint ); + let new_hit_result = ray_triangle_intersect( r, t ); + + alt (res, new_hit_result){ + (option::none(), option::some(hr)) { + res = option::some((hr,tri_index as uint)); + closest_hit = hr.t; + } + (option::some((hr1,_)), option::some(hr2)) if hr1.t > hr2.t { + res = option::some((hr2,tri_index as uint)); + closest_hit = hr2.t; + } + _ {} + } + + tri_index += 1u32; + } + + + + if ( vec::len(stack) > 0u ){ + let (n,mn,mx) = vec::pop(stack); + cur_node = n; + mint = mn; + maxt = mx; + } else { + ret res; + } + + } + model::node(axis, splitter, right_tree) { + + // find the scalar direction/origin for the current axis + let (inv_dir_scalar, origin) = alt axis { + model::x() { (inv_dir.x, r.origin.x) } + model::y() { (inv_dir.y, r.origin.y) } + model::z() { (inv_dir.z, r.origin.z) } + }; + + // figure out which side of the spliting plane the ray origin is + // i.e. which child we need to test first. + + let (near,far) = if origin < splitter || (origin == splitter && inv_dir_scalar >= 0f32) { + ((cur_node+1u) as uint,right_tree as uint) + } else { + (right_tree as uint, (cur_node+1u) as uint) + }; + + // find intersection with plane + // origin + dir*plane_dist = splitter + let plane_dist = (splitter - origin) * inv_dir_scalar; + + if plane_dist > maxt || plane_dist <= 0f32 { + cur_node = near; + } else if plane_dist < mint { + cur_node = far; + } else{ + vec::push(stack, (far, plane_dist, maxt) ); + + cur_node = near; + maxt = plane_dist; + } + } + } + } + + } -#[inline(always)] -fn trace_kd_tree_shadow( - polys: model::polysoup, - kd_tree_nodes: [model::kd_tree_node], - kd_tree_root: uint, - r: ray, - inv_dir: vec3, - inmint: f32, - inmaxt: f32 ) - -> bool { - - let mut stack : [(u32, f32, f32)] = []; - let mut mint = inmint; - let mut maxt = inmaxt; - let mut cur_node = kd_tree_root; - loop { - - alt kd_tree_nodes[cur_node] { - model::leaf(tri_begin, tri_count) { - - let mut tri_index = tri_begin; - while tri_index < tri_begin + tri_count { - let t = get_triangle( polys, tri_index as uint); - if option::is_some( ray_triangle_intersect( r, t ) ) { - ret true; - } - tri_index += 1u32; - } - if ( vec::len(stack) > 0u ){ - let (n,mn,mx) = vec::pop(stack); - cur_node = n as uint; - mint = mn; - maxt = mx; - } else { - ret false; - } - } - model::node(axis, splitter, right_tree){ - - // find the scalar direction/origin for the current axis - let (inv_dir_scalar, origin) = alt axis { - model::x() { (inv_dir.x, r.origin.x) } - model::y() { (inv_dir.y, r.origin.y) } - model::z() { (inv_dir.z, r.origin.z) } - }; - - // figure out which side of the spliting plane the ray origin is - // i.e. which child we need to test first. - let (near,far) = if origin < splitter || (origin == splitter && inv_dir_scalar >= 0f32) { - ((cur_node+1u) as u32,right_tree) - } else { - (right_tree, (cur_node+1u) as u32) - }; - - // find intersection with plane - // origin + dir*t = splitter - let plane_dist = (splitter - origin) * inv_dir_scalar; - - if plane_dist > maxt || plane_dist < 0f32 { - cur_node = near as uint; - } else if plane_dist < mint { - cur_node = far as uint; - } else{ - vec::push(stack, (far, plane_dist, maxt)); - cur_node = near as uint; - maxt = plane_dist; - } - } - } - } +#[inline(always)] +fn trace_kd_tree_shadow( + polys: model::polysoup, + kd_tree_nodes: [model::kd_tree_node], + kd_tree_root: uint, + r: ray, + inv_dir: vec3, + inmint: f32, + inmaxt: f32 ) + -> bool { + + let mut stack : [(u32, f32, f32)] = []; + let mut mint = inmint; + let mut maxt = inmaxt; + let mut cur_node = kd_tree_root; + loop { + + alt kd_tree_nodes[cur_node] { + model::leaf(tri_begin, tri_count) { + + let mut tri_index = tri_begin; + while tri_index < tri_begin + tri_count { + let t = get_triangle( polys, tri_index as uint); + if option::is_some( ray_triangle_intersect( r, t ) ) { + ret true; + } + tri_index += 1u32; + } + if ( vec::len(stack) > 0u ){ + let (n,mn,mx) = vec::pop(stack); + cur_node = n as uint; + mint = mn; + maxt = mx; + } else { + ret false; + } + } + model::node(axis, splitter, right_tree){ + + // find the scalar direction/origin for the current axis + let (inv_dir_scalar, origin) = alt axis { + model::x() { (inv_dir.x, r.origin.x) } + model::y() { (inv_dir.y, r.origin.y) } + model::z() { (inv_dir.z, r.origin.z) } + }; + + // figure out which side of the spliting plane the ray origin is + // i.e. which child we need to test first. + let (near,far) = if origin < splitter || (origin == splitter && inv_dir_scalar >= 0f32) { + ((cur_node+1u) as u32,right_tree) + } else { + (right_tree, (cur_node+1u) as u32) + }; + + // find intersection with plane + // origin + dir*t = splitter + let plane_dist = (splitter - origin) * inv_dir_scalar; + + if plane_dist > maxt || plane_dist < 0f32 { + cur_node = near as uint; + } else if plane_dist < mint { + cur_node = far as uint; + } else{ + vec::push(stack, (far, plane_dist, maxt)); + cur_node = near as uint; + maxt = plane_dist; + } + } + } + } } -#[inline(always)] +#[inline(always)] fn trace_soup( polys: model::polysoup, r: ray) -> option<(hit_result, uint)>{ - - let mut res : option<(hit_result, uint)> = option::none; - - for uint::range( 0u, vec::len( polys.indices ) / 3u) |tri_ix| { - let tri = get_triangle(polys,tri_ix); - - let new_hit = ray_triangle_intersect( r, tri ); - - alt (res, new_hit) { - (option::none(),option::some(hit)) { - res = option::some((hit, tri_ix)); - } - (option::some((old_hit,_)), option::some(hit)) - if hit.t < old_hit.t && hit.t > 0f32 { - res = option::some((hit, tri_ix)); - } - _ {} - } - } - - ret res; + + let mut res : option<(hit_result, uint)> = option::none; + + for uint::range( 0u, vec::len( polys.indices ) / 3u) |tri_ix| { + let tri = get_triangle(polys,tri_ix); + + let new_hit = ray_triangle_intersect( r, tri ); + + alt (res, new_hit) { + (option::none(),option::some(hit)) { + res = option::some((hit, tri_ix)); + } + (option::some((old_hit,_)), option::some(hit)) + if hit.t < old_hit.t && hit.t > 0f32 { + res = option::some((hit, tri_ix)); + } + _ {} + } + } + + ret res; } type light = { pos: vec3, strength: f32, radius: f32, color: vec3}; fn make_light( pos: vec3, strength: f32, radius: f32, color: vec3 ) -> light { - { pos: pos, strength: strength, radius: radius, color: color } + { pos: pos, strength: strength, radius: radius, color: color } } #[inline(always)] fn direct_lighting( lights: [light], pos: vec3, n: vec3, view_vec: vec3, rnd: rand_env, depth: uint, occlusion_probe: fn(vec3) -> bool ) -> vec3 { - let mut direct_light = vec3(0f32,0f32,0f32); - for lights.each |l| { - - // compute shadow contribution - let mut shadow_contrib = 0f32; - - - let num_samples = if depth == 0u { // do one tap in reflections and GI - NUM_LIGHT_SAMPLES - } else { - 1u - }; - - let rot_to_up = rotate_to_up(normalized(sub(pos,l.pos))); - let shadow_sample_weight = 1f32 / (num_samples as f32); - do sample_disk(rnd,num_samples) |u,v| { // todo: stratify this - - // scale and rotate disk sample, and position it at the light's location - let sample_pos = add(l.pos,transform(rot_to_up, vec3(u*l.radius,0f32,v*l.radius) )); - - if !occlusion_probe( sub(sample_pos, pos)) { - shadow_contrib += shadow_sample_weight; - } - } - - let light_vec = sub(l.pos, pos); - let light_contrib = - if shadow_contrib == 0f32{ - vec3(0f32, 0f32, 0f32) - } else { - - let light_vec_n = normalized(light_vec); - let half_vector = normalized(add(light_vec_n, view_vec)); - - let s = dot(n,half_vector); - let specular = f32::pow(s,175f32); - - let atten = shadow_contrib*l.strength*(1.0f32/length_sq(light_vec) + specular*0.05f32); - - let intensity = atten*dot(n, light_vec_n ); - - scale(l.color, intensity) - }; - - direct_light = add(direct_light, light_contrib); - } - - ret direct_light; + let mut direct_light = vec3(0f32,0f32,0f32); + for lights.each |l| { + + // compute shadow contribution + let mut shadow_contrib = 0f32; + + + let num_samples = if depth == 0u { // do one tap in reflections and GI + NUM_LIGHT_SAMPLES + } else { + 1u + }; + + let rot_to_up = rotate_to_up(normalized(sub(pos,l.pos))); + let shadow_sample_weight = 1f32 / (num_samples as f32); + do sample_disk(rnd,num_samples) |u,v| { // todo: stratify this + + // scale and rotate disk sample, and position it at the light's location + let sample_pos = add(l.pos,transform(rot_to_up, vec3(u*l.radius,0f32,v*l.radius) )); + + if !occlusion_probe( sub(sample_pos, pos)) { + shadow_contrib += shadow_sample_weight; + } + } + + let light_vec = sub(l.pos, pos); + let light_contrib = + if shadow_contrib == 0f32{ + vec3(0f32, 0f32, 0f32) + } else { + + let light_vec_n = normalized(light_vec); + let half_vector = normalized(add(light_vec_n, view_vec)); + + let s = dot(n,half_vector); + let specular = f32::pow(s,175f32); + + let atten = shadow_contrib*l.strength*(1.0f32/length_sq(light_vec) + specular*0.05f32); + + let intensity = atten*dot(n, light_vec_n ); + + scale(l.color, intensity) + }; + + direct_light = add(direct_light, light_contrib); + } + + ret direct_light; } -#[inline(always)] +#[inline(always)] fn shade( - pos: vec3, n: vec3, n_face: vec3, r: ray, color: vec3, reflectivity: f32, lights: [light], rnd: rand_env, depth: uint, - occlusion_probe: fn(vec3) -> bool, - color_probe: fn(vec3) -> vec3 ) -> vec3 { - - let view_vec = normalized(sub(r.origin, pos)); - - // pass in n or n_face for smooth/flat shading - let shading_normal = if USE_SMOOTH_NORMALS_FOR_DIRECT_LIGHTING { n } else { n_face }; - - let direct_light = direct_lighting(lights, pos, shading_normal, view_vec, rnd, depth, occlusion_probe); - let reflection = sub(scale(shading_normal, dot(view_vec, shading_normal)*2f32), view_vec); - let rcolor = if reflectivity > 0.001f32 { color_probe( reflection ) } else { vec3(0f32,0f32,0f32) }; - - let mut ambient; - //ambient = vec3(0.5f32,0.5f32,0.5f32); - - /*let mut ao = 0f32; - let rot_to_up = rotate_to_up(n_face); - const NUM_AO_SAMPLES: uint = 5u; - sample_stratified_2d( rnd, NUM_AO_SAMPLES, NUM_AO_SAMPLES ) { |u,v| - let sample_vec = transform(rot_to_up, cosine_hemisphere_sample(u,v) ); - //let sample_vec = cosine_hemisphere_sample(u,v); - if !occlusion_probe( scale(sample_vec, 0.1f32) ) { - ao += 1f32/((NUM_AO_SAMPLES*NUM_AO_SAMPLES) as f32); - } - }; - ambient = scale(ambient,ao); // todo: add ambient color */ - - // Final gather GI - let gi_normal = if USE_SMOOTH_NORMALS_FOR_GI { n } else { n_face }; - ambient = vec3(0f32,0f32,0f32); - if depth == 0u && NUM_GI_SAMPLES_SQRT > 0u { - do sample_cosine_hemisphere( rnd, gi_normal ) |sample_vec| { - ambient = add( ambient, color_probe( sample_vec ) ); - }; - ambient = scale(ambient, 1f32 / (((NUM_GI_SAMPLES_SQRT * NUM_GI_SAMPLES_SQRT) as f32) * f32::consts::pi )); - } - - lerp( mul(color,add(direct_light, ambient)), rcolor, reflectivity) + pos: vec3, n: vec3, n_face: vec3, r: ray, color: vec3, reflectivity: f32, lights: [light], rnd: rand_env, depth: uint, + occlusion_probe: fn(vec3) -> bool, + color_probe: fn(vec3) -> vec3 ) -> vec3 { + + let view_vec = normalized(sub(r.origin, pos)); + + // pass in n or n_face for smooth/flat shading + let shading_normal = if USE_SMOOTH_NORMALS_FOR_DIRECT_LIGHTING { n } else { n_face }; + + let direct_light = direct_lighting(lights, pos, shading_normal, view_vec, rnd, depth, occlusion_probe); + let reflection = sub(scale(shading_normal, dot(view_vec, shading_normal)*2f32), view_vec); + let rcolor = if reflectivity > 0.001f32 { color_probe( reflection ) } else { vec3(0f32,0f32,0f32) }; + + let mut ambient; + //ambient = vec3(0.5f32,0.5f32,0.5f32); + + /*let mut ao = 0f32; + let rot_to_up = rotate_to_up(n_face); + const NUM_AO_SAMPLES: uint = 5u; + sample_stratified_2d( rnd, NUM_AO_SAMPLES, NUM_AO_SAMPLES ) { |u,v| + let sample_vec = transform(rot_to_up, cosine_hemisphere_sample(u,v) ); + //let sample_vec = cosine_hemisphere_sample(u,v); + if !occlusion_probe( scale(sample_vec, 0.1f32) ) { + ao += 1f32/((NUM_AO_SAMPLES*NUM_AO_SAMPLES) as f32); + } + }; + ambient = scale(ambient,ao); // todo: add ambient color */ + + // Final gather GI + let gi_normal = if USE_SMOOTH_NORMALS_FOR_GI { n } else { n_face }; + ambient = vec3(0f32,0f32,0f32); + if depth == 0u && NUM_GI_SAMPLES_SQRT > 0u { + do sample_cosine_hemisphere( rnd, gi_normal ) |sample_vec| { + ambient = add( ambient, color_probe( sample_vec ) ); + }; + ambient = scale(ambient, 1f32 / (((NUM_GI_SAMPLES_SQRT * NUM_GI_SAMPLES_SQRT) as f32) * f32::consts::pi )); + } + + lerp( mul(color,add(direct_light, ambient)), rcolor, reflectivity) } type intersection = { pos: vec3, n: vec3, n_face: vec3, color: vec3, reflectivity: f32 }; -#[inline(always)] +#[inline(always)] fn trace_checkerboard( checkerboard_height: f32, r : ray, mint: f32, maxt: f32) -> (option, f32) { - // trace against checkerboard first - let checker_hit_t = (checkerboard_height - r.origin.y) / r.dir.y; - - // compute checkerboard color, if we hit the floor plane - if checker_hit_t > mint && checker_hit_t < maxt { - - let pos = add(r.origin, scale(r.dir, checker_hit_t)); - - // hacky checkerboard pattern - let (u,v) = (f32::floor(pos.x*5f32) as int, f32::floor(pos.z*5f32) as int); - let is_white = (u + v) % 2 == 0 ; - let color = if is_white { vec3(1f32,1f32,1f32) } else { vec3(1.0f32,0.5f32,0.5f32) }; - let intersection = option::some( { - pos: pos, - n: vec3(0f32,1f32,0f32), - n_face: vec3(0f32,1f32,0f32), - color: color, - reflectivity: if is_white {0.3f32} else {0.0f32} } ); - (intersection, checker_hit_t) - } else { - (option::none, maxt) - } + // trace against checkerboard first + let checker_hit_t = (checkerboard_height - r.origin.y) / r.dir.y; + + // compute checkerboard color, if we hit the floor plane + if checker_hit_t > mint && checker_hit_t < maxt { + + let pos = add(r.origin, scale(r.dir, checker_hit_t)); + + // hacky checkerboard pattern + let (u,v) = (f32::floor(pos.x*5f32) as int, f32::floor(pos.z*5f32) as int); + let is_white = (u + v) % 2 == 0 ; + let color = if is_white { vec3(1f32,1f32,1f32) } else { vec3(1.0f32,0.5f32,0.5f32) }; + let intersection = option::some( { + pos: pos, + n: vec3(0f32,1f32,0f32), + n_face: vec3(0f32,1f32,0f32), + color: color, + reflectivity: if is_white {0.3f32} else {0.0f32} } ); + (intersection, checker_hit_t) + } else { + (option::none, maxt) + } } -#[inline(always)] +#[inline(always)] fn trace_ray( r : ray, mesh : model::mesh, mint: f32, maxt: f32) -> option { - let use_kd_tree = true; - - let y_size = math3d::sub(mesh.bounding_box.max, mesh.bounding_box.min).y; - - // compute checkerboard color, if we hit the floor plane - let (checker_intersection, new_maxt) = trace_checkerboard(-y_size*0.5f32,r,mint,maxt); - - - // check scene bounding box first - if !ray_aabb_check( r, new_maxt, mesh.bounding_box ){ - ret checker_intersection; - } - - // trace against scene - let trace_result = if use_kd_tree { - trace_kd_tree( mesh.polys, mesh.kd_tree.nodes, mesh.kd_tree.root, r, recip( r.dir ), mint, new_maxt ) + let use_kd_tree = true; + + let y_size = math3d::sub(mesh.bounding_box.max, mesh.bounding_box.min).y; + + // compute checkerboard color, if we hit the floor plane + let (checker_intersection, new_maxt) = trace_checkerboard(-y_size*0.5f32,r,mint,maxt); + + + // check scene bounding box first + if !ray_aabb_check( r, new_maxt, mesh.bounding_box ){ + ret checker_intersection; + } + + // trace against scene + let trace_result = if use_kd_tree { + trace_kd_tree( mesh.polys, mesh.kd_tree.nodes, mesh.kd_tree.root, r, recip( r.dir ), mint, new_maxt ) } else { - trace_soup( mesh.polys, r) + trace_soup( mesh.polys, r) }; - - alt trace_result { - option::some((hit_info, tri_ix)) if hit_info.t > 0f32 { - let pos = add( r.origin, scale(r.dir, hit_info.t)); - - let (i0,i1,i2) = ( mesh.polys.indices[tri_ix*3u], - mesh.polys.indices[tri_ix*3u+1u], - mesh.polys.indices[tri_ix*3u+2u] ); - - // interpolate vertex normals... - let n = normalized( - add( scale( mesh.polys.normals[i0], hit_info.barycentric.z), - add( scale( mesh.polys.normals[i1], hit_info.barycentric.x), - scale( mesh.polys.normals[i2], hit_info.barycentric.y)))); - - // compute face-normal - let (v0,v1,v2) = ( mesh.polys.vertices[i0], - mesh.polys.vertices[i1], - mesh.polys.vertices[i2] ); - let n_face = normalized( cross(sub(v1,v0), sub(v2,v0))); - - option::some( { - pos: pos, - n: n, - n_face: n_face, - color: vec3(1.0f32,1.0f32,1.0f32), - reflectivity: 0.0f32 } ) - } - _ { - checker_intersection - } - } + + alt trace_result { + option::some((hit_info, tri_ix)) if hit_info.t > 0f32 { + let pos = add( r.origin, scale(r.dir, hit_info.t)); + + let (i0,i1,i2) = ( mesh.polys.indices[tri_ix*3u], + mesh.polys.indices[tri_ix*3u+1u], + mesh.polys.indices[tri_ix*3u+2u] ); + + // interpolate vertex normals... + let n = normalized( + add( scale( mesh.polys.normals[i0], hit_info.barycentric.z), + add( scale( mesh.polys.normals[i1], hit_info.barycentric.x), + scale( mesh.polys.normals[i2], hit_info.barycentric.y)))); + + // compute face-normal + let (v0,v1,v2) = ( mesh.polys.vertices[i0], + mesh.polys.vertices[i1], + mesh.polys.vertices[i2] ); + let n_face = normalized( cross(sub(v1,v0), sub(v2,v0))); + + option::some( { + pos: pos, + n: n, + n_face: n_face, + color: vec3(1.0f32,1.0f32,1.0f32), + reflectivity: 0.0f32 } ) + } + _ { + checker_intersection + } + } } -#[inline(always)] +#[inline(always)] fn trace_ray_shadow( r : ray, mesh : model::mesh, mint: f32, maxt: f32) -> bool { - let y_size = math3d::sub(mesh.bounding_box.max, mesh.bounding_box.min).y; - - // compute checkerboard color, if we hit the floor plane - let (checker_intersection, new_maxt) = trace_checkerboard(-y_size*0.5f32,r,mint,maxt); - - if ( option::is_some( checker_intersection ) ){ - ret true; - } - - // check scene bounding box first - if !ray_aabb_check( r, new_maxt, mesh.bounding_box ){ - ret false; - } - - // trace against scene - trace_kd_tree_shadow( mesh.polys, mesh.kd_tree.nodes, mesh.kd_tree.root, r, recip( r.dir ), mint, new_maxt ) + let y_size = math3d::sub(mesh.bounding_box.max, mesh.bounding_box.min).y; + + // compute checkerboard color, if we hit the floor plane + let (checker_intersection, new_maxt) = trace_checkerboard(-y_size*0.5f32,r,mint,maxt); + + if ( option::is_some( checker_intersection ) ){ + ret true; + } + + // check scene bounding box first + if !ray_aabb_check( r, new_maxt, mesh.bounding_box ){ + ret false; + } + + // trace against scene + trace_kd_tree_shadow( mesh.polys, mesh.kd_tree.nodes, mesh.kd_tree.root, r, recip( r.dir ), mint, new_maxt ) } -#[inline(always)] +#[inline(always)] fn get_color( r: ray, mesh: model::mesh, lights: [light], rnd: rand_env, tmin: f32, tmax: f32, depth: uint) -> vec3 { - let theta = dot( vec3(0f32,1f32,0f32), r.dir ); - let default_color = vec3(clamp(1f32-theta*4f32,0f32,0.75f32)+0.25f32, clamp(0.5f32-theta*3f32,0f32,0.75f32)+0.25f32, theta); // fake sky colour - - - if depth >= MAX_TRACE_DEPTH { - ret default_color; - } - - alt trace_ray( r, mesh, tmin, tmax ) { - option::some({pos,n,n_face,color,reflectivity}) { - let surface_origin = add(pos, scale(n_face, 0.000002f32)); - - shade(pos, n, n_face, r, color, reflectivity, lights, rnd, depth, - |occlusion_vec| { - let occlusion_ray = {origin: surface_origin, dir: occlusion_vec}; - trace_ray_shadow(occlusion_ray, mesh, 0f32, 1f32) - }, - |ray_dir| { - let reflection_ray = {origin: surface_origin, dir: normalized(ray_dir)}; - get_color(reflection_ray, mesh, lights, rnd, tmin, tmax, depth + 1u) - }) - - } - _ { default_color } - } + let theta = dot( vec3(0f32,1f32,0f32), r.dir ); + let default_color = vec3(clamp(1f32-theta*4f32,0f32,0.75f32)+0.25f32, clamp(0.5f32-theta*3f32,0f32,0.75f32)+0.25f32, theta); // fake sky colour + + + if depth >= MAX_TRACE_DEPTH { + ret default_color; + } + + alt trace_ray( r, mesh, tmin, tmax ) { + option::some({pos,n,n_face,color,reflectivity}) { + let surface_origin = add(pos, scale(n_face, 0.000002f32)); + + shade(pos, n, n_face, r, color, reflectivity, lights, rnd, depth, + |occlusion_vec| { + let occlusion_ray = {origin: surface_origin, dir: occlusion_vec}; + trace_ray_shadow(occlusion_ray, mesh, 0f32, 1f32) + }, + |ray_dir| { + let reflection_ray = {origin: surface_origin, dir: normalized(ray_dir)}; + get_color(reflection_ray, mesh, lights, rnd, tmin, tmax, depth + 1u) + }) + + } + _ { default_color } + } } fn gamma_correct( v : vec3 ) -> vec3 { - vec3( f32::pow( v.x, 1f32/2.2f32 ), - f32::pow( v.y, 1f32/2.2f32 ), - f32::pow( v.z, 1f32/2.2f32 )) + vec3( f32::pow( v.x, 1f32/2.2f32 ), + f32::pow( v.y, 1f32/2.2f32 ), + f32::pow( v.z, 1f32/2.2f32 )) } -fn generate_raytraced_image( - mesh: model::mesh, - horizontalFOV: f32, - width: uint, - height: uint, - sample_grid_size: uint) -> [color] -{ - let sample_coverage_inv = 1f32 / (sample_grid_size as f32); - let rnd = get_rand_env(); - - let lights = [ make_light(vec3(-3f32, 3f32, 0f32),10f32, 0.3f32, vec3(1f32,1f32,1f32)) ]; //, - //make_light(vec3(0f32, 0f32, 0f32), 10f32, 0.25f32, vec3(1f32,1f32,1.0f32))]; - - for_each_pixel( width, height, |x,y| { - let mut shaded_color = vec3(0f32,0f32,0f32); - - do sample_stratified_2d(rnd, sample_grid_size, sample_grid_size) |u,v| { - let sample = if sample_grid_size == 1u { - (0f32,0f32) - } else { - (u-0.5f32, v-0.5f32) - }; - - let r = get_ray(horizontalFOV, width, height, x, y, sample ); - shaded_color = add( shaded_color, get_color(r, mesh, lights, rnd, 0f32, f32::infinity, 0u)); - } - - - shaded_color = scale(gamma_correct(scale( shaded_color, sample_coverage_inv*sample_coverage_inv)), 255f32); - - { r: clamp(shaded_color.x, 0f32, 255f32) as u8, - g: clamp(shaded_color.y, 0f32, 255f32) as u8, - b: clamp(shaded_color.z, 0f32, 255f32) as u8 } - }) -} +fn generate_raytraced_image( + mesh: model::mesh, + horizontalFOV: f32, + width: uint, + height: uint, + sample_grid_size: uint) -> [color] +{ + let sample_coverage_inv = 1f32 / (sample_grid_size as f32); + let rnd = get_rand_env(); + + let lights = [ make_light(vec3(-3f32, 3f32, 0f32),10f32, 0.3f32, vec3(1f32,1f32,1f32)) ]; //, + //make_light(vec3(0f32, 0f32, 0f32), 10f32, 0.25f32, vec3(1f32,1f32,1.0f32))]; + + for_each_pixel( width, height, |x,y| { + let mut shaded_color = vec3(0f32,0f32,0f32); + + do sample_stratified_2d(rnd, sample_grid_size, sample_grid_size) |u,v| { + let sample = if sample_grid_size == 1u { + (0f32,0f32) + } else { + (u-0.5f32, v-0.5f32) + }; + let r = get_ray(horizontalFOV, width, height, x, y, sample ); + shaded_color = add( shaded_color, get_color(r, mesh, lights, rnd, 0f32, f32::infinity, 0u)); + } + + + shaded_color = scale(gamma_correct(scale( shaded_color, sample_coverage_inv*sample_coverage_inv)), 255f32); + + { r: clamp(shaded_color.x, 0f32, 255f32) as u8, + g: clamp(shaded_color.y, 0f32, 255f32) as u8, + b: clamp(shaded_color.z, 0f32, 255f32) as u8 } + }) +} From 6bb73f9650e3e8a8996d80957d2ba945a5bfc9d6 Mon Sep 17 00:00:00 2001 From: Mikhail Glushenkov Date: Sat, 14 Jul 2012 00:51:08 +0200 Subject: [PATCH 4/4] Explain how to compile rustray. --- README.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.txt b/README.txt index ad85bac..d629bbd 100644 --- a/README.txt +++ b/README.txt @@ -3,6 +3,9 @@ January 2012 A raytracing proof-of-concept in Rust +COMPILING: + $ rustc -W no-old-vecs rustray.rc + DEMO: $ wget http://groups.csail.mit.edu/graphics/classes/6.837/F03/models/cow-nonormals.obj $ ./rustray cow-nonormals.obj @@ -15,5 +18,3 @@ DEMO: Tracing rays... Done! Writing "./oput.ppm"...Done! $ gimp oput.ppm - -