First version of adaptive subsampling.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Add debugging images, and move rendering to output module.
This commit is contained in:
parent
f0f90a6b80
commit
5d9e180817
19
rtiow/Cargo.lock
generated
19
rtiow/Cargo.lock
generated
@ -96,7 +96,7 @@ dependencies = [
|
|||||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -428,7 +428,7 @@ dependencies = [
|
|||||||
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tinytemplate 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tinytemplate 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -1566,7 +1566,7 @@ dependencies = [
|
|||||||
"mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1614,6 +1614,7 @@ dependencies = [
|
|||||||
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"stderrlog 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"stderrlog 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -1641,7 +1642,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "0.2.7"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1732,11 +1733,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.38"
|
version = "1.0.41"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1926,7 +1927,7 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2593,7 +2594,7 @@ dependencies = [
|
|||||||
"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619"
|
"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619"
|
||||||
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
||||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
|
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
|
||||||
"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9"
|
"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9"
|
||||||
"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267"
|
"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267"
|
||||||
"checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56"
|
"checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56"
|
||||||
@ -2606,7 +2607,7 @@ dependencies = [
|
|||||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
"checksum serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)" = "9f301d728f2b94c9a7691c90f07b0b4e8a4517181d9461be94c04bddeb4bd850"
|
"checksum serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)" = "9f301d728f2b94c9a7691c90f07b0b4e8a4517181d9461be94c04bddeb4bd850"
|
||||||
"checksum serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)" = "beed18e6f5175aef3ba670e57c60ef3b1b74d250d962a26604bff4c80e970dd4"
|
"checksum serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)" = "beed18e6f5175aef3ba670e57c60ef3b1b74d250d962a26604bff4c80e970dd4"
|
||||||
"checksum serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "27dce848e7467aa0e2fcaf0a413641499c0b745452aaca1194d24dedde9e13c9"
|
"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2"
|
||||||
"checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2"
|
"checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2"
|
||||||
"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
||||||
"checksum signal-hook 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1f272d1b7586bec132ed427f532dd418d8beca1ca7f2caf7df35569b1415a4b4"
|
"checksum signal-hook 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1f272d1b7586bec132ed427f532dd418d8beca1ca7f2caf7df35569b1415a4b4"
|
||||||
|
|||||||
@ -20,6 +20,7 @@ num_cpus = "1.8.0"
|
|||||||
rand = "0.5.5"
|
rand = "0.5.5"
|
||||||
serde = "1.0.79"
|
serde = "1.0.79"
|
||||||
serde_derive = "1.0.79"
|
serde_derive = "1.0.79"
|
||||||
|
serde_json = "1.0.41"
|
||||||
stderrlog = "0.4.1"
|
stderrlog = "0.4.1"
|
||||||
structopt = "0.2.10"
|
structopt = "0.2.10"
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ pub mod kdtree;
|
|||||||
pub mod material;
|
pub mod material;
|
||||||
pub mod moving_sphere;
|
pub mod moving_sphere;
|
||||||
pub mod noise;
|
pub mod noise;
|
||||||
|
pub mod output;
|
||||||
pub mod ray;
|
pub mod ray;
|
||||||
pub mod rect;
|
pub mod rect;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
|
|||||||
139
rtiow/src/output.rs
Normal file
139
rtiow/src/output.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use chrono::Local;
|
||||||
|
use image::RgbImage;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::vec3::Vec3;
|
||||||
|
|
||||||
|
// Main RGB image output from rendering the scene.
|
||||||
|
pub const MAIN_IMAGE: &str = "@final";
|
||||||
|
// Debug image for adaptive pixels subsampling.
|
||||||
|
// Red indicates recursion hit maximum depth splitting the pixel.
|
||||||
|
// Green indicates no subdivision necessary
|
||||||
|
pub const ADAPTIVE_DEPTH: &str = "adaptive_depth";
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref DEBUGGER: Arc<Mutex<Debugger>> = Arc::new(Mutex::new(Debugger::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Debugger {
|
||||||
|
images: HashMap<String, RgbImage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debugger {
|
||||||
|
fn new() -> Debugger {
|
||||||
|
Debugger {
|
||||||
|
images: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_image(name: String, dimensions: (u32, u32)) {
|
||||||
|
let mut debugger = DEBUGGER.lock().unwrap();
|
||||||
|
debugger
|
||||||
|
.images
|
||||||
|
.insert(name, RgbImage::new(dimensions.0, dimensions.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_pixel(name: &str, x: usize, y: usize, pixel: Vec3) {
|
||||||
|
let mut debugger = DEBUGGER.lock().unwrap();
|
||||||
|
let img = debugger
|
||||||
|
.images
|
||||||
|
.get_mut(name)
|
||||||
|
.expect(&format!("couldn't find image named '{}'", name));
|
||||||
|
let y_inv = img.height() - y as u32 - 1;
|
||||||
|
img.put_pixel(
|
||||||
|
x as u32,
|
||||||
|
y_inv as u32,
|
||||||
|
image::Rgb([
|
||||||
|
(pixel[0] * 255.).min(255.) as u8,
|
||||||
|
(pixel[1] * 255.).min(255.) as u8,
|
||||||
|
(pixel[2] * 255.).min(255.) as u8,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_images<P: AsRef<Path>>(output_dir: P) -> std::io::Result<()> {
|
||||||
|
let output_dir: &Path = output_dir.as_ref();
|
||||||
|
let debugger = DEBUGGER.lock().unwrap();
|
||||||
|
let now = Local::now();
|
||||||
|
/*
|
||||||
|
let style = r#"
|
||||||
|
<style>
|
||||||
|
.frame {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
figure {
|
||||||
|
margin: 0.25em;
|
||||||
|
}
|
||||||
|
figcaption {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
max-width: 97%;
|
||||||
|
height: auto;
|
||||||
|
margin: 0;
|
||||||
|
border: 1px solid #999;
|
||||||
|
padding: 5px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
"#;
|
||||||
|
writeln!(f, "{}", style)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"<h1>Run @ {} ({})</h1>",
|
||||||
|
now.to_rfc2822(),
|
||||||
|
now.timestamp()
|
||||||
|
)?;
|
||||||
|
*/
|
||||||
|
// Write out images in consistent order.
|
||||||
|
let mut names = debugger.images.keys().collect::<Vec<_>>();
|
||||||
|
names.sort();
|
||||||
|
let mut ratio = 1.;
|
||||||
|
let mut size = (0, 0);
|
||||||
|
for name in &names {
|
||||||
|
let img = debugger.images.get(*name).unwrap();
|
||||||
|
if *name == MAIN_IMAGE {
|
||||||
|
let (w, h) = img.dimensions();
|
||||||
|
ratio = w as f32 / h as f32;
|
||||||
|
size = img.dimensions();
|
||||||
|
}
|
||||||
|
let filename = format!("{}.png", name);
|
||||||
|
/*
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
r#"<div class="frame"><figure><a href="{0}?t={2}"><img src="{0}?t={2}"></a><figcaption>{1} ({2})</figcaption></figure></div>"#,
|
||||||
|
filename, name, now.timestamp())?;
|
||||||
|
*/
|
||||||
|
let path = output_dir.join(filename);
|
||||||
|
info!("Saving {}", path.to_string_lossy());
|
||||||
|
img.save(path)?;
|
||||||
|
}
|
||||||
|
let f = File::create(output_dir.join("data.json"))?;
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Data {
|
||||||
|
timestamp: i64,
|
||||||
|
ratio: f32,
|
||||||
|
size: (u32, u32),
|
||||||
|
names: Vec<String>,
|
||||||
|
images: Vec<String>,
|
||||||
|
}
|
||||||
|
serde_json::ser::to_writer(
|
||||||
|
f,
|
||||||
|
&Data {
|
||||||
|
timestamp: now.timestamp(),
|
||||||
|
ratio,
|
||||||
|
size,
|
||||||
|
names: names.iter().map(|s| s.to_string()).collect(),
|
||||||
|
images: names.iter().map(|s| format!("{}.png", s)).collect(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ops::Range;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str;
|
use std::str;
|
||||||
@ -13,8 +14,6 @@ use std::sync::Mutex;
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
use image;
|
|
||||||
use image::RgbImage;
|
|
||||||
#[cfg(feature = "prom")]
|
#[cfg(feature = "prom")]
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use num_cpus;
|
use num_cpus;
|
||||||
@ -25,6 +24,7 @@ use crate::camera::Camera;
|
|||||||
use crate::hitable::Hit;
|
use crate::hitable::Hit;
|
||||||
use crate::human;
|
use crate::human;
|
||||||
use crate::material::Lambertian;
|
use crate::material::Lambertian;
|
||||||
|
use crate::output;
|
||||||
use crate::ray::Ray;
|
use crate::ray::Ray;
|
||||||
use crate::scenes;
|
use crate::scenes;
|
||||||
use crate::sphere::Sphere;
|
use crate::sphere::Sphere;
|
||||||
@ -150,9 +150,6 @@ pub struct Opt {
|
|||||||
/// Sub-samples per pixel
|
/// Sub-samples per pixel
|
||||||
#[structopt(short = "s", long = "subsample", default_value = "8")]
|
#[structopt(short = "s", long = "subsample", default_value = "8")]
|
||||||
pub subsamples: usize,
|
pub subsamples: usize,
|
||||||
/// Use adaptive subsampling
|
|
||||||
#[structopt(long = "adaptive")]
|
|
||||||
pub adaptive_subsampling: bool,
|
|
||||||
/// Select scene to render, one of: "bench", "book", "tutorial", "bvh", "test", "cornell_box",
|
/// Select scene to render, one of: "bench", "book", "tutorial", "bvh", "test", "cornell_box",
|
||||||
/// "cornell_smoke", "perlin_debug", "final"
|
/// "cornell_smoke", "perlin_debug", "final"
|
||||||
#[structopt(long = "model", default_value = "book")]
|
#[structopt(long = "model", default_value = "book")]
|
||||||
@ -188,7 +185,7 @@ pub struct Scene {
|
|||||||
pub camera: Camera,
|
pub camera: Camera,
|
||||||
pub subsamples: usize,
|
pub subsamples: usize,
|
||||||
/// overrides subsamples setting.
|
/// overrides subsamples setting.
|
||||||
pub adaptive_subsampling: bool,
|
pub adaptive_subsampling: Option<f32>,
|
||||||
pub num_threads: Option<usize>,
|
pub num_threads: Option<usize>,
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
pub height: usize,
|
pub height: usize,
|
||||||
@ -223,7 +220,7 @@ impl Default for Scene {
|
|||||||
)),
|
)),
|
||||||
camera,
|
camera,
|
||||||
subsamples: 0,
|
subsamples: 0,
|
||||||
adaptive_subsampling: false,
|
adaptive_subsampling: None,
|
||||||
num_threads: None,
|
num_threads: None,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
@ -278,7 +275,103 @@ fn color(
|
|||||||
Vec3::new(0., 0., 0.)
|
Vec3::new(0., 0., 0.)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_pixel(x: usize, y: usize, scene: &Scene) -> Vec3 {
|
const MAX_ADAPTIVE_DEPTH: usize = 10;
|
||||||
|
fn trace_pixel_adaptive(
|
||||||
|
depth: usize,
|
||||||
|
threshold: f32,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
x_range: Range<f32>,
|
||||||
|
y_range: Range<f32>,
|
||||||
|
scene: &Scene,
|
||||||
|
) -> Vec3 {
|
||||||
|
let w = scene.width as f32;
|
||||||
|
let h = scene.height as f32;
|
||||||
|
let x_mid = x_range.start + ((x_range.end - x_range.start) / 2.);
|
||||||
|
let y_mid = y_range.start + ((y_range.end - y_range.start) / 2.);
|
||||||
|
let mc = ((x_mid + x as f32) / w, (y_mid + y as f32) / h);
|
||||||
|
let center = color(
|
||||||
|
scene.camera.get_ray(mc.0, mc.1),
|
||||||
|
scene.world.as_ref(),
|
||||||
|
0,
|
||||||
|
scene.global_illumination,
|
||||||
|
&scene.env_map,
|
||||||
|
);
|
||||||
|
if depth == 0 {
|
||||||
|
output::set_pixel(output::ADAPTIVE_DEPTH, x, y, [1., 0., 0.].into());
|
||||||
|
return center;
|
||||||
|
}
|
||||||
|
// t = top
|
||||||
|
// m = middle
|
||||||
|
// b = bottom
|
||||||
|
// l = left
|
||||||
|
// c = center
|
||||||
|
// r = right
|
||||||
|
let tl = (
|
||||||
|
(x_range.start + x as f32) / w,
|
||||||
|
(y_range.start + y as f32) / h,
|
||||||
|
);
|
||||||
|
let tr = ((x_range.end + x as f32) / w, (y_range.start + y as f32) / h);
|
||||||
|
let bl = ((x_range.start + x as f32) / w, (y_range.end + y as f32) / h);
|
||||||
|
let br = ((x_range.end + x as f32) / w, (y_range.end + y as f32) / h);
|
||||||
|
|
||||||
|
let corners = [tl, tr, mc, bl, br]
|
||||||
|
.iter()
|
||||||
|
.map(|(u, v)| {
|
||||||
|
color(
|
||||||
|
scene.camera.get_ray(*u, *v),
|
||||||
|
scene.world.as_ref(),
|
||||||
|
0,
|
||||||
|
scene.global_illumination,
|
||||||
|
&scene.env_map,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.fold([0., 0., 0.].into(), |a: Vec3, b: Vec3| a + b)
|
||||||
|
/ 5.;
|
||||||
|
if (corners - center).length() > threshold {
|
||||||
|
let pixel = (trace_pixel_adaptive(
|
||||||
|
depth - 1,
|
||||||
|
threshold,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
x_range.start..x_mid,
|
||||||
|
y_range.start..y_mid,
|
||||||
|
scene,
|
||||||
|
) + trace_pixel_adaptive(
|
||||||
|
depth - 1,
|
||||||
|
threshold,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
x_mid..x_range.end,
|
||||||
|
y_range.start..y_mid,
|
||||||
|
scene,
|
||||||
|
) + trace_pixel_adaptive(
|
||||||
|
depth - 1,
|
||||||
|
threshold,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
x_range.start..x_mid,
|
||||||
|
y_mid..y_range.end,
|
||||||
|
scene,
|
||||||
|
) + trace_pixel_adaptive(
|
||||||
|
depth - 1,
|
||||||
|
threshold,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
x_mid..x_range.end,
|
||||||
|
y_mid..y_range.end,
|
||||||
|
scene,
|
||||||
|
)) / 4.;
|
||||||
|
pixel
|
||||||
|
} else {
|
||||||
|
if depth == MAX_ADAPTIVE_DEPTH {
|
||||||
|
output::set_pixel(output::ADAPTIVE_DEPTH, x, y, [0., 1., 0.].into());
|
||||||
|
}
|
||||||
|
corners
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trace_pixel_random(x: usize, y: usize, scene: &Scene) -> Vec3 {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let u = (rng.gen_range::<f32>(0., 1.) + x as f32) / scene.width as f32;
|
let u = (rng.gen_range::<f32>(0., 1.) + x as f32) / scene.width as f32;
|
||||||
let v = (rng.gen_range::<f32>(0., 1.) + y as f32) / scene.height as f32;
|
let v = (rng.gen_range::<f32>(0., 1.) + y as f32) / scene.height as f32;
|
||||||
@ -306,17 +399,25 @@ static PIXEL_COUNT: AtomicUsize = AtomicUsize::new(0);
|
|||||||
|
|
||||||
fn render_pixel(scene: &Scene, x: usize, y: usize) -> Vec3 {
|
fn render_pixel(scene: &Scene, x: usize, y: usize) -> Vec3 {
|
||||||
let mut pixel: Vec3 = Default::default();
|
let mut pixel: Vec3 = Default::default();
|
||||||
let pixel = if scene.adaptive_subsampling {
|
let pixel = if let Some(threshold) = scene.adaptive_subsampling {
|
||||||
Default::default()
|
trace_pixel_adaptive(
|
||||||
|
MAX_ADAPTIVE_DEPTH,
|
||||||
|
threshold,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
0.0..1.0,
|
||||||
|
0.0..1.0,
|
||||||
|
scene,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
for _ in 0..scene.subsamples {
|
for _ in 0..scene.subsamples {
|
||||||
pixel = pixel + trace_pixel(x, y, scene);
|
pixel = pixel + trace_pixel_random(x, y, scene);
|
||||||
}
|
}
|
||||||
pixel / scene.subsamples as f32
|
pixel / scene.subsamples as f32
|
||||||
};
|
};
|
||||||
|
PIXEL_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||||
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or
|
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or
|
||||||
// sqrt.
|
// sqrt.
|
||||||
PIXEL_COUNT.fetch_add(1, Ordering::SeqCst);
|
|
||||||
Vec3::new(pixel[0].sqrt(), pixel[1].sqrt(), pixel[2].sqrt())
|
Vec3::new(pixel[0].sqrt(), pixel[1].sqrt(), pixel[2].sqrt())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,7 +462,7 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
let scene = Arc::new(scene);
|
let scene = Arc::new(scene);
|
||||||
let pixel_req_rx = Arc::new(Mutex::new(pixel_req_rx));
|
let pixel_req_rx = Arc::new(Mutex::new(pixel_req_rx));
|
||||||
info!("Creating {} render threads", num_threads);
|
info!("Creating {} render threads", num_threads);
|
||||||
info!("Adaptive subsampling: {}", scene.adaptive_subsampling);
|
info!("Adaptive subsampling: {:?}", scene.adaptive_subsampling);
|
||||||
for i in 0..num_threads {
|
for i in 0..num_threads {
|
||||||
let s = sync::Arc::clone(&scene);
|
let s = sync::Arc::clone(&scene);
|
||||||
let pixel_req_rx = pixel_req_rx.clone();
|
let pixel_req_rx = pixel_req_rx.clone();
|
||||||
@ -394,6 +495,18 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
drop(pixel_req_tx);
|
drop(pixel_req_tx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
info!("Rendering with {} subsamples", scene.subsamples);
|
||||||
|
output::register_image(
|
||||||
|
output::MAIN_IMAGE.to_string(),
|
||||||
|
(scene.width as u32, scene.height as u32),
|
||||||
|
);
|
||||||
|
if scene.adaptive_subsampling.is_some() {
|
||||||
|
output::register_image(
|
||||||
|
output::ADAPTIVE_DEPTH.to_string(),
|
||||||
|
(scene.width as u32, scene.height as u32),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let pixel_total = scene.width * scene.height;
|
let pixel_total = scene.width * scene.height;
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut last_time = time::Instant::now();
|
let mut last_time = time::Instant::now();
|
||||||
@ -423,40 +536,17 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
info!("Rendering with {} subsamples", scene.subsamples);
|
|
||||||
let mut img = RgbImage::new(scene.width as u32, scene.height as u32);
|
|
||||||
for resp in pixel_resp_rx {
|
for resp in pixel_resp_rx {
|
||||||
match resp {
|
match resp {
|
||||||
Response::Pixel { x, y, pixel } => {
|
Response::Pixel { x, y, pixel } => {
|
||||||
let y_inv = scene.height - y - 1;
|
output::set_pixel(output::MAIN_IMAGE, x, y, pixel);
|
||||||
img.put_pixel(
|
|
||||||
x as u32,
|
|
||||||
y_inv as u32,
|
|
||||||
image::Rgb([
|
|
||||||
(pixel[0] * 255.).min(255.) as u8,
|
|
||||||
(pixel[1] * 255.).min(255.) as u8,
|
|
||||||
(pixel[2] * 255.).min(255.) as u8,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Response::Line { y, pixels } => {
|
Response::Line { y, pixels } => {
|
||||||
for (x, pixel) in pixels.iter().enumerate() {
|
for (x, pixel) in pixels.iter().enumerate() {
|
||||||
let y_inv = scene.height - y - 1;
|
output::set_pixel(output::MAIN_IMAGE, x, y, *pixel);
|
||||||
img.put_pixel(
|
|
||||||
x as u32,
|
|
||||||
y_inv as u32,
|
|
||||||
image::Rgb([
|
|
||||||
(pixel[0] * 255.).min(255.) as u8,
|
|
||||||
(pixel[1] * 255.).min(255.) as u8,
|
|
||||||
(pixel[2] * 255.).min(255.) as u8,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let path = output_dir.join("final.png");
|
output::write_images(output_dir)
|
||||||
// Write the contents of this image to the Writer in PNG format.
|
|
||||||
trace!(target: "renderer", "Saving {}", path.display());
|
|
||||||
img.save(path)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
camera,
|
camera,
|
||||||
world,
|
world,
|
||||||
subsamples: opt.subsamples,
|
subsamples: opt.subsamples,
|
||||||
adaptive_subsampling: opt.adaptive_subsampling,
|
adaptive_subsampling: Some(0.5),
|
||||||
num_threads: opt.num_threads,
|
num_threads: opt.num_threads,
|
||||||
width: opt.width,
|
width: opt.width,
|
||||||
height: opt.height,
|
height: opt.height,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use crate::camera::Camera;
|
|||||||
use crate::hitable::Hit;
|
use crate::hitable::Hit;
|
||||||
use crate::hitable_list::HitableList;
|
use crate::hitable_list::HitableList;
|
||||||
use crate::kdtree::KDTree;
|
use crate::kdtree::KDTree;
|
||||||
|
use crate::material::Dielectric;
|
||||||
use crate::material::Lambertian;
|
use crate::material::Lambertian;
|
||||||
use crate::material::Metal;
|
use crate::material::Metal;
|
||||||
use crate::moving_sphere::MovingSphere;
|
use crate::moving_sphere::MovingSphere;
|
||||||
@ -12,7 +13,7 @@ use crate::texture::ConstantTexture;
|
|||||||
use crate::vec3::Vec3;
|
use crate::vec3::Vec3;
|
||||||
|
|
||||||
pub fn new(opt: &Opt) -> Scene {
|
pub fn new(opt: &Opt) -> Scene {
|
||||||
let lookfrom = Vec3::new(3., 3., 2.);
|
let lookfrom = Vec3::new(3., 2., 2.);
|
||||||
let lookat = Vec3::new(0., 0., -1.);
|
let lookat = Vec3::new(0., 0., -1.);
|
||||||
let dist_to_focus = (lookfrom - lookat).length();
|
let dist_to_focus = (lookfrom - lookat).length();
|
||||||
let aperture = 0.1;
|
let aperture = 0.1;
|
||||||
@ -37,6 +38,7 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
|
|
||||||
let objects: Vec<Box<dyn Hit>> = vec![
|
let objects: Vec<Box<dyn Hit>> = vec![
|
||||||
//let world: Box<Hit> = Box::new(HitableList::new(vec![
|
//let world: Box<Hit> = Box::new(HitableList::new(vec![
|
||||||
|
Box::new(Sphere::new([1., 0., 0.], 0.5, Dielectric::new(1.5))),
|
||||||
Box::new(Sphere::new(
|
Box::new(Sphere::new(
|
||||||
Vec3::new(0., 0., -1.),
|
Vec3::new(0., 0., -1.),
|
||||||
0.5,
|
0.5,
|
||||||
@ -70,6 +72,8 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
camera,
|
camera,
|
||||||
world,
|
world,
|
||||||
subsamples: opt.subsamples,
|
subsamples: opt.subsamples,
|
||||||
|
// adaptive_subsampling: None,
|
||||||
|
adaptive_subsampling: Some(0.5),
|
||||||
num_threads: opt.num_threads,
|
num_threads: opt.num_threads,
|
||||||
width: opt.width,
|
width: opt.width,
|
||||||
height: opt.height,
|
height: opt.height,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user