diff --git a/Cargo.lock b/Cargo.lock index 1150ab5..a879c7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1240,6 +1240,7 @@ dependencies = [ "ops", "pretty_env_logger", "rand 0.10.1", + "rayon", "serde", "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index 96da94d..4e096d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,11 +5,12 @@ edition = "2024" [dependencies] dotenv = "0.15.0" -image = "0.25.10" +image = {version = "0.25.10", features = ["png"]} is_close = "0.1.3" log = "0.4.29" ops = "0.6.0" pretty_env_logger = "0.5.0" rand = "0.10.1" +rayon = "1.12.0" serde = {version = "1.0.228", features = ["derive"]} serde_json = "1.0.149" diff --git a/scenes/failsMatBounds.json b/scenes/failsMatBounds.json deleted file mode 100644 index b7d0a37..0000000 --- a/scenes/failsMatBounds.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "camera": { - "image_width": 1920, - "image_height": 1080, - "anti_alias_rate": 1, - "max_depth": 10, - "fov": 90.0, - "look_from": { "x": -10, "y": 5, "z": 10 }, - "look_at": { "x": 0.0, "y": 0.0, "z": -1.0 }, - "vup": { "x": 0.0, "y": 1.0, "z": 0.0 } - }, - "materials": [ - { "type": "metal", "albedo": { "x": 0.2, "y": 0.4, "z": 0.8 }, "prob": 1.0, "fuzz": 0.1 } - - ], - "objects": [ - { "type": "sphere", "center": { "x": 0, "y": 0.7, "z": -0.4 }, "radius": 0.2, "material": 1} - ] -} - - - diff --git a/scenes/scene.json b/scenes/scene.json index 8928587..39b0a36 100644 --- a/scenes/scene.json +++ b/scenes/scene.json @@ -1,12 +1,13 @@ { + "filename": "output.png", + "image_width": 1920, + "image_height": 1080, + "max_depth": 50, "camera": { - "image_width": 1920, - "image_height": 1080, - "anti_alias_rate": 2, - "max_depth": 50, - "fov": 90.0, - "look_from": [15, 4, 15], - "look_at": [0.0, 0.0, 0.0], + "anti_alias_rate": 23, + "fov": 70.0, + "look_from": [-10, 4, 15], + "look_at": [-11.0, 0.0, 0.0], "vup": [0.0, 1.0, 0.0], "defocus_angle": 0, "focus_dist": 15.68 @@ -28,7 +29,8 @@ { "type": "quad", "p1": [-20, -1, 20], "p2": [-20, -1, -20], "p3": [-20, 20, -20], "p4": [-20, 20, 20], "material": 0}, { "type": "quad", "p1": [-20, -1, 20], "p2": [20, -1, 20], "p3": [20, -1, -20], "p4": [-20, -1, -20], "material": 0}, { "type": "quad", "p1": [20, -1, 20], "p2": [20, -1, -20], "p3": [20, 20, -20], "p4": [20, 20, 20], "material": 0}, - { "type": "cube", "p1": [8, 0, 2], "p2": [12, 0, 2], "p3": [12, 4, 2], "p4": [8, 4, 2], "p5": [8, 0, -2], "p6": [12, 0, -2], "p7": [12, 4, -2], "p8": [8, 4, -2], "material": 3} + { "type": "cube", "p1": [8, 0, 2], "p2": [12, 0, 2], "p3": [12, 4, 2], "p4": [8, 4, 2], "p5": [8, 0, -2], "p6": [12, 0, -2], "p7": [12, 4, -2], "p8": [8, 4, -2], "material": 3}, + { "type": "circle", "center": [-9, 3, 0], "radius": 3, "normal": [0, 1, 0.5], "material": {"type": "metal", "albedo": [0.9, 0.9, 0.9], "prob": 1.0, "fuzz": 0.3}} ] } diff --git a/src/camera.rs b/src/camera.rs index fdab07f..f965798 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,34 +1,23 @@ -use std::{f32::consts::PI, sync::Arc}; +use std::f32::consts::PI; -use log::info; - -use crate::{ - objects::{hit::Hit, traits::Hittable}, - ray::Ray, - vec3::{Colour, Vec3}, -}; +use crate::{ray::Ray, vec3::Vec3}; #[derive(Debug)] pub struct Camera { - // output - image_width: u32, - image_height: u32, - // raytracing - anti_alias_rate: u32, - max_depth: u32, - pixel00_loc: Vec3, - pixel_delta_u: Vec3, - pixel_delta_v: Vec3, + pub anti_alias_rate: u32, + pub pixel00_loc: Vec3, + pub pixel_delta_u: Vec3, + pub pixel_delta_v: Vec3, // camera dirty: bool, - fov: f32, - look_from: Vec3, - look_at: Vec3, + pub fov: f32, + pub look_from: Vec3, + pub look_at: Vec3, vup: Vec3, - defocus_angle: f32, - focus_dist: f32, + pub defocus_angle: f32, + pub focus_dist: f32, // camera helpers u: Vec3, @@ -44,12 +33,9 @@ fn deg_to_rad(deg: f32) -> f32 { } impl Camera { - pub fn new(image_width: u32, image_height: u32) -> Self { + pub fn new() -> Self { Self { - image_width, - image_height, anti_alias_rate: 1, - max_depth: 10, dirty: true, fov: 60., pixel00_loc: Vec3::default(), @@ -69,10 +55,7 @@ impl Camera { } pub fn new_full( - image_width: u32, - image_height: u32, anti_alias_rate: u32, - max_depth: u32, fov: f32, look_from: Vec3, look_at: Vec3, @@ -81,10 +64,7 @@ impl Camera { focus_dist: f32, ) -> Self { Self { - image_width, - image_height, anti_alias_rate, - max_depth, pixel00_loc: Vec3::default(), pixel_delta_u: Vec3::default(), pixel_delta_v: Vec3::default(), @@ -103,7 +83,11 @@ impl Camera { } } - fn init(&mut self) { + pub fn init(&mut self, w: f32, hi: f32) { + if !self.dirty { + return; + } + // camera let theta = deg_to_rad(self.fov); let h = (theta / 2.).tan(); @@ -113,13 +97,13 @@ impl Camera { // viewport let viewport_height = 2. * h * self.focus_dist; - let viewport_width = viewport_height * (self.image_width as f32 / self.image_height as f32); + let viewport_width = viewport_height * (w / hi); let viewport_u = viewport_width * self.u; let viewport_v = viewport_height * -self.v; // variables - self.pixel_delta_u = viewport_u / self.image_width as f32; - self.pixel_delta_v = viewport_v / self.image_height as f32; + self.pixel_delta_u = viewport_u / w; + self.pixel_delta_v = viewport_v / hi; self.pixel00_loc = self.look_from - (self.focus_dist * self.w) - viewport_u / 2. - viewport_v / 2.; self.dirty = false; @@ -131,6 +115,19 @@ impl Camera { } } + pub fn get_ray(&self, pixel_tl: Vec3, x: u32, y: u32) -> Ray { + let pixel_loc = pixel_tl + + (x * self.pixel_delta_u / (self.anti_alias_rate + 1) as f32) + + (y * self.pixel_delta_v / (self.anti_alias_rate + 1) as f32); + let ray_orig = if self.defocus_angle > 0. { + self.defocus_disk_sample() + } else { + self.look_from + }; + let ray_dir = pixel_loc - ray_orig; + Ray::new(ray_orig, ray_dir) + } + pub fn set_fov(&mut self, fov: f32) { if self.fov != fov { self.fov = fov; @@ -138,13 +135,6 @@ impl Camera { } } - pub fn set_max_depth(&mut self, depth: u32) { - if self.max_depth != depth { - self.max_depth = depth; - self.dirty = true; - } - } - pub fn set_anti_alias_rate(&mut self, rate: u32) { if self.anti_alias_rate != rate { self.anti_alias_rate = rate; @@ -181,72 +171,8 @@ impl Camera { } } - fn defocus_disk_sample(&self) -> Vec3 { + pub fn defocus_disk_sample(&self) -> Vec3 { let p = Vec3::random_in_unit_disk(); self.look_from + (p.x() * self.defocus_disk_u) + (p.y() * self.defocus_disk_v) } - - pub fn render(&mut self, hittables: &Vec>) { - if self.dirty { - self.init() - } - - let mut imgbuf = image::ImageBuffer::new(self.image_width, self.image_height); - - // render - for j in 0..self.image_height { - info!("{}\tScanlines remaining.", (self.image_height - j)); - for i in 0..self.image_width { - let pixel_tl = - self.pixel00_loc + (i * self.pixel_delta_u) + (j * self.pixel_delta_v); - - let mut pixel_colour = Colour::default(); - for y in 1..(self.anti_alias_rate + 1) { - for x in 1..(self.anti_alias_rate + 1) { - let pixel_loc = pixel_tl - + (x * self.pixel_delta_u / (self.anti_alias_rate + 1) as f32) - + (y * self.pixel_delta_v / (self.anti_alias_rate + 1) as f32); - let ray_orig = if self.defocus_angle > 0. { - self.defocus_disk_sample() - } else { - self.look_from - }; - let ray_dir = pixel_loc - ray_orig; - let r = Ray::new(ray_orig, ray_dir); - pixel_colour += self.ray_colour(hittables, &r, self.max_depth); - } - } - - let pixel = imgbuf.get_pixel_mut(i, j); - *pixel = - (pixel_colour / (self.anti_alias_rate * self.anti_alias_rate) as f32).output(); - } - } - - info!("Writing image file..."); - imgbuf.save("output.png").unwrap(); - } - - fn ray_colour(&self, hittables: &Vec>, r: &Ray, depth: u32) -> Colour { - if depth == 0 { - return Colour::default(); - } - - let closest = Hit::hit_list(hittables, r); - if let Some(hit) = closest { - if let Some((scattered, att)) = hit.mat().scatter(&hit, r) { - if let Some(ray) = scattered { - return att * self.ray_colour(hittables, &ray, depth - 1); - } - return att; - } - return Colour::default(); - } - - // background - let unit_dir = r.dir().get_unit(); - let a = 0.5 * (unit_dir.y() + 1.); - - (1.0 - a) * Colour::new(1., 1., 1.) + a * Colour::new(0.5, 0.7, 1.0) - } } diff --git a/src/main.rs b/src/main.rs index dc739fe..f11924b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod camera; mod objects; mod ray; +mod raytracer; mod scenes; mod vec3; @@ -15,6 +16,7 @@ use crate::objects::materials::lambertian::{Lambertian, Metal}; use crate::objects::sphere::Sphere; use crate::objects::traits::Hittable; use crate::ray::Ray; +use crate::raytracer::render; use crate::scenes::scene::Scene; use crate::vec3::Vec3; use dotenv::dotenv; @@ -29,7 +31,7 @@ fn main() { let json_file = "./scenes/scene.json"; let json_str = fs::read_to_string(json_file).expect("Reading specified scene file failed!"); let mut scene: Scene = serde_json::from_str(&json_str).unwrap(); - scene.render(); + render(&mut scene); return; // random spheres code; thought: make this available as cli flag? @@ -108,14 +110,14 @@ fn main() { Arc::new(Metal::rgb(0.7, 0.6, 0.5, 1., 0.)), ))); - let mut c = Camera::new(1920, 1080); + let mut c = Camera::new(); c.set_fov(20.); c.set_anti_alias_rate(23); - c.set_max_depth(50); c.set_vup(Vec3::new(0., 1., 0.)); c.set_look_from(Vec3::new(13., 2., 3.)); c.set_look_at(Vec3::new(0., 0., 0.)); c.add_defocus_blur(0.6, 10.); - c.render(&world); + let mut s = Scene::new(c, world, "output.png".to_string(), 1920, 1080, 50); + render(&mut s); } diff --git a/src/objects.rs b/src/objects.rs index 40f7466..b90ac27 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -1,7 +1,10 @@ +pub mod circle; +pub mod cube; +pub mod cylinder; pub mod hit; pub mod materials; -pub mod sphere; -pub mod triangle; +pub mod mesh; pub mod quad; -pub mod cube; +pub mod sphere; pub mod traits; +pub mod triangle; diff --git a/src/objects/circle.rs b/src/objects/circle.rs new file mode 100644 index 0000000..ee545fc --- /dev/null +++ b/src/objects/circle.rs @@ -0,0 +1,52 @@ +use std::sync::Arc; + +use crate::{ + objects::{materials::traits::Material, traits::Hittable}, + ray::Ray, + vec3::Vec3, +}; + +use super::hit::Hit; + +#[derive(Debug)] +pub struct Circle { + radius: f32, + center: Vec3, + normal: Vec3, + material: Arc, +} + +impl Circle { + pub fn new(radius: f32, center: Vec3, normal: Vec3, material: Arc) -> Self { + Self { + radius, + center, + normal: normal.get_unit(), + material, + } + } +} + +impl Hittable for Circle { + fn hit(&self, r: &Ray) -> Option { + // check if ray parallel to plane + let dot = self.normal.dot(r.dir()); + if dot == 0.0 { + return None; + } + + // hitpoint on plane + let t = self.normal.dot(&(self.center - r.origin())) / dot; + + // hits behind camera + if t < 0. { + return None; + }; + + let p = r.at(t); + if (p - self.center).length() < self.radius { + return Some(Hit::new(t, p, self.normal, self.material.clone(), self.normal.dot(&r.dir()) < 0.)); + } + None + } +} diff --git a/src/objects/cube.rs b/src/objects/cube.rs index a2adb70..04e3e5b 100644 --- a/src/objects/cube.rs +++ b/src/objects/cube.rs @@ -41,9 +41,4 @@ impl Hittable for Cube { fn hit(&self, r: &Ray) -> Option { Hit::hit_list(&self.faces, r) } - - fn normal_at(&self, _p: &Vec3) -> Vec3 { - // TODO: normal calc for cube - todo!() - } } diff --git a/src/objects/cylinder.rs b/src/objects/cylinder.rs new file mode 100644 index 0000000..78d052d --- /dev/null +++ b/src/objects/cylinder.rs @@ -0,0 +1,26 @@ +use crate::{objects::traits::Hittable, vec3::Vec3}; + +#[derive(Debug)] +pub struct Cylinder { + radius: f32, + length: f32, + up: Vec3, + bottom_center: Vec3, +} + +impl Cylinder { + pub fn new(radius: f32, length: f32, up: Vec3, bottom_center: Vec3) -> Self { + Self { + radius, + length, + up, + bottom_center, + } + } +} + +impl Hittable for Cylinder { + fn hit(&self, r: &crate::ray::Ray) -> Option { + todo!() + } +} diff --git a/src/objects/materials/dielectric.rs b/src/objects/materials/dielectric.rs index ff6c1d7..bb1dad3 100644 --- a/src/objects/materials/dielectric.rs +++ b/src/objects/materials/dielectric.rs @@ -16,9 +16,7 @@ pub struct Dielectric { impl Dielectric { pub fn new(refraction_index: f32) -> Self { - Self { - refraction_index, - } + Self { refraction_index } } fn reflectance(cos: f32, refraction_index: f32) -> f32 { @@ -46,7 +44,8 @@ impl Material for Dielectric { let cannot_refract = ri * sin_theta > 1.; let mut rng = rand::rng(); - let dir = if cannot_refract || Dielectric::reflectance(cos_theta, ri) > rng.random::() { + let dir = if cannot_refract || Dielectric::reflectance(cos_theta, ri) > rng.random::() + { unit.reflect(hit.n()) } else { unit.refract(hit.n(), ri) diff --git a/src/objects/materials/lambertian.rs b/src/objects/materials/lambertian.rs index 8420bc1..bfbba05 100644 --- a/src/objects/materials/lambertian.rs +++ b/src/objects/materials/lambertian.rs @@ -15,10 +15,7 @@ pub struct Lambertian { impl Lambertian { pub fn new(albedo: Colour, prob: f32) -> Self { - Self { - albedo, - prob, - } + Self { albedo, prob } } pub fn rgb(r: f32, g: f32, b: f32, prob: f32) -> Self { @@ -53,11 +50,7 @@ pub struct Metal { impl Metal { pub fn new(albedo: Colour, prob: f32, fuzz: f32) -> Self { - Self { - albedo, - prob, - fuzz, - } + Self { albedo, prob, fuzz } } pub fn rgb(r: f32, g: f32, b: f32, prob: f32, fuzz: f32) -> Self { diff --git a/src/objects/mesh.rs b/src/objects/mesh.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/quad.rs b/src/objects/quad.rs index db57ee5..5d3a3ea 100644 --- a/src/objects/quad.rs +++ b/src/objects/quad.rs @@ -69,9 +69,4 @@ impl Hittable for Quad { r, ) } - - fn normal_at(&self, _p: &Vec3) -> Vec3 { - // FIXME: might cause ownership issues - self.normal - } } diff --git a/src/objects/sphere.rs b/src/objects/sphere.rs index be6fb80..943e54e 100644 --- a/src/objects/sphere.rs +++ b/src/objects/sphere.rs @@ -2,12 +2,11 @@ use core::f32::math::sqrt; use std::fmt::{self, Debug}; use std::sync::Arc; - +use crate::Vec3; use crate::objects::hit::Hit; use crate::objects::materials::traits::Material; use crate::objects::traits::Hittable; use crate::ray::Ray; -use crate::Vec3; pub struct Sphere { center: Vec3, @@ -63,14 +62,10 @@ impl Hittable for Sphere { Some(Hit::new( t, p, - self.normal_at(&p), + (p - self.center).get_unit(), self.material.clone(), out_n.dot(r.dir()) < 0., )) } } - - fn normal_at(&self, p: &Vec3) -> Vec3 { - (*p - self.center).get_unit() - } } diff --git a/src/objects/traits.rs b/src/objects/traits.rs index bfc1c4e..2a33df5 100644 --- a/src/objects/traits.rs +++ b/src/objects/traits.rs @@ -1,10 +1,8 @@ use std::fmt::Debug; -use crate::objects::hit::Hit; use crate::Ray; -use crate::Vec3; +use crate::objects::hit::Hit; pub trait Hittable: Debug + Send + Sync { fn hit(&self, r: &Ray) -> Option; - fn normal_at(&self, p: &Vec3) -> Vec3; } diff --git a/src/objects/triangle.rs b/src/objects/triangle.rs index 051d212..c0b1f64 100644 --- a/src/objects/triangle.rs +++ b/src/objects/triangle.rs @@ -54,7 +54,7 @@ impl Triangle { let diff = (a4 - a1 - a2 - a3).abs(); if diff < 0.001 { - Some(Hit::new(t, p, normal, material, normal.dot(&-r.dir()) > 0.)) + Some(Hit::new(t, p, normal, material, normal.dot(&r.dir()) < 0.)) } else { None } @@ -83,9 +83,4 @@ impl Hittable for Triangle { r, ) } - - fn normal_at(&self, _p: &Vec3) -> Vec3 { - // FIXME: might cause ownership issues - self.normal - } } diff --git a/src/ray.rs b/src/ray.rs index 6f651d4..bf65407 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -7,10 +7,7 @@ pub struct Ray { impl Ray { pub fn new(origin: Vec3, dir: Vec3) -> Self { - Self { - origin, - dir, - } + Self { origin, dir } } pub fn at(&self, t: f32) -> Vec3 { diff --git a/src/raytracer.rs b/src/raytracer.rs new file mode 100644 index 0000000..b2b4167 --- /dev/null +++ b/src/raytracer.rs @@ -0,0 +1,96 @@ +use image::{ExtendedColorType, ImageEncoder, codecs::png::PngEncoder}; +use log::{error, info}; +use rayon::iter::IntoParallelIterator; +use rayon::prelude::*; +use std::{fs::File, sync::Arc}; + +use crate::{ + objects::{hit::Hit, traits::Hittable}, + ray::Ray, + scenes::scene::Scene, + vec3::Colour, +}; + +pub fn write_image(filename: &str, pixels: &[u8], width: u32, height: u32) { + match File::create(filename) { + Ok(out) => { + let enc = PngEncoder::new(out); + if let Err(e) = enc.write_image(pixels, width, height, ExtendedColorType::Rgb8) { + error!("Failed writing image: {}", e); + } + } + Err(e) => error!("Failed to create image file: {} ", e), + } +} + +pub fn render(scene: &mut Scene) { + let camera = &mut scene.camera; + camera.init(scene.image_width as f32, scene.image_height as f32); + + // TODO: currently splits per vertical line, but could be more granular (per chunk) + let mut pixels = vec![0 as u8; (scene.image_width * scene.image_height * 3) as usize]; + let scanlines: Vec<(usize, &mut [u8])> = pixels + .chunks_mut(scene.image_width as usize * 3) + .enumerate() + .collect(); + + scanlines.into_par_iter().for_each(|(i, chunk)| { + render_chunk(chunk, scene, i as u32); + }); + + info!("Writing image file..."); + write_image( + &scene.filename, + &pixels, + scene.image_width, + scene.image_height, + ); +} + +pub fn render_chunk(chunk: &mut [u8], scene: &Scene, y: u32) { + let camera = &scene.camera; + + for i in 0..scene.image_width { + let pixel_tl = camera.pixel00_loc + (i * camera.pixel_delta_u) + (y * camera.pixel_delta_v); + // THOUGHT: the above seems more efficient (esp for larger aa rates) but can test for a + // get_ray_no_tl() function and compare performance. + + let mut pixel_colour = Colour::default(); + for y in 1..(camera.anti_alias_rate + 1) { + for x in 1..(camera.anti_alias_rate + 1) { + let r = camera.get_ray(pixel_tl, x, y); + pixel_colour += ray_colour(&scene.objects, &r, scene.max_depth); + } + } + + let (r, g, b) = + (pixel_colour / (camera.anti_alias_rate * camera.anti_alias_rate) as f32).output(); + chunk[(i * 3) as usize] = r; + chunk[(i * 3) as usize + 1] = g; + chunk[(i * 3) as usize + 2] = b; + } + info!("Line {} finished.", y); +} + +pub fn ray_colour(hittables: &Vec>, r: &Ray, depth: u32) -> Colour { + if depth == 0 { + return Colour::default(); + } + + let closest = Hit::hit_list(hittables, r); + if let Some(hit) = closest { + if let Some((scattered, att)) = hit.mat().scatter(&hit, r) { + if let Some(ray) = scattered { + return att * ray_colour(hittables, &ray, depth - 1); + } + return att; + } + return Colour::default(); + } + + // background + let unit_dir = r.dir().get_unit(); + let a = 0.5 * (unit_dir.y() + 1.); + + (1.0 - a) * Colour::new(1., 1., 1.) + a * Colour::new(0.5, 0.7, 1.0) +} diff --git a/src/scenes/hittable_def.rs b/src/scenes/hittable_def.rs index ff547c1..dd6f3af 100644 --- a/src/scenes/hittable_def.rs +++ b/src/scenes/hittable_def.rs @@ -5,8 +5,8 @@ use serde::Deserialize; use crate::{ objects::{ - cube::Cube, materials::traits::Material, quad::Quad, sphere::Sphere, traits::Hittable, - triangle::Triangle, + circle::Circle, cube::Cube, materials::traits::Material, quad::Quad, sphere::Sphere, + traits::Hittable, triangle::Triangle, }, scenes::material_def::MaterialDef, vec3::Vec3, @@ -19,11 +19,11 @@ pub(crate) enum HittableDef { Triangle(RawTriangle), Quad(RawQuad), Cube(RawCube), + Circle(RawCircle), } impl HittableDef { pub(crate) fn into_arc(self, materials: &Vec>) -> Option> { - // THOUGHT: i think this can be done better; in the map/filter call in scnene.deserialize? match self { HittableDef::Sphere(s) => { let material = s.material.into_arc(materials); @@ -55,6 +55,13 @@ impl HittableDef { None => None, } } + HittableDef::Circle(c) => { + let material = c.material.into_arc(materials); + match material { + Some(m) => Some(Arc::new(Circle::new(c.radius, c.center, c.normal, m))), + None => None, + } + } } } } @@ -96,6 +103,14 @@ pub(crate) struct RawCube { pub material: RawMaterial, } +#[derive(Deserialize)] +pub(crate) struct RawCircle { + radius: f32, + center: Vec3, + normal: Vec3, + material: RawMaterial, +} + #[derive(Deserialize)] #[serde(untagged)] pub(crate) enum RawMaterial { diff --git a/src/scenes/raw_camera.rs b/src/scenes/raw_camera.rs index c8b2e98..c8a4af9 100644 --- a/src/scenes/raw_camera.rs +++ b/src/scenes/raw_camera.rs @@ -4,15 +4,9 @@ use crate::{camera::Camera, vec3::Vec3}; #[derive(Deserialize)] pub struct RawCamera { - // output - image_width: u32, - image_height: u32, - // raytracing #[serde(default)] anti_alias_rate: u32, - #[serde(default)] - max_depth: u32, // camera #[serde(default)] @@ -32,10 +26,7 @@ pub struct RawCamera { impl Default for RawCamera { fn default() -> Self { Self { - image_width: 400, - image_height: 300, anti_alias_rate: 1, - max_depth: 10, fov: 70., look_from: Vec3::new(0., 0., 0.), look_at: Vec3::new(0., 0., -1.), @@ -49,10 +40,7 @@ impl Default for RawCamera { impl From for Camera { fn from(raw: RawCamera) -> Self { Camera::new_full( - raw.image_width, - raw.image_height, raw.anti_alias_rate, - raw.max_depth, raw.fov, raw.look_from, raw.look_at, diff --git a/src/scenes/scene.rs b/src/scenes/scene.rs index 458a29c..527966a 100644 --- a/src/scenes/scene.rs +++ b/src/scenes/scene.rs @@ -10,14 +10,33 @@ use crate::{ #[derive(Debug)] pub struct Scene { - pub camera: Camera, - pub _materials: Vec>, + pub camera: Camera, // FIXME: should not be public pub objects: Vec>, + // image + pub filename: String, + pub image_width: u32, + pub image_height: u32, + // raytracing // TODO: think about organisation of these vars, also in Camera + pub max_depth: u32, } impl Scene { - pub fn render(&mut self) { - self.camera.render(&self.objects); + pub fn new( + camera: Camera, + objects: Vec>, + filename: String, + image_width: u32, + image_height: u32, + max_depth: u32, + ) -> Self { + Self { + camera, + objects, + filename, + image_width, + image_height, + max_depth, + } } } @@ -26,6 +45,10 @@ struct SceneDef { pub camera: RawCamera, pub materials: Vec, pub objects: Vec, + pub filename: String, + pub image_width: u32, + pub image_height: u32, + pub max_depth: u32, } impl<'de> Deserialize<'de> for Scene { @@ -46,8 +69,11 @@ impl<'de> Deserialize<'de> for Scene { .collect(); Ok(Self { camera: Camera::from(conc.camera), - _materials: mats, objects: objs, + filename: conc.filename, + image_width: conc.image_width, + image_height: conc.image_height, + max_depth: conc.max_depth, }) } } diff --git a/src/vec3.rs b/src/vec3.rs index 582988a..8dcba26 100644 --- a/src/vec3.rs +++ b/src/vec3.rs @@ -122,7 +122,7 @@ impl Vec3 { r_out_perp + r_out_parr } - pub fn output(self) -> image::Rgb { + pub fn output(self) -> (u8, u8, u8) { // gamma correction let r = if self.x > 0. { sqrt(self.x).clamp(0., 1.) @@ -144,7 +144,7 @@ impl Vec3 { let ig = (255.599 * g) as u8; let ib = (255.599 * b) as u8; - image::Rgb([ir, ig, ib]) + (ir, ig, ib) } pub fn clone(&self) -> Self {