M src/main.rs => src/main.rs +11 -6
@@ 61,18 61,18 @@ fn main() {
// Image
let aspect_ratio = 16.0 / 9.0;
let image_width = 400;
- // let image_width = 1000;
- let image_width = 1600;
+ let image_width = 1000;
+ // let image_width = 1600;
let image_height = (image_width as f64 / aspect_ratio) as i32;
- let samples_per_pixel = 100;
+ let samples_per_pixel = 50;
let max_depth = 50;
// World
let mut world = HittableList::empty();
let material_ground: MaterialPtr = Arc::new(Lambertian::new(Color::new(0.8, 0.8, 0.0)));
- let material_center: MaterialPtr = Arc::new(Lambertian::new(Color::new(0.7, 0.3, 0.3)));
- let material_left: MaterialPtr = Arc::new(Metal::new(Color::new(0.8, 0.8, 0.8), 0.3));
- let material_right: MaterialPtr = Arc::new(Metal::new(Color::new(0.8, 0.6, 0.2), 1.0));
+ let material_center: MaterialPtr = Arc::new(Lambertian::new(Color::new(0.1, 0.2, 0.5)));
+ let material_left: MaterialPtr = Arc::new(Dielectric::new(1.5));
+ let material_right: MaterialPtr = Arc::new(Metal::new(Color::new(0.8, 0.6, 0.2), 0.0));
world.add(Arc::new(Sphere::new(
Point3::new(0.0, -100.5, -1.0),
@@ 87,6 87,11 @@ fn main() {
world.add(Arc::new(Sphere::new(
Point3::new(-1.0, 0.0, -1.0),
0.5,
+ material_left.clone(),
+ )));
+ world.add(Arc::new(Sphere::new(
+ Point3::new(-1.0, 0.0, -1.0),
+ -0.4,
material_left,
)));
world.add(Arc::new(Sphere::new(
M src/material.rs => src/material.rs +46 -1
@@ 1,5 1,6 @@
use crate::{
- dot, random_in_unit_sphere, random_unit_vector, reflect, unit_vector, Color, HitRecord, Ray,
+ dot, random_double, random_in_unit_sphere, random_unit_vector, reflect, refract, unit_vector,
+ Color, HitRecord, Ray,
};
use std::fmt::Debug;
@@ 60,3 61,47 @@ impl Material for Metal {
}
}
}
+
+#[derive(Debug)]
+pub struct Dielectric {
+ ir: f64, // Index of Refraction
+}
+
+impl Dielectric {
+ pub fn new(ir: f64) -> Self {
+ Self { ir }
+ }
+
+ fn reflectance(cosine: f64, ref_idx: f64) -> f64 {
+ // Use Schlick's approximation for reflectance.
+ let mut r0 = (1.0 - ref_idx) / (1.0 + ref_idx);
+ r0 = r0 * r0;
+ r0 + (1.0 - r0) * (1.0 - cosine).powi(5)
+ }
+}
+
+impl Material for Dielectric {
+ fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> Option<(Color, Ray)> {
+ let attenuation = Color::new(1.0, 1.0, 1.0);
+ let refraction_ratio = if rec.front_face {
+ (1.0 / self.ir)
+ } else {
+ self.ir
+ };
+
+ let unit_direction = unit_vector(r_in.direction());
+ let cos_theta = dot(&-unit_direction, &rec.normal).min(1.0);
+ let sin_theta = (1.0 - cos_theta * cos_theta).sqrt();
+
+ let cannot_refract = refraction_ratio * sin_theta > 1.0;
+ let direction =
+ if cannot_refract || Self::reflectance(cos_theta, refraction_ratio) > random_double() {
+ reflect(&unit_direction, &rec.normal)
+ } else {
+ refract(&unit_direction, &rec.normal, refraction_ratio)
+ };
+
+ let scattered = Ray::new(&rec.p, &direction);
+ Some((attenuation, scattered))
+ }
+}
M src/vec3.rs => src/vec3.rs +18 -1
@@ 55,7 55,17 @@ impl std::ops::Neg for &Vec3 {
fn neg(self) -> Vec3 {
Vec3 {
- e: [-self.e[0], -self.e[1], -self.e[1]],
+ e: [-self.e[0], -self.e[1], -self.e[2]],
+ }
+ }
+}
+
+impl std::ops::Neg for Vec3 {
+ type Output = Vec3;
+
+ fn neg(self) -> Vec3 {
+ Vec3 {
+ e: [-self.e[0], -self.e[1], -self.e[2]],
}
}
}
@@ 228,3 238,10 @@ pub fn random_unit_vector() -> Vec3 {
pub fn reflect(v: &Vec3, n: &Vec3) -> Vec3 {
*v - 2.0 * dot(v, n) * n
}
+
+pub fn refract(uv: &Vec3, n: &Vec3, etai_over_etat: f64) -> Vec3 {
+ let cos_theta = dot(&-uv, n).min(1.0);
+ let r_out_perp = etai_over_etat * (*uv + cos_theta * n);
+ let r_out_parallel = -((1.0 - r_out_perp.length_squared()).abs()).sqrt() * n;
+ r_out_perp + r_out_parallel
+}