Compare commits
0
Commits
main
..
549707fbb3
No files matched your search
+1
-2
@@ -1,4 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
*.png
|
output.png
|
||||||
.env
|
.env
|
||||||
!textures/*.png
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"filename": "testing.png",
|
|
||||||
"image_width": 800,
|
|
||||||
"image_height": 600,
|
|
||||||
"max_depth": 50,
|
|
||||||
"camera": {
|
|
||||||
"anti_alias_rate": 16,
|
|
||||||
"fov": 70.0,
|
|
||||||
"look_from": [-10, 1, 15],
|
|
||||||
"look_at": [-11.0, 7.0, 0.0],
|
|
||||||
"vup": [0.0, 1.0, 0.0],
|
|
||||||
"defocus_angle": 0,
|
|
||||||
"focus_dist": 15.68
|
|
||||||
},
|
|
||||||
"materials": [
|
|
||||||
{ "type": "lambertian", "colour": [0.2, 0.2, 0.2], "prob": 0.8 },
|
|
||||||
{ "type": "lambertian", "colour": [0.9, 0.9, 0.0], "prob": 1.0, "fuzz": 0.1 },
|
|
||||||
{ "type": "dielectric", "refraction_index": 1.5},
|
|
||||||
{ "type": "normal"}
|
|
||||||
],
|
|
||||||
"objects": [
|
|
||||||
{ "type": "sphere", "center": [0.0, 0.0, -1.2], "radius": 0.5, "material": { "type": "metal", "colour": [0.7, 0.4, 0.2], "prob": 1.0, "fuzz": 0.1 }},
|
|
||||||
{ "type": "sphere", "center": [-1, 0, -1], "radius": 0.4, "material": 3},
|
|
||||||
{ "type": "sphere", "center": [1, 0, -1], "radius": 0.5, "material": { "type": "metal", "colour": [0.8, 0.6, 0.2], "prob": 1.0, "fuzz": 1.0 }},
|
|
||||||
{ "type": "triangle", "p1": [-4, 0, -4], "p2": [0, 0, -4], "p3": [-2, 2, -4], "material": 1},
|
|
||||||
{ "type": "triangle", "p1": [0, 0, -4], "p2": [4, 0, -4], "p3": [2, 2, -4], "material": 1},
|
|
||||||
{ "type": "triangle", "p1": [-2, 2, -4], "p2": [2, 2, -4], "p3": [0, 4, -4], "material": 3},
|
|
||||||
{ "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, 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": "circle", "center": [-9, 3, 0], "radius": 3, "normal": [0, 1, 0.5], "material": {"type": "metal", "colour": [0.9, 0.9, 0.9], "prob": 1.0, "fuzz": 0.3}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+5
-5
@@ -13,13 +13,13 @@
|
|||||||
"focus_dist": 15.68
|
"focus_dist": 15.68
|
||||||
},
|
},
|
||||||
"materials": [
|
"materials": [
|
||||||
{ "type": "metal", "colour": [0.2, 0.4, 0.8 ], "prob": 1.0, "fuzz": 0.1},
|
{ "type": "metal", "albedo": [0.2, 0.4, 0.8 ], "prob": 1.0, "fuzz": 0.1},
|
||||||
{ "type": "metal", "colour": [0.7, 0.4, 0.2 ], "prob": 1.0, "fuzz": 0.1},
|
{ "type": "metal", "albedo": [0.7, 0.4, 0.2 ], "prob": 1.0, "fuzz": 0.1},
|
||||||
{ "type": "lambertian", "colour": [0.8, 0.8, 0.0 ], "prob": 1.0},
|
{ "type": "lambertian", "albedo": [0.8, 0.8, 0.0 ], "prob": 1.0},
|
||||||
{ "type": "lambertian", "colour": [0.1, 0.2, 0.5 ], "prob": 1.0},
|
{ "type": "lambertian", "albedo": [0.1, 0.2, 0.5 ], "prob": 1.0},
|
||||||
{ "type": "dielectric", "refraction_index": 1.5},
|
{ "type": "dielectric", "refraction_index": 1.5},
|
||||||
{ "type": "dielectric", "refraction_index": 0.67},
|
{ "type": "dielectric", "refraction_index": 0.67},
|
||||||
{ "type": "metal", "colour": [0.8, 0.6, 0.2 ], "prob": 1.0, "fuzz": 1.0}
|
{ "type": "metal", "albedo": [0.8, 0.6, 0.2 ], "prob": 1.0, "fuzz": 1.0}
|
||||||
|
|
||||||
],
|
],
|
||||||
"objects": [
|
"objects": [
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"filename": "lowDef.png",
|
|
||||||
"image_width": 1920,
|
|
||||||
"image_height": 1080,
|
|
||||||
"max_depth": 50,
|
|
||||||
"camera": {
|
|
||||||
"anti_alias_rate": 7,
|
|
||||||
"fov": 20.0,
|
|
||||||
"look_from": [10, 5, 10],
|
|
||||||
"look_at": [0.0, 0.0, -1.0],
|
|
||||||
"vup": [0.0, 1.0, 0.0],
|
|
||||||
"defocus_angle": 0,
|
|
||||||
"focus_dist": 15.68
|
|
||||||
},
|
|
||||||
"materials": [
|
|
||||||
{ "type": "metal", "colour": "./balls", "prob": 1.0, "fuzz": 0.1},
|
|
||||||
{ "type": "metal", "colour": "./balls", "prob": 1.0, "fuzz": 0.1},
|
|
||||||
{ "type": "lambertian", "colour": "balls", "prob": 1.0},
|
|
||||||
{ "type": "metal", "colour": "./balls", "prob": 1.0, "fuzz": 0.1},
|
|
||||||
{ "type": "dielectric", "colour": "balls", "refraction_index": 1.5},
|
|
||||||
{ "type": "dielectric", "colour": "balls", "refraction_index": 1.5},
|
|
||||||
{ "type": "dielectric", "colour": "balls", "refraction_index": 1.5}
|
|
||||||
|
|
||||||
],
|
|
||||||
"objects": [
|
|
||||||
{ "type": "sphere", "center": [0, 0.7, -0.4], "radius": 0.2, "material": 0},
|
|
||||||
{ "type": "sphere", "center": [0.0, 0.5, -0.8], "radius": 0.1, "material": 1},
|
|
||||||
{ "type": "sphere", "center": [0.0, -100.5, -1.0], "radius": 100.0, "material": 2},
|
|
||||||
{ "type": "sphere", "center": [0.0, 0.0, -1.2], "radius": 0.5, "material": 3},
|
|
||||||
{ "type": "sphere", "center": [-1, 0, -1], "radius": 0.4, "material": 5},
|
|
||||||
{ "type": "sphere", "center": [1, 0, -1], "radius": 0.5, "material": 6},
|
|
||||||
{ "type": "sphere", "center": [20, 7, -15], "radius": 10.5, "material": 0}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+6
-6
@@ -4,7 +4,7 @@
|
|||||||
"image_height": 1080,
|
"image_height": 1080,
|
||||||
"max_depth": 50,
|
"max_depth": 50,
|
||||||
"camera": {
|
"camera": {
|
||||||
"anti_alias_rate": 2,
|
"anti_alias_rate": 23,
|
||||||
"fov": 70.0,
|
"fov": 70.0,
|
||||||
"look_from": [-10, 4, 15],
|
"look_from": [-10, 4, 15],
|
||||||
"look_at": [-11.0, 0.0, 0.0],
|
"look_at": [-11.0, 0.0, 0.0],
|
||||||
@@ -13,15 +13,15 @@
|
|||||||
"focus_dist": 15.68
|
"focus_dist": 15.68
|
||||||
},
|
},
|
||||||
"materials": [
|
"materials": [
|
||||||
{ "type": "lambertian", "colour": [0.2, 0.2, 0.2], "prob": 0.8 },
|
{ "type": "lambertian", "albedo": [0.2, 0.2, 0.2], "prob": 0.8 },
|
||||||
{ "type": "lambertian", "colour": "./textures/ballnold.png", "prob": 1.0, "fuzz": 0.1 },
|
{ "type": "lambertian", "albedo": [0.9, 0.9, 0.0], "prob": 1.0, "fuzz": 0.1 },
|
||||||
{ "type": "dielectric", "refraction_index": 1.5},
|
{ "type": "dielectric", "refraction_index": 1.5},
|
||||||
{ "type": "normal"}
|
{ "type": "normal"}
|
||||||
],
|
],
|
||||||
"objects": [
|
"objects": [
|
||||||
{ "type": "sphere", "center": [0.0, 0.0, -1.2], "radius": 0.5, "material": { "type": "metal", "colour": [0.7, 0.4, 0.2], "prob": 1.0, "fuzz": 0.1 }},
|
{ "type": "sphere", "center": [0.0, 0.0, -1.2], "radius": 0.5, "material": { "type": "metal", "albedo": [0.7, 0.4, 0.2], "prob": 1.0, "fuzz": 0.1 }},
|
||||||
{ "type": "sphere", "center": [-1, 0, -1], "radius": 0.4, "material": 3},
|
{ "type": "sphere", "center": [-1, 0, -1], "radius": 0.4, "material": 3},
|
||||||
{ "type": "sphere", "center": [1, 0, -1], "radius": 0.5, "material": { "type": "metal", "colour": [0.8, 0.6, 0.2], "prob": 1.0, "fuzz": 1.0 }},
|
{ "type": "sphere", "center": [1, 0, -1], "radius": 0.5, "material": { "type": "metal", "albedo": [0.8, 0.6, 0.2], "prob": 1.0, "fuzz": 1.0 }},
|
||||||
{ "type": "triangle", "p1": [-4, 0, -4], "p2": [0, 0, -4], "p3": [-2, 2, -4], "material": 1},
|
{ "type": "triangle", "p1": [-4, 0, -4], "p2": [0, 0, -4], "p3": [-2, 2, -4], "material": 1},
|
||||||
{ "type": "triangle", "p1": [0, 0, -4], "p2": [4, 0, -4], "p3": [2, 2, -4], "material": 1},
|
{ "type": "triangle", "p1": [0, 0, -4], "p2": [4, 0, -4], "p3": [2, 2, -4], "material": 1},
|
||||||
{ "type": "triangle", "p1": [-2, 2, -4], "p2": [2, 2, -4], "p3": [0, 4, -4], "material": 3},
|
{ "type": "triangle", "p1": [-2, 2, -4], "p2": [2, 2, -4], "p3": [0, 4, -4], "material": 3},
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
{ "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, -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": "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", "colour": [0.9, 0.9, 0.9], "prob": 1.0, "fuzz": 0.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}}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-22
@@ -1,21 +1,23 @@
|
|||||||
use crate::{ray::Ray, util::deg_to_rad, vec3::Vec3};
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
use crate::{ray::Ray, vec3::Vec3};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Camera {
|
pub struct Camera {
|
||||||
// raytracing
|
// raytracing
|
||||||
anti_alias_rate: u32,
|
pub anti_alias_rate: u32,
|
||||||
pixel00_loc: Vec3,
|
pub pixel00_loc: Vec3,
|
||||||
pixel_delta_u: Vec3,
|
pub pixel_delta_u: Vec3,
|
||||||
pixel_delta_v: Vec3,
|
pub pixel_delta_v: Vec3,
|
||||||
|
|
||||||
// camera
|
// camera
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
fov: f32,
|
pub fov: f32,
|
||||||
look_from: Vec3,
|
pub look_from: Vec3,
|
||||||
look_at: Vec3,
|
pub look_at: Vec3,
|
||||||
vup: Vec3,
|
vup: Vec3,
|
||||||
defocus_angle: f32,
|
pub defocus_angle: f32,
|
||||||
focus_dist: f32,
|
pub focus_dist: f32,
|
||||||
|
|
||||||
// camera helpers
|
// camera helpers
|
||||||
u: Vec3,
|
u: Vec3,
|
||||||
@@ -25,6 +27,11 @@ pub struct Camera {
|
|||||||
defocus_disk_v: Vec3,
|
defocus_disk_v: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: kinda out of place in this file.
|
||||||
|
fn deg_to_rad(deg: f32) -> f32 {
|
||||||
|
deg * PI / 180.
|
||||||
|
}
|
||||||
|
|
||||||
impl Camera {
|
impl Camera {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -76,7 +83,7 @@ impl Camera {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(&mut self, width: u32, height: u32) {
|
pub fn init(&mut self, w: f32, hi: f32) {
|
||||||
if !self.dirty {
|
if !self.dirty {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -90,13 +97,13 @@ impl Camera {
|
|||||||
|
|
||||||
// viewport
|
// viewport
|
||||||
let viewport_height = 2. * h * self.focus_dist;
|
let viewport_height = 2. * h * self.focus_dist;
|
||||||
let viewport_width = viewport_height * (width as f32 / height as f32);
|
let viewport_width = viewport_height * (w / hi);
|
||||||
let viewport_u = viewport_width * self.u;
|
let viewport_u = viewport_width * self.u;
|
||||||
let viewport_v = viewport_height * -self.v;
|
let viewport_v = viewport_height * -self.v;
|
||||||
|
|
||||||
// variables
|
// variables
|
||||||
self.pixel_delta_u = viewport_u / width;
|
self.pixel_delta_u = viewport_u / w;
|
||||||
self.pixel_delta_v = viewport_v / height;
|
self.pixel_delta_v = viewport_v / hi;
|
||||||
self.pixel00_loc =
|
self.pixel00_loc =
|
||||||
self.look_from - (self.focus_dist * self.w) - viewport_u / 2. - viewport_v / 2.;
|
self.look_from - (self.focus_dist * self.w) - viewport_u / 2. - viewport_v / 2.;
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
@@ -108,14 +115,6 @@ impl Camera {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_anti_alias_rate(&self) -> u32 {
|
|
||||||
self.anti_alias_rate
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_pixel_tl(&self, x: u32, y: u32) -> Vec3 {
|
|
||||||
self.pixel00_loc + (x * self.pixel_delta_u) + (y * self.pixel_delta_v)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_ray(&self, pixel_tl: Vec3, x: u32, y: u32) -> Ray {
|
pub fn get_ray(&self, pixel_tl: Vec3, x: u32, y: u32) -> Ray {
|
||||||
let pixel_loc = pixel_tl
|
let pixel_loc = pixel_tl
|
||||||
+ (x * self.pixel_delta_u / (self.anti_alias_rate + 1) as f32)
|
+ (x * self.pixel_delta_u / (self.anti_alias_rate + 1) as f32)
|
||||||
|
|||||||
+97
-3
@@ -5,23 +5,29 @@ mod objects;
|
|||||||
mod ray;
|
mod ray;
|
||||||
mod raytracer;
|
mod raytracer;
|
||||||
mod scenes;
|
mod scenes;
|
||||||
mod util;
|
|
||||||
mod vec3;
|
mod vec3;
|
||||||
|
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::camera::Camera;
|
||||||
|
use crate::objects::materials::dielectric::Dielectric;
|
||||||
|
use crate::objects::materials::lambertian::{Lambertian, Metal};
|
||||||
|
use crate::objects::sphere::Sphere;
|
||||||
|
use crate::objects::traits::Hittable;
|
||||||
use crate::ray::Ray;
|
use crate::ray::Ray;
|
||||||
use crate::raytracer::render;
|
use crate::raytracer::render;
|
||||||
use crate::scenes::scene::Scene;
|
use crate::scenes::scene::Scene;
|
||||||
use crate::vec3::Vec3;
|
use crate::vec3::Vec3;
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
|
use rand::RngExt;
|
||||||
|
|
||||||
// TODO (low priority): implement scene serialization
|
// TODO: implement scene serialization
|
||||||
fn main() {
|
fn main() {
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
// TODO (medium priority): better cli parsing || add random flag to generate random scene
|
// TODO: better cli parsing
|
||||||
let mut json_file = "./scenes/scene.json";
|
let mut json_file = "./scenes/scene.json";
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
if args.len() > 1 {
|
if args.len() > 1 {
|
||||||
@@ -31,4 +37,92 @@ fn main() {
|
|||||||
let json_str = fs::read_to_string(json_file).expect("Reading specified scene file failed!");
|
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();
|
let mut scene: Scene = serde_json::from_str(&json_str).unwrap();
|
||||||
render(&mut scene);
|
render(&mut scene);
|
||||||
|
return;
|
||||||
|
|
||||||
|
// random spheres code; thought: make this available as cli flag?
|
||||||
|
let ground = Lambertian::rgb(-2.5, 0.5, 0.5, 1.);
|
||||||
|
let mut world: Vec<Arc<dyn Hittable>> = vec![Arc::new(Sphere::xyz(
|
||||||
|
0.,
|
||||||
|
-1000.,
|
||||||
|
0.,
|
||||||
|
1000.,
|
||||||
|
Arc::new(ground),
|
||||||
|
))];
|
||||||
|
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
let point = Vec3::new(4., 0.2, 0.);
|
||||||
|
for a in -11..11 {
|
||||||
|
for b in -11..11 {
|
||||||
|
let mat = rng.random_range((0.)..1.);
|
||||||
|
let center = Vec3::new(
|
||||||
|
a as f32 + 0.9 * rng.random_range((0.)..1.),
|
||||||
|
0.2,
|
||||||
|
b as f32 + 0.9 * rng.random_range((0.)..1.),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (center - point).length() > 0.9 {
|
||||||
|
if mat < 0.8 {
|
||||||
|
// diffuse
|
||||||
|
world.push(Arc::new(Sphere::new(
|
||||||
|
center,
|
||||||
|
0.2,
|
||||||
|
Arc::new(Lambertian::new(Vec3::random() * Vec3::random(), 1.)),
|
||||||
|
)));
|
||||||
|
} else if mat < 0.95 {
|
||||||
|
// metal
|
||||||
|
world.push(Arc::new(Sphere::new(
|
||||||
|
center,
|
||||||
|
0.2,
|
||||||
|
Arc::new(Metal::rgb(
|
||||||
|
rng.random_range(0.5..1.),
|
||||||
|
rng.random_range(0.5..1.),
|
||||||
|
rng.random_range(0.5..1.),
|
||||||
|
1.,
|
||||||
|
rng.random_range((0.)..0.5),
|
||||||
|
)),
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
// glass
|
||||||
|
world.push(Arc::new(Sphere::new(
|
||||||
|
center,
|
||||||
|
0.2,
|
||||||
|
Arc::new(Dielectric::new(1.5)),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
world.push(Arc::new(Sphere::xyz(
|
||||||
|
0.,
|
||||||
|
1.,
|
||||||
|
0.,
|
||||||
|
1.,
|
||||||
|
Arc::new(Dielectric::new(1.5)),
|
||||||
|
)));
|
||||||
|
world.push(Arc::new(Sphere::xyz(
|
||||||
|
-4.,
|
||||||
|
1.,
|
||||||
|
0.,
|
||||||
|
1.,
|
||||||
|
Arc::new(Lambertian::rgb(0.4, 0.2, 0.1, 1.)),
|
||||||
|
)));
|
||||||
|
world.push(Arc::new(Sphere::xyz(
|
||||||
|
4.,
|
||||||
|
1.,
|
||||||
|
0.,
|
||||||
|
1.,
|
||||||
|
Arc::new(Metal::rgb(0.7, 0.6, 0.5, 1., 0.)),
|
||||||
|
)));
|
||||||
|
|
||||||
|
let mut c = Camera::new();
|
||||||
|
c.set_fov(20.);
|
||||||
|
c.set_anti_alias_rate(23);
|
||||||
|
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.);
|
||||||
|
|
||||||
|
let mut s = Scene::new(c, world, "output.png".to_string(), 1920, 1080, 50);
|
||||||
|
render(&mut s);
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
pub mod circle;
|
pub mod circle;
|
||||||
pub mod cube;
|
pub mod cube;
|
||||||
|
pub mod cylinder;
|
||||||
pub mod hit;
|
pub mod hit;
|
||||||
pub mod materials;
|
pub mod materials;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
|
|||||||
+2
-21
@@ -1,4 +1,4 @@
|
|||||||
use std::{f32::consts::PI, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{materials::traits::Material, traits::Hittable},
|
objects::{materials::traits::Material, traits::Hittable},
|
||||||
@@ -45,27 +45,8 @@ impl Hittable for Circle {
|
|||||||
|
|
||||||
let p = r.at(t);
|
let p = r.at(t);
|
||||||
if (p - self.center).length() < self.radius {
|
if (p - self.center).length() < self.radius {
|
||||||
let uv = self.to_uv(&p);
|
return Some(Hit::new(t, p, self.normal, self.material.clone(), self.normal.dot(&r.dir()) < 0.));
|
||||||
return Some(Hit::new(
|
|
||||||
t,
|
|
||||||
p,
|
|
||||||
self.normal,
|
|
||||||
self.material.clone(),
|
|
||||||
self.normal.dot(r.dir()) < 0.,
|
|
||||||
*uv.x(),
|
|
||||||
*uv.y(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_uv(&self, point: &Vec3) -> Vec3 {
|
|
||||||
let p = *point - self.center;
|
|
||||||
// TODO (low priority): add rotated texture support
|
|
||||||
Vec3::new(
|
|
||||||
0.5 + p.y().atan2(*p.x()) / (2. * PI),
|
|
||||||
1. - (p.z() / self.radius).acos() / PI,
|
|
||||||
0.0,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -41,9 +41,4 @@ impl Hittable for Cube {
|
|||||||
fn hit(&self, r: &Ray) -> Option<Hit> {
|
fn hit(&self, r: &Ray) -> Option<Hit> {
|
||||||
Hit::hit_list(&self.faces, r)
|
Hit::hit_list(&self.faces, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_uv(&self, _point: &Vec3) -> Vec3 {
|
|
||||||
// TODO (low priority): map to [0.1] relative to specific face
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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<super::hit::Hit> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-56
@@ -12,47 +12,16 @@ pub struct Hit {
|
|||||||
n: Vec3,
|
n: Vec3,
|
||||||
mat: Arc<dyn Material>,
|
mat: Arc<dyn Material>,
|
||||||
front_face: bool,
|
front_face: bool,
|
||||||
u: f32,
|
|
||||||
v: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hit {
|
impl Hit {
|
||||||
pub fn new(
|
pub fn new(t: f32, p: Vec3, n: Vec3, mat: Arc<dyn Material>, front_face: bool) -> Self {
|
||||||
t: f32,
|
|
||||||
p: Vec3,
|
|
||||||
n: Vec3,
|
|
||||||
mat: Arc<dyn Material>,
|
|
||||||
front_face: bool,
|
|
||||||
u: f32,
|
|
||||||
v: f32,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
t,
|
t,
|
||||||
p,
|
p,
|
||||||
n: if front_face { n } else { -n },
|
n: if front_face { n } else { -n },
|
||||||
mat,
|
mat,
|
||||||
front_face,
|
front_face,
|
||||||
u,
|
|
||||||
v,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_minimal(
|
|
||||||
mh: MinimalHit,
|
|
||||||
n: Vec3,
|
|
||||||
mat: Arc<dyn Material>,
|
|
||||||
front_face: bool,
|
|
||||||
u: f32,
|
|
||||||
v: f32,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
t: mh.t,
|
|
||||||
p: mh.p,
|
|
||||||
n,
|
|
||||||
mat,
|
|
||||||
front_face,
|
|
||||||
u,
|
|
||||||
v,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,11 +45,7 @@ impl Hit {
|
|||||||
self.front_face
|
self.front_face
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uv(&self) -> (f32, f32) {
|
// TODO: use front_face to discard back-hits for culling
|
||||||
(self.u, self.v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO (low priority): use front_face to discard back-hits for culling
|
|
||||||
pub fn hit_list(hittables: &Vec<Arc<dyn Hittable>>, r: &Ray) -> Option<Hit> {
|
pub fn hit_list(hittables: &Vec<Arc<dyn Hittable>>, r: &Ray) -> Option<Hit> {
|
||||||
let mut closest: Option<Hit> = None;
|
let mut closest: Option<Hit> = None;
|
||||||
for hittable in hittables {
|
for hittable in hittables {
|
||||||
@@ -96,22 +61,3 @@ impl Hit {
|
|||||||
closest
|
closest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MinimalHit {
|
|
||||||
t: f32,
|
|
||||||
p: Vec3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MinimalHit {
|
|
||||||
pub fn new(t: f32, p: Vec3) -> Self {
|
|
||||||
Self { t, p }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn t(&self) -> f32 {
|
|
||||||
self.t
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn p(&self) -> &Vec3 {
|
|
||||||
&self.p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
pub mod colour_source;
|
|
||||||
pub mod dielectric;
|
pub mod dielectric;
|
||||||
pub mod lambertian;
|
pub mod lambertian;
|
||||||
pub mod normal;
|
pub mod normal;
|
||||||
pub mod texture;
|
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
objects::{hit::Hit, materials::texture::Texture},
|
|
||||||
vec3::Colour,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ColourSource {
|
|
||||||
Colour(Colour),
|
|
||||||
Texture(Texture),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ColourSource {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::rgb(1., 1., 1.)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ColourSource {
|
|
||||||
pub fn get_colour(&self, hit: &Hit) -> Colour {
|
|
||||||
match self {
|
|
||||||
ColourSource::Colour(vec3) => *vec3,
|
|
||||||
ColourSource::Texture(texture) => texture.colour_at(hit),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rgb(r: f32, g: f32, b: f32) -> Self {
|
|
||||||
Self::Colour(Colour::new(r, g, b))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum RawColourSource {
|
|
||||||
Colour(Colour),
|
|
||||||
Texture(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RawColourSource> for ColourSource {
|
|
||||||
fn from(value: RawColourSource) -> Self {
|
|
||||||
match value {
|
|
||||||
RawColourSource::Colour(c) => Self::Colour(c),
|
|
||||||
RawColourSource::Texture(t) => Self::Texture(Texture::new(&t)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for ColourSource {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let raw = RawColourSource::deserialize(deserializer)?;
|
|
||||||
Ok(raw.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,33 +4,19 @@ use rand::RngExt;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{hit::Hit, materials::traits::Material},
|
||||||
hit::Hit,
|
|
||||||
materials::{colour_source::ColourSource, traits::Material},
|
|
||||||
},
|
|
||||||
ray::Ray,
|
ray::Ray,
|
||||||
vec3::Colour,
|
vec3::Colour,
|
||||||
};
|
};
|
||||||
|
|
||||||
// THOUGHT: not entirely happy with this implementation; i ideally want to specify transparent
|
|
||||||
// dielectrics, but that will require a more substantial refactor wrt compositing + vec4 colours.
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Dielectric {
|
pub struct Dielectric {
|
||||||
#[serde(default)]
|
|
||||||
colour: ColourSource,
|
|
||||||
refraction_index: f32,
|
refraction_index: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dielectric {
|
impl Dielectric {
|
||||||
pub fn new(refraction_index: f32) -> Self {
|
pub fn new(refraction_index: f32) -> Self {
|
||||||
Self::new_coloured(refraction_index, ColourSource::rgb(1., 1., 1.))
|
Self { refraction_index }
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_coloured(refraction_index: f32, colour: ColourSource) -> Self {
|
|
||||||
Self {
|
|
||||||
refraction_index,
|
|
||||||
colour,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reflectance(cos: f32, refraction_index: f32) -> f32 {
|
fn reflectance(cos: f32, refraction_index: f32) -> f32 {
|
||||||
@@ -64,6 +50,6 @@ impl Material for Dielectric {
|
|||||||
} else {
|
} else {
|
||||||
unit.refract(hit.n(), ri)
|
unit.refract(hit.n(), ri)
|
||||||
};
|
};
|
||||||
Some((Some(Ray::new(*hit.p(), dir)), self.colour.get_colour(hit)))
|
Some((Some(Ray::new(*hit.p(), dir)), Colour::new(1., 1., 1.)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,44 +1,31 @@
|
|||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{hit::Hit, materials::traits::Material},
|
||||||
hit::Hit,
|
|
||||||
materials::{colour_source::ColourSource, traits::Material},
|
|
||||||
},
|
|
||||||
ray::Ray,
|
ray::Ray,
|
||||||
vec3::{Colour, Vec3},
|
vec3::{Colour, Vec3},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Lambertian {
|
pub struct Lambertian {
|
||||||
colour: ColourSource,
|
albedo: Colour,
|
||||||
prob: f32,
|
prob: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lambertian {
|
impl Lambertian {
|
||||||
pub fn new(colour: ColourSource, prob: f32) -> Self {
|
pub fn new(albedo: Colour, prob: f32) -> Self {
|
||||||
Self { colour, prob }
|
Self { albedo, prob }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rgb(r: f32, g: f32, b: f32, prob: f32) -> Self {
|
pub fn rgb(r: f32, g: f32, b: f32, prob: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
colour: ColourSource::rgb(r, g, b),
|
albedo: Colour::new(r, g, b),
|
||||||
prob,
|
prob,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Lambertian {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("Lambertian")
|
|
||||||
.field("prob", &self.prob)
|
|
||||||
.field("colour", &self.colour)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Material for Lambertian {
|
impl Material for Lambertian {
|
||||||
fn scatter(&self, hit: &Hit, _ray: &Ray) -> Option<(Option<Ray>, Colour)> {
|
fn scatter(&self, hit: &Hit, _ray: &Ray) -> Option<(Option<Ray>, Colour)> {
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
@@ -48,7 +35,7 @@ impl Material for Lambertian {
|
|||||||
dir = *hit.n();
|
dir = *hit.n();
|
||||||
}
|
}
|
||||||
let scattered = Ray::new(*hit.p(), dir);
|
let scattered = Ray::new(*hit.p(), dir);
|
||||||
return Some((Some(scattered), self.colour.get_colour(hit)));
|
return Some((Some(scattered), self.albedo));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -56,19 +43,19 @@ impl Material for Lambertian {
|
|||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Metal {
|
pub struct Metal {
|
||||||
colour: ColourSource,
|
albedo: Colour,
|
||||||
prob: f32,
|
prob: f32,
|
||||||
fuzz: f32,
|
fuzz: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metal {
|
impl Metal {
|
||||||
pub fn new(colour: ColourSource, prob: f32, fuzz: f32) -> Self {
|
pub fn new(albedo: Colour, prob: f32, fuzz: f32) -> Self {
|
||||||
Self { colour, prob, fuzz }
|
Self { albedo, prob, fuzz }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rgb(r: f32, g: f32, b: f32, prob: f32, fuzz: f32) -> Self {
|
pub fn rgb(r: f32, g: f32, b: f32, prob: f32, fuzz: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
colour: ColourSource::rgb(r, g, b),
|
albedo: Colour::new(r, g, b),
|
||||||
prob,
|
prob,
|
||||||
fuzz,
|
fuzz,
|
||||||
}
|
}
|
||||||
@@ -81,7 +68,7 @@ impl Material for Metal {
|
|||||||
if self.prob >= rng.random::<f32>() {
|
if self.prob >= rng.random::<f32>() {
|
||||||
let mut refl = ray.dir().reflect(hit.n());
|
let mut refl = ray.dir().reflect(hit.n());
|
||||||
refl = refl.get_unit() + self.fuzz * Vec3::random_unit();
|
refl = refl.get_unit() + self.fuzz * Vec3::random_unit();
|
||||||
return Some((Some(Ray::new(*hit.p(), refl)), self.colour.get_colour(hit)));
|
return Some((Some(Ray::new(*hit.p(), refl)), self.albedo));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use image::{DynamicImage, ImageReader, Rgb};
|
|
||||||
use image::{RgbImage};
|
|
||||||
use log::warn;
|
|
||||||
|
|
||||||
use crate::{objects::hit::Hit, vec3::Vec3};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Texture {
|
|
||||||
source: DynamicImage,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
stride: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texture {
|
|
||||||
pub fn new(texture: &str) -> Self {
|
|
||||||
match ImageReader::open(texture) {
|
|
||||||
Ok(r) => {
|
|
||||||
match r.decode() {
|
|
||||||
Ok(img) => Self::_build(img),
|
|
||||||
Err(e) => {
|
|
||||||
// ImageError
|
|
||||||
warn!("Failed to parse image \"{}\": {}", texture, e);
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
// IoError
|
|
||||||
warn!("Failed to read file \"{}\": {}", texture, e);
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _build(img: DynamicImage) -> Self {
|
|
||||||
let stride = match img.color() {
|
|
||||||
// TODO: support other types of image
|
|
||||||
image::ColorType::L8 => todo!(),
|
|
||||||
image::ColorType::La8 => todo!(),
|
|
||||||
image::ColorType::Rgb8 => 3,
|
|
||||||
image::ColorType::Rgba8 => 4,
|
|
||||||
image::ColorType::L16 => todo!(),
|
|
||||||
image::ColorType::La16 => todo!(),
|
|
||||||
image::ColorType::Rgb16 => todo!(),
|
|
||||||
image::ColorType::Rgba16 => todo!(),
|
|
||||||
image::ColorType::Rgb32F => todo!(),
|
|
||||||
image::ColorType::Rgba32F => todo!(),
|
|
||||||
_ => 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
|
||||||
width: img.width(),
|
|
||||||
height: img.height(),
|
|
||||||
source: img,
|
|
||||||
stride,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _idx(&self, u: f32, v: f32) -> usize {
|
|
||||||
let x: usize = (u * ((self.width - 1) as f32)) as usize;
|
|
||||||
let y: usize = (v * ((self.height - 1) as f32)) as usize;
|
|
||||||
|
|
||||||
y * self.width as usize + x
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _at(&self, u: f32, v: f32) -> Vec3 {
|
|
||||||
let b = self.source.as_bytes();
|
|
||||||
let idx = self._idx(u, v);
|
|
||||||
|
|
||||||
Vec3::from_u8(
|
|
||||||
b[self.stride * idx],
|
|
||||||
b[self.stride * idx + 2],
|
|
||||||
b[self.stride * idx + 1],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn colour_at(&self, hit: &Hit) -> Vec3 {
|
|
||||||
let (u, v) = hit.uv();
|
|
||||||
self._at(u, 1. - v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Texture {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut img = RgbImage::new(1, 1);
|
|
||||||
img.put_pixel(0,0,Rgb([250,0,250]));
|
|
||||||
Self {
|
|
||||||
source: DynamicImage::ImageRgb8(img),
|
|
||||||
width: 1,
|
|
||||||
height: 1,
|
|
||||||
stride: 3,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
+26
-23
@@ -13,10 +13,6 @@ pub struct Quad {
|
|||||||
p4: Vec3,
|
p4: Vec3,
|
||||||
material: Arc<dyn Material>,
|
material: Arc<dyn Material>,
|
||||||
normal: Vec3,
|
normal: Vec3,
|
||||||
|
|
||||||
// helpers
|
|
||||||
t1: Triangle,
|
|
||||||
t2: Triangle,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Quad {
|
impl Quad {
|
||||||
@@ -26,12 +22,27 @@ impl Quad {
|
|||||||
p2,
|
p2,
|
||||||
p3,
|
p3,
|
||||||
p4,
|
p4,
|
||||||
material: material.clone(),
|
material,
|
||||||
normal: (p2 - p1).cross(&(p4 - p1)).get_unit(),
|
normal: (p2 - p1).cross(&(p4 - p1)).get_unit(),
|
||||||
t1: Triangle::new(p1, p2, p4, material.clone()),
|
|
||||||
t2: Triangle::new(p2, p3, p4, material),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hit(
|
||||||
|
p1: Vec3,
|
||||||
|
p2: Vec3,
|
||||||
|
p3: Vec3,
|
||||||
|
p4: Vec3,
|
||||||
|
material: Arc<dyn Material>,
|
||||||
|
normal: Vec3,
|
||||||
|
r: &Ray,
|
||||||
|
) -> Option<Hit> {
|
||||||
|
let isct1 = Triangle::hit(p1, p2, p4, material.clone(), normal, r);
|
||||||
|
let isct2 = Triangle::hit(p2, p3, p4, material.clone(), normal, r);
|
||||||
|
if isct1.is_some() {
|
||||||
|
return isct1;
|
||||||
|
}
|
||||||
|
isct2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Quad {
|
impl Debug for Quad {
|
||||||
@@ -48,22 +59,14 @@ impl Debug for Quad {
|
|||||||
|
|
||||||
impl Hittable for Quad {
|
impl Hittable for Quad {
|
||||||
fn hit(&self, r: &Ray) -> Option<Hit> {
|
fn hit(&self, r: &Ray) -> Option<Hit> {
|
||||||
let mh = self.t1.calculate_hit(r).or(self.t2.calculate_hit(r))?;
|
Quad::hit(
|
||||||
let uvw = self.to_uv(mh.p());
|
self.p1,
|
||||||
|
self.p2,
|
||||||
Some(Hit::from_minimal(
|
self.p3,
|
||||||
mh,
|
self.p4,
|
||||||
self.normal,
|
|
||||||
self.material.clone(),
|
self.material.clone(),
|
||||||
false,
|
self.normal,
|
||||||
*uvw.x(),
|
r,
|
||||||
*uvw.y(),
|
)
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_uv(&self, point: &Vec3) -> Vec3 {
|
|
||||||
let u = (*point - self.p1).dot(&(self.p2 - self.p1)) / (self.p2 - self.p1).length_squared();
|
|
||||||
let v = (*point - self.p1).dot(&(self.p4 - self.p1)) / (self.p4 - self.p1).length_squared();
|
|
||||||
Vec3::new(u, v, 0.)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-15
@@ -1,13 +1,12 @@
|
|||||||
use core::f32::math::sqrt;
|
use core::f32::math::sqrt;
|
||||||
use std::f32::consts::PI;
|
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::Vec3;
|
||||||
use crate::objects::hit::Hit;
|
use crate::objects::hit::Hit;
|
||||||
use crate::objects::materials::traits::Material;
|
use crate::objects::materials::traits::Material;
|
||||||
use crate::objects::traits::Hittable;
|
use crate::objects::traits::Hittable;
|
||||||
use crate::ray::Ray;
|
use crate::ray::Ray;
|
||||||
use crate::Vec3;
|
|
||||||
|
|
||||||
pub struct Sphere {
|
pub struct Sphere {
|
||||||
center: Vec3,
|
center: Vec3,
|
||||||
@@ -15,6 +14,7 @@ pub struct Sphere {
|
|||||||
material: Arc<dyn Material>,
|
material: Arc<dyn Material>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Debug for Sphere {
|
impl Debug for Sphere {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Sphere")
|
f.debug_struct("Sphere")
|
||||||
@@ -59,26 +59,13 @@ impl Hittable for Sphere {
|
|||||||
let t = if tl > 0.001 { tl } else { tr };
|
let t = if tl > 0.001 { tl } else { tr };
|
||||||
let p = r.at(t);
|
let p = r.at(t);
|
||||||
let out_n = (p - self.center) / self.radius;
|
let out_n = (p - self.center) / self.radius;
|
||||||
let uv = self.to_uv(&p);
|
|
||||||
Some(Hit::new(
|
Some(Hit::new(
|
||||||
t,
|
t,
|
||||||
p,
|
p,
|
||||||
(p - self.center).get_unit(),
|
(p - self.center).get_unit(),
|
||||||
self.material.clone(),
|
self.material.clone(),
|
||||||
out_n.dot(r.dir()) < 0.,
|
out_n.dot(r.dir()) < 0.,
|
||||||
*uv.x(),
|
|
||||||
*uv.y(),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_uv(&self, point: &Vec3) -> Vec3 {
|
|
||||||
let p = *point - self.center;
|
|
||||||
// TODO (medium priority): add rotated texture support
|
|
||||||
Vec3::new(
|
|
||||||
0.5 + p.y().atan2(*p.x()) / (2. * PI),
|
|
||||||
1. - (p.z() / self.radius).acos() / PI,
|
|
||||||
0.0,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::objects::hit::Hit;
|
|
||||||
use crate::vec3::Vec3;
|
|
||||||
use crate::Ray;
|
use crate::Ray;
|
||||||
|
use crate::objects::hit::Hit;
|
||||||
|
|
||||||
pub trait Hittable: Debug + Send + Sync {
|
pub trait Hittable: Debug + Send + Sync {
|
||||||
fn to_uv(&self, point: &Vec3) -> Vec3;
|
|
||||||
fn hit(&self, r: &Ray) -> Option<Hit>;
|
fn hit(&self, r: &Ray) -> Option<Hit>;
|
||||||
}
|
}
|
||||||
+25
-54
@@ -1,4 +1,4 @@
|
|||||||
use crate::objects::hit::{Hit, MinimalHit};
|
use crate::objects::hit::Hit;
|
||||||
use crate::objects::traits::Hittable;
|
use crate::objects::traits::Hittable;
|
||||||
use crate::ray::Ray;
|
use crate::ray::Ray;
|
||||||
use crate::{objects::materials::traits::Material, vec3::Vec3};
|
use crate::{objects::materials::traits::Material, vec3::Vec3};
|
||||||
@@ -11,66 +11,50 @@ pub struct Triangle {
|
|||||||
p3: Vec3,
|
p3: Vec3,
|
||||||
material: Arc<dyn Material>,
|
material: Arc<dyn Material>,
|
||||||
normal: Vec3,
|
normal: Vec3,
|
||||||
|
|
||||||
// barycentric helpers
|
|
||||||
v0: Vec3,
|
|
||||||
v1: Vec3,
|
|
||||||
d00: f32,
|
|
||||||
d01: f32,
|
|
||||||
d11: f32,
|
|
||||||
denom: f32,
|
|
||||||
a4: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Triangle {
|
impl Triangle {
|
||||||
pub fn new(p1: Vec3, p2: Vec3, p3: Vec3, material: Arc<dyn Material>) -> Self {
|
pub fn new(p1: Vec3, p2: Vec3, p3: Vec3, material: Arc<dyn Material>) -> Self {
|
||||||
// barycentric helpers cached to save computations
|
|
||||||
let v0 = p2 - p1;
|
|
||||||
let v1 = p3 - p1;
|
|
||||||
let d00 = v0.dot(&v0);
|
|
||||||
let d01 = v0.dot(&v1);
|
|
||||||
let d11 = v1.dot(&v1);
|
|
||||||
let a4 = (p3 - p1).cross(&(p2 - p1)).length();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
p1,
|
p1,
|
||||||
p2,
|
p2,
|
||||||
p3,
|
p3,
|
||||||
material,
|
material,
|
||||||
normal: (p2 - p1).cross(&(p3 - p1)).get_unit(),
|
normal: (p2 - p1).cross(&(p3 - p1)).get_unit(),
|
||||||
v0,
|
|
||||||
v1,
|
|
||||||
d00,
|
|
||||||
d01,
|
|
||||||
d11,
|
|
||||||
denom: d00 * d11 - d01 * d01,
|
|
||||||
a4,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_hit(&self, r: &Ray) -> Option<MinimalHit> {
|
pub fn hit(
|
||||||
|
p1: Vec3,
|
||||||
|
p2: Vec3,
|
||||||
|
p3: Vec3,
|
||||||
|
material: Arc<dyn Material>,
|
||||||
|
normal: Vec3,
|
||||||
|
r: &Ray,
|
||||||
|
) -> Option<Hit> {
|
||||||
// check if ray parallel to plane
|
// check if ray parallel to plane
|
||||||
let dot = self.normal.dot(r.dir());
|
let dot = normal.dot(r.dir());
|
||||||
if dot == 0.0 {
|
if dot == 0.0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let d = (-self.normal).dot(&self.p1);
|
let d = (-normal).dot(&p1);
|
||||||
// hitpoint on plane
|
// hitpoint on plane
|
||||||
let t = -(self.normal.dot(&(r.origin() + d))) / dot;
|
let t = -(normal.dot(&(r.origin() + d))) / dot;
|
||||||
// hits behind camera
|
// hits behind camera
|
||||||
if t < 0. {
|
if t < 0. {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
let p = r.at(t);
|
let p = r.at(t);
|
||||||
let a3 = (self.p3 - p).cross(&(self.p2 - p)).length();
|
let a4 = (p3 - p1).cross(&(p2 - p1)).length();
|
||||||
let a2 = (self.p3 - p).cross(&(self.p1 - p)).length();
|
let a3 = (p3 - p).cross(&(p2 - p)).length();
|
||||||
let a1 = (self.p2 - p).cross(&(self.p1 - p)).length();
|
let a2 = (p3 - p).cross(&(p1 - p)).length();
|
||||||
|
let a1 = (p2 - p).cross(&(p1 - p)).length();
|
||||||
|
|
||||||
let diff = (self.a4 - a1 - a2 - a3).abs();
|
let diff = (a4 - a1 - a2 - a3).abs();
|
||||||
if diff < 0.001 {
|
if diff < 0.001 {
|
||||||
Some(MinimalHit::new(t, p))
|
Some(Hit::new(t, p, normal, material, normal.dot(&r.dir()) < 0.))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -90,26 +74,13 @@ impl Debug for Triangle {
|
|||||||
|
|
||||||
impl Hittable for Triangle {
|
impl Hittable for Triangle {
|
||||||
fn hit(&self, r: &Ray) -> Option<Hit> {
|
fn hit(&self, r: &Ray) -> Option<Hit> {
|
||||||
let mh = self.calculate_hit(r)?;
|
Triangle::hit(
|
||||||
let uvw = self.to_uv(mh.p());
|
self.p1,
|
||||||
|
self.p2,
|
||||||
Some(Hit::from_minimal(
|
self.p3,
|
||||||
mh,
|
|
||||||
self.normal,
|
|
||||||
self.material.clone(),
|
self.material.clone(),
|
||||||
self.normal.dot(r.dir()) < 0.,
|
self.normal,
|
||||||
*uvw.x(),
|
r,
|
||||||
*uvw.y(),
|
)
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_uv(&self, point: &Vec3) -> Vec3 {
|
|
||||||
let v2 = *point - self.p1;
|
|
||||||
let d20 = v2.dot(&self.v0);
|
|
||||||
let d21 = v2.dot(&self.v1);
|
|
||||||
|
|
||||||
let v = (self.d11 * d20 - self.d01 * d21) / self.denom;
|
|
||||||
let w = (self.d00 * d21 - self.d01 * d20) / self.denom;
|
|
||||||
Vec3::new(1. - v - w, v, w)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+21
-24
@@ -1,8 +1,8 @@
|
|||||||
use image::{ExtendedColorType, ImageEncoder, codecs::png::PngEncoder};
|
use image::{ExtendedColorType, ImageEncoder, codecs::png::PngEncoder};
|
||||||
use log::{error, info, trace};
|
use log::{error, info};
|
||||||
use rayon::iter::IntoParallelIterator;
|
use rayon::iter::IntoParallelIterator;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::{fs::File, sync::Arc, time::Instant};
|
use std::{fs::File, sync::Arc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{hit::Hit, traits::Hittable},
|
objects::{hit::Hit, traits::Hittable},
|
||||||
@@ -24,55 +24,52 @@ pub fn write_image(filename: &str, pixels: &[u8], width: u32, height: u32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(scene: &mut Scene) {
|
pub fn render(scene: &mut Scene) {
|
||||||
scene.init();
|
let camera = &mut scene.camera;
|
||||||
|
camera.init(scene.image_width as f32, scene.image_height as f32);
|
||||||
|
|
||||||
// TODO (low priority): currently splits per vertical line, but could be more granular (per chunk)
|
// TODO: currently splits per vertical line, but could be more granular (per chunk)
|
||||||
let mut pixels = vec![0_u8; (scene.get_image_width() * scene.get_image_height() * 3) as usize];
|
let mut pixels = vec![0 as u8; (scene.image_width * scene.image_height * 3) as usize];
|
||||||
let scanlines: Vec<(usize, &mut [u8])> = pixels
|
let scanlines: Vec<(usize, &mut [u8])> = pixels
|
||||||
.chunks_mut(scene.get_image_width() as usize * 3)
|
.chunks_mut(scene.image_width as usize * 3)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let now = Instant::now();
|
|
||||||
scanlines.into_par_iter().for_each(|(i, chunk)| {
|
scanlines.into_par_iter().for_each(|(i, chunk)| {
|
||||||
render_chunk(chunk, scene, i as u32);
|
render_chunk(chunk, scene, i as u32);
|
||||||
});
|
});
|
||||||
info!(
|
|
||||||
"rendering took {}.{} seconds.",
|
|
||||||
now.elapsed().as_secs(),
|
|
||||||
now.elapsed().subsec_nanos()
|
|
||||||
);
|
|
||||||
|
|
||||||
info!("Writing image file...");
|
info!("Writing image file...");
|
||||||
write_image(
|
write_image(
|
||||||
&scene.get_filename(),
|
&scene.filename,
|
||||||
&pixels,
|
&pixels,
|
||||||
scene.get_image_width() as u32,
|
scene.image_width,
|
||||||
scene.get_image_height() as u32,
|
scene.image_height,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_chunk(chunk: &mut [u8], scene: &Scene, y: u32) {
|
pub fn render_chunk(chunk: &mut [u8], scene: &Scene, y: u32) {
|
||||||
let camera = &scene.get_camera();
|
let camera = &scene.camera;
|
||||||
let aa_rate = camera.get_anti_alias_rate();
|
|
||||||
|
|
||||||
for i in 0..scene.get_image_width() {
|
for i in 0..scene.image_width {
|
||||||
let pixel_tl = camera.get_pixel_tl(i, y);
|
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();
|
let mut pixel_colour = Colour::default();
|
||||||
for y in 1..(aa_rate + 1) {
|
for y in 1..(camera.anti_alias_rate + 1) {
|
||||||
for x in 1..(aa_rate + 1) {
|
for x in 1..(camera.anti_alias_rate + 1) {
|
||||||
let r = camera.get_ray(pixel_tl, x, y);
|
let r = camera.get_ray(pixel_tl, x, y);
|
||||||
pixel_colour += ray_colour(&scene.get_objects(), &r, scene.get_max_depth());
|
pixel_colour += ray_colour(&scene.objects, &r, scene.max_depth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (r, g, b) = (pixel_colour / (aa_rate * aa_rate) as f32).output();
|
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] = r;
|
||||||
chunk[(i * 3) as usize + 1] = g;
|
chunk[(i * 3) as usize + 1] = g;
|
||||||
chunk[(i * 3) as usize + 2] = b;
|
chunk[(i * 3) as usize + 2] = b;
|
||||||
}
|
}
|
||||||
trace!("Line {:4} finished.", y);
|
info!("Line {}\tfinished.", y);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ray_colour(hittables: &Vec<Arc<dyn Hittable>>, r: &Ray, depth: u32) -> Colour {
|
pub fn ray_colour(hittables: &Vec<Arc<dyn Hittable>>, r: &Ray, depth: u32) -> Colour {
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
pub mod hittable_def;
|
|
||||||
pub mod material_def;
|
|
||||||
pub mod raw_camera;
|
pub mod raw_camera;
|
||||||
|
pub mod material_def;
|
||||||
|
pub mod hittable_def;
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
@@ -2,12 +2,8 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::objects::materials::{
|
use crate::objects::materials::{dielectric::Dielectric, lambertian::{Lambertian, Metal}, normal::Normal, traits::Material};
|
||||||
dielectric::Dielectric,
|
|
||||||
lambertian::{Lambertian, Metal},
|
|
||||||
normal::Normal,
|
|
||||||
traits::Material,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(tag = "type", rename_all = "lowercase")]
|
#[serde(tag = "type", rename_all = "lowercase")]
|
||||||
@@ -28,3 +24,4 @@ impl MaterialDef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+8
-147
@@ -1,36 +1,23 @@
|
|||||||
use crate::objects::materials::colour_source::ColourSource;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use rand::RngExt;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
camera::Camera,
|
camera::Camera,
|
||||||
objects::{
|
objects::{materials::traits::Material, traits::Hittable},
|
||||||
materials::{
|
|
||||||
dielectric::Dielectric,
|
|
||||||
lambertian::{Lambertian, Metal},
|
|
||||||
traits::Material,
|
|
||||||
},
|
|
||||||
sphere::Sphere,
|
|
||||||
traits::Hittable,
|
|
||||||
},
|
|
||||||
scenes::{hittable_def::HittableDef, material_def::MaterialDef, raw_camera::RawCamera},
|
scenes::{hittable_def::HittableDef, material_def::MaterialDef, raw_camera::RawCamera},
|
||||||
vec3::Vec3,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
camera: Camera,
|
pub camera: Camera, // FIXME: should not be public
|
||||||
objects: Vec<Arc<dyn Hittable>>,
|
pub objects: Vec<Arc<dyn Hittable>>,
|
||||||
|
|
||||||
// image
|
// image
|
||||||
filename: String,
|
pub filename: String,
|
||||||
image_width: u32,
|
pub image_width: u32,
|
||||||
image_height: u32,
|
pub image_height: u32,
|
||||||
|
// raytracing // TODO: think about organisation of these vars, also in Camera
|
||||||
// raytracing
|
pub max_depth: u32,
|
||||||
max_depth: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
@@ -51,132 +38,6 @@ impl Scene {
|
|||||||
max_depth,
|
max_depth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn random() -> Self {
|
|
||||||
let ground = Lambertian::rgb(-2.5, 0.5, 0.5, 1.);
|
|
||||||
let mut world: Vec<Arc<dyn Hittable>> = vec![Arc::new(Sphere::xyz(
|
|
||||||
0.,
|
|
||||||
-1000.,
|
|
||||||
0.,
|
|
||||||
1000.,
|
|
||||||
Arc::new(ground),
|
|
||||||
))];
|
|
||||||
|
|
||||||
let mut rng = rand::rng();
|
|
||||||
let point = Vec3::new(4., 0.2, 0.);
|
|
||||||
for a in -11..11 {
|
|
||||||
for b in -11..11 {
|
|
||||||
let mat = rng.random_range((0.)..1.);
|
|
||||||
let center = Vec3::new(
|
|
||||||
a as f32 + 0.9 * rng.random_range((0.)..1.),
|
|
||||||
0.2,
|
|
||||||
b as f32 + 0.9 * rng.random_range((0.)..1.),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (center - point).length() > 0.9 {
|
|
||||||
if mat < 0.8 {
|
|
||||||
// diffuse
|
|
||||||
world.push(Arc::new(Sphere::new(
|
|
||||||
center,
|
|
||||||
0.2,
|
|
||||||
Arc::new(Lambertian::new(
|
|
||||||
ColourSource::Colour(Vec3::random() * Vec3::random()),
|
|
||||||
1.,
|
|
||||||
)),
|
|
||||||
)));
|
|
||||||
} else if mat < 0.95 {
|
|
||||||
// metal
|
|
||||||
world.push(Arc::new(Sphere::new(
|
|
||||||
center,
|
|
||||||
0.2,
|
|
||||||
Arc::new(Metal::rgb(
|
|
||||||
rng.random_range(0.5..1.),
|
|
||||||
rng.random_range(0.5..1.),
|
|
||||||
rng.random_range(0.5..1.),
|
|
||||||
1.,
|
|
||||||
rng.random_range((0.)..0.5),
|
|
||||||
)),
|
|
||||||
)));
|
|
||||||
} else {
|
|
||||||
// glass
|
|
||||||
world.push(Arc::new(Sphere::new(
|
|
||||||
center,
|
|
||||||
0.2,
|
|
||||||
Arc::new(Dielectric::new(1.5)),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
world.push(Arc::new(Sphere::xyz(
|
|
||||||
0.,
|
|
||||||
1.,
|
|
||||||
0.,
|
|
||||||
1.,
|
|
||||||
Arc::new(Dielectric::new(1.5)),
|
|
||||||
)));
|
|
||||||
world.push(Arc::new(Sphere::xyz(
|
|
||||||
-4.,
|
|
||||||
1.,
|
|
||||||
0.,
|
|
||||||
1.,
|
|
||||||
Arc::new(Lambertian::rgb(0.4, 0.2, 0.1, 1.)),
|
|
||||||
)));
|
|
||||||
world.push(Arc::new(Sphere::xyz(
|
|
||||||
4.,
|
|
||||||
1.,
|
|
||||||
0.,
|
|
||||||
1.,
|
|
||||||
Arc::new(Metal::rgb(0.7, 0.6, 0.5, 1., 0.)),
|
|
||||||
)));
|
|
||||||
|
|
||||||
let mut c = Camera::new();
|
|
||||||
c.set_fov(20.);
|
|
||||||
c.set_anti_alias_rate(4);
|
|
||||||
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.);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
camera: c,
|
|
||||||
objects: world,
|
|
||||||
filename: "random.png".to_string(),
|
|
||||||
image_width: 1920,
|
|
||||||
image_height: 1080,
|
|
||||||
max_depth: 50,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_camera(&self) -> &Camera {
|
|
||||||
&self.camera
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_image_width(&self) -> u32 {
|
|
||||||
self.image_width
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_image_height(&self) -> u32 {
|
|
||||||
self.image_height
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_objects(&self) -> &Vec<Arc<dyn Hittable>> {
|
|
||||||
&self.objects
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_max_depth(&self) -> u32 {
|
|
||||||
self.max_depth
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_filename(&self) -> &String {
|
|
||||||
&self.filename
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(&mut self) {
|
|
||||||
self.camera
|
|
||||||
.init(self.get_image_width(), self.get_image_height());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
use std::f32::consts::PI;
|
|
||||||
|
|
||||||
pub(crate) fn deg_to_rad(deg: f32) -> f32 {
|
|
||||||
deg * PI / 180.
|
|
||||||
}
|
|
||||||
+7
-23
@@ -29,14 +29,6 @@ impl Vec3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_u8(r: u8, b: u8, g: u8) -> Self {
|
|
||||||
Self {
|
|
||||||
x: r as f32 / 255.,
|
|
||||||
y: g as f32 / 255.,
|
|
||||||
z: b as f32 / 255.,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random() -> Self {
|
pub fn random() -> Self {
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
Self {
|
Self {
|
||||||
@@ -67,11 +59,7 @@ impl Vec3 {
|
|||||||
|
|
||||||
pub fn random_unit_hemisphere(n: &Self) -> Self {
|
pub fn random_unit_hemisphere(n: &Self) -> Self {
|
||||||
let v = Self::random_unit();
|
let v = Self::random_unit();
|
||||||
if n.dot(&v) > 0.0 {
|
if n.dot(&v) > 0.0 { v } else { -v }
|
||||||
v
|
|
||||||
} else {
|
|
||||||
-v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn x(&self) -> &f32 {
|
pub fn x(&self) -> &f32 {
|
||||||
@@ -448,20 +436,16 @@ impl Div<Vec3> for Vec3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Div<u32> for Vec3 {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn div(self, u: u32) -> Self::Output {
|
|
||||||
self / u as f32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div<i32> for Vec3 {
|
impl Div<i32> for Vec3 {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn div(self, i: i32) -> Self::Output {
|
fn div(self, i: i32) -> Self::Output {
|
||||||
self / i as f32
|
let f: f32 = i as f32;
|
||||||
|
Self {
|
||||||
|
x: self.x / f,
|
||||||
|
y: self.y / f,
|
||||||
|
z: self.z / f,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 747 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 424 B |
Binary file not shown.
|
Before Width: | Height: | Size: 572 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 323 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 192 B |
Binary file not shown.
|
Before Width: | Height: | Size: 192 B |