Implemented basic multi-threaded rendering.
It's a bit inefficient because a thread is spawned for each pixel. Need to implement bucketing.
This commit is contained in:
parent
fac46d6db3
commit
a681ba461e
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -4,7 +4,9 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nom 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nom 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num_cpus 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"scoped_threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -52,6 +54,14 @@ name = "nom"
|
||||||
version = "1.2.3"
|
version = "1.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "0.1.71"
|
version = "0.1.71"
|
||||||
|
@ -74,6 +84,11 @@ name = "rustc-serialize"
|
||||||
version = "0.3.19"
|
version = "0.3.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped_threadpool"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
|
|
@ -6,4 +6,6 @@ authors = ["Nathan Vegdahl <cessen@cessen.com>"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
docopt = "0.6"
|
docopt = "0.6"
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
nom = "1.2"
|
nom = "1.2"
|
||||||
|
scoped_threadpool = "0.1"
|
||||||
|
num_cpus = "0.2"
|
|
@ -1,6 +1,6 @@
|
||||||
Scene $Scene_fr1 {
|
Scene $Scene_fr1 {
|
||||||
Output {
|
Output {
|
||||||
Path ["cube.ppm"]
|
Path ["test_renders/cube.ppm"]
|
||||||
}
|
}
|
||||||
RenderSettings {
|
RenderSettings {
|
||||||
Resolution [960 540]
|
Resolution [960 540]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
|
extern crate scoped_threadpool;
|
||||||
|
extern crate num_cpus;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate nom;
|
extern crate nom;
|
||||||
|
|
||||||
|
@ -99,7 +101,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Rendering scene...");
|
println!("Rendering scene...");
|
||||||
r.render();
|
r.render(num_cpus::get() as u32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -606,8 +606,7 @@ mod tests {
|
||||||
let (token8, input9) = next_token(input8);
|
let (token8, input9) = next_token(input8);
|
||||||
|
|
||||||
assert_eq!((token1, input2),
|
assert_eq!((token1, input2),
|
||||||
(Token::TypeName("Thing"),
|
(Token::TypeName("Thing"), (5, " $yar { # A comment\n\tThing2 []\n}")));
|
||||||
(5, " $yar { # A comment\n\tThing2 []\n}")));
|
|
||||||
assert_eq!((token2, input3),
|
assert_eq!((token2, input3),
|
||||||
(Token::Ident("$yar"), (10, " { # A comment\n\tThing2 []\n}")));
|
(Token::Ident("$yar"), (10, " { # A comment\n\tThing2 []\n}")));
|
||||||
assert_eq!((token3, input4),
|
assert_eq!((token3, input4),
|
||||||
|
|
|
@ -48,15 +48,13 @@ pub fn parse_scene(tree: &DataTree) -> Result<Renderer, PsyParseError> {
|
||||||
|
|
||||||
// Parse output info
|
// Parse output info
|
||||||
let output_info = try!(parse_output_info(tree.iter_children_with_type("Output")
|
let output_info = try!(parse_output_info(tree.iter_children_with_type("Output")
|
||||||
.nth(0)
|
.nth(0)
|
||||||
.unwrap()));
|
.unwrap()));
|
||||||
|
|
||||||
// Parse render settings
|
// Parse render settings
|
||||||
let render_settings = try!(parse_render_settings(tree.iter_children_with_type("Rende\
|
let render_settings = try!(parse_render_settings(tree.iter_children_with_type("RenderSettings")
|
||||||
rSett\
|
.nth(0)
|
||||||
ings")
|
.unwrap()));
|
||||||
.nth(0)
|
|
||||||
.unwrap()));
|
|
||||||
|
|
||||||
// Parse camera
|
// Parse camera
|
||||||
let camera = try!(parse_camera(tree.iter_children_with_type("Camera").nth(0).unwrap()));
|
let camera = try!(parse_camera(tree.iter_children_with_type("Camera").nth(0).unwrap()));
|
||||||
|
|
138
src/renderer.rs
138
src/renderer.rs
|
@ -1,6 +1,9 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use scoped_threadpool::Pool;
|
||||||
|
|
||||||
use tracer::Tracer;
|
use tracer::Tracer;
|
||||||
use halton;
|
use halton;
|
||||||
|
@ -18,10 +21,11 @@ pub struct Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
pub fn render(&self) {
|
pub fn render(&self, thread_count: u32) {
|
||||||
let mut rays = Vec::new();
|
let mut tpool = Pool::new(thread_count);
|
||||||
let mut tracer = Tracer::from_assembly(&self.scene.root);
|
|
||||||
let mut img = Image::new(self.resolution.0, self.resolution.1);
|
let img = Mutex::new(RefCell::new(Image::new(self.resolution.0, self.resolution.1)));
|
||||||
|
|
||||||
|
|
||||||
// Pre-calculate some useful values related to the image plane
|
// Pre-calculate some useful values related to the image plane
|
||||||
let cmpx = 1.0 / self.resolution.0 as f32;
|
let cmpx = 1.0 / self.resolution.0 as f32;
|
||||||
|
@ -35,62 +39,88 @@ impl Renderer {
|
||||||
|
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
for y in 0..img.height() {
|
tpool.scoped(|scope| {
|
||||||
for x in 0..img.width() {
|
let (img_width, img_height) = {
|
||||||
let offset = hash_u32(((x as u32) << 16) ^ (y as u32), 0);
|
let i = img.lock().unwrap();
|
||||||
|
let w = i.borrow().width();
|
||||||
|
let h = i.borrow().height();
|
||||||
|
(w, h)
|
||||||
|
};
|
||||||
|
for y in 0..img_height {
|
||||||
|
for x in 0..img_width {
|
||||||
|
let img = &img;
|
||||||
|
scope.execute(move || {
|
||||||
|
let mut rays = Vec::new();
|
||||||
|
let mut tracer = Tracer::from_assembly(&self.scene.root);
|
||||||
|
|
||||||
// Generate rays
|
let offset = hash_u32(((x as u32) << 16) ^ (y as u32), 0);
|
||||||
rays.clear();
|
|
||||||
for si in 0..self.spp {
|
|
||||||
let mut ray = {
|
|
||||||
let filter_x = fast_logit(halton::sample(3, offset + si as u32), 1.5) + 0.5;
|
|
||||||
let filter_y = fast_logit(halton::sample(4, offset + si as u32), 1.5) + 0.5;
|
|
||||||
let samp_x = (filter_x + x as f32) * cmpx;
|
|
||||||
let samp_y = (filter_y + y as f32) * cmpy;
|
|
||||||
|
|
||||||
self.scene.camera.generate_ray((samp_x - 0.5) * x_extent,
|
// Generate rays
|
||||||
(0.5 - samp_y) * y_extent,
|
rays.clear();
|
||||||
halton::sample(0, offset + si as u32),
|
for si in 0..self.spp {
|
||||||
halton::sample(1, offset + si as u32),
|
let mut ray = {
|
||||||
halton::sample(2, offset + si as u32))
|
let filter_x =
|
||||||
};
|
fast_logit(halton::sample(3, offset + si as u32), 1.5) + 0.5;
|
||||||
ray.id = si as u32;
|
let filter_y =
|
||||||
rays.push(ray);
|
fast_logit(halton::sample(4, offset + si as u32), 1.5) + 0.5;
|
||||||
|
let samp_x = (filter_x + x as f32) * cmpx;
|
||||||
|
let samp_y = (filter_y + y as f32) * cmpy;
|
||||||
|
|
||||||
|
self.scene.camera.generate_ray((samp_x - 0.5) * x_extent,
|
||||||
|
(0.5 - samp_y) * y_extent,
|
||||||
|
halton::sample(0,
|
||||||
|
offset + si as u32),
|
||||||
|
halton::sample(1,
|
||||||
|
offset + si as u32),
|
||||||
|
halton::sample(2,
|
||||||
|
offset + si as u32))
|
||||||
|
};
|
||||||
|
ray.id = si as u32;
|
||||||
|
rays.push(ray);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test rays against scene
|
||||||
|
let isects = tracer.trace(&rays);
|
||||||
|
|
||||||
|
// Calculate color based on ray hits
|
||||||
|
let mut r = 0.0;
|
||||||
|
let mut g = 0.0;
|
||||||
|
let mut b = 0.0;
|
||||||
|
for isect in isects.iter() {
|
||||||
|
if let &surface::SurfaceIntersection::Hit { t: _,
|
||||||
|
pos: _,
|
||||||
|
nor: _,
|
||||||
|
space: _,
|
||||||
|
uv } = isect {
|
||||||
|
r += uv.0;
|
||||||
|
g += uv.1;
|
||||||
|
b += (1.0 - uv.0 - uv.1).max(0.0);
|
||||||
|
} else {
|
||||||
|
r += 0.02;
|
||||||
|
g += 0.02;
|
||||||
|
b += 0.02;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r = 255.0 * srgb_gamma(r / self.spp as f32);
|
||||||
|
g = 255.0 * srgb_gamma(g / self.spp as f32);
|
||||||
|
b = 255.0 * srgb_gamma(b / self.spp as f32);
|
||||||
|
|
||||||
|
// Set pixel color
|
||||||
|
let img = img.lock().unwrap();
|
||||||
|
img.borrow_mut().set(x, y, (r as u8, g as u8, b as u8));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test rays against scene
|
|
||||||
let isects = tracer.trace(&rays);
|
|
||||||
|
|
||||||
// Calculate color based on ray hits
|
|
||||||
let mut r = 0.0;
|
|
||||||
let mut g = 0.0;
|
|
||||||
let mut b = 0.0;
|
|
||||||
for isect in isects.iter() {
|
|
||||||
if let &surface::SurfaceIntersection::Hit { t: _,
|
|
||||||
pos: _,
|
|
||||||
nor: _,
|
|
||||||
space: _,
|
|
||||||
uv } = isect {
|
|
||||||
r += uv.0;
|
|
||||||
g += uv.1;
|
|
||||||
b += (1.0 - uv.0 - uv.1).max(0.0);
|
|
||||||
} else {
|
|
||||||
r += 0.02;
|
|
||||||
g += 0.02;
|
|
||||||
b += 0.02;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r = 255.0 * srgb_gamma(r / self.spp as f32);
|
|
||||||
g = 255.0 * srgb_gamma(g / self.spp as f32);
|
|
||||||
b = 255.0 * srgb_gamma(b / self.spp as f32);
|
|
||||||
|
|
||||||
// Set pixel color
|
|
||||||
img.set(x, y, (r as u8, g as u8, b as u8));
|
|
||||||
}
|
}
|
||||||
}
|
// scope.defer(|| println!("Exiting scope"));
|
||||||
|
// scope.spawn(|| println!("Running child thread in scope"))
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Write rendered image to disk
|
// Write rendered image to disk
|
||||||
let _ = img.write_binary_ppm(Path::new(&self.output_file));
|
{
|
||||||
|
let img = &img.lock().unwrap();
|
||||||
|
let _ = img.borrow().write_binary_ppm(Path::new(&self.output_file));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,6 @@ pub enum SurfaceIntersection {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Surface: Boundable + Debug {
|
pub trait Surface: Boundable + Debug + Sync {
|
||||||
fn intersect_rays(&self, rays: &mut [Ray], isects: &mut [SurfaceIntersection]);
|
fn intersect_rays(&self, rays: &mut [Ray], isects: &mut [SurfaceIntersection]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user