aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2021-08-01 22:55:07 +0200
committerJulian T <julian@jtle.dk>2021-08-01 22:55:07 +0200
commit86ad7845219e8db06fe47b62794180e1b40f90a5 (patch)
tree2fe1de5c4a854522536a397b7913d7c299b35c67
parentae460c3f34838e3baf0ffafe4ccbcf1fbfe03095 (diff)
Implement dielectric material from RTIAW
-rw-r--r--src/core/mod.rs4
-rw-r--r--src/main.rs15
-rw-r--r--src/material/dielectric.rs59
-rw-r--r--src/material/mod.rs4
-rw-r--r--src/world/shapes/sphere.rs15
5 files changed, 83 insertions, 14 deletions
diff --git a/src/core/mod.rs b/src/core/mod.rs
index 07793ec..95d450c 100644
--- a/src/core/mod.rs
+++ b/src/core/mod.rs
@@ -18,14 +18,14 @@ pub use ray::Ray;
use crate::Number;
-fn min<T: Number> (a: T, b: T) -> T {
+pub fn min<T: Number> (a: T, b: T) -> T {
if b < a {
return b;
}
a
}
-fn max<T: Number> (a: T, b: T) -> T {
+pub fn max<T: Number> (a: T, b: T) -> T {
if b > a {
return b;
}
diff --git a/src/main.rs b/src/main.rs
index 5f336c1..a05c1c1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,23 +13,26 @@ fn main() {
let cam = Camera::new(&CameraSettings {
target: Vector3f::new_xyz(0.0, 0.0, -1.0),
- origin: Vector3f::new_xyz(1.7, 0.0, 0.3),
+ origin: Vector3f::new_xyz(0.0, 0.0, 1.0),
up: Vector3f::new_xyz(0.0, 1.0, 0.0),
- fov: 60.0,
+ fov: 45.0,
filmsize: res,
focus: None,
- aperture: Some(20.0),
+ aperture: Some(10.0),
+ //aperture: None,
});
let brown = Arc::new(Lambertian::new(Spectrum::new_rgb(0.5, 0.3, 0.0)));
let blue = Arc::new(Lambertian::new(Spectrum::new_rgb(0.0, 0.3, 0.7)));
- let metal = Arc::new(Reflectant::new(Spectrum::new_rgb(0.8, 0.8, 0.9), Some(1.0)));
+ let green = Arc::new(Lambertian::new(Spectrum::new_rgb(0.0, 0.7, 0.3)));
+ let metal = Arc::new(Dielectric::new(1.5));
let sun = Arc::new(DiffuseLight::new_white(13.0));
let mut scn = Scene::new();
scn.add_objects(vec![
Object::new(metal, Sphere::new(0.2, Vector3f::new_xyz(0.0, 0.0, -1.0))),
- Object::new(blue, Sphere::new(0.5, Vector3f::new_xyz(1.0, 0.0, -1.0))),
+ Object::new(blue.clone(), Sphere::new(0.5, Vector3f::new_xyz(1.0, 0.0, -1.5))),
+ Object::new(green, Sphere::new(0.3, Vector3f::new_xyz(0.5, 0.0, -2.5))),
Object::new(brown, Sphere::new(100.0, Vector3f::new_xyz(0.0, -100.5, -1.0))),
Object::new(sun, Sphere::new(0.4, Vector3f::new_xyz(-1.0, 0.7, 0.0))),
]);
@@ -45,7 +48,7 @@ fn main() {
let mut film = Film::new(res);
{
- let coord = RenderCoord::new(&mut film, Vector2i::new_xy(64, 64), 400);
+ let coord = RenderCoord::new(&mut film, Vector2i::new_xy(64, 64), 50);
coord.run_threaded(&ctx, &mut sampler, 8);
}
diff --git a/src/material/dielectric.rs b/src/material/dielectric.rs
new file mode 100644
index 0000000..41f47c2
--- /dev/null
+++ b/src/material/dielectric.rs
@@ -0,0 +1,59 @@
+use super::Material;
+use crate::core::{min, Vector3f, Spectrum, Ray};
+use crate::world::Intersection;
+use crate::sample::Sampler;
+use crate::Float;
+
+pub struct Dielectric {
+ ratio: Float,
+}
+
+fn reflect(v: Vector3f, n: Vector3f) -> Vector3f {
+ v - n * (2.0 * v.dot(&n))
+}
+
+// Implementation from RTIOW
+fn refract(v: Vector3f, n: Vector3f, r_ratio: Float, cos_theta: Float) -> Vector3f {
+ let r_perp = (v + n * cos_theta) * r_ratio;
+ let r_parallel = n * (-(1.0 - r_perp.len_squared()).abs().sqrt());
+
+ r_perp + r_parallel
+}
+
+// Schlick Approximation, explained in RTIOW
+fn fresnel(cos: Float, ratio: Float) -> Float {
+ let mut r0 = (1.0-ratio) / (1.0+ratio);
+ r0 = r0 * r0;
+
+ r0 + (1.0-r0)*(1.0-cos).powi(5)
+}
+
+impl Dielectric {
+ pub fn new(ratio: Float) -> Self {
+ Self { ratio }
+ }
+}
+
+impl Material for Dielectric {
+ // Implementation from RTIOW
+ fn scatter(&self, ray: &Ray, i: &Intersection, sampler: &mut dyn Sampler) -> Option<(Spectrum, Ray)> {
+ let ratio = if i.front {1.0/self.ratio} else {self.ratio};
+
+ let ray_dir = ray.direction.norm();
+ let cos_theta = min((-ray_dir).dot(&i.n), 1.0);
+ let sin_theta = (1.0 - cos_theta*cos_theta).sqrt();
+
+ // Test if it is possible for the ray the retract or if it must reflect.
+ let cannot_refract = (ratio * sin_theta) > 1.0;
+ let direction = if cannot_refract || (fresnel(cos_theta, ratio) > sampler.get_sample()) {
+ reflect(ray_dir, i.n)
+ } else {
+ refract(ray_dir, i.n, ratio, cos_theta)
+ };
+
+ Some((
+ Spectrum::WHITE,
+ Ray::new(i.p, direction),
+ ))
+ }
+}
diff --git a/src/material/mod.rs b/src/material/mod.rs
index 3e92bf6..d3c3154 100644
--- a/src/material/mod.rs
+++ b/src/material/mod.rs
@@ -3,14 +3,14 @@ use crate::world::Intersection;
use crate::sample::Sampler;
mod lambertian;
-mod reflectant;
mod diffuse_light;
mod sky_light;
+mod dielectric;
pub use lambertian::Lambertian;
-pub use reflectant::Reflectant;
pub use diffuse_light::DiffuseLight;
pub use sky_light::SkyLight;
+pub use dielectric::Dielectric;
pub trait Material: Sync + Send {
fn scatter(&self, _: &Ray, _: &Intersection, _: &mut dyn Sampler) -> Option<(Spectrum, Ray)> {
diff --git a/src/world/shapes/sphere.rs b/src/world/shapes/sphere.rs
index 9ecedd6..1df9c35 100644
--- a/src/world/shapes/sphere.rs
+++ b/src/world/shapes/sphere.rs
@@ -1,7 +1,7 @@
//! Implements sphere
//!
//! Spheres are relatively easy to calculate intersections between
-use crate::Float;
+use crate::{Float, NEAR_ZERO};
use crate::core::{Ray, Vector3f, Bound3f};
use crate::world::{Hittable, Intersection};
@@ -37,10 +37,17 @@ impl Hittable for Sphere {
if disc < 0.0 {
None
} else {
- let distance = (-half_b - disc.sqrt()) / a;
- if distance < 0.0 {
- return None
+ let disc_sqrt = disc.sqrt();
+
+ let mut distance = -half_b - disc_sqrt;
+ if distance <= NEAR_ZERO {
+ distance = -half_b + disc_sqrt;
+ }
+ if distance <= NEAR_ZERO {
+ return None;
}
+
+ distance /= a;
let w = ray.at(distance);
Some(Intersection::new(
self.norm_at(&w),