~ttt/ray_tracing_in_one_weekend

569eea8c424b06448f1136bcad893b979d7e701b — Tomasz Kłak 2 years ago e15bf0f
rayon
7 files changed, 320 insertions(+), 47 deletions(-)

M Cargo.lock
M Cargo.toml
M src/hittable_list.rs
A src/image.rs
M src/main.rs
M src/material.rs
M src/vec3.rs
M Cargo.lock => Cargo.lock +226 -0
@@ 1,12 1,90 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"

[[package]]
name = "cc"
version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"

[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "crossbeam-channel"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
dependencies = [
 "cfg-if",
 "crossbeam-utils",
]

[[package]]
name = "crossbeam-deque"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
dependencies = [
 "cfg-if",
 "crossbeam-epoch",
 "crossbeam-utils",
]

[[package]]
name = "crossbeam-epoch"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d60ab4a8dba064f2fbb5aa270c28da5cf4bbd0e72dae1140a6b0353a779dbe00"
dependencies = [
 "cfg-if",
 "crossbeam-utils",
 "lazy_static",
 "loom",
 "memoffset",
 "scopeguard",
]

[[package]]
name = "crossbeam-utils"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bae8f328835f8f5a6ceb6a7842a7f2d0c03692adb5c889347235d59194731fe3"
dependencies = [
 "autocfg",
 "cfg-if",
 "lazy_static",
 "loom",
]

[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"

[[package]]
name = "generator"
version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cdc09201b2e8ca1b19290cf7e65de2246b8e91fb6874279722189c4de7b94dc"
dependencies = [
 "cc",
 "libc",
 "log",
 "rustc_version",
 "winapi",
]

[[package]]
name = "getrandom"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 18,12 96,75 @@ dependencies = [
]

[[package]]
name = "hermit-abi"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
 "libc",
]

[[package]]
name = "itertools"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319"
dependencies = [
 "either",
]

[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

[[package]]
name = "libc"
version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"

[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
 "cfg-if",
]

[[package]]
name = "loom"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d44c73b4636e497b4917eb21c33539efa3816741a2d3ff26c6316f1b529481a4"
dependencies = [
 "cfg-if",
 "generator",
 "scoped-tls",
]

[[package]]
name = "memoffset"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
dependencies = [
 "autocfg",
]

[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
 "hermit-abi",
 "libc",
]

[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 73,11 214,96 @@ dependencies = [
name = "ray_tracing_in_one_weekend"
version = "0.1.0"
dependencies = [
 "itertools",
 "rand",
 "rayon",
]

[[package]]
name = "rayon"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
dependencies = [
 "autocfg",
 "crossbeam-deque",
 "either",
 "rayon-core",
]

[[package]]
name = "rayon-core"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
dependencies = [
 "crossbeam-channel",
 "crossbeam-deque",
 "crossbeam-utils",
 "lazy_static",
 "num_cpus",
]

[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
 "semver",
]

[[package]]
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"

[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"

[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
 "semver-parser",
]

[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"

[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"

[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
 "winapi-i686-pc-windows-gnu",
 "winapi-x86_64-pc-windows-gnu",
]

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

M Cargo.toml => Cargo.toml +2 -0
@@ 8,6 8,8 @@ edition = "2018"

[dependencies]
rand = "0.8"
itertools="0.10"
rayon = "1.5"

[profile.dev]
lto=true

M src/hittable_list.rs => src/hittable_list.rs +2 -2
@@ 4,7 4,7 @@ use std::sync::Arc;

#[derive(Clone)]
pub struct HittableList {
    objects: Vec<Arc<dyn Hittable>>,
    objects: Vec<Arc<dyn Hittable + Send + Sync>>,
}

impl HittableList {


@@ 12,7 12,7 @@ impl HittableList {
        HittableList { objects: vec![] }
    }

    pub fn add(&mut self, object: Arc<dyn Hittable>) {
    pub fn add(&mut self, object: Arc<dyn Hittable + Send + Sync>) {
        self.objects.push(object);
    }
}

A src/image.rs => src/image.rs +59 -0
@@ 0,0 1,59 @@
use crate::{Color};
use std::collections::HashMap;
use std::sync::Mutex;

pub struct Image {
    pixels: Mutex<HashMap<(usize, usize), Color>>,
    samples_per_pixel: usize,
    w: usize,
    h: usize,
}

impl Image {
    pub fn new(w: usize, h: usize, samples_per_pixel: usize) -> Self {
        Self {
            pixels: Default::default(),
            samples_per_pixel,
            w, h
        }
    }

    pub fn set(&self, x: usize, y: usize, val: Color) {
        self.pixels.lock().unwrap().insert((x, y), val);
        let len = self.pixels.lock().unwrap().len() as f64;
        let total = (self.w * self.h) as f64;
        eprint!("\r{:.1}%", 100.0 * len / total);
    }

    pub fn to_ppm(&self, out: &mut impl std::io::Write) -> std::io::Result<()> {
        let mut pixels : Vec<((_, _), Color)> = self.pixels.lock().unwrap().iter().map(|(p, c)| (p.clone(), c.clone())).collect();
        pixels.sort_by(|l, r| l.0.0.cmp(&r.0.0));
        pixels.sort_by(|l, r| r.0.1.cmp(&l.0.1));
        write!(out, "P3\n{} {}\n255\n", self.w, self.h)?;
        for p in pixels {
            write_color(out, p.1, self.samples_per_pixel)?;
        }
        Ok(())
    }
}

fn write_color(out: &mut impl std::io::Write, pixel_color: Color, samples_per_pixel: usize) -> std::io::Result<()> {
    let mut r = pixel_color.x();
    let mut g = pixel_color.y();
    let mut b = pixel_color.z();

    // Divide the color by the number of samples.
    let scale = 1.0 / samples_per_pixel as f64;
    r = (scale * r).sqrt();
    g = (scale * g).sqrt();
    b = (scale * b).sqrt();

    write!(
        out,
        "{} {} {}\n",
        (256.0 * r.clamp(0.0, 0.999)).round() as usize,
        (256.0 * g.clamp(0.0, 0.999)).round() as usize,
        (256.0 * b.clamp(0.0, 0.999)).round() as usize,
    )
}


M src/main.rs => src/main.rs +30 -23
@@ 1,3 1,6 @@
use itertools::Itertools;
use rayon::prelude::*;
pub use std::f64::consts::PI;
use std::sync::Arc;

mod vec3;


@@ 24,7 27,8 @@ use camera::*;
mod material;
use material::*;

pub use std::f64::consts::PI;
mod image;
use image::*;

fn ray_color(r: &Ray, world: &dyn Hittable, depth: isize) -> Color {
    // If we've exceeded the ray bounce limit, no more light is gathered.


@@ 43,9 47,6 @@ fn ray_color(r: &Ray, world: &dyn Hittable, depth: isize) -> Color {
}

fn main() {
    let stdout = std::io::stdout();
    let mut handle = stdout.lock();

    // Image
    let aspect_ratio = 3.0 / 2.0;
    let image_width = 1200;


@@ 73,29 74,35 @@ fn main() {
    );

    // Render

    print!("P3\n{} {}\n255\n", image_width, image_height);

    for j in (0..=(image_height - 1)).rev() {
        eprint!("\rScanlines remaining: {} ", j);
        for i in 0..image_width {
            let mut pixel_color = Color::default();
            for _ in 0..samples_per_pixel {
                let u = (i as f64 + random_double()) / (image_width - 1) as f64;
                let v = (j as f64 + random_double()) / (image_height - 1) as f64;
                let r = cam.get_ray(u, v);
                pixel_color += ray_color(&r, &world, max_depth);
            }
            write_color(&mut handle, pixel_color, samples_per_pixel);
    let img = Image::new(image_width, image_height as usize, samples_per_pixel);

    let aux = move |i: usize, j: usize| {
        let mut pixel_color = Color::default();
        for _ in 0..samples_per_pixel {
            let u = (i as f64 + random_double()) / (image_width - 1) as f64;
            let v = (j as f64 + random_double()) / (image_height - 1) as f64;
            let r = cam.get_ray(u, v);
            pixel_color += ray_color(&r, &world, max_depth);
        }
    }
    eprintln!("\nDone!");
        pixel_color
    };

    let stdout = std::io::stdout();

    (0..image_height)
        .cartesian_product(0..image_width)
        .collect::<Vec<_>>()
        .par_iter()
        .for_each(|(j, i)| img.set(*i, *j as usize, aux(*i, *j as usize)));

    let r = img.to_ppm(&mut stdout.lock());
    eprintln!("\nDone: {:?}", r);
}

fn random_scene() -> HittableList {
    let mut world = HittableList::empty();

    let ground_material: MaterialPtr = Arc::new(Lambertian::new(Color::new(0.5, 0.5, 0.5)));
    let ground_material = Arc::new(Lambertian::new(Color::new(0.5, 0.5, 0.5)));
    world.add(Arc::new(Sphere::new(
        Point3::new(0., -1000., 0.),
        1000.,


@@ 115,13 122,13 @@ fn random_scene() -> HittableList {
                if choose_mat < 0.8 {
                    // diffuse
                    let albedo = Color::random() * Color::random();
                    let sphere_material: MaterialPtr = Arc::new(Lambertian::new(albedo));
                    let sphere_material = Arc::new(Lambertian::new(albedo));
                    world.add(Arc::new(Sphere::new(center, 0.2, sphere_material)));
                } else if choose_mat < 0.95 {
                    // metal
                    let albedo = Color::random_range(0.5..1.);
                    let fuzz = random_double_range(0f64..0.5);
                    let sphere_material: MaterialPtr = Arc::new(Metal::new(albedo, fuzz));
                    let sphere_material = Arc::new(Metal::new(albedo, fuzz));
                    world.add(Arc::new(Sphere::new(center, 0.2, sphere_material)));
                } else {
                    // glass

M src/material.rs => src/material.rs +1 -1
@@ 8,7 8,7 @@ pub trait Material: Debug {
    fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> Option<(Color, Ray)>;
}

pub type MaterialPtr = std::sync::Arc<dyn Material>;
pub type MaterialPtr = std::sync::Arc<dyn Material + Send + Sync>;

#[derive(Debug)]
pub struct Lambertian {

M src/vec3.rs => src/vec3.rs +0 -21
@@ 200,27 200,6 @@ pub fn unit_vector(v: Vec3) -> Vec3 {
    v / v.length()
}

pub fn write_color(out: &mut impl std::io::Write, pixel_color: Color, samples_per_pixel: usize) {
    let mut r = pixel_color.x();
    let mut g = pixel_color.y();
    let mut b = pixel_color.z();

    // Divide the color by the number of samples.
    let scale = 1.0 / samples_per_pixel as f64;
    r = (scale * r).sqrt();
    g = (scale * g).sqrt();
    b = (scale * b).sqrt();

    write!(
        out,
        "{} {} {}\n",
        (256.0 * r.clamp(0.0, 0.999)).round() as usize,
        (256.0 * g.clamp(0.0, 0.999)).round() as usize,
        (256.0 * b.clamp(0.0, 0.999)).round() as usize,
    )
    .unwrap();
}

pub fn random_in_unit_sphere() -> Vec3 {
    loop {
        let p = Vec3::random_range(-1.0..1.0);