Shadows, New rays and niceness

This commit is contained in:
STP
2023-11-26 04:19:25 -05:00
parent 4e67bbef8d
commit 2e70fc9a68
11 changed files with 284 additions and 198 deletions

View File

@@ -1,35 +1,33 @@
let scene = Scene(); let scene = Scene();
let eye = P(0.0, 0.0, 3.0);
let target = P(0.0, 0.0, 0.0);
let up = V(0.0, 1.0, 3.0);
let cam = Camera(eye, target, up, 70.0);
let material = Material(V(0.5,0.5,0.5), V(0.8, 0.8, 0.8), 25.0); let material = Material(V(0.5,0.5,0.5), V(0.8, 0.8, 0.8), 25.0);
let ambient = Light(P(10.0,0.0,0.0), V(1.0,1.0,1.0), V(0.0, 0.0, 0.0)); //let ambient = Light(P(10.0,0.0,0.0), V(1.0,1.0,1.0), V(0.0, 0.0, 0.0));
//scene.addLight(ambient);
let light2 = Light(P(0.0,0.0,10.0), V(0.0,1.0,1.0), V(0.1, 0.01, 0.001)); let light = Light(P(3.0,3.0,1.0), V(0.0,1.0,1.0), V(0.1, 0.01, 0.001));
scene.addLight(light);
scene.addLight(light2);
scene.addLight(ambient);
// let sphere = Sphere(P(0.0,0.0,0.0), 1.0, material); let sphere = Sphere(P(0.0,0.0,0.0), 1.0, material);
// let sphere_node = Node(sphere); let sphere_node = Node(sphere);
// let child = sphere_node.child(sphere);
// child.translate(V(1.0,1.0,1.0));
// scene.addNode(sphere_node); // scene.addNode(sphere_node);
// scene.addNode(child);
// let cube = CubeUnit(material); // let cube = CubeUnit(material);
// let cube_node = Node(cube); // let cube_node = Node(cube);
// scene.addNode(cube_node); // scene.addNode(cube_node);
let gnonom = Gnonom(material); // let gnonom = Gnonom(material);
let gnonom_node = Node(gnonom); // let gnonom_node = Node(gnonom);
scene.addNode(gnonom_node); // scene.addNode(gnonom_node);
// let cylinder = Cylinder(1.0,1.0, material); let cylinder = Cylinder(1.0,1.0, material);
// let cylinder_node = Node(cylinder); let cylinder_node = Node(cylinder);
// scene.addNode(cylinder_node); cylinder_node.scale(V(4.0,0.3,0.3));
cylinder_node.translate(V(0.0,-4.0,1.0));
scene.addNode(cylinder_node);
scene scene

View File

@@ -1 +0,0 @@

View File

@@ -1,9 +1,4 @@
use crate::ray::Ray; use nalgebra::{Matrix4, Point3, Vector3};
use crate::{EPSILON, INFINITY};
use nalgebra::{Matrix4, Perspective3, Point3, Unit, Vector3};
const ZNEAR: f64 = EPSILON;
const ZFAR: f64 = INFINITY;
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone)] #[derive(Clone)]
@@ -11,8 +6,8 @@ pub struct Camera {
eye: Point3<f64>, eye: Point3<f64>,
target: Point3<f64>, target: Point3<f64>,
up: Vector3<f64>, up: Vector3<f64>,
view: Matrix4<f64>, pub view: Matrix4<f64>,
inv_view: Matrix4<f64>, pub inv_view: Matrix4<f64>,
} }
#[allow(dead_code)] #[allow(dead_code)]

View File

@@ -1,16 +1,24 @@
use crate::{camera::Camera, scene::Scene, state::INIT_FILE, UP_VECTOR_F32, ZERO_VECTOR_F32}; use crate::{
camera::Camera,
light::Light,
primitive::*,
scene::{Node, Scene},
state::INIT_FILE,
UP_VECTOR_F32, ZERO_VECTOR_F32,
};
use imgui::*; use imgui::*;
use nalgebra::{Point3, Vector3}; use nalgebra::{Point3, Vector3};
use pixels::{wgpu, PixelsContext}; use pixels::{wgpu, PixelsContext};
use rhai::Engine;
use std::time::Instant; use std::time::Instant;
const BUFFER_PROPORTION_INIT: f32 = 1.0; const BUFFER_PROPORTION_INIT: f32 = 1.0;
const BUFFER_PROPORTION_MIN: f32 = 0.5; const BUFFER_PROPORTION_MIN: f32 = 0.5;
const BUFFER_PROPORTION_MAX: f32 = 1.0; const BUFFER_PROPORTION_MAX: f32 = 1.0;
const RAYS_INIT: i32 = 9000; const RAYS_INIT: i32 = 1000;
const RAYS_MIN: i32 = 100; const RAYS_MIN: i32 = 100;
const RAYS_MAX: i32 = 10000; const RAYS_MAX: i32 = 100000;
const CAMERA_MIN_FOV: f32 = 10.0; const CAMERA_MIN_FOV: f32 = 10.0;
const CAMERA_MAX_FOV: f32 = 160.0; const CAMERA_MAX_FOV: f32 = 160.0;
@@ -18,7 +26,7 @@ const CAMERA_INIT: f32 = 5.0;
/// Manages all state required for rendering Dear ImGui over `Pixels`test. /// Manages all state required for rendering Dear ImGui over `Pixels`test.
pub enum GuiEvent { pub enum GuiEvent {
BufferResize(f32), BufferResize(f32, f32),
CameraUpdate(Camera), CameraUpdate(Camera),
SceneLoad(Scene), SceneLoad(Scene),
} }
@@ -34,6 +42,7 @@ pub struct Gui {
script_filename: String, script_filename: String,
script: String, script: String,
engine: Engine,
scene: Scene, scene: Scene,
pub ray_num: i32, pub ray_num: i32,
@@ -63,7 +72,7 @@ impl Gui {
// Configure Dear ImGui fonts // Configure Dear ImGui fonts
let hidpi_factor = window.scale_factor(); let hidpi_factor = window.scale_factor();
let font_size = (11.0 * hidpi_factor) as f32; let font_size = (16.0 * hidpi_factor) as f32;
imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32; imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
imgui imgui
.fonts() .fonts()
@@ -96,6 +105,7 @@ impl Gui {
script_filename: String::from(INIT_FILE), script_filename: String::from(INIT_FILE),
script: String::new(), script: String::new(),
engine: init_engine(),
scene: Scene::empty(), scene: Scene::empty(),
ray_num: RAYS_INIT, ray_num: RAYS_INIT,
@@ -156,9 +166,13 @@ impl Gui {
BUFFER_PROPORTION_MAX, BUFFER_PROPORTION_MAX,
&mut self.buffer_proportion, &mut self.buffer_proportion,
); );
ui.slider("fov", CAMERA_MIN_FOV, CAMERA_MAX_FOV, &mut self.camera_fov);
//Apply changes //Apply changes
if ui.button("Apply") { if ui.button("Apply") {
self.event = Some(GuiEvent::BufferResize(self.buffer_proportion)); self.event = Some(GuiEvent::BufferResize(
self.buffer_proportion,
self.camera_fov,
));
}; };
} }
//Camera options //Camera options
@@ -167,7 +181,6 @@ impl Gui {
ui.input_float3("Eye", &mut self.camera_eye).build(); ui.input_float3("Eye", &mut self.camera_eye).build();
ui.input_float3("Target", &mut self.camera_target).build(); ui.input_float3("Target", &mut self.camera_target).build();
ui.input_float3("Up", &mut self.camera_up).build(); ui.input_float3("Up", &mut self.camera_up).build();
ui.slider("fov", CAMERA_MIN_FOV, CAMERA_MAX_FOV, &mut self.camera_fov);
// Create three input fields for x, y, and z components // Create three input fields for x, y, and z components
if ui.button("Apply Camera") { if ui.button("Apply Camera") {
println!("Camera changed: {:?}", self.camera_eye); println!("Camera changed: {:?}", self.camera_eye);
@@ -175,14 +188,10 @@ impl Gui {
let (ex, ey, ez) = (eye[0] as f64, eye[1] as f64, eye[2] as f64); let (ex, ey, ez) = (eye[0] as f64, eye[1] as f64, eye[2] as f64);
let (tx, ty, tz) = (target[0] as f64, target[1] as f64, target[2] as f64); let (tx, ty, tz) = (target[0] as f64, target[1] as f64, target[2] as f64);
let (ux, uy, uz) = (up[0] as f64, up[1] as f64, up[2] as f64); let (ux, uy, uz) = (up[0] as f64, up[1] as f64, up[2] as f64);
let camera = Camera::new( let camera = Camera::new(
Point3::new(ex, ey, ez), Point3::new(ex, ey, ez),
Point3::new(tx, ty, tz), Point3::new(tx, ty, tz),
Vector3::new(ux, uy, uz), Vector3::new(ux, uy, uz),
1,
1,
self.camera_fov as f64,
); );
self.event = Some(GuiEvent::CameraUpdate(camera)); self.event = Some(GuiEvent::CameraUpdate(camera));
} }
@@ -199,7 +208,7 @@ impl Gui {
} }
} }
if ui.button("Apply script") { if ui.button("Apply script") {
match Scene::from_rhai(&self.script) { match self.engine.eval(&self.script) {
Ok(scene) => { Ok(scene) => {
self.scene = scene; self.scene = scene;
self.event = Some(GuiEvent::SceneLoad(self.scene.clone())); self.event = Some(GuiEvent::SceneLoad(self.scene.clone()));
@@ -208,7 +217,7 @@ impl Gui {
} }
} }
//Script block //Script block
ui.input_text_multiline("script", &mut self.script, [500., 900.]) ui.input_text_multiline("script", &mut self.script, [600., 1500.])
.build(); .build();
} }
@@ -244,3 +253,74 @@ impl Gui {
.handle_event(self.imgui.io_mut(), window, event); .handle_event(self.imgui.io_mut(), window, event);
} }
} }
pub fn init_engine() -> Engine {
let mut engine = Engine::new();
engine
.register_type::<Vector3<f64>>()
.register_fn("V", Vector3::<f64>::new);
engine
.register_type::<Point3<f64>>()
.register_fn("P", Point3::<f64>::new);
engine
.register_type::<Camera>()
.register_fn("Camera", Camera::new);
engine
.register_type::<Scene>()
.register_fn("Scene", Scene::empty)
.register_fn("addNode", Scene::add_node)
.register_fn("addLight", Scene::add_light);
engine
.register_type::<Node>()
.register_fn("Node", Node::new)
.register_fn("translate", Node::translate)
.register_fn("rotate", Node::rotate)
.register_fn("scale", Node::scale)
.register_fn("child", Node::child);
engine
.register_type::<Light>()
.register_fn("Light", Light::new);
engine
.register_type::<Material>()
.register_fn("Material", Material::new)
.register_fn("MaterialRed", Material::red)
.register_fn("MaterialBlue", Material::blue)
.register_fn("MaterialGreen", Material::green)
.register_fn("MaterialMagenta", Material::magenta)
.register_fn("MaterialTurquoise", Material::turquoise);
engine
.register_type::<Sphere>()
.register_fn("Sphere", Sphere::new)
.register_fn("SphereUnit", Sphere::unit);
engine
.register_type::<Cube>()
.register_fn("Cube", Cube::new)
.register_fn("CubeUnit", Cube::unit);
engine
.register_type::<Cone>()
.register_fn("Cone", Cone::new)
.register_fn("ConeUnit", Cone::unit);
engine
.register_type::<Cylinder>()
.register_fn("Cylinder", Cylinder::new);
engine
.register_type::<Circle>()
.register_fn("Circle", Circle::new)
.register_fn("CircleUnit", Circle::unit);
engine
.register_type::<Rectangle>()
.register_fn("Rectangle", Rectangle::new)
.register_fn("RectangleUnit", Rectangle::unit);
engine
.register_type::<SteinerSurface>()
.register_fn("Steiner", SteinerSurface::new);
engine
.register_type::<Torus>()
.register_fn("Torus", Torus::new);
engine
.register_type::<Gnonom>()
.register_fn("Gnonom", Gnonom::new);
engine
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -1,7 +1,7 @@
use crate::state::run; use crate::state::run;
use error_iter::ErrorIter; use error_iter::ErrorIter;
const EPSILON: f64 = 1e-9; const EPSILON: f64 = 1e-6;
const INFINITY: f64 = f64::MAX; const INFINITY: f64 = f64::MAX;
const EPSILON_VECTOR: Vector3<f64> = Vector3::new(EPSILON, EPSILON, EPSILON); const EPSILON_VECTOR: Vector3<f64> = Vector3::new(EPSILON, EPSILON, EPSILON);
static ZERO_VECTOR: Vector3<f64> = Vector3::new(0.0, 0.0, 0.0); static ZERO_VECTOR: Vector3<f64> = Vector3::new(0.0, 0.0, 0.0);

View File

@@ -1,8 +1,8 @@
#[allow(dead_code)] #[allow(dead_code)]
use crate::ray::Ray; use crate::ray::Ray;
use crate::{EPSILON, EPSILON_VECTOR, INFINITY}; use crate::{EPSILON, EPSILON_VECTOR, INFINITY};
use nalgebra::{distance, Point3, Unit, Vector3}; use nalgebra::{distance, Matrix4, Point3, Vector3};
use roots::{find_roots_cubic, find_roots_quadratic, find_roots_quartic, Roots}; use roots::{find_roots_quadratic, find_roots_quartic, Roots};
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use std::sync::Arc; use std::sync::Arc;
@@ -54,11 +54,22 @@ impl Material {
pub struct Intersection { pub struct Intersection {
// Information about an intersection // Information about an intersection
pub point: Point3<f64>, pub point: Point3<f64>,
pub normal: Unit<Vector3<f64>>, pub normal: Vector3<f64>,
pub incidence: Unit<Vector3<f64>>, pub incidence: Vector3<f64>,
pub material: Arc<Material>, pub material: Arc<Material>,
pub distance: f64, pub distance: f64,
} }
impl Intersection {
pub fn transform(&self, trans: &Matrix4<f64>, inv_trans: &Matrix4<f64>) -> Intersection {
Intersection {
point: trans.transform_point(&self.point),
normal: inv_trans.transpose().transform_vector(&self.normal),
incidence: trans.transform_vector(&self.incidence),
material: self.material.clone(),
distance: self.distance,
}
}
}
// BOUNDING BOX ----------------------------------------------------------------- // BOUNDING BOX -----------------------------------------------------------------
#[derive(Clone)] #[derive(Clone)]
@@ -154,7 +165,7 @@ impl Primitive for Sphere {
}; };
let intersect = ray.at_t(t); let intersect = ray.at_t(t);
let normal = Unit::new_normalize(intersect - self.position); let normal = (intersect - self.position).normalize();
Some(Intersection { Some(Intersection {
point: intersect, point: intersect,
normal, normal,
@@ -238,7 +249,7 @@ impl Primitive for Circle {
false => { false => {
return Some(Intersection { return Some(Intersection {
point: intersect, point: intersect,
normal: Unit::new_normalize(self.normal), normal: self.normal.normalize(),
incidence: ray.b, incidence: ray.b,
material: Arc::clone(&self.material), material: Arc::clone(&self.material),
distance: t, distance: t,
@@ -329,9 +340,9 @@ impl Primitive for Cylinder {
let normal = Vector3::new(2.0 * intersect.x, 0.0, 2.0 * intersect.z); let normal = Vector3::new(2.0 * intersect.x, 0.0, 2.0 * intersect.z);
Some(Intersection { Some(Intersection {
point: intersect, point: intersect,
normal: Unit::new_normalize(normal), normal: normal,
material: Arc::clone(&self.material),
incidence: ray.b, incidence: ray.b,
material: Arc::clone(&self.material),
distance: t, distance: t,
}) })
} else { } else {
@@ -462,9 +473,9 @@ impl Primitive for Cone {
match intersect.y >= self.base && intersect.y <= self.apex { match intersect.y >= self.base && intersect.y <= self.apex {
true => Some(Intersection { true => Some(Intersection {
point: intersect, point: intersect,
normal: Unit::new_normalize(self.get_normal(intersect)), normal: self.get_normal(intersect),
material: Arc::clone(&self.material),
incidence: ray.b, incidence: ray.b,
material: Arc::clone(&self.material),
distance: t, distance: t,
}), }),
false => None, false => None,
@@ -568,7 +579,7 @@ impl Primitive for Rectangle {
if pi_dot_r1 >= -w2 && pi_dot_r1 <= w2 && pi_dot_r2 >= -h2 && pi_dot_r2 <= h2 { if pi_dot_r1 >= -w2 && pi_dot_r1 <= w2 && pi_dot_r2 >= -h2 && pi_dot_r2 <= h2 {
return Some(Intersection { return Some(Intersection {
point: intersect, point: intersect,
normal: Unit::new_normalize(self.normal), normal: self.normal,
incidence: ray.b, incidence: ray.b,
material: Arc::clone(&self.material), material: Arc::clone(&self.material),
distance: t, distance: t,
@@ -657,7 +668,7 @@ impl Primitive for Cube {
Some(Intersection { Some(Intersection {
point: intersect, point: intersect,
normal: Unit::new_normalize(normal), normal: normal,
incidence: ray.b, incidence: ray.b,
material: Arc::clone(&self.material), material: Arc::clone(&self.material),
distance: tmin, distance: tmin,
@@ -748,7 +759,7 @@ impl Primitive for Triangle {
{ {
Some(Intersection { Some(Intersection {
point: intersect, point: intersect,
normal: Unit::new_normalize(normal), normal: normal,
incidence: ray.b, incidence: ray.b,
material: Arc::clone(&self.material), material: Arc::clone(&self.material),
distance: t, distance: t,
@@ -958,11 +969,12 @@ impl Primitive for SteinerSurface {
//Now we have the smallest non-zero t //Now we have the smallest non-zero t
let point = ray.at_t(t); let point = ray.at_t(t);
let (x, y, z) = (point.x, point.y, point.z); let (x, y, z) = (point.x, point.y, point.z);
let normal = Unit::new_normalize(Vector3::new( let normal = Vector3::new(
2.0 * x * y * y + 2.0 * x * z * z + y * z, 2.0 * x * y * y + 2.0 * x * z * z + y * z,
2.0 * x * x * y + 2.0 * z * z * y + x * z, 2.0 * x * x * y + 2.0 * z * z * y + x * z,
2.0 * x * x * z + 2.0 * z * y * y + x * y, 2.0 * x * x * z + 2.0 * z * y * y + x * y,
)); )
.normalize();
Some(Intersection { Some(Intersection {
point, point,
@@ -1106,7 +1118,7 @@ impl Primitive for Torus {
-4.0 * (r2.powf(2.0) - r1.powf(2.0) + x.powf(2.0) + y.powf(2.0) + z.powf(2.0)) * r1; -4.0 * (r2.powf(2.0) - r1.powf(2.0) + x.powf(2.0) + y.powf(2.0) + z.powf(2.0)) * r1;
let dz = -8.0 * r2.powf(2.0) * x let dz = -8.0 * r2.powf(2.0) * x
+ 4.0 * (r2.powf(2.0) - r1.powf(2.0) + x.powf(2.0) + y.powf(2.0) + z.powf(2.0)) * x; + 4.0 * (r2.powf(2.0) - r1.powf(2.0) + x.powf(2.0) + y.powf(2.0) + z.powf(2.0)) * x;
let normal = Unit::new_normalize(Vector3::new(dx, dy, dz)); let normal = Vector3::new(dx, dy, dz).normalize();
Some(Intersection { Some(Intersection {
point, point,

View File

@@ -1,83 +1,102 @@
use crate::{ use crate::{
primitive::Intersection, primitive::{self, Intersection},
raytracer::phong_shade_point, raytracer::phong_shade_point,
scene::{Node, Scene}, scene::{Node, Scene},
EPSILON, INFINITY, EPSILON, INFINITY,
}; };
use nalgebra::{Point3, Unit, Vector3}; use nalgebra::{Matrix4, Point3, Vector3};
#[derive(Clone)] #[derive(Clone)]
pub struct Ray { pub struct Ray {
pub a: Point3<f64>, pub a: Point3<f64>,
pub b: Unit<Vector3<f64>>, pub b: Vector3<f64>,
} }
impl Ray { impl Ray {
pub fn new(a: Point3<f64>, b: Unit<Vector3<f64>>) -> Ray { pub fn new(a: Point3<f64>, b: Vector3<f64>) -> Ray {
Ray { a, b } Ray {
a,
b: b.normalize(),
}
} }
pub fn unit() -> Ray { pub fn unit() -> Ray {
let a = Point3::new(0.0, 0.0, 0.0); let a = Point3::new(0.0, 0.0, 0.0);
let b = Unit::new_normalize(Vector3::new(0.0, 1.0, 0.0)); let b = Vector3::new(0.0, 1.0, 0.0);
Ray { a, b } Ray { a, b }
} }
pub fn at_t(&self, t: f64) -> Point3<f64> { pub fn at_t(&self, t: f64) -> Point3<f64> {
self.a + self.b.into_inner() * (t + EPSILON) self.a + self.b * t
} }
//Shade a single ray //Shade a single ray
pub fn shade_ray(&self, scene: &Scene) -> Option<Vector3<u8>> { pub fn shade_ray(&self, scene: &Scene) -> Option<Vector3<u8>> {
let intersect = self.get_closest_intersection(&scene.nodes); let intersect = self.get_closest_intersection(&scene.nodes);
match intersect { match intersect {
Some(intersect) => Some(phong_shade_point(&scene, &intersect)), Some(intersect) => Some(phong_shade_point(&scene, &intersect)),
None => None, None => None,
} }
} }
// Find the closest intersection, given a ray in world coordinates // Find the closest intersection
pub fn get_closest_intersection(&self, nodes: &Vec<Node>) -> Option<Intersection> { pub fn get_closest_intersection(&self, nodes: &Vec<Node>) -> Option<Intersection> {
let mut closest_distance = INFINITY; let mut closest_distance = INFINITY;
let mut closest_intersect: Option<Intersection> = None; let mut closest_intersect: Option<Intersection> = None;
for node in nodes { for node in nodes {
let primitive = node.primitive.clone(); let primitive = node.primitive.clone();
let trans = node.trans;
let inv_trans = node.inv_trans;
if let Some(intersect) = primitive.intersect_ray(self) { //Transform ray to view coords
let ray = self.transform(&node.inv_viewmodel);
if primitive.intersect_bounding_box(&ray).is_some() {
if let Some(intersect) = primitive.intersect_ray(&ray) {
if intersect.distance < closest_distance { if intersect.distance < closest_distance {
closest_distance = intersect.distance; closest_distance = intersect.distance;
//Convert back to world coords
let intersect = intersect.transform(&node.model, &node.inv_model);
closest_intersect = Some(intersect); closest_intersect = Some(intersect);
} }
} }
} }
}
closest_intersect closest_intersect
} }
pub fn transform(&self, trans: &Matrix4<f64>) -> Ray {
Ray {
a: trans.transform_point(&self.a),
b: trans.transform_vector(&self.b),
}
}
pub fn cast_rays(fovy: f64, width: u32, height: u32) -> Vec<Ray> { pub fn cast_rays(fovy: f64, width: u32, height: u32) -> Vec<Ray> {
let aspect = width as f64 / height as f64; let aspect = width as f64 / height as f64;
let fovy_radians = fovy.to_radians(); let fovy_radians = fovy.to_radians();
//Verify this part later let fovh_radians = 2.0 * ((fovy_radians / 2.0).tan() * aspect).atan();
let dir = Vector3::new(0.0, 0.0, 1.0); let dir = Vector3::new(0.0, 0.0, 1.0);
let up = Vector3::new(0.0, 1.0, 0.0); let up = Vector3::new(0.0, 1.0, 0.0);
let hor = Vector3::new(1.0, 0.0, 0.0); let hor = Vector3::new(1.0, 0.0, 0.0);
let half_height = fovy_radians.tan(); let vheight = 2.0 * (fovy_radians / 2.0).tan();
let half_width = aspect * half_height; let vwidth = 2.0 * (fovh_radians / 2.0).tan();
let d_hor_vec = hor * (2.0 * half_width / width as f64) as f64; let d_hor_vec = hor * (vwidth / width as f64) as f64;
let d_vert_vec = up * (2.0 * half_height / height as f64) as f64; let d_vert_vec = up * (vheight / height as f64) as f64;
//All good let half_width = width / 2;
let half_height = height / 2;
let mut rays = Vec::with_capacity(width as usize * height as usize); let mut rays = Vec::with_capacity(width as usize * height as usize);
for j in 0..height as i32 { for j in 0..height as i32 {
for i in 0..width as i32 { for i in 0..width as i32 {
let horizontal = (i - half_width as i32) as f64 * (d_hor_vec); let x = i - half_width as i32;
let vertical = (-j + half_height as i32) as f64 * (d_vert_vec); let y = -j + half_height as i32;
let horizontal = x as f64 * d_hor_vec;
let vertical = y as f64 * (d_vert_vec);
let direction = dir + horizontal + vertical; let direction = dir + horizontal + vertical;
let ray = Ray::new(Point3::new(0.0, 0.0, 0.0), Unit::new_normalize(direction)); let ray = Ray::new(Point3::new(0.0, 0.0, 0.0), direction);
rays.push(ray); rays.push(ray);
} }
} }

View File

@@ -1,4 +1,4 @@
use crate::{light::Light, primitive::Intersection, scene::*, ZERO_VECTOR}; use crate::{light::Light, primitive::Intersection, ray::Ray, scene::*, EPSILON, ZERO_VECTOR};
use nalgebra::{Unit, Vector3}; use nalgebra::{Unit, Vector3};
@@ -11,6 +11,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
material, material,
.. ..
} = intersect; } = intersect;
let kd = material.kd; let kd = material.kd;
let ks = material.ks; let ks = material.ks;
let shininess = material.shininess; let shininess = material.shininess;
@@ -28,9 +29,15 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
// Point to light // Point to light
let to_light = light_position - point; let to_light = light_position - point;
let light_distance = to_light.norm(); let light_distance = to_light.norm();
let to_light = Unit::new_normalize(to_light); let to_light = to_light;
let to_light_ray = Ray::new(point.clone() + normal * EPSILON, to_light);
if light_blocked(scene, to_light_ray) {
continue;
}
// Point to camera // Point to camera
let to_camera = Unit::new_normalize(-incidence.into_inner()); let to_camera = -incidence;
// Diffuse component // Diffuse component
let n_dot_l = normal.dot(&to_light).max(0.0); let n_dot_l = normal.dot(&to_light).max(0.0);
let diffuse = n_dot_l * kd; let diffuse = n_dot_l * kd;
@@ -58,3 +65,15 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
let (r, g, b) = (colour.x as u8, colour.y as u8, colour.z as u8); let (r, g, b) = (colour.x as u8, colour.y as u8, colour.z as u8);
Vector3::new(r, g, b) Vector3::new(r, g, b)
} }
fn light_blocked(scene: &Scene, ray: Ray) -> bool {
for node in &scene.nodes {
let ray = ray.transform(&node.inv_model);
if node.primitive.intersect_bounding_box(&ray).is_some() {
if node.primitive.intersect_ray(&ray).is_some() {
return true;
}
}
}
false
}

View File

@@ -1,38 +1,66 @@
use crate::camera::Camera; use crate::camera::Camera;
use crate::light::Light; use crate::light::Light;
use crate::primitive::*; use crate::primitive::*;
use nalgebra::{Matrix4, Point3, Vector3}; use nalgebra::{Matrix4, Vector3};
use rhai::{Engine, EvalAltResult};
use std::sync::Arc; use std::sync::Arc;
#[derive(Clone)] #[derive(Clone)]
pub struct Node { pub struct Node {
pub primitive: Arc<dyn Primitive>, pub primitive: Arc<dyn Primitive>,
pub model_transform: Matrix4<f32>, pub model: Matrix4<f64>,
pub inv_model: Matrix4<f64>,
pub viewmodel: Matrix4<f64>,
pub inv_viewmodel: Matrix4<f64>,
} }
impl Node { impl Node {
pub fn new(primitive: Arc<dyn Primitive>) -> Self { pub fn new(primitive: Arc<dyn Primitive>) -> Node {
Node { Node {
primitive, primitive,
model_transform: Matrix4::identity(), model: Matrix4::identity(),
inv_model: Matrix4::identity(),
viewmodel: Matrix4::identity(),
inv_viewmodel: Matrix4::identity(),
} }
} }
pub fn rotate(&mut self, roll: f32, pitch: f32, yaw: f32) { pub fn rotate(&mut self, roll: f64, pitch: f64, yaw: f64) {
let roll = (roll as f64).to_radians() as f32; let roll = roll.to_radians();
let pitch = (pitch as f64).to_radians() as f32; let pitch = pitch.to_radians();
let yaw = (yaw as f64).to_radians() as f32; let yaw = yaw.to_radians();
let rotation_matrix = Matrix4::from_euler_angles(roll, pitch, yaw); let rotation_matrix = Matrix4::from_euler_angles(roll, pitch, yaw);
self.model_transform = rotation_matrix * self.model_transform; self.model = rotation_matrix * self.model;
self.inv_model = self.model.try_inverse().unwrap();
self.viewmodel = rotation_matrix * self.viewmodel;
self.inv_viewmodel = self.inv_viewmodel.try_inverse().unwrap();
} }
pub fn translate(&mut self, translation: &Vector3<f32>) { pub fn translate(&mut self, translation: Vector3<f64>) {
let translation_matrix = Matrix4::new_translation(translation); let translation_matrix = Matrix4::new_translation(&translation);
self.model_transform = translation_matrix * self.model_transform; self.model = translation_matrix * self.model;
self.inv_model = self.model.try_inverse().unwrap();
self.viewmodel = translation_matrix * self.viewmodel;
self.inv_viewmodel = self.inv_viewmodel.try_inverse().unwrap();
} }
pub fn scale(&mut self, scale: &Vector3<f32>) { pub fn scale(&mut self, scale: Vector3<f64>) {
let scale_matrix = Matrix4::new_nonuniform_scaling(scale); let scale_matrix = Matrix4::new_nonuniform_scaling(&scale);
self.model_transform = scale_matrix * self.model_transform; self.model = scale_matrix * self.model;
self.inv_model = self.model.try_inverse().unwrap();
self.viewmodel = scale_matrix * self.viewmodel;
self.inv_viewmodel = self.inv_viewmodel.try_inverse().unwrap();
}
pub fn child(self, primitive: Arc<dyn Primitive>) -> Node {
Node {
primitive,
model: self.model,
inv_model: self.inv_model,
viewmodel: self.model,
inv_viewmodel: self.inv_model,
}
}
pub fn compute(&mut self, view: &Matrix4<f64>, inv_view: &Matrix4<f64>) {
self.viewmodel = view * self.model;
self.inv_viewmodel = self.inv_model * inv_view;
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Scene { pub struct Scene {
pub nodes: Vec<Node>, pub nodes: Vec<Node>,
@@ -51,94 +79,21 @@ impl Scene {
cameras: Vec::new(), cameras: Vec::new(),
} }
} }
fn add_node(&mut self, node: Node) { pub fn add_node(&mut self, node: Node) {
self.nodes.push(node); self.nodes.push(node);
} }
fn add_material(&mut self, material: Material) { pub fn add_material(&mut self, material: Material) {
self.materials.push(material); self.materials.push(material);
} }
fn add_light(&mut self, light: Light) { pub fn add_light(&mut self, light: Light) {
self.lights.push(light); self.lights.push(light);
} }
fn add_camera(&mut self, camera: Camera) { pub fn add_camera(&mut self, camera: Camera) {
self.cameras.push(camera); self.cameras.push(camera);
} }
pub fn compute(&mut self, view: &Matrix4<f64>, inv_view: &Matrix4<f64>) {
pub fn from_rhai(script: &str) -> Result<Scene, Box<EvalAltResult>> { for node in &mut self.nodes {
let mut engine = Engine::new(); node.compute(view, inv_view);
}
engine
.register_type::<Vector3<f64>>()
.register_fn("V", Vector3::<f64>::new);
engine
.register_type::<Point3<f64>>()
.register_fn("P", Point3::<f64>::new);
engine
.register_type::<Scene>()
.register_fn("Scene", Scene::empty)
.register_fn("addNode", Scene::add_node)
.register_fn("addLight", Scene::add_light);
engine
.register_type::<Node>()
.register_fn("Node", Node::new)
.register_fn("translate", Node::translate)
.register_fn("rotate", Node::rotate)
.register_fn("scale", Node::scale);
engine
.register_type::<Camera>()
.register_fn("Camera", Camera::new_sizeless);
engine
.register_type::<Light>()
.register_fn("Light", Light::new);
engine
.register_type::<Material>()
.register_fn("Material", Material::new)
.register_fn("MaterialRed", Material::red)
.register_fn("MaterialBlue", Material::blue)
.register_fn("MaterialGreen", Material::green)
.register_fn("MaterialMagenta", Material::magenta)
.register_fn("MaterialTurquoise", Material::turquoise);
engine
.register_type::<Sphere>()
.register_fn("Sphere", Sphere::new)
.register_fn("SphereUnit", Sphere::unit);
engine
.register_type::<Cube>()
.register_fn("Cube", Cube::new)
.register_fn("CubeUnit", Cube::unit);
engine
.register_type::<Cone>()
.register_fn("Cone", Cone::new)
.register_fn("ConeUnit", Cone::unit);
engine
.register_type::<Cylinder>()
.register_fn("Cylinder", Cylinder::new);
engine
.register_type::<Circle>()
.register_fn("Circle", Circle::new)
.register_fn("CircleUnit", Circle::unit);
engine
.register_type::<Rectangle>()
.register_fn("Rectangle", Rectangle::new)
.register_fn("RectangleUnit", Rectangle::unit);
engine
.register_type::<SteinerSurface>()
.register_fn("Steiner", SteinerSurface::new);
engine
.register_type::<Torus>()
.register_fn("Torus", Torus::new);
engine
.register_type::<AdamShape>()
.register_fn("Adam", AdamShape::new);
engine
.register_type::<AdamShape2>()
.register_fn("Adam2", AdamShape2::new);
engine
.register_type::<AdamShape3>()
.register_fn("Adam3", AdamShape3::new);
let scene: Scene = engine.eval(script.into())?;
Ok(scene)
} }
} }

View File

@@ -1,6 +1,7 @@
//Use linear algebra module //Use linear algebra module
use crate::camera::Camera; use crate::camera::Camera;
use crate::ray::Ray;
use crate::{gui::Gui, scene::Scene}; use crate::{gui::Gui, scene::Scene};
use crate::{gui::GuiEvent, log_error}; use crate::{gui::GuiEvent, log_error};
@@ -35,6 +36,7 @@ pub struct State {
pixels: Arc<Mutex<Pixels>>, pixels: Arc<Mutex<Pixels>>,
gui: Gui, gui: Gui,
rays: Vec<Ray>,
ray_queue: Vec<usize>, ray_queue: Vec<usize>,
} }
@@ -43,6 +45,7 @@ impl State {
let scene = Scene::empty(); let scene = Scene::empty();
let window_size = window.inner_size(); let window_size = window.inner_size();
let camera = Camera::unit(); let camera = Camera::unit();
let rays = Vec::new();
Self { Self {
scene, scene,
@@ -52,6 +55,7 @@ impl State {
buffer_height: window_size.height as u32, buffer_height: window_size.height as u32,
pixels: Arc::new(Mutex::new(pixels)), pixels: Arc::new(Mutex::new(pixels)),
gui, gui,
rays,
ray_queue: Vec::new(), ray_queue: Vec::new(),
} }
} }
@@ -59,25 +63,33 @@ impl State {
fn update(&mut self) -> Result<(), Box<dyn Error>> { fn update(&mut self) -> Result<(), Box<dyn Error>> {
if let Some(event) = self.gui.event.take() { if let Some(event) = self.gui.event.take() {
match event { match event {
GuiEvent::BufferResize(proportion) => self.resize_buffer(proportion)?, GuiEvent::BufferResize(proportion, fov) => {
GuiEvent::CameraUpdate(camera) => self.set_camera(camera)?, self.resize_buffer(proportion, fov as f64)?
}
GuiEvent::CameraUpdate(camera) => {
self.camera = camera;
self.clear()?;
self.reset_queue();
}
GuiEvent::SceneLoad(scene) => { GuiEvent::SceneLoad(scene) => {
self.scene = scene; self.scene = scene;
self.clear()?; self.reset_queue();
} }
} }
}; };
Ok(()) Ok(())
} }
fn resize_buffer(&mut self, proportion: f32) -> Result<(), Box<dyn Error>> { fn resize_buffer(&mut self, proportion: f32, fovy: f64) -> Result<(), Box<dyn Error>> {
let size = self.window.inner_size(); let size = self.window.inner_size();
self.buffer_width = (size.width as f32 * proportion) as u32; self.buffer_width = (size.width as f32 * proportion) as u32;
self.buffer_height = (size.height as f32 * proportion) as u32; self.buffer_height = (size.height as f32 * proportion) as u32;
self.camera.set_size(self.buffer_width, self.buffer_height);
self.clear()?; self.clear()?;
self.reset_queue();
self.rays = Ray::cast_rays(fovy, self.buffer_width, self.buffer_height);
let mut pixels = self.pixels.lock().unwrap(); let mut pixels = self.pixels.lock().unwrap();
pixels.resize_buffer(self.buffer_width, self.buffer_height)?; pixels.resize_buffer(self.buffer_width, self.buffer_height)?;
@@ -85,6 +97,9 @@ impl State {
} }
fn resize(&mut self, size: &PhysicalSize<u32>) -> Result<(), Box<dyn Error>> { fn resize(&mut self, size: &PhysicalSize<u32>) -> Result<(), Box<dyn Error>> {
self.buffer_width = (size.width) as u32;
self.buffer_height = (size.height) as u32;
self.reset_queue();
let mut pixels = self.pixels.lock().unwrap(); let mut pixels = self.pixels.lock().unwrap();
pixels.resize_surface(size.width, size.height)?; pixels.resize_surface(size.width, size.height)?;
Ok(()) Ok(())
@@ -105,7 +120,7 @@ impl State {
//Get random index from queue //Get random index from queue
let index = self.ray_queue.pop().unwrap(); let index = self.ray_queue.pop().unwrap();
//Shade colour for selected ray //Shade colour for selected ray
let colour = &self.camera.rays[index].shade_ray(&self.scene); let colour = &self.rays[index].shade_ray(&self.scene);
//Assign colour to frame //Assign colour to frame
let rgba = colour.map_or(COLOUR_CLEAR, |colour| [colour.x, colour.y, colour.z, 255]); let rgba = colour.map_or(COLOUR_CLEAR, |colour| [colour.x, colour.y, colour.z, 255]);
let mut pixels = self.pixels.lock().unwrap(); let mut pixels = self.pixels.lock().unwrap();
@@ -128,19 +143,12 @@ impl State {
Ok(()) Ok(())
} }
fn set_camera(&mut self, camera: Camera) -> Result<(), Box<dyn Error>> {
self.clear()?;
self.reset_queue();
self.camera = camera;
self.camera.set_size(self.buffer_width, self.buffer_height);
Ok(())
}
fn reset_queue(&mut self) { fn reset_queue(&mut self) {
let size = self.buffer_height as usize * self.buffer_width as usize; let size = self.buffer_height as usize * self.buffer_width as usize;
let mut ray_queue: Vec<usize> = (0..size).collect(); let mut ray_queue: Vec<usize> = (0..size).collect();
ray_queue.shuffle(&mut thread_rng()); ray_queue.shuffle(&mut thread_rng());
self.ray_queue = ray_queue; self.ray_queue = ray_queue;
self.scene.compute(&self.camera.view, &self.camera.inv_view);
} }
fn render(&mut self) -> Result<(), Box<dyn Error>> { fn render(&mut self) -> Result<(), Box<dyn Error>> {
@@ -171,7 +179,7 @@ pub fn run() -> Result<(), Box<dyn Error>> {
let gui = Gui::new(&window, &pixels); let gui = Gui::new(&window, &pixels);
let mut state = State::new(window, pixels, gui); let mut state = State::new(window, pixels, gui);
state.resize_buffer(1.0)?; state.resize_buffer(1.0, 90.0)?;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
state.gui.handle_event(&state.window, &event); state.gui.handle_event(&state.window, &event);
@@ -186,6 +194,7 @@ pub fn run() -> Result<(), Box<dyn Error>> {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
} }
} }
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
if let Err(_e) = state.render() { if let Err(_e) = state.render() {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
@@ -193,7 +202,7 @@ pub fn run() -> Result<(), Box<dyn Error>> {
} }
_ => state.window.request_redraw(), _ => state.window.request_redraw(),
} }
}); })
} }
fn create_window(event_loop: &EventLoop<()>) -> Window { fn create_window(event_loop: &EventLoop<()>) -> Window {