Hook up the new renderer
The threading code is gone, now. We're back to just having a single loop to drive the whole thing. Along with this, I realized that the Distrs container thing wasn't actually being used. It's a real pain to cart it around, and very few things actually use it. TODO: Reinstate the small wiggle done by the uv mapping routine. This version no longer nudges the coordinate, so I expect there to be some small visual differences.
This commit is contained in:
119
src/main.rs
119
src/main.rs
@@ -3,26 +3,37 @@ mod primitives;
|
||||
mod scene;
|
||||
mod renderer;
|
||||
|
||||
use crate::primitives::Vec3;
|
||||
use crate::primitives::{
|
||||
Vec2i,
|
||||
Vec3,
|
||||
Rect,
|
||||
};
|
||||
use crate::scene::{
|
||||
Camera,
|
||||
Scene
|
||||
};
|
||||
use crate::renderer::RenderCommand;
|
||||
|
||||
use crate::renderer::{
|
||||
Tile,
|
||||
RenderProperties,
|
||||
};
|
||||
|
||||
use rand::SeedableRng;
|
||||
use rand::rngs::SmallRng;
|
||||
use std::thread;
|
||||
|
||||
|
||||
fn main() {
|
||||
// image
|
||||
let aspect_ratio = 3.0 / 2.0;
|
||||
let image = (
|
||||
400,
|
||||
(400.0 / aspect_ratio) as i32
|
||||
);
|
||||
let samples_per_pixel: u32 = 10;
|
||||
let max_depth = 50;
|
||||
let image = Vec2i {
|
||||
x: 400,
|
||||
y: (400.0 / aspect_ratio) as i32
|
||||
};
|
||||
|
||||
let render_config = RenderProperties {
|
||||
samples: 10,
|
||||
bounces: 50
|
||||
};
|
||||
|
||||
// random generator
|
||||
let mut small_rng = SmallRng::seed_from_u64(0);
|
||||
@@ -44,84 +55,20 @@ fn main() {
|
||||
// render
|
||||
// The render loop should now be a job submission mechanism
|
||||
// Iterate lines, submitting them as tasks to the thread.
|
||||
println!("P3\n{} {}\n255", image.0, image.1);
|
||||
let context = renderer::RenderContext {
|
||||
camera: &scene.camera,
|
||||
image,
|
||||
max_depth,
|
||||
samples_per_pixel,
|
||||
world: scene.world,
|
||||
};
|
||||
|
||||
thread::scope(|s| {
|
||||
let (mut dispatcher, scanline_receiver) = renderer::Dispatcher::new(&small_rng, 12);
|
||||
|
||||
s.spawn(move || {
|
||||
for y in (0..image.1).rev() {
|
||||
eprintln!("Submitting scanline: {}", y);
|
||||
let job = RenderCommand::Line { line_num: y, context: context.clone() };
|
||||
dispatcher.submit_job(job).unwrap();
|
||||
}
|
||||
|
||||
dispatcher.submit_job(RenderCommand::Stop).unwrap();
|
||||
// ... also I happen to know there are 4 threads.
|
||||
});
|
||||
|
||||
/*
|
||||
* Store received results in the segments buffer.
|
||||
* Some will land before their previous segments and will need to be held
|
||||
* until the next-to-write arrives.
|
||||
*
|
||||
* Elements are sorted in reverse order so that they can be popped from the
|
||||
* Vec quickly.
|
||||
*
|
||||
* The queue is scanned every single time a new item is received. In the
|
||||
* happy path where the received item is next-up, it'll be buffered, checked
|
||||
* and then printed. In the case where it isn't, it'll get buffered and
|
||||
* stick around for more loops. When the next-to-write finally lands, it
|
||||
* means the n+1 element is up, now. If that element is already in the buffer
|
||||
* we want to write it out. Hence the loop that scans the whole buffer each
|
||||
* receive.
|
||||
*
|
||||
* TODO: There could be an up-front conditional that checks to see if the
|
||||
* received item *is* the next-to-write and skip the buffering step.
|
||||
* But I need to make the concept work at all, first.
|
||||
*/
|
||||
let mut raster_segments = Vec::<renderer::RenderResult>::new();
|
||||
let mut sl_output_index = image.1-1; // scanlines count down, start at image height.
|
||||
while let Ok(scanline) = scanline_receiver.recv() {
|
||||
eprintln!("Received scanline: {}", scanline.line_num);
|
||||
|
||||
raster_segments.push(scanline);
|
||||
raster_segments.sort_by( |a, b| b.cmp(a) );
|
||||
|
||||
loop {
|
||||
if raster_segments.len() == 0 { break; } // can this ever happen? Not while every
|
||||
// single element gets pushed to the
|
||||
// buffer first. With the happy path
|
||||
// short-circuit noted above, it could.
|
||||
|
||||
let last_ind = raster_segments.len() - 1;
|
||||
if raster_segments[last_ind].line_num == sl_output_index{
|
||||
let scanline = raster_segments.pop().unwrap();
|
||||
print_scanline(scanline, samples_per_pixel);
|
||||
sl_output_index -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!("P3\n{} {}\n255", image.x, image.y);
|
||||
for row in (0..image.y).rev() {
|
||||
let bounds = Rect{ // render boundary (a horizontal slice)
|
||||
x: 0,
|
||||
y: row,
|
||||
w: image.x,
|
||||
h: 1
|
||||
};
|
||||
let tile = Tile::render_line(bounds, row, image, &scene, &render_config, &mut small_rng);
|
||||
eprintln!("Printing scanline #{}", row);
|
||||
for pixel in tile.pixels {
|
||||
println!("{}", pixel.print_ppm(render_config.samples))
|
||||
}
|
||||
eprintln!("Size of raster_segments at finish: {}", raster_segments.len());
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
// TODO: Dispatcher shutdown mechanism. Right now, we might technically be leaking threads.
|
||||
eprintln!("Done!");
|
||||
}
|
||||
|
||||
fn print_scanline(scanline: renderer::RenderResult, samples_per_pixel: u32){
|
||||
eprintln!("Printing scanline num: {}", scanline.line_num);
|
||||
for color in &scanline.line {
|
||||
println!("{}", color.print_ppm(samples_per_pixel));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,18 +12,12 @@ use crate::scene::{
|
||||
};
|
||||
|
||||
use rand::rngs::SmallRng;
|
||||
use rand::distributions::Uniform;
|
||||
|
||||
const SKY_COLOR: Vec3 = Vec3 { x: 0.5, y: 0.7, z: 1.0};
|
||||
|
||||
struct Distrs{
|
||||
zero_one: Uniform<f32>,
|
||||
one_one: Uniform<f32>,
|
||||
}
|
||||
|
||||
struct RenderProperties {
|
||||
samples: u32, // samples are averaged results over a pixel
|
||||
bounces: u32, // bounces are how far the ray will travel (in hits not total distance)
|
||||
pub struct RenderProperties {
|
||||
pub samples: u32, // samples are averaged results over a pixel
|
||||
pub bounces: u32, // bounces are how far the ray will travel (in hits not total distance)
|
||||
}
|
||||
|
||||
fn to_uv(coord: Vec2i, img_size: Vec2i) -> Vec2f {
|
||||
@@ -35,7 +29,6 @@ fn to_uv(coord: Vec2i, img_size: Vec2i) -> Vec2f {
|
||||
fn ray_color(
|
||||
r: Ray, surface: &Hittable, depth: u32,
|
||||
rng: &mut SmallRng,
|
||||
distr: &Distrs,
|
||||
) -> Vec3 {
|
||||
// recursion guard
|
||||
if depth == 0 {
|
||||
@@ -57,7 +50,7 @@ fn ray_color(
|
||||
rng
|
||||
) {
|
||||
return attenuation * ray_color(
|
||||
scattered, surface, depth-1, rng, distr
|
||||
scattered, surface, depth-1, rng
|
||||
);
|
||||
}
|
||||
} // TODO: explicit else block
|
||||
@@ -79,7 +72,6 @@ fn sample_pixel(
|
||||
img_size: Vec2i,
|
||||
// Supplied by the execution environment (the thread)
|
||||
rng: &mut SmallRng,
|
||||
dist: &Distrs,
|
||||
) -> Vec3{
|
||||
(0..render_props.samples)
|
||||
.fold(
|
||||
@@ -87,22 +79,26 @@ fn sample_pixel(
|
||||
|color, _sample| -> Vec3 {
|
||||
let uv = to_uv(coord, img_size);
|
||||
let ray = scene.camera.get_ray(uv.x, uv.y, rng);
|
||||
color + ray_color(ray, &scene.world, render_props.bounces, rng, dist)
|
||||
if ray.dir.x.is_nan() {
|
||||
panic!("Ray dir.x is NAN");
|
||||
}
|
||||
color + ray_color(ray, &scene.world, render_props.bounces, rng)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
struct Tile {
|
||||
bounds: Rect,
|
||||
pixels: Vec<Vec3>,
|
||||
pub struct Tile {
|
||||
_bounds: Rect,
|
||||
pub pixels: Vec<Vec3>,
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
pub fn render_line(
|
||||
bounds: Rect, y: i32, // bounding rect and line
|
||||
img_size: Vec2i,
|
||||
scene: &Scene,
|
||||
properties: &RenderProperties,
|
||||
rng: &mut SmallRng, distr: &Distrs // rng utils
|
||||
rng: &mut SmallRng, // rng utils
|
||||
) -> Self {
|
||||
let pixels = (0..bounds.w)
|
||||
.map ( |x| -> Vec3{
|
||||
@@ -110,12 +106,11 @@ impl Tile {
|
||||
Vec2i{x, y},
|
||||
scene,
|
||||
properties,
|
||||
bounds.size(),
|
||||
img_size,
|
||||
rng,
|
||||
distr
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
Self { bounds, pixels }
|
||||
Self { _bounds: bounds, pixels }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user