Compare commits
15 Commits
1076e6dcaf
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| f93d215fc2 | |||
| 593632a9e3 | |||
| ed2ee749cd | |||
| 42f3daefaa | |||
| 9430a1e7da | |||
| d3153032b1 | |||
| 35071b06ac | |||
| 5f0e7a26dd | |||
| 6fbdb49ce1 | |||
| 23bc5b0bf0 | |||
| 37137ac9ca | |||
| 9353ff675e | |||
| 4b8bd84a84 | |||
| e19ec20c7b | |||
| deb46acb5a |
1101
rtiow/Cargo.lock
generated
1101
rtiow/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
"noise_explorer",
|
"noise_explorer",
|
||||||
@@ -10,3 +11,5 @@ members = [
|
|||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 3
|
||||||
|
|||||||
@@ -23,12 +23,14 @@ num_cpus = "1.15.0"
|
|||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
serde = "1.0.152"
|
serde = "1.0.152"
|
||||||
serde_derive = "1.0.152"
|
serde_derive = "1.0.152"
|
||||||
serde_json = "1.0.91"
|
serde_json = "1.0.93"
|
||||||
structopt = "0.2.18"
|
structopt = "0.2.18"
|
||||||
vec3 = {path = "../vec3"}
|
vec3 = {path = "../vec3"}
|
||||||
stl = {path = "../../../stl"}
|
stl = {path = "../../../stl"}
|
||||||
strum = { version = "0.24.1", features = ["derive"] }
|
strum = { version = "0.24.1", features = ["derive"] }
|
||||||
strum_macros = "0.24.3"
|
strum_macros = "0.24.3"
|
||||||
|
thiserror = "1.0.38"
|
||||||
|
tev_client = "0.5.2"
|
||||||
#stl = {git = "https://git-private.z.xinu.tv/wathiede/stl"}
|
#stl = {git = "https://git-private.z.xinu.tv/wathiede/stl"}
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ impl<M> BVHTriangles<M>
|
|||||||
where
|
where
|
||||||
M: Material,
|
M: Material,
|
||||||
{
|
{
|
||||||
pub fn new(stl: &STL, material: M) -> BVHTriangles<M> {
|
pub fn new(stl: &STL, material: M, scale_factor: f32) -> BVHTriangles<M> {
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
|
|
||||||
assert_eq!(std::mem::size_of::<BVHNode>(), 32);
|
assert_eq!(std::mem::size_of::<BVHNode>(), 32);
|
||||||
@@ -129,9 +129,9 @@ where
|
|||||||
.triangles
|
.triangles
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| {
|
.map(|t| {
|
||||||
let v0 = t.verts[0];
|
let v0 = t.verts[0] * scale_factor;
|
||||||
let v1 = t.verts[1];
|
let v1 = t.verts[1] * scale_factor;
|
||||||
let v2 = t.verts[2];
|
let v2 = t.verts[2] * scale_factor;
|
||||||
let centroid = (v0 + v1 + v2) * div3;
|
let centroid = (v0 + v1 + v2) * div3;
|
||||||
Triangle {
|
Triangle {
|
||||||
centroid,
|
centroid,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ fn random_in_unit_disk() -> Vec3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Camera {
|
pub struct Camera {
|
||||||
origin: Vec3,
|
origin: Vec3,
|
||||||
lower_left_corner: Vec3,
|
lower_left_corner: Vec3,
|
||||||
|
|||||||
46
rtiow/renderer/src/colors.rs
Normal file
46
rtiow/renderer/src/colors.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
use rand::{self, Rng};
|
||||||
|
|
||||||
|
use crate::vec3::Vec3;
|
||||||
|
|
||||||
|
// HSV values in [0..1]
|
||||||
|
// returns [r, g, b] values from 0 to 255
|
||||||
|
//From https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
|
||||||
|
pub fn hsv_to_rgb(h: f32, s: f32, v: f32) -> Vec3 {
|
||||||
|
let h_i = (h * 6.) as i32;
|
||||||
|
let f = h * 6. - h_i as f32;
|
||||||
|
let p = v * (1. - s);
|
||||||
|
let q = v * (1. - f * s);
|
||||||
|
let t = v * (1. - (1. - f) * s);
|
||||||
|
match h_i {
|
||||||
|
0 => Vec3::new(v, t, p),
|
||||||
|
1 => Vec3::new(q, v, p),
|
||||||
|
2 => Vec3::new(p, v, t),
|
||||||
|
3 => Vec3::new(p, q, v),
|
||||||
|
4 => Vec3::new(t, p, v),
|
||||||
|
5 => Vec3::new(v, p, q),
|
||||||
|
_ => panic!("Unknown H value {}", h_i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_rainbow(num: usize) -> Vec<Vec3> {
|
||||||
|
(0..num)
|
||||||
|
.map(|n| {
|
||||||
|
let h = n as f32 / num as f32;
|
||||||
|
hsv_to_rgb(h, 0.99, 0.99)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
pub fn generate_palette(num: usize) -> Vec<Vec3> {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let mut random = || rng.gen();
|
||||||
|
// use golden ratio
|
||||||
|
let golden_ratio_conjugate = 0.618_034;
|
||||||
|
let mut h = random();
|
||||||
|
(0..num)
|
||||||
|
.map(|_| {
|
||||||
|
h += golden_ratio_conjugate;
|
||||||
|
h %= 1.0;
|
||||||
|
hsv_to_rgb(h, 0.99, 0.99)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
44
rtiow/renderer/src/debug_hit.rs
Normal file
44
rtiow/renderer/src/debug_hit.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use crate::{
|
||||||
|
aabb::AABB,
|
||||||
|
hitable::{Hit, HitRecord},
|
||||||
|
material::Lambertian,
|
||||||
|
ray::Ray,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DebugHit<H>
|
||||||
|
where
|
||||||
|
H: Hit,
|
||||||
|
{
|
||||||
|
hitable: H,
|
||||||
|
material: Lambertian<[f32; 3]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H> DebugHit<H>
|
||||||
|
where
|
||||||
|
H: Hit,
|
||||||
|
{
|
||||||
|
pub fn new(hitable: H) -> DebugHit<H>
|
||||||
|
where {
|
||||||
|
DebugHit {
|
||||||
|
hitable,
|
||||||
|
material: Lambertian::new([0.2, 0.2, 0.2]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H> Hit for DebugHit<H>
|
||||||
|
where
|
||||||
|
H: Hit,
|
||||||
|
{
|
||||||
|
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
||||||
|
if let Some(hit) = self.hitable.hit(r, t_min, t_max) {
|
||||||
|
return Some(HitRecord { t: hit.t, ..hit });
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_box(&self, t_min: f32, t_max: f32) -> Option<AABB> {
|
||||||
|
self.hitable.bounding_box(t_min, t_max)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,10 @@ pub mod aabb;
|
|||||||
pub mod bvh;
|
pub mod bvh;
|
||||||
pub mod bvh_triangles;
|
pub mod bvh_triangles;
|
||||||
pub mod camera;
|
pub mod camera;
|
||||||
|
pub mod colors;
|
||||||
pub mod constant_medium;
|
pub mod constant_medium;
|
||||||
pub mod cuboid;
|
pub mod cuboid;
|
||||||
|
pub mod debug_hit;
|
||||||
pub mod flip_normals;
|
pub mod flip_normals;
|
||||||
pub mod glowybox;
|
pub mod glowybox;
|
||||||
pub mod hitable;
|
pub mod hitable;
|
||||||
@@ -14,6 +16,7 @@ pub mod material;
|
|||||||
pub mod moving_sphere;
|
pub mod moving_sphere;
|
||||||
pub mod noise;
|
pub mod noise;
|
||||||
pub mod output;
|
pub mod output;
|
||||||
|
pub mod parser;
|
||||||
pub mod ray;
|
pub mod ray;
|
||||||
pub mod rect;
|
pub mod rect;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::{
|
|||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::BufWriter,
|
io::BufWriter,
|
||||||
|
net::TcpStream,
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
time,
|
time,
|
||||||
@@ -9,9 +10,9 @@ use std::{
|
|||||||
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use image;
|
use image;
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde_derive::Serialize;
|
use serde_derive::Serialize;
|
||||||
|
use tev_client::{PacketCreateImage, PacketUpdateImage, TevClient};
|
||||||
|
|
||||||
use crate::{renderer::Scene, vec3::Vec3};
|
use crate::{renderer::Scene, vec3::Vec3};
|
||||||
|
|
||||||
@@ -24,10 +25,6 @@ pub const ADAPTIVE_DEPTH: &str = "adaptive_depth";
|
|||||||
// Grey scale showing rays cast per pixel.
|
// Grey scale showing rays cast per pixel.
|
||||||
pub const RAYS_PER_PIXEL: &str = "rays_per_pixel";
|
pub const RAYS_PER_PIXEL: &str = "rays_per_pixel";
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref DEBUGGER: Arc<Mutex<Debugger>> = Arc::new(Mutex::new(Debugger::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct ImageMetadata {
|
struct ImageMetadata {
|
||||||
name: String,
|
name: String,
|
||||||
@@ -74,65 +71,86 @@ impl Image {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Debugger {
|
pub struct OutputManager {
|
||||||
images: HashMap<String, (ImageType, Image)>,
|
images: Arc<Mutex<HashMap<String, (ImageType, Image)>>>,
|
||||||
|
tev_client: Option<Arc<Mutex<TevClient>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debugger {
|
impl OutputManager {
|
||||||
fn new() -> Debugger {
|
pub fn new(tev_addr: &Option<String>) -> std::io::Result<OutputManager> {
|
||||||
Debugger {
|
let tev_client = if let Some(addr) = tev_addr {
|
||||||
images: HashMap::new(),
|
Some(Arc::new(Mutex::new(TevClient::wrap(TcpStream::connect(
|
||||||
|
addr,
|
||||||
|
)?))))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Ok(OutputManager {
|
||||||
|
images: Arc::new(Mutex::new(HashMap::new())),
|
||||||
|
tev_client,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn register_image(&self, name: String, dimensions: (usize, usize), it: ImageType) {
|
||||||
|
let mut images = self.images.lock().unwrap();
|
||||||
|
images.insert(name.clone(), (it, Image::new(dimensions.0, dimensions.1)));
|
||||||
|
self.tev_client.clone().map(|c| {
|
||||||
|
c.lock().unwrap().send(PacketCreateImage {
|
||||||
|
image_name: &name,
|
||||||
|
grab_focus: false,
|
||||||
|
width: dimensions.0 as u32,
|
||||||
|
height: dimensions.1 as u32,
|
||||||
|
channel_names: &["R", "G", "B"],
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_image(name: String, dimensions: (usize, usize), it: ImageType) {
|
pub fn set_pixel(&self, name: &str, x: usize, y: usize, pixel: Vec3) {
|
||||||
let mut debugger = DEBUGGER.lock().unwrap();
|
let mut images = self.images.lock().unwrap();
|
||||||
debugger
|
let (_it, img) = images
|
||||||
.images
|
|
||||||
.insert(name, (it, Image::new(dimensions.0, dimensions.1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_pixel(name: &str, x: usize, y: usize, pixel: Vec3) {
|
|
||||||
let mut debugger = DEBUGGER.lock().unwrap();
|
|
||||||
let (_it, img) = debugger
|
|
||||||
.images
|
|
||||||
.get_mut(name)
|
.get_mut(name)
|
||||||
.unwrap_or_else(|| panic!("couldn't find image named '{}'", name));
|
.unwrap_or_else(|| panic!("couldn't find image named '{}'", name));
|
||||||
let y_inv = img.h - y - 1;
|
let y_inv = img.h - y - 1;
|
||||||
img.put_pixel(x, y_inv, pixel);
|
img.put_pixel(x, y_inv, pixel);
|
||||||
}
|
self.tev_client.clone().map(|c| {
|
||||||
|
c.lock().unwrap().send(PacketUpdateImage {
|
||||||
|
image_name: &name,
|
||||||
|
grab_focus: false,
|
||||||
|
channel_names: &["R", "G", "B"],
|
||||||
|
channel_offsets: &[0, 1, 2],
|
||||||
|
channel_strides: &[0, 0, 0],
|
||||||
|
x: x as u32,
|
||||||
|
y: y_inv as u32,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
data: &[pixel.x, pixel.y, pixel.z],
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_pixel_grey(name: &str, x: usize, y: usize, grey: f32) {
|
pub fn set_pixel_grey(&self, name: &str, x: usize, y: usize, grey: f32) {
|
||||||
let mut debugger = DEBUGGER.lock().unwrap();
|
let mut images = self.images.lock().unwrap();
|
||||||
let (_it, img) = debugger
|
let (_it, img) = images
|
||||||
.images
|
|
||||||
.get_mut(name)
|
.get_mut(name)
|
||||||
.unwrap_or_else(|| panic!("couldn't find image named '{}'", name));
|
.unwrap_or_else(|| panic!("couldn't find image named '{}'", name));
|
||||||
let y_inv = img.h - y - 1;
|
let y_inv = img.h - y - 1;
|
||||||
img.put_pixel(x, y_inv, [grey, grey, grey].into());
|
img.put_pixel(x, y_inv, [grey, grey, grey].into());
|
||||||
}
|
}
|
||||||
|
pub fn write_images<P: AsRef<Path>>(
|
||||||
trait ImageSaver {
|
&self,
|
||||||
fn save<Q>(&self, path: Q) -> std::io::Result<()>
|
|
||||||
where
|
|
||||||
Q: AsRef<Path> + Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_images<P: AsRef<Path>>(
|
|
||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
render_time: time::Duration,
|
render_time: time::Duration,
|
||||||
output_dir: P,
|
output_dir: P,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
let output_dir: &Path = output_dir.as_ref();
|
let output_dir: &Path = output_dir.as_ref();
|
||||||
let debugger = DEBUGGER.lock().unwrap();
|
|
||||||
let now = Local::now();
|
let now = Local::now();
|
||||||
|
let images = self.images.lock().unwrap();
|
||||||
// Write out images in consistent order.
|
// Write out images in consistent order.
|
||||||
let mut names = debugger.images.keys().collect::<Vec<_>>();
|
let mut names = images.keys().collect::<Vec<_>>();
|
||||||
names.sort();
|
names.sort();
|
||||||
let mut image_metadata = Vec::new();
|
let mut image_metadata = Vec::new();
|
||||||
for name in &names {
|
for name in &names {
|
||||||
let (it, img) = debugger.images.get(*name).unwrap();
|
let (it, img) = images.get(*name).unwrap();
|
||||||
let image = format!("{}.png", name);
|
let image = format!("{}.png", name);
|
||||||
let binary = format!("{}.json", name);
|
let binary = format!("{}.json", name);
|
||||||
let ratio = img.w as f32 / img.h as f32;
|
let ratio = img.w as f32 / img.h as f32;
|
||||||
@@ -203,7 +221,10 @@ pub fn write_images<P: AsRef<Path>>(
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
ImageType::Grey01 | ImageType::GreyNormalized => {
|
ImageType::Grey01 | ImageType::GreyNormalized => {
|
||||||
serde_json::ser::to_writer(f, &img.pix.iter().map(|v| v.x).collect::<Vec<f32>>())?;
|
serde_json::ser::to_writer(
|
||||||
|
f,
|
||||||
|
&img.pix.iter().map(|v| v.x).collect::<Vec<f32>>(),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -219,4 +240,11 @@ pub fn write_images<P: AsRef<Path>>(
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ImageSaver {
|
||||||
|
fn save<Q>(&self, path: Q) -> std::io::Result<()>
|
||||||
|
where
|
||||||
|
Q: AsRef<Path> + Sized;
|
||||||
}
|
}
|
||||||
|
|||||||
206
rtiow/renderer/src/parser.rs
Normal file
206
rtiow/renderer/src/parser.rs
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
use crate::{
|
||||||
|
bvh_triangles::BVHTriangles,
|
||||||
|
camera::Camera,
|
||||||
|
cuboid::Cuboid,
|
||||||
|
hitable::Hit,
|
||||||
|
hitable_list::HitableList,
|
||||||
|
material::{Dielectric, DiffuseLight, Isotropic, Lambertian, Material, Metal},
|
||||||
|
renderer::Scene,
|
||||||
|
sphere::Sphere,
|
||||||
|
texture::{EnvMap, Texture},
|
||||||
|
};
|
||||||
|
use chrono::IsoWeek;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::{collections::HashMap, fs::File, io::BufReader, path::PathBuf, sync::Arc};
|
||||||
|
use stl::STL;
|
||||||
|
use thiserror::Error;
|
||||||
|
use vec3::Vec3;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
scene: SceneConfig,
|
||||||
|
camera: CameraConfig,
|
||||||
|
materials: Vec<MaterialConfig>,
|
||||||
|
hitables: Vec<HitableConfig>,
|
||||||
|
envmap: Option<EnvMapConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ConfigError {
|
||||||
|
#[error("failed to load image")]
|
||||||
|
ImageError(#[from] image::ImageError),
|
||||||
|
#[error("failed to parser STL")]
|
||||||
|
STLError(#[from] stl::ParseError),
|
||||||
|
#[error("I/O error")]
|
||||||
|
IOError(#[from] std::io::Error),
|
||||||
|
#[error("duplication material named '{0}'")]
|
||||||
|
DuplicateMaterial(String),
|
||||||
|
#[error("unkown material named '{0}'")]
|
||||||
|
UnknownMaterial(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Config> for Scene {
|
||||||
|
type Error = ConfigError;
|
||||||
|
|
||||||
|
fn try_from(c: Config) -> Result<Scene, Self::Error> {
|
||||||
|
let mut materials = HashMap::new();
|
||||||
|
for mc in c.materials {
|
||||||
|
let v: Arc<dyn Material> = match mc.material {
|
||||||
|
Materials::Metal { albedo, fuzzy } => Arc::new(Metal::new(albedo, fuzzy)),
|
||||||
|
Materials::Dielectric { ref_idx } => Arc::new(Dielectric::new(ref_idx)),
|
||||||
|
Materials::DiffuseLight { texture } => Arc::new(DiffuseLight::new(texture)),
|
||||||
|
Materials::Isotropic { texture } => Arc::new(Isotropic::new(texture)),
|
||||||
|
Materials::Lambertian { texture } => Arc::new(Lambertian::new(texture)),
|
||||||
|
};
|
||||||
|
if materials.insert(mc.name.clone(), v).is_some() {
|
||||||
|
return Err(ConfigError::DuplicateMaterial(mc.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let hitables: Result<Vec<Box<dyn Hit>>, Self::Error> = c
|
||||||
|
.hitables
|
||||||
|
.into_iter()
|
||||||
|
.map(|hc| -> Result<Box<dyn Hit>, Self::Error> {
|
||||||
|
match hc.hitable {
|
||||||
|
Hitables::Sphere { center, radius } => Ok(Box::new(Sphere::new(
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
Arc::clone(
|
||||||
|
materials
|
||||||
|
.get(&hc.material_name)
|
||||||
|
.ok_or(ConfigError::UnknownMaterial(hc.material_name))?,
|
||||||
|
),
|
||||||
|
))),
|
||||||
|
Hitables::Cuboid { min, max } => Ok(Box::new(Cuboid::new(
|
||||||
|
min.into(),
|
||||||
|
max.into(),
|
||||||
|
Arc::clone(
|
||||||
|
materials
|
||||||
|
.get(&hc.material_name)
|
||||||
|
.ok_or(ConfigError::UnknownMaterial(hc.material_name))?,
|
||||||
|
),
|
||||||
|
))),
|
||||||
|
Hitables::STL { path, scale } => {
|
||||||
|
let r = BufReader::new(File::open(path)?);
|
||||||
|
let stl = STL::parse(r, false)?;
|
||||||
|
Ok(Box::new(BVHTriangles::new(
|
||||||
|
&stl,
|
||||||
|
Arc::clone(
|
||||||
|
materials
|
||||||
|
.get(&hc.material_name)
|
||||||
|
.ok_or(ConfigError::UnknownMaterial(hc.material_name))?,
|
||||||
|
),
|
||||||
|
scale.unwrap_or(1.),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let hitables = hitables?;
|
||||||
|
|
||||||
|
let world: Box<dyn Hit> = Box::new(HitableList::new(hitables));
|
||||||
|
let mut env_map: Option<EnvMap> = None;
|
||||||
|
|
||||||
|
if let Some(em) = c.envmap {
|
||||||
|
let im = image::open(em.path)?.into_rgb();
|
||||||
|
env_map = Some(EnvMap::new(im));
|
||||||
|
};
|
||||||
|
|
||||||
|
let camera = make_camera(&c.camera, c.scene.width, c.scene.height);
|
||||||
|
|
||||||
|
let scene = Scene {
|
||||||
|
world,
|
||||||
|
camera,
|
||||||
|
env_map,
|
||||||
|
subsamples: c.scene.subsamples.unwrap_or(8),
|
||||||
|
adaptive_subsampling: c.scene.adaptive_subsampling,
|
||||||
|
num_threads: c.scene.num_threads,
|
||||||
|
width: c.scene.width,
|
||||||
|
height: c.scene.height,
|
||||||
|
global_illumination: c.scene.global_illumination.unwrap_or(true),
|
||||||
|
};
|
||||||
|
Ok(scene)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_camera(cfg: &CameraConfig, width: usize, height: usize) -> Camera {
|
||||||
|
Camera::new(
|
||||||
|
cfg.lookfrom.into(),
|
||||||
|
cfg.lookat.into(),
|
||||||
|
Vec3::new(0., 1., 0.),
|
||||||
|
cfg.fov,
|
||||||
|
width as f32 / height as f32,
|
||||||
|
cfg.aperture,
|
||||||
|
cfg.focus_dist,
|
||||||
|
cfg.time_min,
|
||||||
|
cfg.time_max,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct SceneConfig {
|
||||||
|
subsamples: Option<usize>,
|
||||||
|
adaptive_subsampling: Option<f32>,
|
||||||
|
num_threads: Option<usize>,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
global_illumination: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
struct HitableConfig {
|
||||||
|
material_name: String,
|
||||||
|
#[serde(flatten)]
|
||||||
|
hitable: Hitables,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
enum Hitables {
|
||||||
|
#[serde(rename = "sphere")]
|
||||||
|
Sphere { center: [f32; 3], radius: f32 },
|
||||||
|
#[serde(rename = "cuboid")]
|
||||||
|
Cuboid { min: [f32; 3], max: [f32; 3] },
|
||||||
|
#[serde(rename = "stl")]
|
||||||
|
STL { path: PathBuf, scale: Option<f32> },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct MaterialConfig {
|
||||||
|
name: String,
|
||||||
|
#[serde(flatten)]
|
||||||
|
material: Materials,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
enum Materials {
|
||||||
|
#[serde(rename = "metal")]
|
||||||
|
Metal { albedo: [f32; 3], fuzzy: f32 },
|
||||||
|
#[serde(rename = "dielectric")]
|
||||||
|
Dielectric { ref_idx: f32 },
|
||||||
|
// TODO(wathiede): these all take Textures, for now, only support RGB
|
||||||
|
#[serde(rename = "diffuse_light")]
|
||||||
|
DiffuseLight { texture: [f32; 3] },
|
||||||
|
#[serde(rename = "isotropic")]
|
||||||
|
Isotropic { texture: [f32; 3] },
|
||||||
|
#[serde(rename = "lambertian")]
|
||||||
|
Lambertian { texture: [f32; 3] },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct CameraConfig {
|
||||||
|
lookfrom: [f32; 3],
|
||||||
|
lookat: [f32; 3],
|
||||||
|
fov: f32,
|
||||||
|
aperture: f32,
|
||||||
|
focus_dist: f32,
|
||||||
|
time_min: f32,
|
||||||
|
time_max: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct EnvMapConfig {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
fmt,
|
fmt,
|
||||||
ops::{AddAssign, Range},
|
ops::{AddAssign, Range},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
@@ -22,8 +23,9 @@ use crate::{
|
|||||||
camera::Camera,
|
camera::Camera,
|
||||||
hitable::Hit,
|
hitable::Hit,
|
||||||
human,
|
human,
|
||||||
material::Lambertian,
|
material::{Lambertian, Material},
|
||||||
output,
|
output,
|
||||||
|
output::OutputManager,
|
||||||
ray::Ray,
|
ray::Ray,
|
||||||
scenes,
|
scenes,
|
||||||
sphere::Sphere,
|
sphere::Sphere,
|
||||||
@@ -99,15 +101,26 @@ pub struct Opt {
|
|||||||
/// Select scene to render.
|
/// Select scene to render.
|
||||||
#[structopt(long = "model")]
|
#[structopt(long = "model")]
|
||||||
pub model: Option<Model>,
|
pub model: Option<Model>,
|
||||||
|
/// Toml config describing scene.
|
||||||
|
#[structopt(long = "config")]
|
||||||
|
pub config: Option<PathBuf>,
|
||||||
/// Path to store pprof profile data, i.e. /tmp/cpuprofile.pprof
|
/// Path to store pprof profile data, i.e. /tmp/cpuprofile.pprof
|
||||||
#[structopt(long = "pprof", parse(from_os_str))]
|
#[structopt(long = "pprof", parse(from_os_str))]
|
||||||
pub pprof: Option<PathBuf>,
|
pub pprof: Option<PathBuf>,
|
||||||
/// Use acceleration data structure, may be BVH or kd-tree depending on scene.
|
/// Use acceleration data structure, may be BVH or kd-tree depending on scene.
|
||||||
#[structopt(long = "use_accel")]
|
#[structopt(long = "use_accel")]
|
||||||
pub use_accel: bool,
|
pub use_accel: bool,
|
||||||
|
/// Host:port of running tev instance.
|
||||||
|
#[structopt(long = "tev_addr")]
|
||||||
|
pub tev_addr: Option<String>,
|
||||||
|
|
||||||
/// Output directory
|
/// Output directory
|
||||||
#[structopt(parse(from_os_str), default_value = "/tmp/tracer")]
|
#[structopt(
|
||||||
|
short = "o",
|
||||||
|
long = "output",
|
||||||
|
parse(from_os_str),
|
||||||
|
default_value = "/tmp/tracer"
|
||||||
|
)]
|
||||||
pub output: PathBuf,
|
pub output: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,11 +139,13 @@ pub fn opt_hash(opt: &Opt) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(wathiede): implement the skips and then the renderer could use json as an input file type.
|
// TODO(wathiede): implement the skips and then the renderer could use json as an input file type.
|
||||||
#[derive(Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub world: Box<dyn Hit>,
|
pub world: Box<dyn Hit>,
|
||||||
|
//#[serde(skip)]
|
||||||
|
//pub materials: HashMap<String, Box<dyn Material>>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub camera: Camera,
|
pub camera: Camera,
|
||||||
pub subsamples: usize,
|
pub subsamples: usize,
|
||||||
@@ -235,6 +250,7 @@ fn trace_pixel_adaptive(
|
|||||||
x_range: Range<f32>,
|
x_range: Range<f32>,
|
||||||
y_range: Range<f32>,
|
y_range: Range<f32>,
|
||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
|
output: &OutputManager,
|
||||||
) -> (Vec3, usize) {
|
) -> (Vec3, usize) {
|
||||||
let w = scene.width as f32;
|
let w = scene.width as f32;
|
||||||
let h = scene.height as f32;
|
let h = scene.height as f32;
|
||||||
@@ -249,7 +265,7 @@ fn trace_pixel_adaptive(
|
|||||||
&scene.env_map,
|
&scene.env_map,
|
||||||
);
|
);
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
output::set_pixel(output::ADAPTIVE_DEPTH, x, y, [1., 0., 0.].into());
|
output.set_pixel(output::ADAPTIVE_DEPTH, x, y, [1., 0., 0.].into());
|
||||||
return (center, rays);
|
return (center, rays);
|
||||||
}
|
}
|
||||||
// t = top
|
// t = top
|
||||||
@@ -291,6 +307,7 @@ fn trace_pixel_adaptive(
|
|||||||
x_range.start..x_mid,
|
x_range.start..x_mid,
|
||||||
y_range.start..y_mid,
|
y_range.start..y_mid,
|
||||||
scene,
|
scene,
|
||||||
|
output,
|
||||||
);
|
);
|
||||||
let tr = trace_pixel_adaptive(
|
let tr = trace_pixel_adaptive(
|
||||||
depth - 1,
|
depth - 1,
|
||||||
@@ -300,6 +317,7 @@ fn trace_pixel_adaptive(
|
|||||||
x_mid..x_range.end,
|
x_mid..x_range.end,
|
||||||
y_range.start..y_mid,
|
y_range.start..y_mid,
|
||||||
scene,
|
scene,
|
||||||
|
output,
|
||||||
);
|
);
|
||||||
let bl = trace_pixel_adaptive(
|
let bl = trace_pixel_adaptive(
|
||||||
depth - 1,
|
depth - 1,
|
||||||
@@ -309,6 +327,7 @@ fn trace_pixel_adaptive(
|
|||||||
x_range.start..x_mid,
|
x_range.start..x_mid,
|
||||||
y_mid..y_range.end,
|
y_mid..y_range.end,
|
||||||
scene,
|
scene,
|
||||||
|
output,
|
||||||
);
|
);
|
||||||
let br = trace_pixel_adaptive(
|
let br = trace_pixel_adaptive(
|
||||||
depth - 1,
|
depth - 1,
|
||||||
@@ -318,13 +337,14 @@ fn trace_pixel_adaptive(
|
|||||||
x_mid..x_range.end,
|
x_mid..x_range.end,
|
||||||
y_mid..y_range.end,
|
y_mid..y_range.end,
|
||||||
scene,
|
scene,
|
||||||
|
output,
|
||||||
);
|
);
|
||||||
let pixel = (tl.0 + tr.0 + bl.0 + br.0) / 4.;
|
let pixel = (tl.0 + tr.0 + bl.0 + br.0) / 4.;
|
||||||
let rays = tl.1 + tr.1 + bl.1 + br.1;
|
let rays = tl.1 + tr.1 + bl.1 + br.1;
|
||||||
(pixel, rays)
|
(pixel, rays)
|
||||||
} else {
|
} else {
|
||||||
if depth == MAX_ADAPTIVE_DEPTH {
|
if depth == MAX_ADAPTIVE_DEPTH {
|
||||||
output::set_pixel(output::ADAPTIVE_DEPTH, x, y, [0., 1., 0.].into());
|
output.set_pixel(output::ADAPTIVE_DEPTH, x, y, [0., 1., 0.].into());
|
||||||
}
|
}
|
||||||
(corners, rays)
|
(corners, rays)
|
||||||
}
|
}
|
||||||
@@ -407,7 +427,7 @@ enum Response {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_pixel(scene: &Scene, x: usize, y: usize) -> (Vec3, usize) {
|
fn render_pixel(scene: &Scene, x: usize, y: usize, output: &OutputManager) -> (Vec3, usize) {
|
||||||
let (pixel, rays) = if let Some(threshold) = scene.adaptive_subsampling {
|
let (pixel, rays) = if let Some(threshold) = scene.adaptive_subsampling {
|
||||||
trace_pixel_adaptive(
|
trace_pixel_adaptive(
|
||||||
MAX_ADAPTIVE_DEPTH,
|
MAX_ADAPTIVE_DEPTH,
|
||||||
@@ -417,6 +437,7 @@ fn render_pixel(scene: &Scene, x: usize, y: usize) -> (Vec3, usize) {
|
|||||||
0.0..1.0,
|
0.0..1.0,
|
||||||
0.0..1.0,
|
0.0..1.0,
|
||||||
scene,
|
scene,
|
||||||
|
output,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let (pixel, rays) = (0..scene.subsamples)
|
let (pixel, rays) = (0..scene.subsamples)
|
||||||
@@ -425,7 +446,7 @@ fn render_pixel(scene: &Scene, x: usize, y: usize) -> (Vec3, usize) {
|
|||||||
([0., 0., 0.].into(), 0),
|
([0., 0., 0.].into(), 0),
|
||||||
|(p1, r1): (Vec3, usize), (p2, r2): (Vec3, usize)| ((p1 + p2), (r1 + r2)),
|
|(p1, r1): (Vec3, usize), (p2, r2): (Vec3, usize)| ((p1 + p2), (r1 + r2)),
|
||||||
);
|
);
|
||||||
output::set_pixel_grey(output::RAYS_PER_PIXEL, x, y, rays as f32);
|
output.set_pixel_grey(output::RAYS_PER_PIXEL, x, y, rays as f32);
|
||||||
(pixel / scene.subsamples as f32, rays)
|
(pixel / scene.subsamples as f32, rays)
|
||||||
};
|
};
|
||||||
// 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
|
||||||
@@ -441,6 +462,7 @@ fn render_worker(
|
|||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
input_chan: Arc<Mutex<Receiver<Request>>>,
|
input_chan: Arc<Mutex<Receiver<Request>>>,
|
||||||
output_chan: &SyncSender<Response>,
|
output_chan: &SyncSender<Response>,
|
||||||
|
output: &OutputManager,
|
||||||
) {
|
) {
|
||||||
loop {
|
loop {
|
||||||
let job = { input_chan.lock().unwrap().recv() };
|
let job = { input_chan.lock().unwrap().recv() };
|
||||||
@@ -455,7 +477,7 @@ fn render_worker(
|
|||||||
let batch = false;
|
let batch = false;
|
||||||
if batch {
|
if batch {
|
||||||
let (pixels, rays): (Vec<Vec3>, Vec<usize>) = (0..width)
|
let (pixels, rays): (Vec<Vec3>, Vec<usize>) = (0..width)
|
||||||
.map(|x| render_pixel(scene, x, y))
|
.map(|x| render_pixel(scene, x, y, output))
|
||||||
.collect::<Vec<(_, _)>>()
|
.collect::<Vec<(_, _)>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.unzip();
|
.unzip();
|
||||||
@@ -472,7 +494,7 @@ fn render_worker(
|
|||||||
.expect("failed to send pixel response");
|
.expect("failed to send pixel response");
|
||||||
} else {
|
} else {
|
||||||
(0..width).for_each(|x| {
|
(0..width).for_each(|x| {
|
||||||
let (pixel, rays) = render_pixel(scene, x, y);
|
let (pixel, rays) = render_pixel(scene, x, y, output);
|
||||||
output_chan
|
output_chan
|
||||||
.send(Response::Pixel {
|
.send(Response::Pixel {
|
||||||
x,
|
x,
|
||||||
@@ -486,7 +508,7 @@ fn render_worker(
|
|||||||
}
|
}
|
||||||
Request::Pixel { x, y } => {
|
Request::Pixel { x, y } => {
|
||||||
trace!("tid {} x {} y {}", tid, x, y);
|
trace!("tid {} x {} y {}", tid, x, y);
|
||||||
let (pixel, rays) = render_pixel(scene, x, y);
|
let (pixel, rays) = render_pixel(scene, x, y, output);
|
||||||
output_chan
|
output_chan
|
||||||
.send(Response::Pixel {
|
.send(Response::Pixel {
|
||||||
x,
|
x,
|
||||||
@@ -501,7 +523,17 @@ fn render_worker(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::io::Error> {
|
/*
|
||||||
|
lazy_static! {
|
||||||
|
static ref DEBUGGER: Arc<Mutex<OutputManager>> = Arc::new(Mutex::new(OutputManager::new()));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn render(
|
||||||
|
scene: Scene,
|
||||||
|
output_dir: &Path,
|
||||||
|
tev_addr: &Option<String>,
|
||||||
|
) -> std::result::Result<(), std::io::Error> {
|
||||||
// Default to half the cores to disable hyperthreading.
|
// Default to half the cores to disable hyperthreading.
|
||||||
let num_threads = scene.num_threads.unwrap_or_else(|| num_cpus::get() / 2);
|
let num_threads = scene.num_threads.unwrap_or_else(|| num_cpus::get() / 2);
|
||||||
let (pixel_req_tx, pixel_req_rx) = sync_channel(2 * num_threads);
|
let (pixel_req_tx, pixel_req_rx) = sync_channel(2 * num_threads);
|
||||||
@@ -517,20 +549,23 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
} else {
|
} else {
|
||||||
core_ids
|
core_ids
|
||||||
};
|
};
|
||||||
|
let output = output::OutputManager::new(tev_addr)?;
|
||||||
|
let output = Arc::new(output);
|
||||||
|
|
||||||
info!("Creating {} render threads", core_ids.len());
|
info!("Creating {} render threads", core_ids.len());
|
||||||
output::register_image(
|
output.register_image(
|
||||||
output::MAIN_IMAGE.to_string(),
|
output::MAIN_IMAGE.to_string(),
|
||||||
(scene.width, scene.height),
|
(scene.width, scene.height),
|
||||||
output::ImageType::RGB01,
|
output::ImageType::RGB01,
|
||||||
);
|
);
|
||||||
if scene.adaptive_subsampling.is_some() {
|
if scene.adaptive_subsampling.is_some() {
|
||||||
output::register_image(
|
output.register_image(
|
||||||
output::ADAPTIVE_DEPTH.to_string(),
|
output::ADAPTIVE_DEPTH.to_string(),
|
||||||
(scene.width, scene.height),
|
(scene.width, scene.height),
|
||||||
output::ImageType::RGB01,
|
output::ImageType::RGB01,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
output::register_image(
|
output.register_image(
|
||||||
output::RAYS_PER_PIXEL.to_string(),
|
output::RAYS_PER_PIXEL.to_string(),
|
||||||
(scene.width, scene.height),
|
(scene.width, scene.height),
|
||||||
output::ImageType::GreyNormalized,
|
output::ImageType::GreyNormalized,
|
||||||
@@ -544,9 +579,10 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
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();
|
||||||
let pixel_resp_tx = pixel_resp_tx.clone();
|
let pixel_resp_tx = pixel_resp_tx.clone();
|
||||||
|
let output = sync::Arc::clone(&output);
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
core_affinity::set_for_current(id);
|
core_affinity::set_for_current(id);
|
||||||
render_worker(i, &s, pixel_req_rx, &pixel_resp_tx);
|
render_worker(i, &s, pixel_req_rx, &pixel_resp_tx, &output);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -585,12 +621,12 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
match resp {
|
match resp {
|
||||||
Response::Pixel { x, y, pixel, rs } => {
|
Response::Pixel { x, y, pixel, rs } => {
|
||||||
current_stat += rs;
|
current_stat += rs;
|
||||||
output::set_pixel(output::MAIN_IMAGE, x, y, pixel);
|
output.set_pixel(output::MAIN_IMAGE, x, y, pixel);
|
||||||
}
|
}
|
||||||
Response::Line { y, pixels, rs } => {
|
Response::Line { y, pixels, rs } => {
|
||||||
current_stat += rs;
|
current_stat += rs;
|
||||||
for (x, pixel) in pixels.iter().enumerate() {
|
for (x, pixel) in pixels.iter().enumerate() {
|
||||||
output::set_pixel(output::MAIN_IMAGE, x, y, *pixel);
|
output.set_pixel(output::MAIN_IMAGE, x, y, *pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -628,5 +664,5 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
output::write_images(&scene, time_diff, output_dir)
|
output.write_images(&scene, time_diff, output_dir)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use crate::{
|
|||||||
aabb::AABB,
|
aabb::AABB,
|
||||||
hitable::{Hit, HitRecord},
|
hitable::{Hit, HitRecord},
|
||||||
ray::Ray,
|
ray::Ray,
|
||||||
vec3::Vec3,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -11,21 +10,15 @@ where
|
|||||||
H: Hit,
|
H: Hit,
|
||||||
{
|
{
|
||||||
hitable: H,
|
hitable: H,
|
||||||
scale: Vec3,
|
scale: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H> Scale<H>
|
impl<H> Scale<H>
|
||||||
where
|
where
|
||||||
H: Hit,
|
H: Hit,
|
||||||
{
|
{
|
||||||
pub fn new<V>(hitable: H, scale: V) -> Scale<H>
|
pub fn new(hitable: H, scale: f32) -> Scale<H> {
|
||||||
where
|
Scale { hitable, scale }
|
||||||
V: Into<Vec3>,
|
|
||||||
{
|
|
||||||
Scale {
|
|
||||||
hitable,
|
|
||||||
scale: scale.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +31,7 @@ where
|
|||||||
if let Some(rec) = self.hitable.hit(moved_r, t_min, t_max) {
|
if let Some(rec) = self.hitable.hit(moved_r, t_min, t_max) {
|
||||||
return Some(HitRecord {
|
return Some(HitRecord {
|
||||||
p: rec.p * self.scale,
|
p: rec.p * self.scale,
|
||||||
|
t: rec.t * self.scale,
|
||||||
..rec
|
..rec
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
height: opt.height,
|
height: opt.height,
|
||||||
global_illumination: true,
|
global_illumination: true,
|
||||||
env_map: Some(EnvMap::new(skybox)),
|
env_map: Some(EnvMap::new(skybox)),
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
use std::io::{BufReader, Cursor};
|
use std::{
|
||||||
|
f32::consts::PI,
|
||||||
|
io::{BufReader, Cursor},
|
||||||
|
};
|
||||||
|
|
||||||
use stl::STL;
|
use stl::STL;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bvh_triangles::BVHTriangles,
|
bvh_triangles::BVHTriangles,
|
||||||
camera::Camera,
|
camera::Camera,
|
||||||
|
colors::generate_rainbow,
|
||||||
hitable::Hit,
|
hitable::Hit,
|
||||||
hitable_list::HitableList,
|
hitable_list::HitableList,
|
||||||
kdtree::KDTree,
|
kdtree::KDTree,
|
||||||
material::{Dielectric, Lambertian},
|
material::{Lambertian, Metal},
|
||||||
renderer::{Opt, Scene},
|
renderer::{Opt, Scene},
|
||||||
|
rotate::RotateY,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
sphere::Sphere,
|
sphere::Sphere,
|
||||||
texture::{ConstantTexture, EnvMap},
|
texture::{ConstantTexture, EnvMap},
|
||||||
@@ -18,8 +23,8 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn new(opt: &Opt) -> Scene {
|
pub fn new(opt: &Opt) -> Scene {
|
||||||
let lookfrom = Vec3::new(0., 40., -100.);
|
let lookfrom = Vec3::new(0., 80., 80.);
|
||||||
let lookat = Vec3::new(0., 10., 0.);
|
let lookat = Vec3::new(0., 0., 0.);
|
||||||
let dist_to_focus = 10.0;
|
let dist_to_focus = 10.0;
|
||||||
let aperture = 0.0;
|
let aperture = 0.0;
|
||||||
let time_min = 0.;
|
let time_min = 0.;
|
||||||
@@ -35,6 +40,10 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
time_min,
|
time_min,
|
||||||
time_max,
|
time_max,
|
||||||
);
|
);
|
||||||
|
//let dragon_material = Dielectric::new(1.5);
|
||||||
|
let dragon_material = Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.0);
|
||||||
|
//let dragon_material = Lambertian::new(ConstantTexture::new(Vec3::new(1.0, 1.0, 0.2)));
|
||||||
|
|
||||||
let ground_color = if opt.use_accel {
|
let ground_color = if opt.use_accel {
|
||||||
ConstantTexture::new(Vec3::new(1.0, 0.4, 0.4))
|
ConstantTexture::new(Vec3::new(1.0, 0.4, 0.4))
|
||||||
} else {
|
} else {
|
||||||
@@ -42,46 +51,50 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let stl_cube = STL::parse(
|
let stl_cube = STL::parse(
|
||||||
BufReader::new(Cursor::new(include_bytes!(
|
BufReader::new(Cursor::new(include_bytes!("../../stls/dragon.stl"))),
|
||||||
"../../stls/stanford_dragon-lowres.stl" //"../../stls/stanford_dragon.stl"
|
|
||||||
))),
|
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.expect("failed to parse cube");
|
.expect("failed to parse cube");
|
||||||
let _light_size = 50.;
|
let _light_size = 50.;
|
||||||
let _light_height = 200.;
|
let _light_height = 200.;
|
||||||
let objects: Vec<Box<dyn Hit>> = vec![
|
let sphere_radius = 5.;
|
||||||
// Light from above - white
|
let circle_radius = 40.;
|
||||||
|
let num_spheres = 16;
|
||||||
|
let palette = generate_rainbow(num_spheres);
|
||||||
|
let spheres: Vec<Box<dyn Hit>> = (0..num_spheres)
|
||||||
|
.map(|i| (i, i as f32, num_spheres as f32))
|
||||||
|
.map(|(idx, idx_f, n)| (idx, idx_f * 2. * PI / n))
|
||||||
|
.map(|(idx, rad)| -> Box<dyn Hit> {
|
||||||
|
let x = circle_radius * rad.cos();
|
||||||
|
let y = 4. * sphere_radius;
|
||||||
|
let z = circle_radius * rad.sin();
|
||||||
|
let c = palette[idx];
|
||||||
|
Box::new(Sphere::new(
|
||||||
|
[x, y, z],
|
||||||
|
sphere_radius,
|
||||||
|
Lambertian::new(ConstantTexture::new(c)),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mut objects: Vec<Box<dyn Hit>> = vec![
|
||||||
|
Box::new(Sphere::new(
|
||||||
|
Vec3::new(0., 0.1, -0.5),
|
||||||
|
0.1,
|
||||||
|
Lambertian::new([0., 1., 1.]),
|
||||||
|
)),
|
||||||
// Earth sized sphere
|
// Earth sized sphere
|
||||||
Box::new(Sphere::new(
|
//Box::new(Sphere::new( Vec3::new(0., -10000., 0.), 10000., Lambertian::new(ground_color),)),
|
||||||
Vec3::new(0., -10000., 0.),
|
|
||||||
10000.,
|
|
||||||
Lambertian::new(ground_color),
|
|
||||||
)),
|
|
||||||
// Blue sphere
|
|
||||||
Box::new(Sphere::new(
|
|
||||||
Vec3::new(0., 20., 40.),
|
|
||||||
20.,
|
|
||||||
Lambertian::new(ConstantTexture::new(Vec3::new(0.2, 0.2, 1.))),
|
|
||||||
)),
|
|
||||||
Box::new(Sphere::new(
|
|
||||||
Vec3::new(40., 20., 40.),
|
|
||||||
20.,
|
|
||||||
//Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2),
|
|
||||||
Lambertian::new(ConstantTexture::new(Vec3::new(0.2, 1.0, 0.2))),
|
|
||||||
)),
|
|
||||||
Box::new(Sphere::new(
|
|
||||||
Vec3::new(-40., 20., 40.),
|
|
||||||
20.,
|
|
||||||
//Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2),
|
|
||||||
Lambertian::new(ConstantTexture::new(Vec3::new(1.0, 0.2, 0.2))),
|
|
||||||
)),
|
|
||||||
// STL Mesh
|
// STL Mesh
|
||||||
Box::new(Translate::new(
|
Box::new(crate::debug_hit::DebugHit::new(RotateY::new(
|
||||||
Scale::new(BVHTriangles::new(&stl_cube, Dielectric::new(1.5)), 250.),
|
Translate::new(
|
||||||
|
BVHTriangles::new(&stl_cube, dragon_material, 250.),
|
||||||
[0., -10., 0.],
|
[0., -10., 0.],
|
||||||
)),
|
),
|
||||||
|
180.,
|
||||||
|
))),
|
||||||
];
|
];
|
||||||
|
objects.extend(spheres);
|
||||||
|
|
||||||
let world: Box<dyn Hit> = if opt.use_accel {
|
let world: Box<dyn Hit> = if opt.use_accel {
|
||||||
Box::new(KDTree::new(objects, time_min, time_max))
|
Box::new(KDTree::new(objects, time_min, time_max))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -112,5 +112,6 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
height: opt.height,
|
height: opt.height,
|
||||||
global_illumination: true,
|
global_illumination: true,
|
||||||
env_map: Some(EnvMap::new(skybox)),
|
env_map: Some(EnvMap::new(skybox)),
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use crate::{
|
|||||||
renderer::{Opt, Scene},
|
renderer::{Opt, Scene},
|
||||||
sphere::Sphere,
|
sphere::Sphere,
|
||||||
texture::{ConstantTexture, EnvMap},
|
texture::{ConstantTexture, EnvMap},
|
||||||
|
translate::Translate,
|
||||||
vec3::Vec3,
|
vec3::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
let metal = Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2);
|
let metal = Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2);
|
||||||
let red = Lambertian::new(ConstantTexture::new(Vec3::new(1.0, 0.2, 0.2)));
|
let red = Lambertian::new(ConstantTexture::new(Vec3::new(1.0, 0.2, 0.2)));
|
||||||
|
|
||||||
let box_material = glass;
|
//let box_material = glass;
|
||||||
let _ = glass;
|
let _ = glass;
|
||||||
let _ = metal;
|
let _ = metal;
|
||||||
let _ = red;
|
let _ = red;
|
||||||
@@ -65,6 +66,11 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
10000.,
|
10000.,
|
||||||
Lambertian::new(ground_color),
|
Lambertian::new(ground_color),
|
||||||
)),
|
)),
|
||||||
|
Box::new(Sphere::new(
|
||||||
|
Vec3::new(0., 20., -40.),
|
||||||
|
10.,
|
||||||
|
Lambertian::new(ConstantTexture::new(Vec3::new(1., 0.2, 1.))),
|
||||||
|
)),
|
||||||
// Blue sphere
|
// Blue sphere
|
||||||
Box::new(Sphere::new(
|
Box::new(Sphere::new(
|
||||||
Vec3::new(0., 20., 40.),
|
Vec3::new(0., 20., 40.),
|
||||||
@@ -84,11 +90,15 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
Lambertian::new(ConstantTexture::new(Vec3::new(1.0, 0.2, 0.2))),
|
Lambertian::new(ConstantTexture::new(Vec3::new(1.0, 0.2, 0.2))),
|
||||||
)),
|
)),
|
||||||
// STL Mesh
|
// STL Mesh
|
||||||
Box::new(BVHTriangles::new(&stl_cube, box_material.clone())),
|
Box::new(Translate::new(
|
||||||
|
BVHTriangles::new(&stl_cube, glass, 1.),
|
||||||
|
[0., 10., 0.],
|
||||||
|
)),
|
||||||
|
//Box::new(BVHTriangles::new(&stl_cube, box_material.clone())),
|
||||||
Box::new(Cuboid::new(
|
Box::new(Cuboid::new(
|
||||||
[-20., 0., 0.].into(),
|
[-20., 0., 0.].into(),
|
||||||
[0., 20., 20.].into(),
|
[0., 20., 20.].into(),
|
||||||
Arc::new(box_material),
|
Arc::new(red),
|
||||||
)),
|
)),
|
||||||
];
|
];
|
||||||
let world: Box<dyn Hit> = if opt.use_accel {
|
let world: Box<dyn Hit> = if opt.use_accel {
|
||||||
|
|||||||
@@ -1,48 +1,12 @@
|
|||||||
#![allow(clippy::many_single_char_names)]
|
#![allow(clippy::many_single_char_names)]
|
||||||
use rand::{self, Rng};
|
|
||||||
|
|
||||||
use crate::{texture::Texture, vec3::Vec3};
|
use crate::{colors::generate_palette, texture::Texture, vec3::Vec3};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Mandelbrot {
|
pub struct Mandelbrot {
|
||||||
palette: Vec<Vec3>,
|
palette: Vec<Vec3>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// HSV values in [0..1]
|
|
||||||
// returns [r, g, b] values from 0 to 255
|
|
||||||
//From https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
|
|
||||||
fn hsv_to_rgb(h: f32, s: f32, v: f32) -> Vec3 {
|
|
||||||
let h_i = (h * 6.) as i32;
|
|
||||||
let f = h * 6. - h_i as f32;
|
|
||||||
let p = v * (1. - s);
|
|
||||||
let q = v * (1. - f * s);
|
|
||||||
let t = v * (1. - (1. - f) * s);
|
|
||||||
match h_i {
|
|
||||||
0 => Vec3::new(v, t, p),
|
|
||||||
1 => Vec3::new(q, v, p),
|
|
||||||
2 => Vec3::new(p, v, t),
|
|
||||||
3 => Vec3::new(p, q, v),
|
|
||||||
4 => Vec3::new(t, p, v),
|
|
||||||
5 => Vec3::new(v, p, q),
|
|
||||||
_ => panic!("Unknown H value {}", h_i),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_palette(num: usize) -> Vec<Vec3> {
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let mut random = || rng.gen();
|
|
||||||
// use golden ratio
|
|
||||||
let golden_ratio_conjugate = 0.618_034;
|
|
||||||
let mut h = random();
|
|
||||||
(0..num)
|
|
||||||
.map(|_| {
|
|
||||||
h += golden_ratio_conjugate;
|
|
||||||
h %= 1.0;
|
|
||||||
hsv_to_rgb(h, 0.99, 0.99)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Mandelbrot {
|
impl Default for Mandelbrot {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Mandelbrot {
|
Mandelbrot {
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ impl Texture for Box<dyn Texture> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Texture for [f32; 3] {
|
||||||
|
fn value(&self, u: f32, v: f32, p: Vec3) -> Vec3 {
|
||||||
|
(*self).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
1
rtiow/renderer/stls/dragon.stl
Symbolic link
1
rtiow/renderer/stls/dragon.stl
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
stanford_dragon.stl
|
||||||
@@ -3,12 +3,15 @@ name = "tracer"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Bill Thiede <git@xinu.tv>"]
|
authors = ["Bill Thiede <git@xinu.tv>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
default-run = "tracer"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.69"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
renderer = { path = "../renderer" }
|
renderer = { path = "../renderer" }
|
||||||
stderrlog = "0.4.3"
|
stderrlog = "0.4.3"
|
||||||
structopt = "0.2.18"
|
structopt = "0.2.18"
|
||||||
strum = "0.24.1"
|
strum = "0.24.1"
|
||||||
|
toml = "0.7.2"
|
||||||
|
|||||||
74
rtiow/tracer/configs/test.toml
Normal file
74
rtiow/tracer/configs/test.toml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
[scene]
|
||||||
|
width = 768
|
||||||
|
height = 512
|
||||||
|
subsamples = 100
|
||||||
|
|
||||||
|
[camera]
|
||||||
|
lookfrom = [0.0, 50.0, 100.0]
|
||||||
|
lookat = [0.0, 10.0, 0.0]
|
||||||
|
fov = 45
|
||||||
|
aperture = 0.0
|
||||||
|
focus_dist = 10.0
|
||||||
|
time_min = 0.0
|
||||||
|
time_max = 1.0
|
||||||
|
|
||||||
|
[[materials]]
|
||||||
|
name = "light1"
|
||||||
|
type = "isotropic"
|
||||||
|
texture = [20, 10, 10]
|
||||||
|
[[materials]]
|
||||||
|
name = "yellow"
|
||||||
|
type = "isotropic"
|
||||||
|
texture = [1, 1, 0]
|
||||||
|
[[materials]]
|
||||||
|
name = "magenta"
|
||||||
|
type = "lambertian"
|
||||||
|
texture = [1, 0, 1]
|
||||||
|
[[materials]]
|
||||||
|
name = "green"
|
||||||
|
type = "diffuse_light"
|
||||||
|
texture = [0, 1, 0]
|
||||||
|
[[materials]]
|
||||||
|
name = "metal"
|
||||||
|
type = "metal"
|
||||||
|
albedo = [1, 1, 1]
|
||||||
|
fuzzy = 0
|
||||||
|
[[materials]]
|
||||||
|
name = "glass"
|
||||||
|
type = "dielectric"
|
||||||
|
ref_idx = 1.5
|
||||||
|
|
||||||
|
|
||||||
|
[[hitables]]
|
||||||
|
type = "sphere"
|
||||||
|
center = [-30.0, 0.0, 0.0]
|
||||||
|
radius = 10
|
||||||
|
material_name = "yellow"
|
||||||
|
[[hitables]]
|
||||||
|
type = "sphere"
|
||||||
|
center = [30.0, 0.0, 0.0]
|
||||||
|
radius = 10
|
||||||
|
material_name = "green"
|
||||||
|
[[hitables]]
|
||||||
|
type = "sphere"
|
||||||
|
center = [0.0, -10.0, 0.0]
|
||||||
|
radius = 10
|
||||||
|
material_name = "metal"
|
||||||
|
[[hitables]]
|
||||||
|
type = "sphere"
|
||||||
|
center = [0.0, 0.0, -30.0]
|
||||||
|
radius = 10
|
||||||
|
material_name = "magenta"
|
||||||
|
[[hitables]]
|
||||||
|
type = "stl"
|
||||||
|
path = "/net/nasx.h.xinu.tv/x/3dprint/stl/stanford_dragon.stl"
|
||||||
|
scale = 200
|
||||||
|
material_name = "glass"
|
||||||
|
#[[hitables]]
|
||||||
|
#type = "sphere"
|
||||||
|
#center = [0.0, 50.0, -100.0]
|
||||||
|
#radius = 10
|
||||||
|
#material_name = "light1"
|
||||||
|
|
||||||
|
[envmap]
|
||||||
|
path = "/home/wathiede/src/xinu.tv/raytracers/rtiow/renderer/images/52681723945_e1d94d3df9_6k.jpg"
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
#![warn(unused_extern_crates)]
|
#![warn(unused_extern_crates)]
|
||||||
use std::{fs, time::Instant};
|
use std::{fs, time::Instant};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
#[cfg(feature = "profile")]
|
#[cfg(feature = "profile")]
|
||||||
use cpuprofiler::PROFILER;
|
use cpuprofiler::PROFILER;
|
||||||
use log::info;
|
use log::info;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use renderer::renderer::{render, Model, Opt};
|
use renderer::{
|
||||||
|
parser::Config,
|
||||||
|
renderer::{render, Model, Opt},
|
||||||
|
};
|
||||||
use strum::VariantNames;
|
use strum::VariantNames;
|
||||||
|
|
||||||
#[cfg(not(feature = "profile"))]
|
#[cfg(not(feature = "profile"))]
|
||||||
@@ -35,7 +39,7 @@ impl MockProfiler {
|
|||||||
#[cfg(not(feature = "profile"))]
|
#[cfg(not(feature = "profile"))]
|
||||||
static PROFILER: MockProfiler = MockProfiler {};
|
static PROFILER: MockProfiler = MockProfiler {};
|
||||||
|
|
||||||
fn main() -> Result<(), std::io::Error> {
|
fn main() -> Result<()> {
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
stderrlog::new()
|
stderrlog::new()
|
||||||
.verbosity(3)
|
.verbosity(3)
|
||||||
@@ -43,12 +47,28 @@ fn main() -> Result<(), std::io::Error> {
|
|||||||
.init()
|
.init()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let opt = Opt::from_args();
|
let opt = Opt::from_args();
|
||||||
if opt.model.is_none() {
|
if opt.model.is_none() && opt.config.is_none() {
|
||||||
eprintln!("--model should be one of {:?}", Model::VARIANTS);
|
eprintln!(
|
||||||
|
"--config <path> or --model should be one of {:?}",
|
||||||
|
Model::VARIANTS
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if opt.model.is_some() && opt.config.is_some() {
|
||||||
|
eprintln!("only specify one of --config or --model");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
info!("{:#?}", opt);
|
info!("{:#?}", opt);
|
||||||
let scene = opt.model.as_ref().unwrap().scene(&opt);
|
let scene = match (&opt.model, &opt.config) {
|
||||||
|
(Some(model), None) => model.scene(&opt),
|
||||||
|
(None, Some(config)) => {
|
||||||
|
let s = std::fs::read_to_string(config)?;
|
||||||
|
let cfg: Config = toml::from_str(&s)?;
|
||||||
|
println!("{:#?}", cfg);
|
||||||
|
cfg.try_into()?
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
fs::create_dir_all(&opt.output)?;
|
fs::create_dir_all(&opt.output)?;
|
||||||
if opt.pprof.is_some() && !cfg!(feature = "profile") {
|
if opt.pprof.is_some() && !cfg!(feature = "profile") {
|
||||||
panic!("profiling disabled at compile time, but -pprof specified");
|
panic!("profiling disabled at compile time, but -pprof specified");
|
||||||
@@ -60,7 +80,7 @@ fn main() -> Result<(), std::io::Error> {
|
|||||||
.start(pprof_path.to_str().unwrap().as_bytes())
|
.start(pprof_path.to_str().unwrap().as_bytes())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
let res = render(scene, &opt.output);
|
let res = render(scene, &opt.output, &opt.tev_addr);
|
||||||
if let Some(pprof_path) = &opt.pprof {
|
if let Some(pprof_path) = &opt.pprof {
|
||||||
info!("Saving pprof to {}", pprof_path.to_string_lossy());
|
info!("Saving pprof to {}", pprof_path.to_string_lossy());
|
||||||
PROFILER.lock().unwrap().stop().unwrap();
|
PROFILER.lock().unwrap().stop().unwrap();
|
||||||
@@ -68,5 +88,5 @@ fn main() -> Result<(), std::io::Error> {
|
|||||||
|
|
||||||
let time_diff = Instant::now() - start_time;
|
let time_diff = Instant::now() - start_time;
|
||||||
info!("Total runtime {} seconds", time_diff.as_secs_f32());
|
info!("Total runtime {} seconds", time_diff.as_secs_f32());
|
||||||
res
|
Ok(res?)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user