From 61b54375610fde6d75dec38a6739ab90463f4fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20R=C3=B6ger?= Date: Wed, 20 Nov 2024 13:42:25 +0100 Subject: [PATCH] feat(plane): add a special checkerboard plane --- src/bin/rt_demo.rs | 29 +++++++++++------- src/raytracer/plane.rs | 67 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 11 deletions(-) diff --git a/src/bin/rt_demo.rs b/src/bin/rt_demo.rs index 026ed88..7ef5fe6 100644 --- a/src/bin/rt_demo.rs +++ b/src/bin/rt_demo.rs @@ -1,6 +1,6 @@ use lispers::raytracer::{ camera::Camera, - plane::Plane, + plane::Checkerboard, scene::Scene, sphere::Sphere, types::{Color, Light, Material, Point3, Vector3}, @@ -12,10 +12,10 @@ use std::time::Instant; fn main() { let mut scene = Scene::new(); - scene.set_ambient(Color::new(0.2, 0.2, 0.2)); + scene.set_ambient(Color::new(0.1, 0.1, 0.1)); scene.add_light(Light { - position: Point3::new(4.0, 7.0, 10.0), + position: Point3::new(5.0, 7.0, 10.0), color: Color::new(1.0, 1.0, 1.0), }); scene.add_light(Light { @@ -23,16 +23,25 @@ fn main() { color: Color::new(1.0, 1.0, 1.0), }); - scene.add_object(Arc::new(Plane::new( + scene.add_object(Arc::new(Checkerboard::new( Point3::new(0.0, -1.0, 0.0), Vector3::new(0.0, 1.0, 0.0), Material::new( - Color::new(0.5, 0.5, 0.5), - Color::new(0.5, 0.5, 0.5), + Color::new(1.0, 1.0, 1.0), + Color::new(1.0, 1.0, 1.0), Color::new(0.0, 0.0, 0.0), 0.0, - 0.6, + 0.5, ), + Material::new( + Color::new(0.0, 0.0, 0.0), + Color::new(0.0, 0.0, 0.0), + Color::new(0.0, 0.0, 0.0), + 0.0, + 0.5, + ), + 0.3, + Vector3::new(0.0, 0.0, 1.0), ))); scene.add_object(Arc::new(Sphere::new( @@ -76,14 +85,14 @@ fn main() { Point3::new(-1.0, -0.5, 0.0), Vector3::new(0.0, 1.0, 0.0), 60.0, - 4 * 256, - 3 * 256, + 4 * 512, + 3 * 512, ); let fname = "demo-scene.png"; print!("Rendering demo scene..."); let start = Instant::now(); - match camera.render(&scene, 5, 3).save(fname) { + match camera.render(&scene, 10, 4).save(fname) { Ok(_) => { println!(" finished ({}s) ", start.elapsed().as_secs_f32()); println!("Image saved to {}", fname) diff --git a/src/raytracer/plane.rs b/src/raytracer/plane.rs index 5501663..90e05fa 100644 --- a/src/raytracer/plane.rs +++ b/src/raytracer/plane.rs @@ -1,4 +1,4 @@ -use super::types::{Intersect, Material, Point3, Vector3}; +use super::types::{Intersect, Material, Point3, Scalar, Vector3}; extern crate nalgebra as na; @@ -12,6 +12,18 @@ pub struct Plane { material: Material, } +/// A infinite checkerboard plane in 3D space. +pub struct Checkerboard { + /// The base plane containing the "white" material + base: Plane, + /// An alternative "black" material + material_alt: Material, + /// The scale of the checkerboard (side-length of each square) + scale: f64, + /// A projection matrix to map 3D points to the 2D plane space. + projection_matrix: na::Matrix2x3, +} + impl Plane { /// Create a new plane. /// - `position` is the position of the plane. @@ -26,6 +38,32 @@ impl Plane { } } +impl Checkerboard { + /// Create a new Checkerboard Plane. + /// - `position` is the position of the plane. + /// - `normal` is the normal of the plane. + /// - `material1` is the material of the "white" squares. + /// - `material2` is the material of the "black" squares. + /// - `scale` is the side-length of each square. + /// - `up` is "y" direction on the plane in 3D-Space. + pub fn new( + position: Point3, + normal: Vector3, + material1: Material, + material2: Material, + scale: f64, + up: Vector3, + ) -> Checkerboard { + let right = up.cross(&normal).normalize(); + Checkerboard { + base: Plane::new(position, normal, material1), + material_alt: material2, + scale, + projection_matrix: na::Matrix3x2::from_columns(&[right, up]).transpose(), + } + } +} + impl Intersect for Plane { fn intersect<'a>( &'a self, @@ -49,3 +87,30 @@ impl Intersect for Plane { None } } + +impl Intersect for Checkerboard { + fn intersect<'a>( + &'a self, + ray: &super::types::Ray, + ) -> Option<( + Point3, + Vector3, + super::types::Scalar, + &'a super::types::Material, + )> { + if let Some((point, normal, t, material)) = self.base.intersect(ray) { + let v3 = point - self.base.position; + let v2 = self.projection_matrix * v3; + + if ((v2.x / self.scale).round() % 2.0 == 0.0) + == ((v2.y / self.scale).round() % 2.0 == 0.0) + { + Some((point, normal, t, material)) + } else { + Some((point, normal, t, &self.material_alt)) + } + } else { + None + } + } +}