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 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 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));
scene.addLight(light2);
scene.addLight(ambient);
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);
// let sphere = Sphere(P(0.0,0.0,0.0), 1.0, material);
// let sphere_node = Node(sphere);
let sphere = Sphere(P(0.0,0.0,0.0), 1.0, material);
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(child);
// let cube = CubeUnit(material);
// let cube_node = Node(cube);
// scene.addNode(cube_node);
let gnonom = Gnonom(material);
let gnonom_node = Node(gnonom);
scene.addNode(gnonom_node);
// let gnonom = Gnonom(material);
// let gnonom_node = Node(gnonom);
// scene.addNode(gnonom_node);
// let cylinder = Cylinder(1.0,1.0, material);
// let cylinder_node = Node(cylinder);
// scene.addNode(cylinder_node);
let cylinder = Cylinder(1.0,1.0, material);
let cylinder_node = Node(cylinder);
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

View File

@@ -1 +0,0 @@

View File

@@ -1,9 +1,4 @@
use crate::ray::Ray;
use crate::{EPSILON, INFINITY};
use nalgebra::{Matrix4, Perspective3, Point3, Unit, Vector3};
const ZNEAR: f64 = EPSILON;
const ZFAR: f64 = INFINITY;
use nalgebra::{Matrix4, Point3, Vector3};
#[allow(dead_code)]
#[derive(Clone)]
@@ -11,8 +6,8 @@ pub struct Camera {
eye: Point3<f64>,
target: Point3<f64>,
up: Vector3<f64>,
view: Matrix4<f64>,
inv_view: Matrix4<f64>,
pub view: Matrix4<f64>,
pub inv_view: Matrix4<f64>,
}
#[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 nalgebra::{Point3, Vector3};
use pixels::{wgpu, PixelsContext};
use rhai::Engine;
use std::time::Instant;
const BUFFER_PROPORTION_INIT: f32 = 1.0;
const BUFFER_PROPORTION_MIN: f32 = 0.5;
const BUFFER_PROPORTION_MAX: f32 = 1.0;
const RAYS_INIT: i32 = 9000;
const RAYS_INIT: i32 = 1000;
const RAYS_MIN: i32 = 100;
const RAYS_MAX: i32 = 10000;
const RAYS_MAX: i32 = 100000;
const CAMERA_MIN_FOV: f32 = 10.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.
pub enum GuiEvent {
BufferResize(f32),
BufferResize(f32, f32),
CameraUpdate(Camera),
SceneLoad(Scene),
}
@@ -34,6 +42,7 @@ pub struct Gui {
script_filename: String,
script: String,
engine: Engine,
scene: Scene,
pub ray_num: i32,
@@ -63,7 +72,7 @@ impl Gui {
// Configure Dear ImGui fonts
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
.fonts()
@@ -96,6 +105,7 @@ impl Gui {
script_filename: String::from(INIT_FILE),
script: String::new(),
engine: init_engine(),
scene: Scene::empty(),
ray_num: RAYS_INIT,
@@ -156,9 +166,13 @@ impl Gui {
BUFFER_PROPORTION_MAX,
&mut self.buffer_proportion,
);
ui.slider("fov", CAMERA_MIN_FOV, CAMERA_MAX_FOV, &mut self.camera_fov);
//Apply changes
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
@@ -167,7 +181,6 @@ impl Gui {
ui.input_float3("Eye", &mut self.camera_eye).build();
ui.input_float3("Target", &mut self.camera_target).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
if ui.button("Apply Camera") {
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 (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 camera = Camera::new(
Point3::new(ex, ey, ez),
Point3::new(tx, ty, tz),
Vector3::new(ux, uy, uz),
1,
1,
self.camera_fov as f64,
);
self.event = Some(GuiEvent::CameraUpdate(camera));
}
@@ -199,7 +208,7 @@ impl Gui {
}
}
if ui.button("Apply script") {
match Scene::from_rhai(&self.script) {
match self.engine.eval(&self.script) {
Ok(scene) => {
self.scene = scene;
self.event = Some(GuiEvent::SceneLoad(self.scene.clone()));
@@ -208,7 +217,7 @@ impl Gui {
}
}
//Script block
ui.input_text_multiline("script", &mut self.script, [500., 900.])
ui.input_text_multiline("script", &mut self.script, [600., 1500.])
.build();
}
@@ -244,3 +253,74 @@ impl Gui {
.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 error_iter::ErrorIter;
const EPSILON: f64 = 1e-9;
const EPSILON: f64 = 1e-6;
const INFINITY: f64 = f64::MAX;
const EPSILON_VECTOR: Vector3<f64> = Vector3::new(EPSILON, EPSILON, EPSILON);
static ZERO_VECTOR: Vector3<f64> = Vector3::new(0.0, 0.0, 0.0);

View File

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

View File

@@ -1,83 +1,102 @@
use crate::{
primitive::Intersection,
primitive::{self, Intersection},
raytracer::phong_shade_point,
scene::{Node, Scene},
EPSILON, INFINITY,
};
use nalgebra::{Point3, Unit, Vector3};
use nalgebra::{Matrix4, Point3, Vector3};
#[derive(Clone)]
pub struct Ray {
pub a: Point3<f64>,
pub b: Unit<Vector3<f64>>,
pub b: Vector3<f64>,
}
impl Ray {
pub fn new(a: Point3<f64>, b: Unit<Vector3<f64>>) -> Ray {
Ray { a, b }
pub fn new(a: Point3<f64>, b: Vector3<f64>) -> Ray {
Ray {
a,
b: b.normalize(),
}
}
pub fn unit() -> Ray {
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 }
}
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
pub fn shade_ray(&self, scene: &Scene) -> Option<Vector3<u8>> {
let intersect = self.get_closest_intersection(&scene.nodes);
match intersect {
Some(intersect) => Some(phong_shade_point(&scene, &intersect)),
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> {
let mut closest_distance = INFINITY;
let mut closest_intersect: Option<Intersection> = None;
for node in nodes {
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 {
closest_distance = intersect.distance;
//Convert back to world coords
let intersect = intersect.transform(&node.model, &node.inv_model);
closest_intersect = Some(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> {
let aspect = width as f64 / height as f64;
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 up = Vector3::new(0.0, 1.0, 0.0);
let hor = Vector3::new(1.0, 0.0, 0.0);
let half_height = fovy_radians.tan();
let half_width = aspect * half_height;
let vheight = 2.0 * (fovy_radians / 2.0).tan();
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_vert_vec = up * (2.0 * half_height / height as f64) as f64;
let d_hor_vec = hor * (vwidth / width 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);
for j in 0..height as i32 {
for i in 0..width as i32 {
let horizontal = (i - half_width as i32) as f64 * (d_hor_vec);
let vertical = (-j + half_height as i32) as f64 * (d_vert_vec);
let x = i - half_width as i32;
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 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);
}
}

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};
@@ -11,6 +11,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
material,
..
} = intersect;
let kd = material.kd;
let ks = material.ks;
let shininess = material.shininess;
@@ -28,9 +29,15 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
// Point to light
let to_light = light_position - point;
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
let to_camera = Unit::new_normalize(-incidence.into_inner());
let to_camera = -incidence;
// Diffuse component
let n_dot_l = normal.dot(&to_light).max(0.0);
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);
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::light::Light;
use crate::primitive::*;
use nalgebra::{Matrix4, Point3, Vector3};
use rhai::{Engine, EvalAltResult};
use nalgebra::{Matrix4, Vector3};
use std::sync::Arc;
#[derive(Clone)]
pub struct Node {
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 {
pub fn new(primitive: Arc<dyn Primitive>) -> Self {
pub fn new(primitive: Arc<dyn Primitive>) -> Node {
Node {
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) {
let roll = (roll as f64).to_radians() as f32;
let pitch = (pitch as f64).to_radians() as f32;
let yaw = (yaw as f64).to_radians() as f32;
pub fn rotate(&mut self, roll: f64, pitch: f64, yaw: f64) {
let roll = roll.to_radians();
let pitch = pitch.to_radians();
let yaw = yaw.to_radians();
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>) {
let translation_matrix = Matrix4::new_translation(translation);
self.model_transform = translation_matrix * self.model_transform;
pub fn translate(&mut self, translation: Vector3<f64>) {
let translation_matrix = Matrix4::new_translation(&translation);
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>) {
let scale_matrix = Matrix4::new_nonuniform_scaling(scale);
self.model_transform = scale_matrix * self.model_transform;
pub fn scale(&mut self, scale: Vector3<f64>) {
let scale_matrix = Matrix4::new_nonuniform_scaling(&scale);
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)]
pub struct Scene {
pub nodes: Vec<Node>,
@@ -51,94 +79,21 @@ impl Scene {
cameras: Vec::new(),
}
}
fn add_node(&mut self, node: Node) {
pub fn add_node(&mut self, node: Node) {
self.nodes.push(node);
}
fn add_material(&mut self, material: Material) {
pub fn add_material(&mut self, material: Material) {
self.materials.push(material);
}
fn add_light(&mut self, light: Light) {
pub fn add_light(&mut self, light: Light) {
self.lights.push(light);
}
fn add_camera(&mut self, camera: Camera) {
pub fn add_camera(&mut self, camera: Camera) {
self.cameras.push(camera);
}
pub fn from_rhai(script: &str) -> Result<Scene, Box<EvalAltResult>> {
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::<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)
pub fn compute(&mut self, view: &Matrix4<f64>, inv_view: &Matrix4<f64>) {
for node in &mut self.nodes {
node.compute(view, inv_view);
}
}
}

View File

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