summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSakarias Johansson <sakarias.johansson@goodbyekansas.com>2023-01-16 20:58:55 +0100
committerSakarias Johansson <sakarias.johansson@goodbyekansas.com>2023-01-16 20:58:55 +0100
commit03e5a88f9b7da79749370685e8f5afaf03a25b4a (patch)
tree9e0fd5e12bcd9739f5292864c9da9084a0030489
parent3167ec992f1f81b2252a2db3642fff943c4f14bf (diff)
downloadracer-tracer-03e5a88f9b7da79749370685e8f5afaf03a25b4a.tar.gz
racer-tracer-03e5a88f9b7da79749370685e8f5afaf03a25b4a.tar.xz
racer-tracer-03e5a88f9b7da79749370685e8f5afaf03a25b4a.zip
🎨 Add dialectric material
-rw-r--r--racer-tracer/src/main.rs17
-rw-r--r--racer-tracer/src/material.rs1
-rw-r--r--racer-tracer/src/material/dialectric.rs53
-rw-r--r--racer-tracer/src/render.rs6
-rw-r--r--racer-tracer/src/vec3.rs28
5 files changed, 99 insertions, 6 deletions
diff --git a/racer-tracer/src/main.rs b/racer-tracer/src/main.rs
index d95717d..5331fd1 100644
--- a/racer-tracer/src/main.rs
+++ b/racer-tracer/src/main.rs
@@ -16,7 +16,7 @@ use std::{
vec::Vec,
};
-use material::{lambertian::Lambertian, metal::Metal, Material};
+use material::{dialectric::Dialectric, lambertian::Lambertian, metal::Metal, Material};
use minifb::{Key, Window, WindowOptions};
use synchronoise::SignalEvent;
@@ -39,11 +39,11 @@ fn create_scene() -> Scene {
let material_ground: SharedMaterial =
Arc::new(Box::new(Lambertian::new(Color::new(0.8, 0.8, 0.0))));
let material_center: SharedMaterial =
- Arc::new(Box::new(Lambertian::new(Color::new(0.7, 0.3, 0.3))));
- let material_left: SharedMaterial =
- Arc::new(Box::new(Metal::new(Color::new(0.8, 0.8, 0.8), 0.3)));
+ Arc::new(Box::new(Lambertian::new(Color::new(0.1, 0.2, 0.5))));
+ let material_left: SharedMaterial = Arc::new(Box::new(Dialectric::new(1.5)));
+
let material_right: SharedMaterial =
- Arc::new(Box::new(Metal::new(Color::new(0.8, 0.6, 0.2), 0.1)));
+ Arc::new(Box::new(Metal::new(Color::new(0.8, 0.6, 0.2), 0.0)));
scene.add(Box::new(Sphere::new(
Vec3::new(0.0, -100.5, -1.0),
@@ -60,6 +60,13 @@ fn create_scene() -> Scene {
0.5,
Arc::clone(&material_left),
)));
+
+ scene.add(Box::new(Sphere::new(
+ Vec3::new(-1.0, 0.0, -1.0),
+ -0.4,
+ Arc::clone(&material_left),
+ )));
+
scene.add(Box::new(Sphere::new(
Vec3::new(1.0, 0.0, -1.0),
0.5,
diff --git a/racer-tracer/src/material.rs b/racer-tracer/src/material.rs
index b6ce418..a43b29f 100644
--- a/racer-tracer/src/material.rs
+++ b/racer-tracer/src/material.rs
@@ -1,3 +1,4 @@
+pub mod dialectric;
pub mod lambertian;
pub mod metal;
diff --git a/racer-tracer/src/material/dialectric.rs b/racer-tracer/src/material/dialectric.rs
new file mode 100644
index 0000000..5f3aa02
--- /dev/null
+++ b/racer-tracer/src/material/dialectric.rs
@@ -0,0 +1,53 @@
+use crate::{
+ material::Material,
+ ray::Ray,
+ util::random_double,
+ vec3::{dot, reflect, refract, Color},
+};
+
+pub struct Dialectric {
+ refraction_index: f64,
+}
+
+impl Dialectric {
+ pub fn new(refraction_index: f64) -> Self {
+ Self { refraction_index }
+ }
+
+ fn reflectance(cosine: f64, refraction_index: f64) -> f64 {
+ // Schlick approximation
+ let mut r0 = (1.0 - refraction_index) / (1.0 + refraction_index);
+ r0 = r0 * r0;
+ r0 + (1.0 - r0) * (1.0 - cosine).powf(5.0)
+ }
+}
+
+impl Material for Dialectric {
+ fn scatter(
+ &self,
+ ray: &crate::ray::Ray,
+ rec: &crate::geometry::HitRecord,
+ ) -> Option<(Ray, Color)> {
+ let refraction_ratio = if rec.front_face {
+ 1.0 / self.refraction_index
+ } else {
+ self.refraction_index
+ };
+
+ let unit_direction = &ray.direction().unit_vector();
+ let cos_theta = f64::min(dot(&-unit_direction, &rec.normal), 1.0);
+ let sin_theta = f64::sqrt(1.0 - cos_theta * cos_theta);
+
+ let cannot_refract = refraction_ratio * sin_theta > 1.0;
+
+ let direction = if cannot_refract
+ || Dialectric::reflectance(cos_theta, refraction_ratio) > random_double()
+ {
+ reflect(unit_direction, &rec.normal)
+ } else {
+ refract(unit_direction, &rec.normal, refraction_ratio)
+ };
+
+ Some((Ray::new(rec.point, direction), Color::new(1.0, 1.0, 1.0)))
+ }
+}
diff --git a/racer-tracer/src/render.rs b/racer-tracer/src/render.rs
index 9d029e3..c5013cf 100644
--- a/racer-tracer/src/render.rs
+++ b/racer-tracer/src/render.rs
@@ -50,6 +50,10 @@ pub fn raytrace(
scale: usize,
max_depth: usize,
) {
+ // TODO: This scale shit doesn't work.
+ // Just force power of two or other solutions to avoid this.
+ // Can be ok for preview but the actual render could use a different function.
+
let mut scaled_width = image.width / scale;
let mut scaled_height = image.height / scale;
// In the case where we get an odd one out we patch the widht and
@@ -66,7 +70,7 @@ pub fn raytrace(
scaled_width += 1;
}
- if scaled_width * scale != image.height
+ if scaled_height * scale != image.height
&& (image.y + scaled_height * scale + 1 < image.screen_height)
{
scaled_height += 1;
diff --git a/racer-tracer/src/vec3.rs b/racer-tracer/src/vec3.rs
index 659d3ed..9061eba 100644
--- a/racer-tracer/src/vec3.rs
+++ b/racer-tracer/src/vec3.rs
@@ -146,6 +146,18 @@ impl ops::Add<Vec3> for Vec3 {
}
}
+impl ops::Add<Vec3> for &Vec3 {
+ type Output = Vec3;
+
+ fn add(self, rhs: Vec3) -> Self::Output {
+ Vec3::new(
+ self.data[0] + rhs.data[0],
+ self.data[1] + rhs.data[1],
+ self.data[2] + rhs.data[2],
+ )
+ }
+}
+
impl ops::AddAssign<Vec3> for Vec3 {
fn add_assign(&mut self, rhs: Vec3) {
self.data[0] += rhs.data[0];
@@ -275,6 +287,14 @@ impl ops::Neg for Vec3 {
}
}
+impl ops::Neg for &Vec3 {
+ type Output = Vec3;
+
+ fn neg(self) -> Self::Output {
+ Vec3::new(-self.data[0], -self.data[1], -self.data[2])
+ }
+}
+
impl PartialEq for Vec3 {
fn eq(&self, other: &Self) -> bool {
self.data[0] == other.data[0]
@@ -305,6 +325,14 @@ pub fn reflect(v1: &Vec3, v2: &Vec3) -> Vec3 {
v1 - 2.0 * v1.dot(v2) * v2
}
+pub fn refract(uv: &Vec3, n: &Vec3, etai_over_etat: f64) -> Vec3 {
+ let cos_theta = f64::min(dot(&-uv, n), 1.0);
+
+ let r_out_perp = etai_over_etat * (uv + (cos_theta * n));
+ let r_out_parallel = -f64::sqrt(f64::abs(1.0 - r_out_perp.length_squared())) * n;
+ r_out_perp + r_out_parallel
+}
+
pub fn random_in_unit_sphere() -> Vec3 {
let mut v = Vec3::random_range(-1.0, 1.0);
while v.length_squared() >= 1.0 {