ft: tri support
This commit is contained in:
38
scenes/withTriangle.json
Normal file
38
scenes/withTriangle.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"camera": {
|
||||
"image_width": 400,
|
||||
"image_height": 300,
|
||||
"anti_alias_rate": 23,
|
||||
"max_depth": 10,
|
||||
"fov": 20.0,
|
||||
"look_from": { "x": 20, "y": 25, "z": 40 },
|
||||
"look_at": { "x": 0.0, "y": 0.0, "z": -1.0 },
|
||||
"vup": { "x": 0.0, "y": 1.0, "z": 0.0 },
|
||||
"defocus_blur": false,
|
||||
"defocus_angle": 0,
|
||||
"focus_dist": 15.68
|
||||
},
|
||||
"materials": [
|
||||
{ "type": "metal", "albedo": { "x": 0.9, "y": 0.4, "z": 0.2 }, "prob": 1.0, "fuzz": 0.1 },
|
||||
{ "type": "metal", "albedo": { "x": 0.7, "y": 0.4, "z": 0.2 }, "prob": 1.0, "fuzz": 0.1 },
|
||||
{ "type": "lambertian", "albedo": { "x": 0.8, "y": 0.8, "z": 0.0 }, "prob": 1.0 },
|
||||
{ "type": "lambertian", "albedo": { "x": 0.1, "y": 0.2, "z": 0.5 }, "prob": 1.0 },
|
||||
{ "type": "dielectric", "refraction_index": 1.5},
|
||||
{ "type": "dielectric", "refraction_index": 0.67},
|
||||
{ "type": "metal", "albedo": { "x": 0.8, "y": 0.6, "z": 0.2 }, "prob": 1.0, "fuzz": 1.0 }
|
||||
|
||||
],
|
||||
"objects": [
|
||||
{ "type": "sphere", "center": { "x": 0, "y": 0.7, "z": -0.4 }, "radius": 0.2, "material": 0},
|
||||
{ "type": "sphere", "center": { "x": 0.0, "y": 0.5, "z": -0.8 }, "radius": 0.1, "material": 1},
|
||||
{ "type": "sphere", "center": { "x": 0.0, "y": -100.5, "z": -1.0 }, "radius": 100.0, "material": 2},
|
||||
{ "type": "sphere", "center": { "x": 0.0, "y": 0.0, "z": -1.2 }, "radius": 0.5, "material": 3},
|
||||
{ "type": "sphere", "center": { "x": -1, "y": 0, "z": -1 }, "radius": 0.4, "material": 5},
|
||||
{ "type": "sphere", "center": { "x": 1, "y": 0, "z": -1 }, "radius": 0.5, "material": 6},
|
||||
{ "type": "sphere", "center": { "x": 20, "y": 7, "z": -15 }, "radius": 10.5, "material": 0},
|
||||
{ "type": "triangle", "p1": { "x": 0, "y": 0, "z": -4}, "p2": { "x": 0, "y": 0, "z": 4 }, "p3": { "x": 0, "y": 7, "z": 0 }, "material": 0}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ impl Camera {
|
||||
self.v = self.w.cross(&self.u);
|
||||
|
||||
// viewport
|
||||
let viewport_height = 2. * h * self.focus_dist;
|
||||
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_u = viewport_width * self.u;
|
||||
let viewport_v = viewport_height * -self.v;
|
||||
@@ -125,7 +125,7 @@ impl Camera {
|
||||
self.dirty = false;
|
||||
|
||||
if self.defocus_angle != 0. {
|
||||
let defocus_radius = self.focus_dist * deg_to_rad(self.defocus_angle/2.).tan();
|
||||
let defocus_radius = self.focus_dist * deg_to_rad(self.defocus_angle / 2.).tan();
|
||||
self.defocus_disk_u = self.u * defocus_radius;
|
||||
self.defocus_disk_v = self.v * defocus_radius;
|
||||
}
|
||||
@@ -180,7 +180,7 @@ impl Camera {
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
@@ -206,7 +206,11 @@ impl Camera {
|
||||
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_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);
|
||||
@@ -220,7 +224,7 @@ impl Camera {
|
||||
}
|
||||
|
||||
info!("Writing image file...");
|
||||
imgbuf.save("output2.png").unwrap();
|
||||
imgbuf.save("output.png").unwrap();
|
||||
}
|
||||
|
||||
fn ray_colour(&self, hittables: &Vec<Arc<dyn Hittable>>, r: &Ray, depth: u32) -> Colour {
|
||||
|
||||
@@ -18,6 +18,7 @@ use crate::ray::Ray;
|
||||
use crate::scenes::scene::Scene;
|
||||
use crate::vec3::Vec3;
|
||||
use dotenv::dotenv;
|
||||
use log::warn;
|
||||
use rand::RngExt;
|
||||
|
||||
// TODO: implement scene serialization
|
||||
@@ -26,7 +27,8 @@ fn main() {
|
||||
pretty_env_logger::init();
|
||||
|
||||
// TODO: use cli arg for scenefile
|
||||
let json_file = "./scenes/scene.json";
|
||||
let json_file = "./scenes/withTriangle.json";
|
||||
// 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();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod hit;
|
||||
pub mod materials;
|
||||
pub mod sphere;
|
||||
pub mod triangle;
|
||||
pub mod traits;
|
||||
|
||||
79
src/objects/triangle.rs
Normal file
79
src/objects/triangle.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use crate::objects::hit::Hit;
|
||||
use crate::objects::traits::Hittable;
|
||||
use crate::ray::Ray;
|
||||
use crate::{objects::materials::traits::Material, vec3::Vec3};
|
||||
use is_close::default;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Triangle {
|
||||
p1: Vec3,
|
||||
p2: Vec3,
|
||||
p3: Vec3,
|
||||
material: Arc<dyn Material>,
|
||||
normal: Vec3,
|
||||
}
|
||||
|
||||
impl Triangle {
|
||||
pub fn new(p1: Vec3, p2: Vec3, p3: Vec3, material: Arc<dyn Material>) -> Self {
|
||||
Self {
|
||||
p1,
|
||||
p2,
|
||||
p3,
|
||||
material,
|
||||
normal: (p2 - p1).cross(&(p3 - p1)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Triangle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Triangle")
|
||||
.field("p1", &self.p1)
|
||||
.field("p2", &self.p2)
|
||||
.field("p3", &self.p3)
|
||||
.field("material", &self.material)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hittable for Triangle {
|
||||
fn hit(&self, r: &Ray) -> Option<super::hit::Hit> {
|
||||
// check if ray parallel to plane
|
||||
let dot = self.normal.dot(r.dir());
|
||||
if default().is_close(dot, 0.) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let d = (-self.normal).dot(&self.p1);
|
||||
// hitpoint on plane
|
||||
let t = -(self.normal.dot(&(r.origin() + d))) / dot;
|
||||
// hits behind camera
|
||||
if t < 0. {
|
||||
return None;
|
||||
};
|
||||
|
||||
let p = *r.origin() + t * r.dir();
|
||||
let a4 = (self.p3 - self.p1).cross(&(self.p2 - self.p1)).length();
|
||||
let a3 = (self.p3 - p).cross(&(self.p2 - p)).length();
|
||||
let a2 = (self.p3 - p).cross(&(self.p1 - p)).length();
|
||||
let a1 = (self.p2 - p).cross(&(self.p1 - p)).length();
|
||||
|
||||
let mut normal_copy = self.normal.clone();
|
||||
let diff = (a4 - a1 - a2 - a3).abs();
|
||||
if diff < 0.01 {
|
||||
if self.normal.dot(&-r.dir()) < 0. {
|
||||
normal_copy *= -1.; // TODO: vec3 * integer function
|
||||
}
|
||||
return Some(Hit::new(t, p, self.normal, self.material.clone(), true)); // TODO:
|
||||
// front_face calculation; have to change with if check up there iggg
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
fn normal_at(&self, _p: &Vec3) -> Vec3 {
|
||||
// FIXME: might cause ownership issues
|
||||
return self.normal;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use log::warn;
|
||||
use serde::Deserialize;
|
||||
@@ -13,6 +14,7 @@ use crate::{
|
||||
},
|
||||
sphere::Sphere,
|
||||
traits::Hittable,
|
||||
triangle::Triangle,
|
||||
},
|
||||
scenes::raw_camera::RawCamera,
|
||||
vec3::Vec3,
|
||||
@@ -24,7 +26,29 @@ pub struct Scene {
|
||||
pub objects: Vec<Arc<dyn Hittable>>,
|
||||
}
|
||||
|
||||
impl Debug for Scene {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Scene").field("camera", &self.camera).field("materials", &self.materials).field("objects", &self.objects).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new(image_width: u32, image_height: u32) -> Self {
|
||||
Self {
|
||||
camera: Camera::new(image_width, image_height),
|
||||
materials: vec![],
|
||||
objects: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn camera(&mut self) -> &mut Camera {
|
||||
&mut self.camera
|
||||
}
|
||||
|
||||
pub fn add_hittable(&mut self, hittable: Arc<dyn Hittable>) {
|
||||
self.objects.push(hittable);
|
||||
}
|
||||
|
||||
pub fn render(&mut self) {
|
||||
self.camera.render(&self.objects);
|
||||
}
|
||||
@@ -68,10 +92,19 @@ struct RawSphere {
|
||||
pub material: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct RawTriangle {
|
||||
pub p1: Vec3,
|
||||
pub p2: Vec3,
|
||||
pub p3: Vec3,
|
||||
pub material: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
enum HittableDef {
|
||||
Sphere(RawSphere),
|
||||
Triangle(RawTriangle),
|
||||
}
|
||||
|
||||
impl HittableDef {
|
||||
@@ -92,6 +125,21 @@ impl HittableDef {
|
||||
materials.get(s.material as usize).unwrap().clone(),
|
||||
)))
|
||||
}
|
||||
HittableDef::Triangle(t) => {
|
||||
if t.material as usize >= materials.len() {
|
||||
warn!(
|
||||
"Triangle specified nonexistent material {}; skipping...",
|
||||
t.material
|
||||
);
|
||||
return None;
|
||||
}
|
||||
Some(Arc::new(Triangle::new(
|
||||
t.p1,
|
||||
t.p2,
|
||||
t.p3,
|
||||
materials.get(t.material as usize).unwrap().clone(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
32
src/vec3.rs
32
src/vec3.rs
@@ -59,11 +59,7 @@ impl Vec3 {
|
||||
|
||||
pub fn random_unit_hemisphere(n: &Self) -> Self {
|
||||
let v = Self::random_unit();
|
||||
if n.dot(&v) > 0.0 {
|
||||
v
|
||||
} else {
|
||||
-v
|
||||
}
|
||||
if n.dot(&v) > 0.0 { v } else { -v }
|
||||
}
|
||||
|
||||
pub fn x(&self) -> &f32 {
|
||||
@@ -190,6 +186,18 @@ impl Neg for Vec3 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Vec3> for &Vec3 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn add(self, rhs: &Vec3) -> Vec3 {
|
||||
Vec3 {
|
||||
x: self.x + rhs.x,
|
||||
y: self.y + rhs.y,
|
||||
z: self.z + rhs.z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Vec3> for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
@@ -202,6 +210,19 @@ impl Add<Vec3> for Vec3 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<f32> for &Vec3 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn add(self, f: f32) -> Vec3 {
|
||||
Vec3 {
|
||||
x: self.x + f,
|
||||
y: self.y + f,
|
||||
z: self.z + f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Add<f32> for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
@@ -360,7 +381,6 @@ impl Mul<Vec3> for &f32 {
|
||||
z: rhs.z * self,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl MulAssign<Vec3> for Vec3 {
|
||||
|
||||
Reference in New Issue
Block a user