Annotated code
This commit is contained in:
12
scene.rhai
12
scene.rhai
@@ -19,12 +19,12 @@ let sphere = Sphere(P(0.0,0.0,0.0), 1.0, material);
|
|||||||
let sphere_node = Node(sphere);
|
let sphere_node = Node(sphere);
|
||||||
scene.addNode(sphere_node);
|
scene.addNode(sphere_node);
|
||||||
|
|
||||||
for i in 0..6 {
|
// for i in 0..6 {
|
||||||
let sphere = Sphere(P(0.0,0.0,0.0), 2.0, material);
|
// let sphere = Sphere(P(0.0,0.0,0.0), 2.0, material);
|
||||||
let sphere_node = Node(sphere);
|
// let sphere_node = Node(sphere);
|
||||||
sphere_node.translate(V(2.0*cos(i.to_float()), -4.0, 2.0*sin(i.to_float())));
|
// sphere_node.translate(2.0*cos(i.to_float()), -4.0, 2.0*sin(i.to_float()));
|
||||||
scene.addNode(sphere_node);
|
// scene.addNode(sphere_node);
|
||||||
}
|
// }
|
||||||
// let child = sphere_node.child(sphere);
|
// let child = sphere_node.child(sphere);
|
||||||
// child.translate(V(1.0,1.0,1.0));
|
// child.translate(V(1.0,1.0,1.0));
|
||||||
//scene.addNode(child);
|
//scene.addNode(child);
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
use nalgebra::{Matrix4, Point3, Vector3};
|
use nalgebra::{Matrix4, Point3, Vector3};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
/// Annotate the Camera struct
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Camera {
|
pub struct Camera {
|
||||||
eye: Point3<f64>,
|
pub eye: Point3<f64>,
|
||||||
target: Point3<f64>,
|
pub target: Point3<f64>,
|
||||||
up: Vector3<f64>,
|
pub up: Vector3<f64>,
|
||||||
pub view: Matrix4<f64>,
|
pub _view: Matrix4<f64>,
|
||||||
pub inv_view: Matrix4<f64>,
|
pub _inv_view: Matrix4<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl Camera {
|
impl Camera {
|
||||||
|
/// Create a new camera with the given eye, target, and up vectors
|
||||||
pub fn new(eye: Point3<f64>, target: Point3<f64>, up: Vector3<f64>) -> Self {
|
pub fn new(eye: Point3<f64>, target: Point3<f64>, up: Vector3<f64>) -> Self {
|
||||||
let view = Matrix4::look_at_lh(&eye, &target, &up);
|
let view = Matrix4::look_at_lh(&eye, &target, &up);
|
||||||
let inv_view = view.try_inverse().unwrap();
|
let inv_view = view.try_inverse().unwrap();
|
||||||
@@ -19,11 +20,12 @@ impl Camera {
|
|||||||
eye,
|
eye,
|
||||||
target,
|
target,
|
||||||
up,
|
up,
|
||||||
view,
|
_view: view,
|
||||||
inv_view,
|
_inv_view: inv_view,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a unit camera with default parameters
|
||||||
pub fn unit() -> Self {
|
pub fn unit() -> Self {
|
||||||
let eye = Point3::new(0.0, 0.0, 1.0);
|
let eye = Point3::new(0.0, 0.0, 1.0);
|
||||||
let target = Point3::new(0.0, 0.0, 0.0);
|
let target = Point3::new(0.0, 0.0, 0.0);
|
||||||
@@ -31,23 +33,27 @@ impl Camera {
|
|||||||
Camera::new(eye, target, up)
|
Camera::new(eye, target, up)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the position of the camera's eye
|
||||||
pub fn set_eye(&mut self, new_eye: Point3<f64>) {
|
pub fn set_eye(&mut self, new_eye: Point3<f64>) {
|
||||||
self.eye = new_eye;
|
self.eye = new_eye;
|
||||||
self.recalculate_matrix();
|
self.recalculate_matrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the position the camera is looking at
|
||||||
pub fn set_target(&mut self, new_target: Point3<f64>) {
|
pub fn set_target(&mut self, new_target: Point3<f64>) {
|
||||||
self.target = new_target;
|
self.target = new_target;
|
||||||
self.recalculate_matrix();
|
self.recalculate_matrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the up vector of the camera
|
||||||
pub fn set_up(&mut self, new_up: Vector3<f64>) {
|
pub fn set_up(&mut self, new_up: Vector3<f64>) {
|
||||||
self.up = new_up;
|
self.up = new_up;
|
||||||
self.recalculate_matrix();
|
self.recalculate_matrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recalculate the view and inverse view matrices based on the current eye, target, and up vectors
|
||||||
fn recalculate_matrix(&mut self) {
|
fn recalculate_matrix(&mut self) {
|
||||||
self.view = Matrix4::look_at_lh(&self.eye, &self.target, &self.up);
|
self._view = Matrix4::look_at_lh(&self.eye, &self.target, &self.up);
|
||||||
self.inv_view = self.view.try_inverse().unwrap();
|
self._inv_view = self._view.try_inverse().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
107
src/gui.rs
107
src/gui.rs
@@ -4,7 +4,7 @@ use crate::{
|
|||||||
primitive::*,
|
primitive::*,
|
||||||
scene::{Node, Scene},
|
scene::{Node, Scene},
|
||||||
state::{INIT_FILE, SAVE_FILE},
|
state::{INIT_FILE, SAVE_FILE},
|
||||||
UP_VECTOR_F32, ZERO_VECTOR_F32,
|
EPSILON,
|
||||||
};
|
};
|
||||||
use imgui::*;
|
use imgui::*;
|
||||||
use nalgebra::{Point3, Vector3};
|
use nalgebra::{Point3, Vector3};
|
||||||
@@ -16,7 +16,7 @@ const BUFFER_PROPORTION_INIT: f32 = 0.2;
|
|||||||
const BUFFER_PROPORTION_MIN: f32 = 0.1;
|
const BUFFER_PROPORTION_MIN: f32 = 0.1;
|
||||||
const BUFFER_PROPORTION_MAX: f32 = 1.0;
|
const BUFFER_PROPORTION_MAX: f32 = 1.0;
|
||||||
|
|
||||||
const RAYS_INIT: i32 = 1000;
|
const RAYS_INIT: i32 = 7000;
|
||||||
const RAYS_MIN: i32 = 100;
|
const RAYS_MIN: i32 = 100;
|
||||||
const RAYS_MAX: i32 = 30000;
|
const RAYS_MAX: i32 = 30000;
|
||||||
|
|
||||||
@@ -27,11 +27,10 @@ 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, f32),
|
BufferResize(f32, f32),
|
||||||
CameraUpdate(Camera),
|
CameraUpdate(Camera, f32),
|
||||||
SceneLoad(Scene),
|
SceneLoad(Scene),
|
||||||
SaveImage(String),
|
SaveImage(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gui {
|
pub struct Gui {
|
||||||
imgui: imgui::Context,
|
imgui: imgui::Context,
|
||||||
platform: imgui_winit_support::WinitPlatform,
|
platform: imgui_winit_support::WinitPlatform,
|
||||||
@@ -115,20 +114,19 @@ impl Gui {
|
|||||||
buffer_proportion: BUFFER_PROPORTION_INIT,
|
buffer_proportion: BUFFER_PROPORTION_INIT,
|
||||||
|
|
||||||
camera_eye: [CAMERA_INIT, CAMERA_INIT, CAMERA_INIT],
|
camera_eye: [CAMERA_INIT, CAMERA_INIT, CAMERA_INIT],
|
||||||
camera_target: ZERO_VECTOR_F32.into(),
|
camera_target: Vector3::zeros().into(),
|
||||||
camera_up: UP_VECTOR_F32.into(),
|
camera_up: Vector3::y().into(),
|
||||||
camera_fov: 110.0,
|
camera_fov: 110.0,
|
||||||
|
|
||||||
image_filename: String::from(SAVE_FILE),
|
image_filename: String::from(SAVE_FILE),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepare Dear ImGuBi.
|
/// Prepare Dear ImGui.
|
||||||
pub fn prepare(
|
pub fn prepare(
|
||||||
&mut self,
|
&mut self,
|
||||||
window: &winit::window::Window,
|
window: &winit::window::Window,
|
||||||
) -> Result<(), winit::error::ExternalError> {
|
) -> Result<(), winit::error::ExternalError> {
|
||||||
// Prepare Dear ImGui
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
self.imgui.io_mut().update_delta_time(now - self.last_frame);
|
self.imgui.io_mut().update_delta_time(now - self.last_frame);
|
||||||
self.last_frame = now;
|
self.last_frame = now;
|
||||||
@@ -153,26 +151,27 @@ impl Gui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Top Menu Bar
|
//Top Menu Bar
|
||||||
let mut about_open = false;
|
// let mut about_open = false;
|
||||||
ui.main_menu_bar(|| {
|
// ui.main_menu_bar(|| {
|
||||||
ui.menu("Help", || {
|
// ui.menu("Help", || {
|
||||||
about_open = ui.menu_item("About...");
|
// about_open = ui.menu_item("About...");
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
//Raytracing options
|
//Raytracing options -------------------------------------------
|
||||||
if CollapsingHeader::new("Raytracer").build(ui) {
|
if CollapsingHeader::new("Raytracer").build(ui) {
|
||||||
//Ray Renderer
|
// Numbers of rays to render
|
||||||
ui.slider("# Rays: ", RAYS_MIN, RAYS_MAX, &mut self.ray_num);
|
ui.slider("# Rays: ", RAYS_MIN, RAYS_MAX, &mut self.ray_num);
|
||||||
//Buffer Options
|
// Proportion of the window the buffer occupies
|
||||||
ui.slider(
|
ui.slider(
|
||||||
"% Buffer: ",
|
"% Buffer: ",
|
||||||
BUFFER_PROPORTION_MIN,
|
BUFFER_PROPORTION_MIN,
|
||||||
BUFFER_PROPORTION_MAX,
|
BUFFER_PROPORTION_MAX,
|
||||||
&mut self.buffer_proportion,
|
&mut self.buffer_proportion,
|
||||||
);
|
);
|
||||||
|
// Fov of the buffer
|
||||||
ui.slider("fov", CAMERA_MIN_FOV, CAMERA_MAX_FOV, &mut self.camera_fov);
|
ui.slider("fov", CAMERA_MIN_FOV, CAMERA_MAX_FOV, &mut self.camera_fov);
|
||||||
//Apply changes
|
// Apply stored changes
|
||||||
if ui.button("Apply") {
|
if ui.button("Apply") {
|
||||||
self.event = Some(GuiEvent::BufferResize(
|
self.event = Some(GuiEvent::BufferResize(
|
||||||
self.buffer_proportion,
|
self.buffer_proportion,
|
||||||
@@ -180,13 +179,13 @@ impl Gui {
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
//Camera options
|
// CAMERA OPTIONS ----------------------------------------
|
||||||
if CollapsingHeader::new("Camera").build(ui) {
|
if CollapsingHeader::new("Camera").build(ui) {
|
||||||
|
// Eye, target and up vector inputs
|
||||||
ui.text("Camera options:");
|
ui.text("Camera options:");
|
||||||
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();
|
||||||
// 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);
|
||||||
let (eye, target, up) = (&self.camera_eye, &self.camera_target, &self.camera_up);
|
let (eye, target, up) = (&self.camera_eye, &self.camera_target, &self.camera_up);
|
||||||
@@ -198,21 +197,25 @@ impl Gui {
|
|||||||
Point3::new(tx, ty, tz),
|
Point3::new(tx, ty, tz),
|
||||||
Vector3::new(ux, uy, uz),
|
Vector3::new(ux, uy, uz),
|
||||||
);
|
);
|
||||||
self.event = Some(GuiEvent::CameraUpdate(camera));
|
self.event = Some(GuiEvent::CameraUpdate(camera, self.camera_fov));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Scripting
|
// SCRIPTING --------------------------------------------
|
||||||
if CollapsingHeader::new("Scripting").build(ui) {
|
if CollapsingHeader::new("Scripting").build(ui) {
|
||||||
//Import from file (We just want to replace the contents of self.script)
|
// Import file into multiline script
|
||||||
ui.input_text("Scene file", &mut self.script_filename)
|
ui.input_text("Scene file", &mut self.script_filename)
|
||||||
.build();
|
.build();
|
||||||
if ui.button("Import from File") {
|
if ui.button("Import from File") {
|
||||||
match std::fs::read_to_string(&self.script_filename) {
|
match std::fs::read_to_string(&mut self.script_filename) {
|
||||||
Ok(script) => self.script = script,
|
Ok(script) => {
|
||||||
Err(e) => println!("{e}"),
|
self.script = script;
|
||||||
|
}
|
||||||
|
Err(e) => println!("{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ui.button("Apply script") {
|
ui.same_line();
|
||||||
|
// Load scene from multiline script using engine
|
||||||
|
if ui.button("Load scene") {
|
||||||
match self.engine.eval(&self.script) {
|
match self.engine.eval(&self.script) {
|
||||||
Ok(scene) => {
|
Ok(scene) => {
|
||||||
self.scene = scene;
|
self.scene = scene;
|
||||||
@@ -221,24 +224,68 @@ impl Gui {
|
|||||||
Err(e) => println!("{e}"),
|
Err(e) => println!("{e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ui.same_line();
|
||||||
|
// Save script to file
|
||||||
if ui.button("Save script") {
|
if ui.button("Save script") {
|
||||||
match std::fs::write(&self.script_filename, &self.script) {
|
match std::fs::write(&self.script_filename, &self.script) {
|
||||||
Ok(_) => println!("Script saved successfully"),
|
Ok(_) => println!("Script saved successfully"),
|
||||||
Err(e) => println!("{}", e),
|
Err(e) => println!("{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Script block
|
// Multiline script
|
||||||
ui.input_text_multiline("script", &mut self.script, [600., 1500.])
|
ui.input_text_multiline("##", &mut self.script, [900., 300.])
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
// IMAGE --------------------------------------------
|
||||||
if CollapsingHeader::new("Image").build(ui) {
|
if CollapsingHeader::new("Image").build(ui) {
|
||||||
|
// Image filename
|
||||||
ui.input_text("Image file", &mut self.image_filename)
|
ui.input_text("Image file", &mut self.image_filename)
|
||||||
.build();
|
.build();
|
||||||
|
// Save image to file
|
||||||
if ui.button("Save Image") {
|
if ui.button("Save Image") {
|
||||||
self.event = Some(GuiEvent::SaveImage(self.image_filename.clone()));
|
self.event = Some(GuiEvent::SaveImage(self.image_filename.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// SCENE --------------------------------------------
|
||||||
|
if CollapsingHeader::new("Scene").build(ui) {
|
||||||
|
if ui.button("Update Scene") {
|
||||||
|
for node in &mut self.scene.nodes {
|
||||||
|
node.compute();
|
||||||
|
}
|
||||||
|
self.event = Some(GuiEvent::SceneLoad(self.scene.clone()));
|
||||||
|
}
|
||||||
|
// Edit transformation of nodes
|
||||||
|
if let Some(_t) = ui.tree_node("Nodes") {
|
||||||
|
for node in &mut self.scene.nodes {
|
||||||
|
ui.text("node");
|
||||||
|
ui.slider_config("Translation", -10.0, 10.0)
|
||||||
|
.build_array(&mut node.translation);
|
||||||
|
ui.slider_config("Rotation", -180.0, 180.0)
|
||||||
|
.build_array(&mut node.rotation);
|
||||||
|
ui.slider_config("Scale", -10.0, 10.0)
|
||||||
|
.build_array(&mut node.scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Edit color, position and falloff of lights
|
||||||
|
if let Some(_t) = ui.tree_node("Lights") {
|
||||||
|
for light in &mut self.scene.lights {
|
||||||
|
ui.slider_config("Colour", 0.0, 1.0)
|
||||||
|
.build_array(light.colour.as_mut_slice());
|
||||||
|
ui.slider_config("Position", -10.0, 10.0)
|
||||||
|
.build_array(light.position.coords.as_mut_slice());
|
||||||
|
ui.slider_config("Falloff", 0.0, f32::MAX)
|
||||||
|
.build_array(light.falloff.as_mut_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Use different cameras in the scene
|
||||||
|
if let Some(_t) = ui.tree_node("Cameras") {
|
||||||
|
for camera in &self.scene.cameras {
|
||||||
|
if ui.button("Use camera") {
|
||||||
|
GuiEvent::CameraUpdate(camera.clone(), self.camera_fov);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Render Dear ImGui with WGPU
|
// Render Dear ImGui with WGPU
|
||||||
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ use nalgebra::{Point3, Vector3};
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Light {
|
pub struct Light {
|
||||||
pub position: Point3<f64>,
|
pub position: Point3<f64>,
|
||||||
pub colour: Vector3<f64>,
|
pub colour: Vector3<f32>,
|
||||||
pub falloff: Vector3<f64>,
|
pub falloff: Vector3<f32>,
|
||||||
pub ambient: bool,
|
pub ambient: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Light {
|
impl Light {
|
||||||
pub fn new(position: Point3<f64>, colour: Vector3<f64>, falloff: Vector3<f64>) -> Self {
|
pub fn new(position: Point3<f64>, colour: Vector3<f64>, falloff: Vector3<f64>) -> Self {
|
||||||
|
let colour = colour.cast();
|
||||||
|
let falloff = falloff.cast();
|
||||||
Light {
|
Light {
|
||||||
position,
|
position,
|
||||||
colour,
|
colour,
|
||||||
@@ -20,7 +22,7 @@ impl Light {
|
|||||||
pub fn ambient(colour: Vector3<f64>) -> Self {
|
pub fn ambient(colour: Vector3<f64>) -> Self {
|
||||||
Light {
|
Light {
|
||||||
position: Point3::new(0.0, 0.0, 0.0),
|
position: Point3::new(0.0, 0.0, 0.0),
|
||||||
colour,
|
colour: colour.cast(),
|
||||||
falloff: Vector3::new(0.0, 0.0, 0.0),
|
falloff: Vector3::new(0.0, 0.0, 0.0),
|
||||||
ambient: true,
|
ambient: true,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ use crate::state::run;
|
|||||||
use error_iter::ErrorIter;
|
use error_iter::ErrorIter;
|
||||||
|
|
||||||
const EPSILON: f64 = 1e-6;
|
const EPSILON: f64 = 1e-6;
|
||||||
const INFINITY: f64 = f64::MAX;
|
const INFINITY: f64 = 1e-10;
|
||||||
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_F32: Vector3<f32> = Vector3::new(0.0, 0.0, 0.0);
|
|
||||||
static UP_VECTOR_F32: Vector3<f32> = Vector3::new(0.0, 1.0, 0.0);
|
|
||||||
|
|
||||||
use log::error;
|
use log::error;
|
||||||
use std::env;
|
use std::env;
|
||||||
@@ -21,8 +17,6 @@ mod raytracer;
|
|||||||
mod scene;
|
mod scene;
|
||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
use nalgebra::Vector3;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
use crate::ray::Ray;
|
use crate::ray::Ray;
|
||||||
use crate::{EPSILON, EPSILON_VECTOR, INFINITY};
|
use crate::{EPSILON, INFINITY};
|
||||||
use nalgebra::{distance, Matrix4, Point3, Vector3};
|
use nalgebra::{distance, Matrix4, Point3, Vector3};
|
||||||
use roots::{find_roots_quadratic, find_roots_quartic, Roots};
|
use roots::{find_roots_quadratic, find_roots_quartic, Roots};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@@ -9,17 +9,21 @@ use std::sync::Arc;
|
|||||||
// MATERIAL -----------------------------------------------------------------
|
// MATERIAL -----------------------------------------------------------------
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Material {
|
pub struct Material {
|
||||||
pub kd: Vector3<f64>,
|
pub kd: Vector3<f32>,
|
||||||
pub ks: Vector3<f64>,
|
pub ks: Vector3<f32>,
|
||||||
pub shininess: f64,
|
pub shininess: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Material {
|
impl Material {
|
||||||
pub fn new(kd: Vector3<f64>, ks: Vector3<f64>, shininess: f64) -> Arc<Self> {
|
pub fn new(kd: Vector3<f64>, ks: Vector3<f64>, shininess: f64) -> Arc<Self> {
|
||||||
|
let kd = kd.cast();
|
||||||
|
let ks = ks.cast();
|
||||||
|
let shininess = shininess as f32;
|
||||||
Arc::new(Material { kd, ks, shininess })
|
Arc::new(Material { kd, ks, shininess })
|
||||||
}
|
}
|
||||||
pub fn magenta() -> Arc<Self> {
|
pub fn magenta() -> Arc<Self> {
|
||||||
let kd = Vector3::new(1.0, 0.0, 1.0);
|
let kd = Vector3::new(1.0, 0.0, 1.0);
|
||||||
|
|
||||||
let ks = Vector3::new(1.0, 0.0, 1.0);
|
let ks = Vector3::new(1.0, 0.0, 1.0);
|
||||||
let shininess = 0.5;
|
let shininess = 0.5;
|
||||||
Arc::new(Material { kd, ks, shininess })
|
Arc::new(Material { kd, ks, shininess })
|
||||||
@@ -81,8 +85,8 @@ struct BoundingBox {
|
|||||||
|
|
||||||
impl BoundingBox {
|
impl BoundingBox {
|
||||||
fn new(bln: Point3<f64>, trf: Point3<f64>) -> Self {
|
fn new(bln: Point3<f64>, trf: Point3<f64>) -> Self {
|
||||||
let bln = bln - EPSILON_VECTOR;
|
let bln = bln + Vector3::new(EPSILON, EPSILON, EPSILON);
|
||||||
let trf = trf + EPSILON_VECTOR;
|
let trf = trf - Vector3::new(EPSILON, EPSILON, EPSILON);
|
||||||
BoundingBox { bln, trf }
|
BoundingBox { bln, trf }
|
||||||
}
|
}
|
||||||
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
|
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
|
||||||
|
|||||||
92
src/ray.rs
92
src/ray.rs
@@ -2,11 +2,11 @@ use crate::{
|
|||||||
primitive::Intersection,
|
primitive::Intersection,
|
||||||
raytracer::phong_shade_point,
|
raytracer::phong_shade_point,
|
||||||
scene::{Node, Scene},
|
scene::{Node, Scene},
|
||||||
INFINITY,
|
|
||||||
};
|
};
|
||||||
use nalgebra::{Matrix4, Point3, Vector3};
|
use nalgebra::{Matrix4, Point3, Vector3};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
// Ray struct represents a ray in 3D space with a starting point 'a' and a direction 'b'
|
||||||
pub struct Ray {
|
pub struct Ray {
|
||||||
pub a: Point3<f64>,
|
pub a: Point3<f64>,
|
||||||
pub b: Vector3<f64>,
|
pub b: Vector3<f64>,
|
||||||
@@ -14,91 +14,109 @@ pub struct Ray {
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl Ray {
|
impl Ray {
|
||||||
|
//Create a new ray with a normalized direction
|
||||||
pub fn new(a: Point3<f64>, b: Vector3<f64>) -> Ray {
|
pub fn new(a: Point3<f64>, b: Vector3<f64>) -> Ray {
|
||||||
Ray {
|
Ray {
|
||||||
a,
|
a,
|
||||||
b: b.normalize(),
|
b: b.normalize(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// The starting point is the origin and the direction is negative z-axis
|
||||||
pub fn unit() -> Ray {
|
pub fn unit() -> Ray {
|
||||||
let a = Point3::new(0.0, 0.0, 0.0);
|
let a = Point3::origin();
|
||||||
let b = Vector3::new(0.0, 1.0, 0.0);
|
let b = -Vector3::z();
|
||||||
Ray { a, b }
|
Ray { a, b }
|
||||||
}
|
}
|
||||||
|
//Return the point at distance t along the ray
|
||||||
pub fn at_t(&self, t: f64) -> Point3<f64> {
|
pub fn at_t(&self, t: f64) -> Point3<f64> {
|
||||||
self.a + self.b * t
|
self.a + self.b * t
|
||||||
}
|
}
|
||||||
//Shade a single ray
|
// This function takes a scene and returns the color of the point where the ray intersects the scene
|
||||||
pub fn shade_ray(&self, scene: &Scene) -> Option<Vector3<u8>> {
|
pub fn shade_ray(&self, scene: &Scene) -> Option<Vector3<u8>> {
|
||||||
|
//Get the closest intersection of the ray with the scene
|
||||||
let intersect = self.get_closest_intersection(&scene.nodes);
|
let intersect = self.get_closest_intersection(&scene.nodes);
|
||||||
|
//Shade the intersection point if there is one
|
||||||
match intersect {
|
match intersect {
|
||||||
Some(intersect) => Some(phong_shade_point(&scene, &intersect)),
|
Some(intersect) => Some(phong_shade_point(&scene, &intersect)), // If there is an intersection, shade it
|
||||||
None => None,
|
None => None, // If there is no intersection, return None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the closest intersection
|
// 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;
|
//Assign no intersection
|
||||||
|
let mut closest_distance = f64::MAX;
|
||||||
let mut closest_intersect: Option<Intersection> = None;
|
let mut closest_intersect: Option<Intersection> = None;
|
||||||
|
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
|
// Clone arc to primitive
|
||||||
let primitive = node.primitive.clone();
|
let primitive = node.primitive.clone();
|
||||||
|
// Transform ray into local model cordinates
|
||||||
//Transform ray from view coords
|
let ray = self.transform(&node.inv_model);
|
||||||
let ray = self.transform(&node.inv_viewmodel);
|
// Check bounding box intersection
|
||||||
|
|
||||||
if primitive.intersect_bounding_box(&ray).is_some() {
|
if primitive.intersect_bounding_box(&ray).is_some() {
|
||||||
|
// Check primitive intersection
|
||||||
if let Some(intersect) = primitive.intersect_ray(&ray) {
|
if let Some(intersect) = primitive.intersect_ray(&ray) {
|
||||||
|
// Check for closest distance
|
||||||
if intersect.distance < closest_distance {
|
if intersect.distance < closest_distance {
|
||||||
closest_distance = intersect.distance;
|
closest_distance = intersect.distance;
|
||||||
//Convert back to world coords
|
//Convert back to world coords
|
||||||
let intersect = intersect.transform(&node.model, &node.inv_model);
|
let intersect = intersect.transform(&node.model, &node.inv_model);
|
||||||
|
|
||||||
closest_intersect = Some(intersect);
|
closest_intersect = Some(intersect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Return None if we find no intersection, some if we do find one
|
||||||
closest_intersect
|
closest_intersect
|
||||||
}
|
}
|
||||||
|
// Return a transformed version of the ray
|
||||||
pub fn transform(&self, trans: &Matrix4<f64>) -> Ray {
|
pub fn transform(&self, trans: &Matrix4<f64>) -> Ray {
|
||||||
Ray {
|
Ray {
|
||||||
a: trans.transform_point(&self.a),
|
a: trans.transform_point(&self.a),
|
||||||
b: trans.transform_vector(&self.b),
|
b: trans.transform_vector(&self.b),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Cast a set of rays
|
||||||
pub fn cast_rays(fovy: f64, width: u32, height: u32) -> Vec<Ray> {
|
pub fn cast_rays(
|
||||||
let aspect = width as f64 / height as f64;
|
eye: &Point3<f64>,
|
||||||
|
target: &Point3<f64>,
|
||||||
|
up: &Vector3<f64>,
|
||||||
|
fovy: f64,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Vec<Ray> {
|
||||||
|
//Aspect ratio calculation
|
||||||
|
let (width, height) = (width as f64, height as f64);
|
||||||
|
let aspect = width / height;
|
||||||
|
//X and Y fov calculations
|
||||||
let fovy_radians = fovy.to_radians();
|
let fovy_radians = fovy.to_radians();
|
||||||
let fovh_radians = 2.0 * ((fovy_radians / 2.0).tan() * aspect).atan();
|
let fovh_radians = 2.0 * ((fovy_radians / 2.0).tan() * aspect).atan();
|
||||||
|
// Vectors pointing forward, right and up
|
||||||
let dir = Vector3::new(0.0, 0.0, 1.0);
|
let forward = (target - eye).normalize();
|
||||||
let up = Vector3::new(0.0, 1.0, 0.0);
|
let right = forward.cross(&up).normalize();
|
||||||
let hor = Vector3::new(1.0, 0.0, 0.0);
|
let up = right.cross(&forward).normalize();
|
||||||
|
// ☐ height and width of projection
|
||||||
let vheight = 2.0 * (fovy_radians / 2.0).tan();
|
let vheight = 2.0 * (fovy_radians / 2.0).tan();
|
||||||
let vwidth = 2.0 * (fovh_radians / 2.0).tan();
|
let vwidth = 2.0 * (fovh_radians / 2.0).tan();
|
||||||
|
// Increment of right and up per pixel
|
||||||
let d_hor_vec = hor * (vwidth / width as f64) as f64;
|
let d_hor_vec = right * (vwidth / width);
|
||||||
let d_vert_vec = up * (vheight / height as f64) as f64;
|
let d_vert_vec = up * (vheight / height);
|
||||||
|
// Half the width for later calculation
|
||||||
let half_width = width / 2;
|
let half_width = width / 2.0;
|
||||||
let half_height = height / 2;
|
let half_height = height / 2.0;
|
||||||
|
// Array of rays
|
||||||
let mut rays = Vec::with_capacity(width as usize * height as usize);
|
let mut rays = Vec::with_capacity(width as usize * height as usize);
|
||||||
|
// Iterate column by row
|
||||||
|
for row in 0..height as u32 {
|
||||||
|
for column in 0..width as u32 {
|
||||||
|
let x = (column as f64) - half_width;
|
||||||
|
let y = half_height - (row as f64);
|
||||||
|
|
||||||
for j in 0..height as i32 {
|
let horizontal = x * &d_hor_vec;
|
||||||
for i in 0..width as i32 {
|
let vertical = y * &d_vert_vec;
|
||||||
let x = i - half_width as i32;
|
let direction = (forward + horizontal + vertical).normalize();
|
||||||
let y = -j + half_height as i32;
|
let ray = Ray::new(eye.clone(), direction);
|
||||||
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), direction);
|
|
||||||
rays.push(ray);
|
rays.push(ray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{light::Light, primitive::Intersection, ray::Ray, scene::*, EPSILON, ZERO_VECTOR};
|
use crate::{light::Light, primitive::Intersection, ray::Ray, scene::*, EPSILON};
|
||||||
|
|
||||||
use nalgebra::{Unit, Vector3};
|
use nalgebra::{Unit, Vector3};
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
|
|||||||
let shininess = material.shininess;
|
let shininess = material.shininess;
|
||||||
|
|
||||||
// Compute the ambient light component and set it as base colour
|
// Compute the ambient light component and set it as base colour
|
||||||
let mut colour = ZERO_VECTOR;
|
let mut colour = Vector3::zeros();
|
||||||
|
|
||||||
for light in &scene.lights {
|
for light in &scene.lights {
|
||||||
let Light {
|
let Light {
|
||||||
@@ -34,7 +34,7 @@ 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() as f32;
|
||||||
let to_light = to_light;
|
let to_light = to_light;
|
||||||
|
|
||||||
let to_light_ray = Ray::new(point.clone() + normal * EPSILON, to_light);
|
let to_light_ray = Ray::new(point.clone() + normal * EPSILON, to_light);
|
||||||
@@ -45,14 +45,14 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
|
|||||||
// Point to camera
|
// Point to camera
|
||||||
let to_camera = -incidence;
|
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) as f32;
|
||||||
let diffuse = n_dot_l * kd;
|
let diffuse = n_dot_l * kd;
|
||||||
// Specular component
|
// Specular component
|
||||||
let mut specular = ZERO_VECTOR;
|
let mut specular = Vector3::zeros();
|
||||||
if n_dot_l > 0.0 {
|
if n_dot_l > 0.0 {
|
||||||
// Halfway vector.
|
// Halfway vector.
|
||||||
let h = Unit::new_normalize(to_camera.lerp(&to_light, 0.5));
|
let h = Unit::new_normalize(to_camera.lerp(&to_light, 0.5));
|
||||||
let n_dot_h = normal.dot(&h).max(0.0);
|
let n_dot_h = normal.dot(&h).max(0.0) as f32;
|
||||||
specular = ks * n_dot_h.powf(shininess);
|
specular = ks * n_dot_h.powf(shininess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
98
src/scene.rs
98
src/scene.rs
@@ -5,59 +5,84 @@ use nalgebra::{Matrix4, Vector3};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
|
//Primitive
|
||||||
pub primitive: Arc<dyn Primitive>,
|
pub primitive: Arc<dyn Primitive>,
|
||||||
|
//Transformations
|
||||||
|
pub rotation: [f32; 3],
|
||||||
|
pub scale: [f32; 3],
|
||||||
|
pub translation: [f32; 3],
|
||||||
|
//Model matricies
|
||||||
pub model: Matrix4<f64>,
|
pub model: Matrix4<f64>,
|
||||||
pub inv_model: Matrix4<f64>,
|
pub inv_model: Matrix4<f64>,
|
||||||
pub viewmodel: Matrix4<f64>,
|
|
||||||
pub inv_viewmodel: Matrix4<f64>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
|
//New node with no transformations
|
||||||
pub fn new(primitive: Arc<dyn Primitive>) -> Node {
|
pub fn new(primitive: Arc<dyn Primitive>) -> Node {
|
||||||
Node {
|
Node {
|
||||||
primitive,
|
primitive,
|
||||||
|
rotation: [0.0, 0.0, 0.0],
|
||||||
|
scale: [1.0, 1.0, 1.0],
|
||||||
|
translation: [0.0, 0.0, 0.0],
|
||||||
model: Matrix4::identity(),
|
model: Matrix4::identity(),
|
||||||
inv_model: Matrix4::identity(),
|
inv_model: Matrix4::identity(),
|
||||||
viewmodel: Matrix4::identity(),
|
|
||||||
inv_viewmodel: Matrix4::identity(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//New node with parent transformations
|
||||||
|
pub fn child(self, primitive: Arc<dyn Primitive>) -> Node {
|
||||||
|
let mut child = self.clone();
|
||||||
|
child.primitive = primitive;
|
||||||
|
child
|
||||||
|
}
|
||||||
|
//Rotate a mesh by adding to its rotation
|
||||||
pub fn rotate(&mut self, roll: f64, pitch: f64, yaw: f64) {
|
pub fn rotate(&mut self, roll: f64, pitch: f64, yaw: f64) {
|
||||||
|
//Convert to radians
|
||||||
let roll = roll.to_radians();
|
let roll = roll.to_radians();
|
||||||
|
// Convert pitch and yaw to radians
|
||||||
let pitch = pitch.to_radians();
|
let pitch = pitch.to_radians();
|
||||||
let yaw = yaw.to_radians();
|
let yaw = yaw.to_radians();
|
||||||
let rotation_matrix = Matrix4::from_euler_angles(roll, pitch, yaw);
|
|
||||||
self.model = rotation_matrix * self.model;
|
// Add the roll, pitch, and yaw to the current rotation
|
||||||
self.inv_model = self.model.try_inverse().unwrap();
|
self.rotation[0] += roll as f32;
|
||||||
self.viewmodel = rotation_matrix * self.viewmodel;
|
self.rotation[1] += pitch as f32;
|
||||||
self.inv_viewmodel = self.inv_viewmodel.try_inverse().unwrap();
|
self.rotation[2] += yaw as f32;
|
||||||
|
|
||||||
|
// Recompute the model and inverse model matrices
|
||||||
|
self.compute();
|
||||||
}
|
}
|
||||||
pub fn translate(&mut self, translation: Vector3<f64>) {
|
// Translate a mesh by adding to its current position
|
||||||
|
pub fn translate(&mut self, x: f64, y: f64, z: f64) {
|
||||||
|
self.translation[0] += x as f32;
|
||||||
|
self.translation[1] += y as f32;
|
||||||
|
self.translation[2] += z as f32;
|
||||||
|
|
||||||
|
// Recompute the model and inverse model matrices
|
||||||
|
self.compute();
|
||||||
|
}
|
||||||
|
// Scale a mesh by adding to its current scale
|
||||||
|
pub fn scale(&mut self, x: f64, y: f64, z: f64) {
|
||||||
|
self.scale[0] += x as f32;
|
||||||
|
self.scale[1] += y as f32;
|
||||||
|
self.scale[2] += z as f32;
|
||||||
|
|
||||||
|
// Recompute the model and inverse model matrices
|
||||||
|
self.compute();
|
||||||
|
}
|
||||||
|
// This function computes the model and inverse model matrices
|
||||||
|
pub fn compute(&mut self) {
|
||||||
|
//Translation matrix
|
||||||
|
let translation = Vector3::from_row_slice(&self.translation);
|
||||||
let translation_matrix = Matrix4::new_translation(&translation);
|
let translation_matrix = Matrix4::new_translation(&translation);
|
||||||
self.model = translation_matrix * self.model;
|
// Scale matrix
|
||||||
self.inv_model = self.model.try_inverse().unwrap();
|
let scale = &Vector3::from_row_slice(&self.scale);
|
||||||
self.viewmodel = translation_matrix * self.viewmodel;
|
|
||||||
self.inv_viewmodel = self.inv_viewmodel.try_inverse().unwrap();
|
|
||||||
}
|
|
||||||
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 = scale_matrix * self.model;
|
// Rotation matrix
|
||||||
|
let (roll, pitch, yaw) = (self.rotation[0], self.rotation[1], self.rotation[2]);
|
||||||
|
let rotation_matrix = Matrix4::from_euler_angles(roll, pitch, yaw);
|
||||||
|
// Compute the model matrix by combining the translation, rotation, and scale matrices
|
||||||
|
self.model = (translation_matrix * rotation_matrix * scale_matrix).cast();
|
||||||
|
// Compute the inverse model matrix by inverting the model matrix
|
||||||
self.inv_model = self.model.try_inverse().unwrap();
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,21 +104,20 @@ impl Scene {
|
|||||||
cameras: Vec::new(),
|
cameras: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Adds a node to the scene
|
||||||
pub fn add_node(&mut self, node: Node) {
|
pub fn add_node(&mut self, node: Node) {
|
||||||
self.nodes.push(node);
|
self.nodes.push(node);
|
||||||
}
|
}
|
||||||
|
// Adds a material to the scene
|
||||||
pub fn add_material(&mut self, material: Material) {
|
pub fn add_material(&mut self, material: Material) {
|
||||||
self.materials.push(material);
|
self.materials.push(material);
|
||||||
}
|
}
|
||||||
|
// Adds a light to the scene
|
||||||
pub fn add_light(&mut self, light: Light) {
|
pub fn add_light(&mut self, light: Light) {
|
||||||
self.lights.push(light);
|
self.lights.push(light);
|
||||||
}
|
}
|
||||||
|
// Adds a camera to the scene
|
||||||
pub 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>) {
|
|
||||||
for node in &mut self.nodes {
|
|
||||||
node.compute(view, inv_view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
89
src/state.rs
89
src/state.rs
@@ -6,13 +6,12 @@ use crate::{gui::Gui, scene::Scene};
|
|||||||
use crate::{gui::GuiEvent, log_error};
|
use crate::{gui::GuiEvent, log_error};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use nalgebra::{Point3, Vector3};
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use pixels::{Pixels, SurfaceTexture};
|
use pixels::{Pixels, SurfaceTexture};
|
||||||
use winit::dpi::{LogicalSize, PhysicalSize};
|
use winit::dpi::{LogicalSize, PhysicalSize};
|
||||||
@@ -23,6 +22,7 @@ use winit::window::{Window, WindowBuilder};
|
|||||||
const START_WIDTH: i32 = 1200;
|
const START_WIDTH: i32 = 1200;
|
||||||
const START_HEIGHT: i32 = 1200;
|
const START_HEIGHT: i32 = 1200;
|
||||||
const COLOUR_CLEAR: [u8; 4] = [0x22, 0x00, 0x11, 0xff];
|
const COLOUR_CLEAR: [u8; 4] = [0x22, 0x00, 0x11, 0xff];
|
||||||
|
const PIXEL_CLEAR: [u8; 4] = [0x55, 0x00, 0x22, 0xff];
|
||||||
|
|
||||||
pub const INIT_FILE: &str = "scene.rhai";
|
pub const INIT_FILE: &str = "scene.rhai";
|
||||||
pub const SAVE_FILE: &str = "img.png";
|
pub const SAVE_FILE: &str = "img.png";
|
||||||
@@ -35,7 +35,7 @@ pub struct State {
|
|||||||
buffer_width: u32,
|
buffer_width: u32,
|
||||||
buffer_height: u32,
|
buffer_height: u32,
|
||||||
|
|
||||||
pixels: Arc<Mutex<Pixels>>,
|
pixels: Pixels,
|
||||||
gui: Gui,
|
gui: Gui,
|
||||||
|
|
||||||
rays: Vec<Ray>,
|
rays: Vec<Ray>,
|
||||||
@@ -46,7 +46,7 @@ impl State {
|
|||||||
pub fn new(window: Window, pixels: Pixels, gui: Gui) -> Self {
|
pub fn new(window: Window, pixels: Pixels, gui: Gui) -> Self {
|
||||||
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::new(Point3::new(2.0, 2.0, 2.0), Point3::origin(), Vector3::y());
|
||||||
let rays = Vec::new();
|
let rays = Vec::new();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@@ -55,7 +55,7 @@ impl State {
|
|||||||
window,
|
window,
|
||||||
buffer_width: window_size.width as u32,
|
buffer_width: window_size.width as u32,
|
||||||
buffer_height: window_size.height as u32,
|
buffer_height: window_size.height as u32,
|
||||||
pixels: Arc::new(Mutex::new(pixels)),
|
pixels: pixels,
|
||||||
gui,
|
gui,
|
||||||
rays,
|
rays,
|
||||||
ray_queue: Vec::new(),
|
ray_queue: Vec::new(),
|
||||||
@@ -68,18 +68,26 @@ impl State {
|
|||||||
GuiEvent::BufferResize(proportion, fov) => {
|
GuiEvent::BufferResize(proportion, fov) => {
|
||||||
self.resize_buffer(proportion, fov as f64)?
|
self.resize_buffer(proportion, fov as f64)?
|
||||||
}
|
}
|
||||||
GuiEvent::CameraUpdate(camera) => {
|
GuiEvent::CameraUpdate(camera, fovy) => {
|
||||||
|
self.rays = Ray::cast_rays(
|
||||||
|
&camera.eye,
|
||||||
|
&camera.target,
|
||||||
|
&camera.up,
|
||||||
|
fovy as f64,
|
||||||
|
self.buffer_width,
|
||||||
|
self.buffer_height,
|
||||||
|
);
|
||||||
self.camera = camera;
|
self.camera = camera;
|
||||||
self.clear()?;
|
self.clear()?;
|
||||||
self.reset_queue();
|
self.reset_queue();
|
||||||
}
|
}
|
||||||
GuiEvent::SceneLoad(scene) => {
|
GuiEvent::SceneLoad(scene) => {
|
||||||
self.scene = scene;
|
self.scene = scene;
|
||||||
|
self.clear()?;
|
||||||
self.reset_queue();
|
self.reset_queue();
|
||||||
}
|
}
|
||||||
GuiEvent::SaveImage(filename) => {
|
GuiEvent::SaveImage(filename) => {
|
||||||
let pixels = self.pixels.lock().unwrap();
|
let frame = self.pixels.frame();
|
||||||
let frame = pixels.frame();
|
|
||||||
image::save_buffer(
|
image::save_buffer(
|
||||||
Path::new(&filename),
|
Path::new(&filename),
|
||||||
frame,
|
frame,
|
||||||
@@ -94,25 +102,35 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn resize_buffer(&mut self, proportion: f32, fovy: f64) -> Result<(), Box<dyn Error>> {
|
fn resize_buffer(&mut self, proportion: f32, fovy: f64) -> Result<(), Box<dyn Error>> {
|
||||||
|
// Calculate new buffer dimensions based on proportion
|
||||||
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;
|
||||||
|
|
||||||
|
// Clear the buffer and reset the ray queue
|
||||||
self.clear()?;
|
self.clear()?;
|
||||||
self.reset_queue();
|
self.reset_queue();
|
||||||
|
|
||||||
self.rays = Ray::cast_rays(fovy, self.buffer_width, self.buffer_height);
|
// Recalculate rays with new buffer dimensions
|
||||||
|
self.rays = Ray::cast_rays(
|
||||||
|
&self.camera.eye,
|
||||||
|
&self.camera.target,
|
||||||
|
&self.camera.up,
|
||||||
|
fovy,
|
||||||
|
self.buffer_width,
|
||||||
|
self.buffer_height,
|
||||||
|
);
|
||||||
|
|
||||||
let mut pixels = self.pixels.lock().unwrap();
|
// Resize buffer and surface
|
||||||
pixels.resize_buffer(self.buffer_width, self.buffer_height)?;
|
let pixels = &mut self.pixels;
|
||||||
pixels.resize_surface(size.width, size.height)?;
|
pixels.resize_surface(size.width, size.height)?;
|
||||||
|
pixels.resize_buffer(self.buffer_width, self.buffer_height)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize(&mut self, size: &PhysicalSize<u32>) -> Result<(), Box<dyn Error>> {
|
fn resize(&mut self, size: &PhysicalSize<u32>) -> Result<(), Box<dyn Error>> {
|
||||||
let mut pixels = self.pixels.lock().unwrap();
|
self.pixels.resize_surface(size.width, size.height)?;
|
||||||
pixels.resize_surface(size.width, size.height)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,29 +145,27 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self) -> Result<(), Box<dyn Error>> {
|
fn draw(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
//Draw ray_num in a block
|
||||||
for _ in 0..self.gui.ray_num {
|
for _ in 0..self.gui.ray_num {
|
||||||
//Get random index from queue
|
//Get random index from queue
|
||||||
let index = self.ray_queue.pop().unwrap();
|
let index = match self.ray_queue.pop() {
|
||||||
|
Some(index) => index,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
//Shade colour for selected ray
|
//Shade colour for selected ray
|
||||||
let colour = &self.rays[index].shade_ray(&self.scene);
|
let colour = &self.rays[index].shade_ray(&self.scene);
|
||||||
//Assign colour to frame
|
//Assign colour to pixel in frame
|
||||||
let rgba = colour.map_or(COLOUR_CLEAR, |colour| [colour.x, colour.y, colour.z, 255]);
|
let rgba = colour.map_or(PIXEL_CLEAR, |colour| [colour.x, colour.y, colour.z, 255]);
|
||||||
let mut pixels = self.pixels.lock().unwrap();
|
let frame = self.pixels.frame_mut();
|
||||||
let frame = pixels.frame_mut();
|
|
||||||
frame[index * 4..(index + 1) * 4].copy_from_slice(&rgba);
|
frame[index * 4..(index + 1) * 4].copy_from_slice(&rgba);
|
||||||
|
|
||||||
if self.ray_queue.is_empty() {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) -> Result<(), Box<dyn Error>> {
|
fn clear(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
let mut pixels = self.pixels.lock().unwrap();
|
let frame = self.pixels.frame_mut();
|
||||||
let frame = pixels.frame_mut();
|
|
||||||
for pixel in frame.chunks_exact_mut(4) {
|
for pixel in frame.chunks_exact_mut(4) {
|
||||||
pixel.copy_from_slice(&[0x00, 0x00, 0x00, 0xff]);
|
pixel.copy_from_slice(&COLOUR_CLEAR);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -159,26 +175,35 @@ impl State {
|
|||||||
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>> {
|
||||||
self.update()?; //Update state
|
// Update state
|
||||||
|
self.update()?;
|
||||||
|
// Draw rays if we have remaining rays in queue
|
||||||
if !self.ray_queue.is_empty() {
|
if !self.ray_queue.is_empty() {
|
||||||
self.draw()?;
|
match self.draw() {
|
||||||
|
Err(e) => {
|
||||||
|
println!("ERROR: {}", e);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let pixels = self.pixels.lock().unwrap();
|
// Render Gui
|
||||||
self.gui
|
self.gui
|
||||||
.prepare(&self.window)
|
.prepare(&self.window)
|
||||||
.expect("gui.prepare() failed"); //Prepare imgui
|
.expect("gui.prepare() failed");
|
||||||
if let Err(e) = pixels.render_with(|encoder, render_target, context| {
|
// Try to render pixels
|
||||||
|
if let Err(e) = self.pixels.render_with(|encoder, render_target, context| {
|
||||||
context.scaling_renderer.render(encoder, render_target); // Render pixels
|
context.scaling_renderer.render(encoder, render_target); // Render pixels
|
||||||
self.gui
|
self.gui
|
||||||
.render(&self.window, encoder, render_target, context)?;
|
.render(&self.window, encoder, render_target, context)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}) {
|
}) {
|
||||||
log_error("pixels.render", e);
|
log_error("pixels.render", e);
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user