Refactor main rendering loop to use TileIterator
This removes the old multithreading code, but will be using rayon soon.
This commit is contained in:
parent
d21d288013
commit
bdd05f3527
|
|
@ -12,8 +12,8 @@ use crate::Real;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
struct ImageSampler<T: Real> {
|
struct ImageSampler<T: Real> {
|
||||||
image_height_pixels: u32,
|
image_height_pixels: usize,
|
||||||
image_width_pixels: u32,
|
image_width_pixels: usize,
|
||||||
|
|
||||||
film_width: T,
|
film_width: T,
|
||||||
film_height: T,
|
film_height: T,
|
||||||
|
|
@ -23,7 +23,7 @@ struct ImageSampler<T: Real> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Real> ImageSampler<T> {
|
impl<T: Real> ImageSampler<T> {
|
||||||
pub fn new(width: u32, height: u32, camera_location: Point3<T>) -> ImageSampler<T> {
|
pub fn new(width: usize, height: usize, camera_location: Point3<T>) -> ImageSampler<T> {
|
||||||
let (film_width, film_height) = {
|
let (film_width, film_height) = {
|
||||||
let width: T = convert(width as f64);
|
let width: T = convert(width as f64);
|
||||||
let height: T = convert(height as f64);
|
let height: T = convert(height as f64);
|
||||||
|
|
@ -44,7 +44,7 @@ impl<T: Real> ImageSampler<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale(i: u32, n: u32, l: T) -> T {
|
fn scale(i: usize, n: usize, l: T) -> T {
|
||||||
let one: T = convert(1.0);
|
let one: T = convert(1.0);
|
||||||
let n: T = convert(n as f64);
|
let n: T = convert(n as f64);
|
||||||
let i: T = convert(i as f64);
|
let i: T = convert(i as f64);
|
||||||
|
|
@ -52,7 +52,7 @@ impl<T: Real> ImageSampler<T> {
|
||||||
(i + convert(0.5)) * pixel_size
|
(i + convert(0.5)) * pixel_size
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ray_for_pixel(&self, row: u32, column: u32) -> Ray<T> {
|
fn ray_for_pixel(&self, row: usize, column: usize) -> Ray<T> {
|
||||||
Ray::new(
|
Ray::new(
|
||||||
self.camera_location,
|
self.camera_location,
|
||||||
Vector3::new(
|
Vector3::new(
|
||||||
|
|
@ -75,12 +75,12 @@ pub fn render_scene<T: Real>(output_image: Arc<Mutex<ImageRgbF<T>>>, scene: Arc<
|
||||||
pub fn partial_render_scene<T: Real>(
|
pub fn partial_render_scene<T: Real>(
|
||||||
output_image: Arc<Mutex<ImageRgbF<T>>>,
|
output_image: Arc<Mutex<ImageRgbF<T>>>,
|
||||||
scene: Arc<Scene<T>>,
|
scene: Arc<Scene<T>>,
|
||||||
row_start: u32,
|
row_start: usize,
|
||||||
row_end: u32,
|
row_end: usize,
|
||||||
column_start: u32,
|
column_start: usize,
|
||||||
column_end: u32,
|
column_end: usize,
|
||||||
height: u32,
|
height: usize,
|
||||||
width: u32,
|
width: usize,
|
||||||
) {
|
) {
|
||||||
let image_sampler = ImageSampler::new(width, height, scene.camera_location);
|
let image_sampler = ImageSampler::new(width, height, scene.camera_location);
|
||||||
let ambient_intensity: T = convert(0.0);
|
let ambient_intensity: T = convert(0.0);
|
||||||
|
|
|
||||||
36
src/image.rs
36
src/image.rs
|
|
@ -8,12 +8,12 @@ use crate::Real;
|
||||||
|
|
||||||
pub struct ImageRgbU8 {
|
pub struct ImageRgbU8 {
|
||||||
pixel_data: Vec<u8>,
|
pixel_data: Vec<u8>,
|
||||||
width: u32,
|
width: usize,
|
||||||
height: u32,
|
height: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageRgbU8 {
|
impl ImageRgbU8 {
|
||||||
pub fn new(width: u32, height: u32) -> ImageRgbU8 {
|
pub fn new(width: usize, height: usize) -> ImageRgbU8 {
|
||||||
ImageRgbU8 {
|
ImageRgbU8 {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
|
@ -28,7 +28,7 @@ impl ImageRgbU8 {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_colour(&self, row: u32, column: u32) -> ColourRgbU8 {
|
pub fn get_colour(&self, row: usize, column: usize) -> ColourRgbU8 {
|
||||||
assert!(row < self.height && column < self.width);
|
assert!(row < self.height && column < self.width);
|
||||||
let index = self.calculate_index(row, column);
|
let index = self.calculate_index(row, column);
|
||||||
ColourRgbU8 {
|
ColourRgbU8 {
|
||||||
|
|
@ -38,7 +38,7 @@ impl ImageRgbU8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_colour(&mut self, row: u32, column: u32, colour: ColourRgbU8) {
|
pub fn set_colour(&mut self, row: usize, column: usize, colour: ColourRgbU8) {
|
||||||
assert!(row < self.height && column < self.width);
|
assert!(row < self.height && column < self.width);
|
||||||
let index = self.calculate_index(row, column);
|
let index = self.calculate_index(row, column);
|
||||||
self.pixel_data[index..index + 3].copy_from_slice(&colour.values[..]);
|
self.pixel_data[index..index + 3].copy_from_slice(&colour.values[..]);
|
||||||
|
|
@ -48,19 +48,19 @@ impl ImageRgbU8 {
|
||||||
&self.pixel_data
|
&self.pixel_data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_width(&self) -> u32 {
|
pub fn get_width(&self) -> usize {
|
||||||
self.width
|
self.width
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_height(&self) -> u32 {
|
pub fn get_height(&self) -> usize {
|
||||||
self.height
|
self.height
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num_channels() -> u32 {
|
pub fn num_channels() -> usize {
|
||||||
3
|
3
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_index(&self, row: u32, column: u32) -> usize {
|
fn calculate_index(&self, row: usize, column: usize) -> usize {
|
||||||
assert!(row < self.height && column < self.width);
|
assert!(row < self.height && column < self.width);
|
||||||
(((self.height - (row + 1)) * self.width + column) * Self::num_channels()) as usize
|
(((self.height - (row + 1)) * self.width + column) * Self::num_channels()) as usize
|
||||||
}
|
}
|
||||||
|
|
@ -68,12 +68,12 @@ impl ImageRgbU8 {
|
||||||
|
|
||||||
pub struct ImageRgbF<T: Real> {
|
pub struct ImageRgbF<T: Real> {
|
||||||
pixel_data: Vec<T>,
|
pixel_data: Vec<T>,
|
||||||
width: u32,
|
width: usize,
|
||||||
height: u32,
|
height: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Real> ImageRgbF<T> {
|
impl<T: Real> ImageRgbF<T> {
|
||||||
pub fn new(width: u32, height: u32) -> ImageRgbF<T> {
|
pub fn new(width: usize, height: usize) -> ImageRgbF<T> {
|
||||||
ImageRgbF {
|
ImageRgbF {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
|
@ -88,13 +88,13 @@ impl<T: Real> ImageRgbF<T> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_colour(&self, row: u32, column: u32) -> ColourRgbF<T> {
|
pub fn get_colour(&self, row: usize, column: usize) -> ColourRgbF<T> {
|
||||||
assert!(row < self.height && column < self.width);
|
assert!(row < self.height && column < self.width);
|
||||||
let index = self.calculate_index(row, column);
|
let index = self.calculate_index(row, column);
|
||||||
ColourRgbF::from_vector3(&Vector3::from_row_slice(&self.pixel_data[index..index + 3]))
|
ColourRgbF::from_vector3(&Vector3::from_row_slice(&self.pixel_data[index..index + 3]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_colour(&mut self, row: u32, column: u32, colour: ColourRgbF<T>) {
|
pub fn set_colour(&mut self, row: usize, column: usize, colour: ColourRgbF<T>) {
|
||||||
assert!(row < self.height && column < self.width);
|
assert!(row < self.height && column < self.width);
|
||||||
let index = self.calculate_index(row, column);
|
let index = self.calculate_index(row, column);
|
||||||
self.pixel_data[index..index + 3].copy_from_slice(&colour.as_vector3().as_slice());
|
self.pixel_data[index..index + 3].copy_from_slice(&colour.as_vector3().as_slice());
|
||||||
|
|
@ -104,19 +104,19 @@ impl<T: Real> ImageRgbF<T> {
|
||||||
&self.pixel_data
|
&self.pixel_data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_width(&self) -> u32 {
|
pub fn get_width(&self) -> usize {
|
||||||
self.width
|
self.width
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_height(&self) -> u32 {
|
pub fn get_height(&self) -> usize {
|
||||||
self.height
|
self.height
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num_channels() -> u32 {
|
pub fn num_channels() -> usize {
|
||||||
3
|
3
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_index(&self, row: u32, column: u32) -> usize {
|
fn calculate_index(&self, row: usize, column: usize) -> usize {
|
||||||
assert!(row < self.height && column < self.width);
|
assert!(row < self.height && column < self.width);
|
||||||
(((self.height - (row + 1)) * self.width + column) * Self::num_channels()) as usize
|
(((self.height - (row + 1)) * self.width + column) * Self::num_channels()) as usize
|
||||||
}
|
}
|
||||||
|
|
|
||||||
60
src/main.rs
60
src/main.rs
|
|
@ -7,20 +7,17 @@ use std::time::Duration;
|
||||||
|
|
||||||
use nalgebra::{Point3, Vector3};
|
use nalgebra::{Point3, Vector3};
|
||||||
|
|
||||||
use itertools::iproduct;
|
|
||||||
|
|
||||||
use std::cmp::min;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
use vanrijn::camera::partial_render_scene;
|
use vanrijn::camera::partial_render_scene;
|
||||||
use vanrijn::colour::{ColourRgbF, NamedColour};
|
use vanrijn::colour::{ColourRgbF, NamedColour};
|
||||||
use vanrijn::image::{ClampingToneMapper, ImageRgbF, ImageRgbU8, ToneMapper};
|
use vanrijn::image::{ClampingToneMapper, ImageRgbF, ImageRgbU8, ToneMapper};
|
||||||
use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial};
|
use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial};
|
||||||
use vanrijn::mesh::load_obj;
|
use vanrijn::mesh::load_obj;
|
||||||
use vanrijn::raycasting::{Primitive, Plane, Sphere};
|
use vanrijn::raycasting::{Plane, Primitive, Sphere};
|
||||||
use vanrijn::scene::Scene;
|
use vanrijn::scene::Scene;
|
||||||
|
use vanrijn::util::TileIterator;
|
||||||
|
|
||||||
fn update_texture(image: &ImageRgbU8, texture: &mut Texture) {
|
fn update_texture(image: &ImageRgbU8, texture: &mut Texture) {
|
||||||
texture
|
texture
|
||||||
|
|
@ -33,8 +30,8 @@ fn update_texture(image: &ImageRgbU8, texture: &mut Texture) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_canvas(
|
fn init_canvas(
|
||||||
image_width: u32,
|
image_width: usize,
|
||||||
image_height: u32,
|
image_height: usize,
|
||||||
) -> Result<(Sdl, Canvas<sdl2::video::Window>), Box<dyn std::error::Error>> {
|
) -> Result<(Sdl, Canvas<sdl2::video::Window>), Box<dyn std::error::Error>> {
|
||||||
let sdl_context = sdl2::init()?;
|
let sdl_context = sdl2::init()?;
|
||||||
let video_subsystem = sdl_context.video()?;
|
let video_subsystem = sdl_context.video()?;
|
||||||
|
|
@ -50,8 +47,8 @@ fn init_canvas(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let image_width = 1200;
|
let image_width = 320usize;
|
||||||
let image_height = 900;
|
let image_height = 240usize;
|
||||||
|
|
||||||
let (sdl_context, mut canvas) = init_canvas(image_width, image_height)?;
|
let (sdl_context, mut canvas) = init_canvas(image_width, image_height)?;
|
||||||
|
|
||||||
|
|
@ -117,48 +114,21 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut event_pump = sdl_context.event_pump()?;
|
let mut event_pump = sdl_context.event_pump()?;
|
||||||
let mut i = 0;
|
|
||||||
'running: loop {
|
'running: for tile in TileIterator::new(image_width as usize, image_height as usize, 16) {
|
||||||
let subtile_size = 16;
|
|
||||||
let tile_divisions = 4;
|
|
||||||
let tile_size = subtile_size * tile_divisions;
|
|
||||||
for tile_row in 0..=(image_height + 1) / tile_size {
|
|
||||||
for tile_column in 0..=(image_width + 1) / tile_size {
|
|
||||||
//let row_start = tile_row * tile_size;
|
|
||||||
//let row_end = min(tile_row * tile_size + tile_size, image_height);
|
|
||||||
//let column_start = tile_column * tile_size;
|
|
||||||
//let column_end = min(tile_column * tile_size + tile_size, image_width);
|
|
||||||
let join_handles: Vec<_> = iproduct!(0..tile_divisions, 0..tile_divisions)
|
|
||||||
.map(|(tile_i, tile_j)| {
|
|
||||||
let start_i = tile_row * tile_size + tile_i * subtile_size;
|
|
||||||
let start_j = tile_column * tile_size + tile_j * subtile_size;
|
|
||||||
(
|
|
||||||
start_i,
|
|
||||||
min(start_i + subtile_size, image_height),
|
|
||||||
start_j,
|
|
||||||
min(start_j + subtile_size, image_width),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.map(|(i_min, i_max, j_min, j_max)| {
|
|
||||||
let image_ptr = output_image.clone();
|
let image_ptr = output_image.clone();
|
||||||
let scene_ptr = scene.clone();
|
let scene_ptr = scene.clone();
|
||||||
thread::spawn(move || {
|
|
||||||
partial_render_scene(
|
partial_render_scene(
|
||||||
image_ptr,
|
image_ptr,
|
||||||
scene_ptr,
|
scene_ptr,
|
||||||
i_min,
|
tile.start_row,
|
||||||
i_max,
|
tile.end_row,
|
||||||
j_min,
|
tile.start_column,
|
||||||
j_max,
|
tile.end_column,
|
||||||
image_height,
|
image_height,
|
||||||
image_width,
|
image_width,
|
||||||
);
|
);
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
for h in join_handles {
|
|
||||||
h.join();
|
|
||||||
}
|
|
||||||
let locked_image = output_image.lock().unwrap();
|
let locked_image = output_image.lock().unwrap();
|
||||||
let mut output_image_rgbu8 = ImageRgbU8::new(image_width, image_height);
|
let mut output_image_rgbu8 = ImageRgbU8::new(image_width, image_height);
|
||||||
ClampingToneMapper {}.apply_tone_mapping(&locked_image, &mut output_image_rgbu8);
|
ClampingToneMapper {}.apply_tone_mapping(&locked_image, &mut output_image_rgbu8);
|
||||||
|
|
@ -177,7 +147,9 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
let mut i = 0;
|
||||||
|
'running: loop {
|
||||||
i = (i + 1) % 255;
|
i = (i + 1) % 255;
|
||||||
for event in event_pump.poll_iter() {
|
for event in event_pump.poll_iter() {
|
||||||
match event {
|
match event {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use nalgebra::Point3;
|
use nalgebra::{Point2, Point3};
|
||||||
|
|
||||||
use crate::Real;
|
use crate::Real;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,29 @@
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Tile {
|
pub struct Tile {
|
||||||
pub start_x: usize,
|
pub start_column: usize,
|
||||||
pub end_x: usize,
|
pub end_column: usize,
|
||||||
pub start_y: usize,
|
pub start_row: usize,
|
||||||
pub end_y: usize,
|
pub end_row: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TileIterator {
|
pub struct TileIterator {
|
||||||
tile_size: usize,
|
tile_size: usize,
|
||||||
total_height: usize,
|
total_height: usize,
|
||||||
total_width: usize,
|
total_width: usize,
|
||||||
current_x: usize,
|
current_column: usize,
|
||||||
current_y: usize,
|
current_row: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TileIterator {
|
impl TileIterator {
|
||||||
pub fn new(total_width: usize, total_height: usize, tile_size: usize) -> TileIterator {
|
pub fn new(total_width: usize, total_height: usize, tile_size: usize) -> TileIterator {
|
||||||
// If tile_size*2 is greater than usize::max_value(), increment would overflow
|
// If tile_size*2 is greater than usize::max_value(), increment would overflow
|
||||||
assert!(tile_size > 0 && tile_size*2 < usize::max_value());
|
assert!(tile_size > 0 && tile_size * 2 < usize::max_value());
|
||||||
TileIterator {
|
TileIterator {
|
||||||
tile_size,
|
tile_size,
|
||||||
total_width,
|
total_width,
|
||||||
total_height,
|
total_height,
|
||||||
current_x: 0,
|
current_column: 0,
|
||||||
current_y: 0,
|
current_row: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -31,25 +32,25 @@ impl Iterator for TileIterator {
|
||||||
type Item = Tile;
|
type Item = Tile;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Tile> {
|
fn next(&mut self) -> Option<Tile> {
|
||||||
if self.current_y >= self.total_height {
|
if self.current_row >= self.total_height {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let start_x = self.current_x;
|
let start_column = self.current_column;
|
||||||
let end_x = self.total_width.min(start_x + self.tile_size);
|
let end_column = self.total_width.min(start_column + self.tile_size);
|
||||||
let start_y = self.current_y;
|
let start_row = self.current_row;
|
||||||
let end_y = self.total_height.min(start_y + self.tile_size);
|
let end_row = self.total_height.min(start_row + self.tile_size);
|
||||||
|
|
||||||
self.current_x += self.tile_size;
|
self.current_column += self.tile_size;
|
||||||
if self.current_x >= self.total_width {
|
if self.current_column >= self.total_width {
|
||||||
self.current_y += self.tile_size;
|
self.current_row += self.tile_size;
|
||||||
self.current_x = 0;
|
self.current_column = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Tile {
|
Some(Tile {
|
||||||
start_x,
|
start_column,
|
||||||
end_x,
|
end_column,
|
||||||
start_y,
|
start_row,
|
||||||
end_y,
|
end_row,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +96,7 @@ mod tests {
|
||||||
#[quickcheck]
|
#[quickcheck]
|
||||||
fn tiles_are_expected_size(width: usize, height: usize, tile_size: usize) -> TestResult {
|
fn tiles_are_expected_size(width: usize, height: usize, tile_size: usize) -> TestResult {
|
||||||
let max_size = 10000;
|
let max_size = 10000;
|
||||||
// Check size of width and height first, since width*height my overflow.
|
// Check size of width and height first, since width*height might overflow.
|
||||||
if width > max_size || height > max_size || width * height > max_size {
|
if width > max_size || height > max_size || width * height > max_size {
|
||||||
return TestResult::discard();
|
return TestResult::discard();
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +106,8 @@ mod tests {
|
||||||
|
|
||||||
let mut target = TileIterator::new(width, height, tile_size);
|
let mut target = TileIterator::new(width, height, tile_size);
|
||||||
TestResult::from_bool(target.all(|tile| {
|
TestResult::from_bool(target.all(|tile| {
|
||||||
tile.end_x - tile.start_x <= tile_size && tile.end_y - tile.start_y <= tile_size
|
tile.end_column - tile.start_column <= tile_size
|
||||||
|
&& tile.end_row - tile.start_row <= tile_size
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,7 +118,7 @@ mod tests {
|
||||||
tile_size: usize,
|
tile_size: usize,
|
||||||
) -> TestResult {
|
) -> TestResult {
|
||||||
let max_size = 10000;
|
let max_size = 10000;
|
||||||
// Check size of width and height first, since width*height my overflow.
|
// Check size of width and height first, since width*height might overflow.
|
||||||
if width > max_size || height > max_size || width * height > max_size {
|
if width > max_size || height > max_size || width * height > max_size {
|
||||||
return TestResult::discard();
|
return TestResult::discard();
|
||||||
}
|
}
|
||||||
|
|
@ -128,9 +130,9 @@ mod tests {
|
||||||
let mut index_counts = vec![0; width * height];
|
let mut index_counts = vec![0; width * height];
|
||||||
let mut total_count = 0;
|
let mut total_count = 0;
|
||||||
for tile in target {
|
for tile in target {
|
||||||
for x in tile.start_x..tile.end_x {
|
for column in tile.start_column..tile.end_column {
|
||||||
for y in tile.start_y..tile.end_y {
|
for row in tile.start_row..tile.end_row {
|
||||||
index_counts[y * width + x] += 1;
|
index_counts[row * width + column] += 1;
|
||||||
total_count += 1;
|
total_count += 1;
|
||||||
if total_count > width * height {
|
if total_count > width * height {
|
||||||
return TestResult::failed();
|
return TestResult::failed();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue