ft: 13: optional defocus blur
This commit is contained in:
32
scenes/noImage.json
Normal file
32
scenes/noImage.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"camera": {
|
||||||
|
"anti_alias_rate": 23,
|
||||||
|
"max_depth": 100,
|
||||||
|
"fov": 40.0,
|
||||||
|
"look_from": { "x": -10, "y": 5, "z": 10 },
|
||||||
|
"look_at": { "x": 0.0, "y": 0.0, "z": -1.0 },
|
||||||
|
"vup": { "x": 0.0, "y": 1.0, "z": 0.0 }
|
||||||
|
},
|
||||||
|
"materials": [
|
||||||
|
{ "type": "metal", "albedo": { "x": 0.2, "y": 0.4, "z": 0.8 }, "prob": 1.0, "fuzz": 0.1 },
|
||||||
|
{ "type": "metal", "albedo": { "x": 0.7, "y": 0.4, "z": 0.2 }, "prob": 1.0, "fuzz": 0.1 },
|
||||||
|
{ "type": "lambertian", "albedo": { "x": 0.8, "y": 0.8, "z": 0.0 }, "prob": 1.0 },
|
||||||
|
{ "type": "lambertian", "albedo": { "x": 0.1, "y": 0.2, "z": 0.5 }, "prob": 1.0 },
|
||||||
|
{ "type": "dielectric", "refraction_index": 1.5},
|
||||||
|
{ "type": "dielectric", "refraction_index": 0.67},
|
||||||
|
{ "type": "metal", "albedo": { "x": 0.8, "y": 0.6, "z": 0.2 }, "prob": 1.0, "fuzz": 1.0 }
|
||||||
|
|
||||||
|
],
|
||||||
|
"objects": [
|
||||||
|
{ "type": "sphere", "center": { "x": 0, "y": 0.7, "z": -0.4 }, "radius": 0.2, "material": 0},
|
||||||
|
{ "type": "sphere", "center": { "x": 0.0, "y": 0.5, "z": -0.8 }, "radius": 0.1, "material": 1},
|
||||||
|
{ "type": "sphere", "center": { "x": 0.0, "y": -100.5, "z": -1.0 }, "radius": 100.0, "material": 2},
|
||||||
|
{ "type": "sphere", "center": { "x": 0.0, "y": 0.0, "z": -1.2 }, "radius": 0.5, "material": 3},
|
||||||
|
{ "type": "sphere", "center": { "x": -1, "y": 0, "z": -1 }, "radius": 0.4, "material": 5},
|
||||||
|
{ "type": "sphere", "center": { "x": 1, "y": 0, "z": -1 }, "radius": 0.5, "material": 6},
|
||||||
|
{ "type": "sphere", "center": { "x": 20, "y": 7, "z": -15 }, "radius": 10.5, "material": 0}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -4,10 +4,13 @@
|
|||||||
"image_height": 1080,
|
"image_height": 1080,
|
||||||
"anti_alias_rate": 23,
|
"anti_alias_rate": 23,
|
||||||
"max_depth": 100,
|
"max_depth": 100,
|
||||||
"fov": 40.0,
|
"fov": 20.0,
|
||||||
"look_from": { "x": -10, "y": 5, "z": 10 },
|
"look_from": { "x": -10, "y": 5, "z": 10 },
|
||||||
"look_at": { "x": 0.0, "y": 0.0, "z": -1.0 },
|
"look_at": { "x": 0.0, "y": 0.0, "z": -1.0 },
|
||||||
"vup": { "x": 0.0, "y": 1.0, "z": 0.0 }
|
"vup": { "x": 0.0, "y": 1.0, "z": 0.0 },
|
||||||
|
"defocus_blur": false,
|
||||||
|
"defocus_angle": 2,
|
||||||
|
"focus_dist": 15.68
|
||||||
},
|
},
|
||||||
"materials": [
|
"materials": [
|
||||||
{ "type": "metal", "albedo": { "x": 0.2, "y": 0.4, "z": 0.8 }, "prob": 1.0, "fuzz": 0.1 },
|
{ "type": "metal", "albedo": { "x": 0.2, "y": 0.4, "z": 0.8 }, "prob": 1.0, "fuzz": 0.1 },
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use std::{f32::consts::PI, sync::Arc};
|
use std::{f32::consts::PI, sync::Arc};
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{hit::Hit, traits::Hittable},
|
objects::{hit::Hit, traits::Hittable},
|
||||||
@@ -28,11 +27,15 @@ pub struct Camera {
|
|||||||
look_from: Vec3,
|
look_from: Vec3,
|
||||||
look_at: Vec3,
|
look_at: Vec3,
|
||||||
vup: Vec3,
|
vup: Vec3,
|
||||||
|
defocus_angle: f32,
|
||||||
|
focus_dist: f32,
|
||||||
|
|
||||||
// camera helpers
|
// camera helpers
|
||||||
u: Vec3,
|
u: Vec3,
|
||||||
v: Vec3,
|
v: Vec3,
|
||||||
w: Vec3,
|
w: Vec3,
|
||||||
|
defocus_disk_u: Vec3,
|
||||||
|
defocus_disk_v: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: kinda out of place in this file.
|
// FIXME: kinda out of place in this file.
|
||||||
@@ -55,9 +58,13 @@ impl Camera {
|
|||||||
look_from: Vec3::nil(),
|
look_from: Vec3::nil(),
|
||||||
look_at: Vec3::new(0., 0., -1.),
|
look_at: Vec3::new(0., 0., -1.),
|
||||||
vup: Vec3::new(0., 1., 0.),
|
vup: Vec3::new(0., 1., 0.),
|
||||||
|
defocus_angle: 0.,
|
||||||
|
focus_dist: 10.,
|
||||||
u: Vec3::default(),
|
u: Vec3::default(),
|
||||||
v: Vec3::default(),
|
v: Vec3::default(),
|
||||||
w: Vec3::default(),
|
w: Vec3::default(),
|
||||||
|
defocus_disk_u: Vec3::default(),
|
||||||
|
defocus_disk_v: Vec3::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +77,8 @@ impl Camera {
|
|||||||
look_from: Vec3,
|
look_from: Vec3,
|
||||||
look_at: Vec3,
|
look_at: Vec3,
|
||||||
vup: Vec3,
|
vup: Vec3,
|
||||||
|
defocus_angle: f32,
|
||||||
|
focus_dist: f32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
image_width,
|
image_width,
|
||||||
@@ -84,15 +93,18 @@ impl Camera {
|
|||||||
look_from,
|
look_from,
|
||||||
look_at,
|
look_at,
|
||||||
vup,
|
vup,
|
||||||
|
defocus_angle,
|
||||||
|
focus_dist,
|
||||||
u: Vec3::default(),
|
u: Vec3::default(),
|
||||||
v: Vec3::default(),
|
v: Vec3::default(),
|
||||||
w: Vec3::default(),
|
w: Vec3::default(),
|
||||||
|
defocus_disk_u: Vec3::default(),
|
||||||
|
defocus_disk_v: Vec3::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self) {
|
fn init(&mut self) {
|
||||||
// camera
|
// camera
|
||||||
let focal_length = (self.look_from - self.look_at).length();
|
|
||||||
let theta = deg_to_rad(self.fov);
|
let theta = deg_to_rad(self.fov);
|
||||||
let h = (theta / 2.).tan();
|
let h = (theta / 2.).tan();
|
||||||
self.w = (self.look_from - self.look_at).get_unit();
|
self.w = (self.look_from - self.look_at).get_unit();
|
||||||
@@ -100,7 +112,7 @@ impl Camera {
|
|||||||
self.v = self.w.cross(&self.u);
|
self.v = self.w.cross(&self.u);
|
||||||
|
|
||||||
// viewport
|
// viewport
|
||||||
let viewport_height = 2. * h * focal_length;
|
let viewport_height = 2. * h * self.focus_dist;
|
||||||
let viewport_width = viewport_height * (self.image_width as f32 / self.image_height as f32);
|
let viewport_width = viewport_height * (self.image_width as f32 / self.image_height as f32);
|
||||||
let viewport_u = viewport_width * self.u;
|
let viewport_u = viewport_width * self.u;
|
||||||
let viewport_v = viewport_height * -self.v;
|
let viewport_v = viewport_height * -self.v;
|
||||||
@@ -109,8 +121,14 @@ impl Camera {
|
|||||||
self.pixel_delta_u = viewport_u / self.image_width as f32;
|
self.pixel_delta_u = viewport_u / self.image_width as f32;
|
||||||
self.pixel_delta_v = viewport_v / self.image_height as f32;
|
self.pixel_delta_v = viewport_v / self.image_height as f32;
|
||||||
self.pixel00_loc =
|
self.pixel00_loc =
|
||||||
self.look_from - (focal_length * self.w) - viewport_u / 2. - viewport_v / 2.;
|
self.look_from - (self.focus_dist * self.w) - viewport_u / 2. - viewport_v / 2.;
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
|
|
||||||
|
if self.defocus_angle != 0. {
|
||||||
|
let defocus_radius = self.focus_dist * deg_to_rad(self.defocus_angle/2.).tan();
|
||||||
|
self.defocus_disk_u = self.u * defocus_radius;
|
||||||
|
self.defocus_disk_v = self.v * defocus_radius;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_fov(&mut self, fov: f32) {
|
pub fn set_fov(&mut self, fov: f32) {
|
||||||
@@ -155,6 +173,19 @@ impl Camera {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_defocus_blur(&mut self, defocus_angle: f32, focus_dist: f32) {
|
||||||
|
if self.defocus_angle != defocus_angle || self.focus_dist != focus_dist {
|
||||||
|
self.defocus_angle = defocus_angle;
|
||||||
|
self.focus_dist = focus_dist;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn defocus_disk_sample(&self) -> Vec3 {
|
||||||
|
let p = Vec3::random_in_unit_disk();
|
||||||
|
return self.look_from + (p.x() * self.defocus_disk_u) + (p.y() * self.defocus_disk_v);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render(&mut self, hittables: &Vec<Arc<dyn Hittable>>) {
|
pub fn render(&mut self, hittables: &Vec<Arc<dyn Hittable>>) {
|
||||||
if self.dirty {
|
if self.dirty {
|
||||||
self.init()
|
self.init()
|
||||||
@@ -175,8 +206,9 @@ impl Camera {
|
|||||||
let pixel_loc = pixel_tl
|
let pixel_loc = pixel_tl
|
||||||
+ (x * self.pixel_delta_u / (self.anti_alias_rate + 1) as f32)
|
+ (x * self.pixel_delta_u / (self.anti_alias_rate + 1) as f32)
|
||||||
+ (y * self.pixel_delta_v / (self.anti_alias_rate + 1) as f32);
|
+ (y * self.pixel_delta_v / (self.anti_alias_rate + 1) as f32);
|
||||||
let ray_dir = pixel_loc - self.look_from;
|
let ray_orig = if self.defocus_angle > 0. { self.defocus_disk_sample() } else {self.look_from};
|
||||||
let r = Ray::new(self.look_from, ray_dir);
|
let ray_dir = pixel_loc - ray_orig;
|
||||||
|
let r = Ray::new(ray_orig, ray_dir);
|
||||||
pixel_colour += self.ray_colour(&hittables, &r, self.max_depth);
|
pixel_colour += self.ray_colour(&hittables, &r, self.max_depth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ use crate::vec3::Vec3;
|
|||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
use log::info;
|
use log::info;
|
||||||
use pretty_env_logger;
|
use pretty_env_logger;
|
||||||
use rand::seq::IndexedRandom;
|
|
||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
|
use rand::seq::IndexedRandom;
|
||||||
|
|
||||||
fn random_material() -> Arc<dyn Material> {
|
fn random_material() -> Arc<dyn Material> {
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
@@ -78,7 +78,7 @@ fn main() {
|
|||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
// TODO: use cli arg for scenefile
|
// TODO: use cli arg for scenefile
|
||||||
let json_file = "./scenes/scenes.json";
|
let json_file = "./scenes/scene.json";
|
||||||
// let json_file = "./scenes/failsMatBounds.json";
|
// let json_file = "./scenes/failsMatBounds.json";
|
||||||
let json_str = fs::read_to_string(json_file).expect("Reading specified scene file failed!");
|
let json_str = fs::read_to_string(json_file).expect("Reading specified scene file failed!");
|
||||||
let mut scene: Scene = serde_json::from_str(&json_str).unwrap();
|
let mut scene: Scene = serde_json::from_str(&json_str).unwrap();
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ use crate::{camera::Camera, vec3::Vec3};
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct RawCamera {
|
pub struct RawCamera {
|
||||||
// output
|
// output
|
||||||
image_width: u32, // TODO: test these are now explicitly required (and that default impl does
|
image_width: u32,
|
||||||
// not make these optional)
|
|
||||||
image_height: u32,
|
image_height: u32,
|
||||||
|
|
||||||
// raytracing
|
// raytracing
|
||||||
@@ -24,6 +23,10 @@ pub struct RawCamera {
|
|||||||
look_at: Vec3,
|
look_at: Vec3,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
vup: Vec3,
|
vup: Vec3,
|
||||||
|
#[serde(default)]
|
||||||
|
defocus_angle: f32,
|
||||||
|
#[serde(default)]
|
||||||
|
focus_dist: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RawCamera {
|
impl Default for RawCamera {
|
||||||
@@ -37,6 +40,8 @@ impl Default for RawCamera {
|
|||||||
look_from: Vec3::new(0., 0., 0.),
|
look_from: Vec3::new(0., 0., 0.),
|
||||||
look_at: Vec3::new(0., 0., -1.),
|
look_at: Vec3::new(0., 0., -1.),
|
||||||
vup: Vec3::new(0., 1., 0.),
|
vup: Vec3::new(0., 1., 0.),
|
||||||
|
defocus_angle: 0.,
|
||||||
|
focus_dist: 10.,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,6 +57,8 @@ impl From<RawCamera> for Camera {
|
|||||||
raw.look_from,
|
raw.look_from,
|
||||||
raw.look_at,
|
raw.look_at,
|
||||||
raw.vup,
|
raw.vup,
|
||||||
|
raw.defocus_angle,
|
||||||
|
raw.focus_dist,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/vec3.rs
27
src/vec3.rs
@@ -17,8 +17,8 @@ pub struct Vec3 {
|
|||||||
pub type Colour = Vec3;
|
pub type Colour = Vec3;
|
||||||
|
|
||||||
impl Vec3 {
|
impl Vec3 {
|
||||||
pub fn new(r: f32, g: f32, b: f32) -> Self {
|
pub fn new(x: f32, y: f32, z: f32) -> Self {
|
||||||
Self { x: r, y: g, z: b }
|
Self { x, y, z }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nil() -> Self {
|
pub fn nil() -> Self {
|
||||||
@@ -47,6 +47,16 @@ impl Vec3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn random_in_unit_disk() -> Self {
|
||||||
|
loop {
|
||||||
|
let mut p = Vec3::random();
|
||||||
|
p.z = 0.;
|
||||||
|
if p.length_squared() < 1. {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn random_unit_hemisphere(n: &Self) -> Self {
|
pub fn random_unit_hemisphere(n: &Self) -> Self {
|
||||||
let v = Self::random_unit();
|
let v = Self::random_unit();
|
||||||
if n.dot(&v) > 0.0 {
|
if n.dot(&v) > 0.0 {
|
||||||
@@ -340,6 +350,19 @@ impl Mul<&Vec3> for f32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mul<Vec3> for &f32 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Vec3) -> Self::Output {
|
||||||
|
Vec3 {
|
||||||
|
x: rhs.x * self,
|
||||||
|
y: rhs.y * self,
|
||||||
|
z: rhs.z * self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
impl MulAssign<Vec3> for Vec3 {
|
impl MulAssign<Vec3> for Vec3 {
|
||||||
fn mul_assign(&mut self, rhs: Self) {
|
fn mul_assign(&mut self, rhs: Self) {
|
||||||
*self = Self {
|
*self = Self {
|
||||||
|
|||||||
Reference in New Issue
Block a user