Shadows, New rays and niceness
This commit is contained in:
36
scene.rhai
36
scene.rhai
@@ -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
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -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)]
|
||||
|
||||
106
src/gui.rs
106
src/gui.rs
@@ -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 |
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
61
src/ray.rs
61
src/ray.rs
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
149
src/scene.rs
149
src/scene.rs
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
src/state.rs
41
src/state.rs
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user